tornado 异步高并发

Tornado

简而言之,Tornado的异步包括两个方面,异步服务端异步客户端。无论服务端和客户端,具体的异步模型又可以分为回调(callback)和协程(coroutine)。具体应用场景,也没有很明确的界限。往往一个请求服务里还包含对别的服务的客户端异步请求。

服务端异步方式

服务端异步,可以理解为一个tornado请求之内,需要做一个耗时的任务。直接写在业务逻辑里可能会block整个服务。因此可以把这个任务放到异步处理,实现异步的方式就有两种,一种是yield挂起函数,另外一种就是使用类线程池的方式。

具体实现测试code示例:

import os
import tornado
import tornado.ioloop
import tornado.web
import requests
from concurrent.futures import ThreadPoolExecutor


class Executor(ThreadPoolExecutor):
    """ 创建多线程的线程池,线程池的大小为10
    创建多线程时使用了单例模式,如果Executor的_instance实例已经被创建,
    则不再创建,单例模式的好处在此不做讲解
    """
    _instance = None

    def __new__(cls, *args, **kwargs):
        if not getattr(cls, '_instance', None):
            cls._instance = ThreadPoolExecutor(max_workers=10)
        return cls._instance


# 全部协程+异步线程池实现,yield在此的作用相当于回调函数
# 经过压力测试发现,此种方式的性能在并发量比较大的情况下,要远远优于纯协程实现方案
class Haha1Handler(tornado.web.RequestHandler):
    """ 获取域名所关联的IP信息 """
    # executor为RequestHandler中的一个属性,在使用run_on_executor时,必须要用,不然会报错
    # executor在此设计中为设计模式中的享元模式,所有的对象共享executor的值
    executor = Executor()

    @tornado.web.asynchronous  # 异步处理
    @tornado.gen.coroutine  # 使用协程调度
    def get(self):
        """ get 接口封装 """

        # 可以同时获取POST和GET请求参数
        value = self.get_argument("value", default=None)

        result = yield self._process(value)
        self.write(result)

    @tornado.concurrent.run_on_executor  # 增加并发量
    def _process(self, url):
        # 此处执行具体的任务
        try:
            resp = requests.get(url)
        except IOError as e:
            print(e)
            return 'failed'

        return 'success'


# 全部协程实现
class Haha2Handler(tornado.web.RequestHandler):
    """ 获取域名所关联的IP信息 """

    @tornado.web.asynchronous
    @tornado.gen.coroutine
    def get(self):
        """ get 接口封装 """

        # 可以同时获取POST和GET请求参数
        value = self.get_argument("value", default=None)

        result = yield tornado.gen.Task(self._process, value)
        self.write(result)

    @tornado.gen.coroutine  # 使用协程调度
    def _process(self, url):
        # 此处执行具体的任务
        try:
            resp = requests.get(url)
        except IOError as e:
            print(e)
            return 'failed'

        return 'success'


class WebServerApplication(object):
    def __init__(self, port):
        self.port = port
        self.settings = {'debug': False}

    def make_app(self):
        """ 构建Handler
        (): 一个括号内为一个Handler
        """

        return tornado.web.Application([
            (r"/gethaha1?", Haha1Handler),
            (r"/gethaha2?", Haha2Handler),
        ], ** self.settings)

    def process(self):
        """ 构建app, 监听post, 启动服务 """

        app = self.make_app()
        app.listen(self.port)
        tornado.ioloop.IOLoop.current().start()


if __name__ == "__main__":
    # 定义服务端口
    server_port = "10001"
    server = WebServerApplication(server_port)
    server.process()

回调函数写在代码里面看着比较混乱,现在python3.6出了最新的替代方案就是使用yield代替,实现阻塞挂起的功能,使代码看上起更像是同步的代码,同时此方案在协程中被采纳;回调函数实现的异步再次没有实现,想要实现的可以查阅相关资料实现一下。

协程的出现主要是为了解决IO阻塞问题,提升CPU的利用率,增加单位时间的并发量;tornado实质是一个单线程模型,所以这个并发量还是很受全局解释锁GIL的限制的,为了改变这一现状,tornado框架引入协程模块@tornado.gen.coroutine,只要被该装饰器修饰的方法, 执行就会以协程的方式执行;一些提升性能的模块大部分被封装在@tornado.gen中,此模块值得好好研究

 如果想测试自己搭建的API性能,可以采用ApacheBench带有的免费测试工具来测试,此工具使用很简易,想要了解的请查找的我的别的博客,有详细讲解

使用方式列举:

# ab -n500 -c100  http://127.0.0.1:10001/gethaha1?value=https://www.tornadoweb.org/en/stable/ioloop.html

此博客讲解不错,值得借鉴,但是使用异步多线程的时候程序有报错 

猜你喜欢

转载自blog.csdn.net/u012089823/article/details/88244884
今日推荐