• tornado


    阻塞式IO框架 (Django、Flask)

    大多数的web框架都是阻塞式的,如果一个请求到达服务端且为处理完该请求,后序请求会一致等待。

    常用的解决方案:开启多线程/多进程,提高并发。 但是比较浪费系统资源

    tornado多进程模式(仅linux):

    import tornado.ioloop
    import time
    import tornado.web
    from tornado.httpserver import HTTPServer
    
    class IndexHadlar(tornado.web.RequestHandler):
        def get(self):
            print('请求开始')
            time.sleep(10)
            self.write('hello,world ')
            print("请求结束")
    application=tornado.web.Application([
        (r'/index',IndexHadlar)
    ])
    
    
    if __name__ == '__main__':
        # 单线程模式
        # application.listen(8888)
        # tornado.ioloop.IOLoop.instance().start()
        
        # 多线程模式
        server=HTTPServer(application)
        server.bind(8888)
        server.start(3) #开启4个进程
        tornado.ioloop.IOLoop.instance().start()
    

    Tornado异步非阻塞

    异步非阻塞就是在服务端结合IO多路复用select/poll/epoll模板,做到1个线程在遇到IO操作的情况下,还可以做一些其他的任务(协程化);Tornado默认是阻塞的同时也支持异步非阻塞功能。

    处理流程:

    1.客户端发送请求如果请求内容不涉及IO操作(连接数据、还得去其他网站获取内容)服务端直接响应客户端;

    2.如果请求内容涉及IO操作,服务端把本次连接的socket信息添加到socket监听列表epoll中监听起来;

    然后去连接其它socket(数据库、其它站点)由于是不阻塞的所以服务端把这次发送socket信息也监听起来;(一直循环监听epoll,直到socket监听列表中的socket发生变化)

    3.把socket全部监听之后,就可以去继续接收其它请求了,如果检测到socket监听列表中的socket有变化(有数据返回),找到对应socket响应数据,并从socket监听列表中剔除;

    小结:

    Tornado的异步非阻塞,本质上是请求到达视图 1、先yield 一个Future对象 2、 IO多路复用模块把该socket添加到监听列表epoll循环监听起来;3、 循环监听过程中哪1个socket发生变化有response,执行 Future.set_result(response),请求至此返回结束,否则socket连接一直不断开,IO多路复用模块epoll一直循环监听socket是否发生变化。

    非阻塞模式:

    当发送GET请求时,由于方法被@gen.coroutine装饰且yield 一个 Future对象,那么Tornado会等待,等待用户向Future对象中放置数据或者发送信号,如果获取到数据或信号之后,就开始执行doing方法。

    异步非阻塞体现在当在Tornaod等待用户向Future对象中放置数据时,还可以处理其他请求。

    注意:在等待用户向Future对象中放置数据或信号时,此连接是不断开的。

    import tornado.ioloop
    import time
    import tornado.web
    from tornado import gen             #导入
    from tornado.concurrent import Future
    
    class IndexHadlar(tornado.web.RequestHandler):
        
        @gen.coroutine	# coroutine(协程装饰器)
        def get(self):
            print('请求开始')
            future=Future()
            tornado.ioloop.IOLoop.current().add_timeout(time.time()+10,self.doing)
            yield future #yield 1个future对象,IO之后自动切换到doing方法执行;
    
        def doing(self):
            self.write('请求完成')
            self.finish()           #关闭连接
    
    
    application=tornado.web.Application([
        (r'/index/',IndexHadlar)
    ])
    
    
    if __name__ == '__main__':
        # 单进程模式
        application.listen(8888)
        tornado.ioloop.IOLoop.instance().start()
    

    Tornado httpclient:

    如果服务端接受到客户端的请求,需要去其他API获取数据,再响应给客户端,这就涉及到了IO操作,Tornado提供了httpclient类库用于发送Http请求,其配合Tornado的异步非阻塞使用。

    import tornado.web
    from tornado import gen
    from tornado import httpclient
    
    class AsyncHandler(RequestHandler):
        
        @gen.coroutine	# 协程
        def get(self):
            print('请求开始')
            http = httpclient.AsyncHTTPClient()
            
            yield http.fetch('https://baidu.com',self.done)	# 获取到这个地址的数据或者信号后,就开始执行done方法
    
        def done(self,respose,*args,**kwargs):
            # print(respose)	# 获取yield的结果
            self.write(respose.body)
            self.finish()
    
    
    application = tornado.web.Application([
        (r"/zhanggen/", AsyncHandler),
    ])
    
    if __name__ == '__main__':
        application.listen(8888)
        tornado.ioloop.IOLoop.instance().start()
    
    

    使用异步对象处理耗时操作,比如上面的AsyncHTTPClient。

    调用yield关键字获取异步对象的处理结果,self.done是一个callback回调函数。

    异步非阻塞体现在当在Tornaod等待请求的数据返回时,还可以处理其他请求。

    Tornado-MySQL类库

    如果服务端接收到客户端请求,需要连接数据库再把查询的结果响应客户端,这个过程中连接数据、发送查询SQL、接收数据库返回结果 都会遇到IO阻塞、耗时的问题,所以Tornado提供了Tornado-MySQL模块(对PyMySQL进行二次封装),让我们在使用数据库的时候也可以做到异步非阻塞。

    方式一:

    需要对每个IO操作分别yeild,操作起来比较繁琐。

    # yield cur.execute("SELECT name,email FROM web_models_userprofile where name=%s", (user,))
    

    方式二:可以通过task的方式把IO操作封装到函数中统一进行异步处理(无论什么方式本质都会yelid 1个Future对象);

    """
    需要先安装支持异步操作Mysql的类库:
        Tornado-MySQL: https://github.com/PyMySQL/Tornado-MySQL#installation
    
        pip3 install Tornado-MySQL
    
    """
    
    import tornado.web
    from tornado import gen
    
    import tornado_mysql
    from tornado_mysql import pools
    
    POOL = pools.Pool(
        dict(host='127.0.0.1', port=3306, user='root', passwd='123', db='cmdb'),
        max_idle_connections=1,
        max_recycle_sec=3)
    
    @gen.coroutine
    def get_user_by_conn_pool(user):
        cur = yield POOL.execute("SELECT SLEEP(%s)", (user,))
        row = cur.fetchone()
        raise gen.Return(row)
    
    @gen.coroutine
    def get_user(user):
        conn = yield tornado_mysql.connect(host='127.0.0.1', port=3306, user='root', passwd='123', db='cmdb',charset='utf8')
        cur = conn.cursor()
        # yield cur.execute("SELECT name,email FROM web_models_userprofile where name=%s", (user,))
        yield cur.execute("select sleep(10)")
        row = cur.fetchone()
        cur.close()
        conn.close()
        raise gen.Return(row)
    
    
    class LoginHandler(tornado.web.RequestHandler):
        def get(self, *args, **kwargs):
            self.render('login.html')
    
        @gen.coroutine
        def post(self, *args, **kwargs):
            user = self.get_argument('user')
            data = yield gen.Task(get_user, user)  #把函数添加任务
            if data:
                print(data)
                self.redirect('http://www.xxx.com')
            else:
                self.render('login.html')
    
    
    application = tornado.web.Application([
        (r"/login", LoginHandler),
    ])
    
    if __name__ == "__main__":
        application.listen(8888)
        tornado.ioloop.IOLoop.instance().start()
    
  • 相关阅读:
    Java判断一个字符是数字或字母
    java数组和字符串相互转换
    java 字符串截取的三种方法
    Templates && Algorithms
    挖坑——未完成题目列表QwQ
    作业_2018.08.25
    BZOJ1008 [HNOI2008]越狱 (快速幂,组合)
    UR #3 核聚变反应强度( gcd )
    A Super Hero
    NOIP2015 pj
  • 原文地址:https://www.cnblogs.com/yzm1017/p/13570770.html
Copyright © 2020-2023  润新知