Tornado是一款轻量级的Web服务器,同时又是一个开发框架。采用单线程非阻塞I/O模型(epoll),主要是为了应对高并发 访问量而被开发出来,尤其适用于comet应用。

Tornado服务器3大核心模块:

(1) IOLoop

Tornado为了实现高并发和高性能,使用了一个IOLoop来处理socket的读写事件,IOLoop基于epoll,可以高效的响应网络事件。这是Tornado高效的保证。

tornado.ioloop.IOLoop.instance().start()

IOLoop使用了单例模式,处理所有IO事件,

实现为EPollIOLoop->PollIOLoop->IOLoop->Configurable

IOLoop中有四个重要的数据集: _events 和 _handlers 保存I/O事件和对应的处理器, _callbacks 和 _timeouts 保存(超时)回调。

关键函数:

def initialize(self, impl, time_func=None):
super(PollIOLoop, self).initialize()
self._impl = impl
if hasattr(self._impl, 'fileno'):
set_close_exec(self._impl.fileno())
self.time_func = time_func or time.time
#handlers 是一个函数集字典
self._handlers = {}
self._events = {}
#回调函数集合
self._callbacks = []
self._callback_lock = threading.Lock()
self._timeouts = []
self._cancellations = 0
self._running = False
self._stopped = False
self._closing = False
self._thread_ident = None
self._blocking_signal_threshold = None
self._timeout_counter = itertools.count() # Create a pipe that we send bogus data to when we want to wake
# the I/O loop when it is idle
self._waker = Waker()
self.add_handler(self._waker.fileno(),
lambda fd, events: self._waker.consume(),
self.READ)

其中,waker是一个发伪数据用的类,在需要时,我们可以用它唤醒空闲的I/O Loop。当我们调用add_callback时,为了让回调函数运行,可能会需要使用它发送一个伪数据。

#将文件描述符发生相应的事件时的回调函数对应
def add_handler(self, fd, handler, events):
"""Registers the given handler to receive the given events for fd."""
self._handlers[fd] = stack_context.wrap(handler)
#在 epoll 中注册对应事件
#epoll_ctl
self._impl.register(fd, events | self.ERROR)

其中stack_context.wrap()对handler进行封装,封装后记录了上下文信息。而_impl是对epoll的封装。

所以,只要把所有事件在IOLoop中进行注册,运行start函数后,就会进入进程的监听循环,循环监听所有的fd,并调用fd对应的handler。循环过程参考start()函数。

def start(self):
while True:
with self._callback_lock:
callbacks = self._callbacks
self._callbacks = []
#运行所有callback
for callback in callbacks:
self._run_callback(callback)
#取事件
event_pairs = self._impl.poll(poll_timeout)
self._events.update(event_pairs)
while self._events:
fd, events = self._events.popitem()
try:
#调用事件handler
fd_obj, handler_func = self._handlers[fd]
handler_func(fd_obj, events)
except (OSError, IOError) as e:
if errno_from_exception(e) == errno.EPIPE:
# Happens when the client closes the connection
pass
else:
self.handle_callback_exception(self._handlers.get(fd))
except Exception:
self.handle_callback_exception(self._handlers.get(fd))

当poll中发现fp有read事件时,会调用对应的callback方法。如果fd是监听的fd,那么这个回调handler就是accept_handler函数(见下面HttpConnection的bind和add_scokets函数)。该方法会Accept连接并且紧跟着创建IOStream对象,read_until方法读完数据后,则调用_run_callback把处理函数(self._header_callback)加到IOLoop中,等到下次轮询时在最前面处理。

(2) IOStream

为了在处理请求的时候,实现对socket的异步读写, Tornado实现了IOStream类,用来处理socket的异步读写,负责异步通讯。

主要包括3个函数,

1.read_bytes(bytes,callback)在有固定的字节的数据到来的时候调用回调函数

2.read_until(delimiter,callback)在读取到固定的字符序列结尾后调用回调函数

3.write(data):异步写

(3) HTTPConnection

这个类用来处理http的请求, 包括读取http请求头, 读取post过来的数据,调用用户自定义的处理方法。以及把响应数据写给客户端socket。

def bind(self, port, address=None, family=socket.AF_UNSPEC, backlog=128):
sockets = bind_sockets(port, address=address, family=family,backlog=backlog)
if self._started:
self.add_sockets(sockets)
else:
self._pending_sockets.extend(sockets)
def add_sockets(self, sockets):
if self.io_loop is None:
self.io_loop = IOLoop.current()
for sock in sockets:
self._sockets[sock.fileno()] = sock
add_accept_handler(sock, self._handle_connection,io_loop=self.io_loop)

socket启动后,监听各个sockets,事件到来时,调用_handle_connection。

def _handle_connection(self, connection, address):
if self.ssl_options is not None:
connection = ssl_wrap_socket(connection,self.ssl_options,
server_side=True,
do_handshake_on_connect=False)
if self.ssl_options is not None:
stream = SSLIOStream(connection, io_loop=self.io_loop,
max_buffer_size=self.max_buffer_size,
read_chunk_size=self.read_chunk_size)
else:
stream = IOStream(connection, io_loop=self.io_loop,
max_buffer_size=self.max_buffer_size,
read_chunk_size=self.read_chunk_size)
self.handle_stream(stream, address)
def handle_stream(self, stream, address):
context = _HTTPRequestContext(stream, address,
self.protocol)
conn = HTTP1ServerConnection(
stream, self.conn_params, context)
self._connections.add(conn)
conn.start_serving(self)
def start_serving(self, delegate):
assert isinstance(delegate, httputil.HTTPServerConnectionDelegate)
self._serving_future = self._server_request_loop(delegate)
# Register the future on the IOLoop so its errors get logged.
self.stream.io_loop.add_future(self._serving_future,
lambda f: f.result())

