如何理解 Tornado
假设你还不知道Tornado是什么也不知道为什么应该对它感兴趣,那我将用简短的话来介绍Tornado这个项目。如果你已经对它有了兴趣,你可以跳去看下一节内容。
Tornado是一个用Python编写的异步HTTP服务器,同时也是一个web开发框架。该框架服务于FriendFeed网站,最近Facebook也在使用它。FriendFeed网站有用户数多和应用实时性强的特点,所以性能和可扩展性是很受重视的。由于现在它是开源的了(这得归功于Facebook),我们可以彻底的对它是如何工作的一探究竟。
我觉得对非阻塞式IO (nonblocking IO) 和异步IO (asynchronous IO AIO)很有必要谈一谈。如果你已经完全知道他们是什么了,可以跳去看下一节。我尽可能的使用一些例子来说明它们是什么。
让我们假设你正在写一个需要请求一些来自其他服务器上的数据(比如数据库服务,再比如新浪微博的open api)的应用程序,然后呢这些请求将花费一个比较长的时间,假设需要花费5秒钟。大多数的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,达到每秒上千个请求的效率是非常轻松的事情。服务器请求处理的代码将被改成这样:
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。
源代码
该项目由github托管,你可以通过如下命令获得,虽然通过阅读这篇文章你也可以不需要它是吧。
git clone git://github.com/facebook/tornado.git
在tornado的子目录中,每个模块都应该有一个.py文件,你可以通过检查他们来判断你是否从已经从代码仓库中完整的迁出了项目。在每个源代码的文件中,你都可以发现至少一个大段落的用来解释该模块的doc string,doc string中给出了一到两个关于如何使用该模块的例子。
IOLoop模块
让我们通过查看ioloop.py文件直接进入服务器的核心。这个模块是异步机制的核心。它包含了一系列已经打开的文件描述符(译者:也就是文件指针)和每个描述符的处理器(handlers)。它的功能是选择那些已经准备好读写的文件描述符,然后调用它们各自的处理器(一种IO多路复用的实现,其实就是socket众多IO模型中的select模型,在Java中就是NIO,译者注)。
可以通过调用add_handler()方法将一个socket加入IO循环中:
def add_handler(self, fd, handler, events):
"""Registers the given handler to receive the given events for fd."""
self._handlers[fd] = handler
self._impl.register(fd, events | self.ERROR)
_handlers这个字典类型的变量保存着文件描述符(其实就是socket,译者注)到当该文件描述符准备好时需要调用的方法的映射(在Tornado中,该方法被称为处理器)。然后,文件描述符被注册到epoll(unix中的一种IO轮询机制,貌似,译者注)列表中。Tornado关心三种类型的事件(指发生在文件描述上的事件,译者注):READ,WRITE 和 ERROR。正如你所见,ERROR是默认为你自动添加的。
self._impl是select.epoll()和selet.select()两者中的一个。我们稍后将看到Tornado是如何在它们之间进行选择的。
现在让我们来看看实际的主循环,不知何故,这段代码被放在了start()方法中:
def start(self):
"""Starts the I/O loop.
The loop will run until one of the I/O handlers calls stop(), which
will make the loop stop after the current event iteration completes.
"""
self._running = True
while True:
[ ... ]
if not self._running:
break
[ ... ]
try:
event_pairs = self._impl.poll(poll_timeout)
except Exception, e:
if e.args == (4, "Interrupted system call"):
logging.warning("Interrupted system call", exc_info=1)
continue
else:
raise
# Pop one fd at a time from the set of pending fds and run
# its handler. Since that handler may perform actions on
# other file descriptors, there may be reentrant calls to
# this IOLoop that update self._events
self._events.update(event_pairs)
while self._events:
fd, events = self._events.popitem()
try:
self._handlers[fd](fd, events)
except KeyboardInterrupt:
raise
except OSError, e:
if e[0] == errno.EPIPE:
# Happens when the client closes the connection
pass
else:
logging.error("Exception in I/O handler for fd %d",
fd, exc_info=True)
except:
logging.error("Exception in I/O handler for fd %d",
fd, exc_info=True)
poll()方法返回一个形如(fd: events)的键值对,并赋值给event_pairs变量。由于当一个信号在任何一个事件发生前到来时,C函数库中的poll()方法会返回EINTR(实际是一个值为4的数值),所以"Interrupted system call"这个特殊的异常需要被捕获。更详细的请查看man poll。
在内部的while循环中,event_pairs中的内容被一个一个的取出,然后相应的处理器会被调用。pipe 异常在这里默认不进行处理。为了让这个类适应更一般的情况,在http处理器中处理这个异常是一个更好的方案,但是选择现在这样处理或许是因为更容易一些。
注释中解释了为什么使用字典的popitem()方法,而不是使用更普遍一点的下面这种做法(指使用迭代,译者注):
for fd, events in self._events.items():
原因很简单,在主循环期间,这个_events字典变量可能会被处理器所修改。比如remove_handler()处理器。这个方法把fd(即文件描述符,译者注)从_events字典中取出(extracts,意思是取出并从_events中删除,译者注),所以即使fd被选择到了,它的处理器也不会被调用(作者的意思是,如果使用for迭代循环_events,那么在迭代期间_events就不能被修改,否则会产生不可预计的错误,比如,明明调用了remove_handler()方法删除了某个键值对,但是该handler还是被调用了,译者注)。
如何理解 Tornado
Tornado既是一个web server,也是web framework。而它作为web server 采用的是asynchronous IO的网络模型,这是一种很高效的模型。
1. 高性能的网络库,这可以和gevent, twisted, libevent等做对。提供了异步io支持,超时事件处理,在此基础上提供了tcpserver, httpclient,尤其是curlhttpclient在现有http客户端中肯定排第一。可以用来做爬虫,游戏服务器,据我所知业界已有使用tornado作为游戏服务器。
2. web框架,这可以和django, flask对。提供了路由,模板等web框架必备组件。与其他区别是tornado是异步的,天然适合长轮训,这也是friendfeed发明tornado的原因,当前flask也可以支持,但必须借住gevent等。
3. 较为完备的http服务器,这点可以和nginx, apache对比,但只支持http1.0,所以使用nginx做前段不仅是为了更好利用多核,也是让其支持http1.1。
4. 完备的wsgi服务器,这可以和gunicore,gevent wsgi server做对比,也就是说可以让flask运行在tornado之上,让tornado加速flask。
5. 提供了完备的websocket支持,这让html5的游戏等提供了便利。像知乎长轮训就是使用了websocket,但websocket手机支持的不是很好,前段时间不得不使用定时ajax发送大量请求,期待手机浏览器赶快奋起直追。
如何理解 Tornado
tornado
[英][tɔ:'neɪdəʊ][美][tɔrˈnedo]
n.[大气]龙卷风,陆龙卷; 大雷雨; 具有巨大破坏性的人(或事物);
复数:tornadoestornados
例句
1.
Why do we go in the basement during a tornado?
为什么龙卷风来的时候我们要躲进地下室?
2.
And now a perfect tornado burst upon me.
一场完美的龙卷风就在此刻向我袭来。
如何理解Tornado中的协程模块
本质上什么是协程?举个例子:就是类似goto一样 可以在多个fun之间来回跳转~ 比如A函数执行到一半然后switch到B函数执行~~诸如此类. 那如果只考虑一个函数呢??那对于A函数而言:其实就是其可以执行到一半先停下,干点别的事情后再接着往下运行~ 看到这里你是不是想到了debug时的打断点??执行到断点的时候就停下,只有next往下setp时候这个函数才接着往下执行~对不对? 那如果我们可以实现类似的断点机制,是不是就可以实现多个函数来回跳转呢?A函数执行到断点时就停下,系统去B执行~等B执行完毕了才回来接着step往断点后边的代码航执行~~~~
协程其实就是这么个东西,那么在Python中山门可以实现debug断点功能呢?当然是yield了~~执行到中间的yield系统就停下返回了~之后在调用next时才回接着往下执行~~~ 你看,是不是通过这个东西就可以做到多个函数之间来回跳转??!
恩,那山门时候跳转呢?之前是yield才会,那我是不是可以将之封装一下呢?封装成:一个协程,然后sleep就yield出来~~ 折下来就做到协程的功能来~~
本质上就是这么回事~~~别听别人说的多悬乎~~~只是在这个基础模型上又加了很多manager的功能等等~~
如何理解Tornado中的协程模块
本质上什么是协程?举个例子:就是类似goto一样 可以在多个fun之间来回跳转~ 比如A函数执行到一半然后switch到B函数执行~~诸如此类. 那如果只考虑一个函数呢??那对于A函数而言:其实就是其可以执行到一半先停下,干点别的事情后再接着往下运行~ 看到这里你是不是想到了debug时的打断点??执行到断点的时候就停下,只有next往下setp时候这个函数才接着往下执行~对不对? 那如果我们可以实现类似的断点机制,是不是就可以实现多个函数来回跳转呢?A函数执行到断点时就停下,系统去B执行~等B执行完毕了才回来接着step往断点后边的代码航执行~~~~协程其实就是这么个东西,那么在Python中山门可以实现debug断点功能呢?当然是yield了~~执行到中间的yield系统就停下返回了~之后在调用next时才回接着往下执行~~~ 你看,是不是通过这个东西就可以做到多个函数之间来回跳转??!恩,那山门时候跳转呢?之前是yield才会,那我是不是可以将之封装一下呢?封装成:一个协程,然后sleep就yield出来~~ 折下来就做到协程的功能来~~本质上就是这么回事~~~别听别人说的多悬乎~~~只是在这个基础模型上又加了很多manager的功能等等~~
请问tornado , hurricane ,twister ,typhoon 区别是?
tornado,hurricane,typhoon均属于cyclone。
Typhoon: 热带的气旋。 一般是在西部太平洋的气旋是最常用的。
hurricane: 热带气旋是发生在热带亚热带地区海面上的气旋性环流,由水蒸气冷却凝固时放出潜热发展而出的暖心结构。所以当热带气旋登陆后,或者当热带气旋移到温度较低的洋面上,便会因为失去温暖、潮湿的空气供应能量,而减弱消散,或失去热带气旋的特性,转化为温带气旋。
热带气旋的移动主要受到大尺度气候系统和科里奥利力所影响;此外,科里奥利力与角动量守恒原理也使热带气旋的云系围绕著中心旋转。在北半球,热带气旋沿逆时针方向旋转,在南半球则以顺时针旋转。
tonrnado: 龙卷风啦,这个知道吧,在陆地上的
twister:好像也是龙卷风