【python 异步框架tornado】Tornado原理理解及应用场景

无意中发现了一个巨牛的人工智能教程,忍不住分享一下给大家。教程不仅是零基础,通俗易懂,而且非常风趣幽默,像看小说一样!觉得太牛了,所以分享给大家。点这里可以跳转到教程。人工智能教程

一、应用场景理解
相信大家学习python,有听过flask、django、tornado 这些web框架。那么这些漂亮的框架的应用场景是什么呢。
要性能,Tornado 首选;要开发速度,Django 和Flask 都行,
区别是Flask 把许多功能交给第三方库去完成了,因此Flask 更为灵活。Django适合初学者或者小团队的快速开发,适合做管理类、博客类网站、或者功能十分复杂需求十分多的网站,Tornado适合高度定制,适合访问量大,异步情况多的网站。也可以用于定制api服务。

二、原理理解

2.1 了解下什么是WSGI协议

举个简单易理解的例子:

  1. 中国有三家有名的通信运营商,分别是移动、联通和电信,这三家通信商的手机号是可以跨平台拨打的,假设三家通信商负责通信的协议不同且无法互通,用移动的手机号就无法给联通电信的手机打电话,为了方便通信就需要一个统一的规范。WSGI协议的角色就是这个统一的规范,是描述webserver如何与web application通信的规范,要实现WSGI协议,就必须同时实现web server和webapplication,目前常见的有Tornado、Flask和Django。
  2. WSGI是个同步模型,不支持非阻塞的请求方式,Tornado默认是不推荐使用WSGI的,如果在Tornado中使用WSGI,将无法使用Tornado的异步非阻塞的处理方式,相应的异步接口也就无法使用,性能方面也就大打折扣,这个也是Tornado性能如此优越的原因。
  3. django这类框架,采用WSGI协议与服务器对接的,而这类服务器通常是基于多线程/多进程的,也就是说每有一个网络请求,服务器都会有一个线程/进程进行处理。

2.2、了解下什么是c10k问题

考虑两种高并发场景:

  1. 用户量大,高并发。如秒杀抢购,双十一,618和春节抢票。
  2. 大量的HTTP持久连接。

对于以上两种熟悉的场景,通常基于多进程/线程的服务器是很难应付的。
基于上述高并发场景,引出了C10k问题,是由一名叫DanKegel的软件工程师提出的,即当同时的连接数以万计的时候,服务器性能会出现急剧下降甚至直接崩溃的情况,这就是著名的C10k问题。

值得一提的,腾讯QQ就遇到过C10k问题,当时采用了udp的方式避开了这个问题,当然过程是相当痛苦的,后来也就专用了tcp,主要是当时还没有epoll技术。

简要地分析下C10k的本质问题,其实无外乎是操作系的问题,连接多了,创建的进程线程就多了,数据拷贝也就变得频繁(缓存I/O、内核将数据拷贝到用户进程空间、阻塞),进程/线程上下文切换消耗又大,直接就导致操作系统奔溃。

如何解决c10k问题:

解决一、对于每个连接处理分配一个独立的进程/线程。提升单台机器的能力,尽可能多提供进程/线程,一台机器不够就增加多台机器。

解决二、用一个进程/线程来同时处理若干个连接。


针对方法一,假设每台机器都达到了一万连接,同时有一亿个请求,那么就需要一万台机器,所以这种解决方法不太实际。

针对方法二,需要有新的技术支持这种方案,实际上是可行的,也是现在普遍采取的方法,针对这种方法,tornado采用epoll技术。

2.3、tornado是如何解决c10k问题

Tornado采用了epoll技术,通过这种技术也就解决了著名的C10k问题,实现了用一个进程/线程来同时处理若干个连接的想法,减少了硬件资源的浪费。

2.4、tornado代码例子理解

接着上一篇文章10分钟学习下Tornado,我们学习了第一个demo:


# -*- encoding=utf-8 -*-
import tornado.ioloop
import tornado.web

class IndexHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("Hello, world")

def make_app():
    return tornado.web.Application([(r"/", IndexHandler),])

if __name__ == "__main__":
    app = make_app()
    app.listen(8888)
    tornado.ioloop.IOLoop.current().start()

如果你经常用Tornado,那么对这段代码一定非常熟悉了,那么我们今天的关注点就放在最后一句Torando.ioloop.IOLoop.current().start()代码上,先简单的分析下这句代码,前面一部分Torando.ioloop是Tornado的核心模块ioloop模块,IOLoop是ioloop模块的一个类,current()是IOLoop类的一个方法,结果是返回一个当前线程的IOLoop的实例,start()也是IOLoop的方法,调用后开启循环。

在这里插入图片描述

然后我们分析下Tornado这段代码后的整个逻辑流程:

  1. 首先Tornado需要建立监听,会创建一个socket用于监听,如果有客户端A请求建立连接之后,Tornado会基于原先的socket新创建一个包含客户端A连接的有关信息的socket(分配新的监听端口),用于监听和客户端A的请求。此时对Tornado来说就有两个socket需要进行监控,原先的socket继续用来监听建立新连接,新的socket用于和客户端A进行通信,假如没有epoll技术的话,Tornado需要自己去循环询问哪个socket有新的请求。

  2. 有了epoll技术,Tornado只需要把所有的socket丢给epoll,epoll作为管家帮忙监控,然后Torando.ioloop.IOLoop.current().start()开启循环,不断的去询问epoll是否有请求需要处理,这就是ioloop所做的工作,也是Tornado的核心部分。

  3. 当有客户端进行请求,epoll就发现有socket可处理,当ioloop再次询问epoll时,epoll就把需要处理的socket交由Tornado处理

  4. Tornado对请求进行处理,取出报文,从报文中获取请求路径,然后从tornado.web.Applcation里配置的路由映射中把请求路径映射成对应的处理类,如上图IndexHandler就是处理类。

  5. 处理类处理完成后,生成响应,将响应内容封装成http报文,通过请求时建立的连接(尚未中断)将响应内容返回给客户端。

  6. 当有多个请求同时发生,Tornado会按顺序挨个处理。

看了上面的流程,假如Tornado在处理一个非常耗时的请求时,后面的请求是不是就会被卡死呢?答案是肯定的,所以提到了Tornado的另一个特性—异步处理,当一个请求特别耗时,Tornado就把它丢在那处理,然后继续处理下一个请求,确保后面的请求不会被卡死。

Tornado异步:原生Tornado框架提供异步网络库IOLoop和IOStream以及异步协程库tornado.gen(必须使用Tornado的web框架和HTTP服务器,否则异步接口可能无法使用),方便用户通过更直接的方法实现异步编程,而不是回调的方式,官方推荐yield协程方式完成异步。(异步是Tornado重要且核心部分,期待下篇技术好文重点介绍)。

通过上面所讲,基本上已经对Tornado的整个处理流程了解了,总结一下Tornado之所以能同时处理大量连接的原因:

  1. 利用高效的epoll技术处理请求,单线程/单进程同时处理大量连接。

  2. 没用使用传统的wsgi协议,而是利用Tornado自己的web框架和http服务形成了一整套WSGI方案进行处理。

  3. 异步处理方式,Tornado提供了异步接口可供调用。

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页