Tornado

Tags: tornado 

目录

摘要

计划了解一些互联网的技术, 选择了python, 因为python被广泛的使用, 有了很多的积累, 涉及到领域也比较多。

(另一个可以考虑的语言是golang, 与Android同为google出品, 所以很有想象空间)

选择Tornado, 因为看到别人介绍说, 这一个高效精简的框架, 符合我的口味…

安装

http://www.tornadoweb.org/en/stable/

使用源码中的setup.py脚本进行安装。

安装python3

为了完整的编译python3,你可能需要安装:

openssl-devel ncurses-devel bzip2-devel xz-devel readline-devel gdbm-devel sqlite-devel tk-devel

如果系统缺少一些软件包,python3的部分模块会编译失败,make结束后会给出如下提示:

The necessary bits to build these optional modules were not found:
_bz2                  _curses               _curses_panel      
_dbm                  _gdbm                 _lzma              
_sqlite3              _ssl                  _tkinter           
readline              zlib                                     
To find the necessary bits, look in setup.py in detect_modules() for the module's name.

安装python3, 下载,解压源码

./configure --prefix=/opt/python3.4.1
make 
make test
make install

设置路径

export PATH=/opt/python3.4.1/bin/:$PATH

安装tornado

查看帮助:

python3 ./setup.py help
python3 ./setup.py --help install

编译:

python3 ./setup.py build
python3 ./setup.py  install --prefix=/opt/tornado-3.2.2

设置模块路径:

export PYTHONPATH=/opt/tornado-3.2.2/lib/python3.4/site-packages/:$PYTHONPATH

Demo

运行源码路径下的demo,例如:

python3 helloword.py

然后就可以通过8888端口访问了(注意系统的防火墙是否开启了相应端口)

分析

看了下demo和源码,确实很精简,源码一共没有多少, 网页使用template的方式, tornado根据url将请求路由到不同handler, handler完成对网页模板的填充,返回给客户端。

和golang中的http包的工作方式相同。

IOLoop

tornado.ioloop.IOLoop是tornado的核心流程(事件驱动的过程就是在这里实现的)

结合下面的示例代码进行分析:

# ....省略...

application = tornado.web.Application([
	(r"/", MainHandler),
	(r"/login", LoginHandler)
])

if __name__ == "__main__":
	#application.listen(8888)
	#tornado.ioloop.IOLoop.instance().start()

	server = tornado.httpserver.HTTPServer(application)
	server.bind(8888)
	server.start(1)
	tornado.ioloop.IOLoop.instance().start()      

IOLoop对象创建过程:

1 调用IOLoop.instance()
2 IOLoop.instance()中调用了构造函数IOLoop()
3 因为IOLoop继承了tornado.util.Configurable, 所以IOLoop()执行时,调用父类Configurable的__new__(cls, **kwargs)
4 在__new__()中, 调用子类IOLoop的configurable_default(cls)得到一个类,创建并返回这个类的对象

因此,IOLoop的configurable_default(cls)决定了最终生成的对象的类型。实际实现中从EPollIOLoop、KQueueIOLoop、SelectIOloop中选择一个类返回。

IOLoop对象分析:

假设tornado.ioloop.IOLoop.instance()创建的是一个EPoolIOLoop对象.EPollIOLoop在tornado.platform.epoll中定义.

tornado.platform.epoll.EPollIOLoop 继承了 tornado.ioloop.PollIOLoop 继承了 tornado.ioloop.IOLoop

查看源码可以看到主要的功能是在tornado.ioloop.PollIOLoop中实现的.

tornado.ioloop.PollIOLoop分析:

initialize(self, impl, time_func=None)    //impl是事件驱动的类型例如select.epoll()
add_handler(self, fd, handler, events)    //注册等待的事件
start(self)                               //开始进行等待,一旦事件触发后,调用对应的handler

注意,在这一节的示例代码的最后一行创建IOLoop对象后,直接就执行了start(), 并没有看到有事件注册的过程, 那么server的事件是什么时候被注册的呢?

查看tornado.ioloop.IOLoop.intance()的实现可以发现: IOLoop对象是一个类变量.

@staticmethod
def instance():
	"""Returns a global `IOLoop` instance.

	Most applications have a single, global `IOLoop` running on the
	main thread.  Use this method to get this instance from
	another thread.  To get the current thread's `IOLoop`, use `current()`.
	"""
	if not hasattr(IOLoop, "_instance"):
		with IOLoop._instance_lock:
			if not hasattr(IOLoop, "_instance"):
				# New instance after double check
				IOLoop._instance = IOLoop()
	return IOLoop._instance

所以肯定是在最后一行代码执行之前, 在某个地方已经创建了IOLoop对象. 沿着tornado.httpserver.HTTPServer的代码进行查找,最后发现是在tornado.tcpserver.TCPServer中进行了事件注册.

示例代码中的server.bind(8888)和server.start(1)都是从tornado.tcpserver.TCPServer中继承的, 在这两次调用中完成了事件的注册。

另外一个需要关注的是在多进程的情况下,事件注册必须在各个子进程中进行。也就是说在fork多个进程之前,不能创建IOLoop对象。tornado中对此也做了检查:

#tornado.process.fork_process()

if num_processes is None or num_processes <= 0:                                                               
	num_processes = cpu_count()                                                                               
if ioloop.IOLoop.initialized():                                                                               
	raise RuntimeError("Cannot run in multiple processes: IOLoop instance "                                   
					   "has already been initialized. You cannot call "                                       
					   "IOLoop.instance() before calling start_processes()")  

事件驱动框架大体就是这样的了, 说白了其实就是当socket接收到数据的时候,调用回调函数进行处理, 只不过这个过程是使用epool实现的。

话说最近一些Web Server为什么要特别强调是事件驱动? 莫非最早的Web Server都是来一个连接, fork一个进程?

最后, 事件驱动的原理都是一样的, glib里面也很好的实现, 也就是说用C写一套框架也是OK的, 只不过需要考虑下开发的效率和从业人员的规模…

异步

使用

tornado的核心就是上面分析的处理流程, 作为一个框架,更关心如何使用。

template

tornado使用的template的语法很简洁.

{% apply function %} .. {% end %}
{% autoescape function%}
{% block name %}...{% end %}
{% comment ... %}
{% extends filename %}
{% for var in expr %}...{% end %}
{% from x import y %}
{% if condition %}...{% elif condition %}...{% else %}...{% end %}
{% import module %}
{% include filename %}
{% module expr %}
{% raw expr %}
{% set x = y %}
{% try %}...{% except %}...{% finally %}...{% else %}...{% end %}
{% while condition %}...{% end %}

tornado

  1. Tornado

推荐阅读

Copyright @2011-2019 All rights reserved. 转载请添加原文连接,合作请加微信lijiaocn或者发送邮件: [email protected],备注网站合作

友情链接:  系统软件  程序语言  运营经验  水库文集  网络课程  微信网文  发现知识星球