如前面所述,这里Accept连接并且紧跟着创建IOStream对象(不考虑https),调用handle_stream->start_serving->_server_request_loop处理请求。最后会调用_read_message读取数据,并注册回调函数。

最后抄一张图过来:

参考:

http://www.cnblogs.com/Bozh/archive/2012/07/22/2603976.html

http://kenby.iteye.com/blog/1159621

http://www.nowamagic.net/academy/detail/13321030

http://www.yeolar.com/note/2013/02/09/tornado-async-networking/

源码:

https://github.com/tornadoweb/tornado

Tornado框架简析的更多相关文章

  1. PHP单一文件入口框架简析

    <?php /** * PHP单一文件框架设计简析 * 1.MVC架构实现 * 2.URL路由原理 */ //URL路由原理 /** * 路由作用 * 获取url中的c和a变量,执行c类对应的方 ...

  2. JDK框架简析--java.lang包中的基础类库、基础数据类型

    题记 JDK.Java Development Kit. 我们必须先认识到,JDK不过,不过一套Java基础类库而已,是Sun公司开发的基础类库,仅此而已,JDK本身和我们自行书写总结的类库,从技术含 ...

  3. Linux驱动之USB总线驱动程序框架简析

    通用串行总线(USB)是主机和外围设备之间的一种连接.USB总线规范有1.1版和2.0版,当然现在已经有了3.0版本.USB1.1支持两种传输速度:低速为1.5Mbps,高速为12Mbps.USB2. ...

  4. 简析.NET Core 以及与 .NET Framework的关系

    简析.NET Core 以及与 .NET Framework的关系 一 .NET 的 Framework 们 二 .NET Core的到来 1. Runtime 2. Unified BCL 3. W ...

  5. 简析 .NET Core 构成体系

    简析 .NET Core 构成体系 Roslyn 编译器 RyuJIT 编译器 CoreCLR & CoreRT CoreFX(.NET Core Libraries) .NET Core 代 ...

  6. Java Annotation 及几个常用开源项目注解原理简析

    PDF 版: Java Annotation.pdf, PPT 版:Java Annotation.pptx, Keynote 版:Java Annotation.key 一.Annotation 示 ...

  7. [转载] Thrift原理简析(JAVA)

    转载自http://shift-alt-ctrl.iteye.com/blog/1987416 Apache Thrift是一个跨语言的服务框架,本质上为RPC,同时具有序列化.发序列化机制:当我们开 ...

  8. SpringMVC源码情操陶冶-DispatcherServlet父类简析

    阅读源码有助于陶冶情操,本文对springmvc作个简单的向导 springmvc-web.xml配置 <servlet> <servlet-name>dispatch< ...

  9. 简析 __init__、__new__、__call__ 方法

    简析 __init__.__new__.__call__ 方法 任何事物都有一个从创建,被使用,再到消亡的过程,在程序语言面向对象编程模型中,对象也有相似的命运:创建.初始化.使 用.垃圾回收,不同的 ...

随机推荐

  1. Oracle客户端简易连接报错ORA-12154,TNS-03505

    环境: 服务端:RHEL6.5 + Oracle Server 11.2.0.4 客户端:Win2003 + Oracle Client 10.2.0.1 1.问题现象 2.Troubleshooti ...

  2. OpenCV,计算两幅图像的单应矩阵

    平面射影变换是关于其次3维矢量的一种线性变换,可以使用一个非奇异的$3 \times 3$矩阵H表示,$X' = HX$,射影变换也叫做单应(Homography).计算出两幅图像之间的单应矩阵H,那 ...

  3. linux源码分析(三)-start_kernel

    前置:这里使用的linux版本是4.8,x86体系. start_kernel是过了引导阶段,进入到了内核启动阶段的入口.函数在init/main.c中. set_task_stack_end_mag ...

  4. js的stopPropagation()、cancelBubble、preventDefault()、return false的分析

    个人笔记,如有错误,望指出. 事件冒泡,举个列子: <li> <a href='http://www.baidu.com'>点击a</a> </li> ...

  5. 在Winform程序中设置管理员权限及为用户组添加写入权限

    在我们一些Winform程序中,往往需要具有一些特殊的权限才能操作系统文件,我们可以设置运行程序具有管理员权限或者设置运行程序的目录具有写入的权限,如果是在操作系统里面,我们可以设置运行程序以管理员身 ...

  6. ArcGIS标注

    关于ArcGIS的标注问题整理. 要求: 保留小数点后三位, 若原数据小于1(.12345),对小数加0显示 分行显示两个字段值 解决: 右击图层 -- ”properties“(属性) 中选择“la ...

  7. java反编译获取源码

    最近在研究反射,想做一个东西,把运行的java程序饭编译(Decompile)成.java文件.现思路如下: 1.写出程序反编译一个类 2.将所有类反编译 3.java代码注入一个正在运行的java程 ...

  8. 设计模式-装饰器模式(Decrator Model)

    文 / vincentzh 原文连接:http://www.cnblogs.com/vincentzh/p/6057666.html 目录 1.概述 2.目的 3.结构组成 4.实现 5.总结 1.概 ...

  9. 最短路径——Floyd-Warshall算法

    Floyd-Warshall算法,简称Floyd算法,用于求解任意两点间的最短距离,时间复杂度为O(n^3). 我们平时所见的Floyd算法的一般形式如下: void Floyd() { int i, ...

  10. iOS 支付宝的使用

    支付宝相关资源下载地址:支付宝开放平台  在移动支付功能处下载. 一.使用官方的Demo 需要配置基本信息: 打开“APViewController.m”文件,对以下三个参数进行编辑. 二.集成支付宝 ...