参考:http://www.nowamagic.net/academy/detail/13321002html
Tornado就是咱们在FriendFeed的Web服务器及其经常使用工具的开源版本。Tornado和如今的主流Web服务器框架(包括大多数Python的框架)有着明显的区别:它是非阻塞式服务器,并且速度至关快。它还实现了一个一样简洁的模板框架。Tornado在本身的异步网络访问框架之上完整地实现了HTTP服务器,这也是它的核心框架的重要部分。python
我以为对非阻塞IO (nonblocking IO)或异步IO (asynchronous IO, AIO)颇有必要谈一谈。若是你已经彻底知道他们是什么了,能够跳去看下一节。我尽量的使用一些例子来讲明它们是什么。ios
让咱们假设你正在写一个须要请求一些来自其余服务器上的数据(好比数据库服务,再好比新浪微博的open api)的应用程序,而后呢这些请求将花费一个比较长的时间,假设须要花费5秒钟。大多数的web开发框架中处理请求的代码大概长这样:web
def handler_request(self, request): answ = self.remote_server.query(request) # this takes 5 seconds request.write_response(answ)
若是这些代码运行在单个线程中,你的服务器只能每5秒接收一个客户端的请求。在这5秒钟的时间里,服务器不能干其余任何事情,因此,你的服务效率是每秒0.2个请求,哦,这太糟糕了。数据库
固然,没人那么天真,大部分服务器会使用多线程技术来让服务器一次接收多个客户端的请求,咱们假设你有20个线程,你将在性能上得到20倍的提升,因此如今你的服务器效率是每秒接受4个请求,但这仍是过低了,固然,你能够经过不断地提升线程的数量来解决这个问题,可是,线程在内存和调度方面的开销是昂贵的,我怀疑若是你使用这种提升线程数量的方式将永远不可能达到每秒100个请求的效率。设计模式
若是使用AIO,达到每秒上千个请求的效率是很是轻松的事情。服务器请求处理的代码将被改为这样:api
def handler_request(self, request): self.remote_server.query_async(request, self.response_received) def response_received(self, request, answ): # this is called 5 seconds later request.write(answ)
AIO的思想是当咱们在等待结果的时候不阻塞,转而咱们给框架一个回调函数做为参数,让框架在有结果的时候经过回调函数通知咱们。这样,服务器就能够被解放去接受其余客户端的请求了。浏览器
然而这也是AIO不太好的地方:代码有点不直观了。还有,若是你使用像Tornado这样的单线程AIO服务器软件,你须要时刻当心不要去阻塞什么,由于全部本该在当前返回的请求都会像上述处理那样被延迟返回。服务器
关于异步IO,比当前这篇过度简单的介绍更好的学习资料请看 The C10K problem 。websocket
在深刻到模块进行分析以前,首先来看看Tornado的设计模型。
Tornado框架设计模型
Tornado不只仅是一个WEB框架,它还完整地实现了HTTP服务器和客户端,在此基础上提供WEB服务。它能够分为四层:最底层的EVENT层处理IO事件;TCP层实现了TCP服务器,负责数据传输;HTTP/HTTPS层基于HTTP协议实现了HTTP服务器和客户端;最上层为WEB框架,包含了处理器、模板、数据库链接、认证、本地化等等WEB框架须要具有的功能。
为了方便,约定$root指带tornado的根目录。总的来讲,要用 Tornado 完成一个网站的构建,其实主要须要如下几个文件:
另外可能还须要一些功能库的支持而须要引入的文件就不列举了,好比util和httputil之类的。来看看每一个文件的做用。
Use this whenever saving a callback to be executed later in a different execution context (either in a different thread or asynchronously in the same thread).
tornado网络部分最核心的两个模块就是ioloop.py与iostream.py,咱们主要分析的就是这两个部分。
咱们先来看看 ioloop(文档地址:http://www.tornadoweb.org/en/stable/ioloop.html):
We use epoll (Linux) or kqueue (BSD and Mac OS X) if they are available, or else we fall back on select(). If you are implementing a system that needs to handle thousands of simultaneous connections, you should use a system that supports either epoll or kqueue.
程序中主函数一般调用 tornado.ioloop.IOLoop.instance().start() 来启动IOLoop,可是看了一下 IOLoop 的实现,start 方法是这样的:
def start(self): """Starts the I/O loop. The loop will run until one of the callbacks calls `stop()`, which will make the loop stop after the current event iteration completes. """ raise NotImplementedError()
也就是说 IOLoop 是个抽象的基类,具体工做是由它的子类负责的。若是是 Linux 平台,因此应该用 Epoll,对应的类是 PollIOLoop。PollIOLoop 的 start 方法开始了事件循环。
问题来了,tornado.ioloop.IOLoop.instance() 是怎么返回 PollIOLoop 实例的呢?刚开始有点想不明白,后来看了一下 IOLoop 的代码就豁然开朗了。
class IOLoop(Configurable):
IOLoop 继承自 Configurable,后者位于 tornado/util.py。
A configurable interface is an (abstract) class whose constructor acts as a factory function for one of its implementation subclasses. The implementation subclass as well as optional keyword arguments to its initializer can be set globally at runtime with configure.
Configurable 类实现了一个工厂方法,也就是设计模式中的“工厂模式”,看一下__new__函数的实现:
def __new__(cls, **kwargs): base = cls.configurable_base() args = {} if cls is base: impl = cls.configured_class() if base.__impl_kwargs: args.update(base.__impl_kwargs) else: impl = cls args.update(kwargs) instance = super(Configurable, cls).__new__(impl) # initialize vs __init__ chosen for compatiblity with AsyncHTTPClient # singleton magic. If we get rid of that we can switch to __init__ # here too. instance.initialize(**args) return instance
当建立一个Configurable类的实例的时候,其实建立的是configurable_class()返回的类的实例。
@classmethod def configured_class(cls): """Returns the currently configured class.""" base = cls.configurable_base() if cls.__impl_class is None: base.__impl_class = cls.configurable_default() return base.__impl_class
最后,就是返回的configurable_default()。此函数在IOLoop中的实现以下:
@classmethod def configurable_default(cls): if hasattr(select, "epoll"): from tornado.platform.epoll import EPollIOLoop return EPollIOLoop if hasattr(select, "kqueue"): # Python 2.6+ on BSD or Mac from tornado.platform.kqueue import KQueueIOLoop return KQueueIOLoop from tornado.platform.select import SelectIOLoop return SelectIOLoop
EPollIOLoop 是 PollIOLoop 的子类。至此,这个流程就理清楚了。
未完,待续。。。。。。。。。。。。。。。。。。。。。。