Python Tornado web server 是一个用Python语言写成的Web服务器兼Web应用框架。
与web.py相同,tornado也是一种非常轻量的web框架,其具有异步非阻塞IO的处理方式。
1.安装
# pip 安装
$ sudo pip install tornado
# yum/apt安装
$ sudo yum install python-tornado
$ sudo apt install python-tornado
2.hello world
按照国际管理,先来一个入门的
## 主函数部分
if __name__ == "__main__":
handlers = [(r"/cmd", CmdHander),
(r"/cmd/(.*)", CmdHander),
]
application = tornado.web.Application(handlers)
## 设置监听端口
application.listen(7890)
## 启动
tornado.ioloop.IOLoop.instance().start()
## 实现子类和方法重载
#=============================================================================
# 下面的方式实现了两个简单的方法get/post,可以分别使用以下curl命令简单测试:
# curl -X GET http://127.0.0.1:7980/cmd/test
# curl -X POST http://127.0.0.1:7890/cmd/test --data aaa=123 --data BBB=112
# *(也可以用request / urllib2编写测试demo , 这里不再介绍。)
#=============================================================================
class CmdHander(tornado.web.RequestHandler):
def get(self, name):
#temp = self.request.uri.split('/')[-1]
print "hander",name
## sleep
time.sleep(10)
## 此处阻塞会阻塞整个服务,包括cmd hander和其他hander
print "finish"
self.set_header('Content-Type', 'application/json; charset=UTF-8')
self.write(json.dumps({"result":"success"}))
self.finish()
return
def post(self, *args, **kwargs):
param = self.request.body.decode('utf-8')
temp = self.request.uri.split('/')[-1]
print "API:",temp
print "parameter:",param
if "=" in param :
extend = dict(urlparse.parse_qsl(param))
else :
extend = json.loads(param, encoding='UTF-8', )
## sleep
time.sleep(10)
print "type of extend: ", type(extend)
print "parameter extend:",extend
self.set_header('Content-Type', 'application/json; charset=UTF-8')
self.write(json.dumps({"result":"success"}))
self.finish()
return
# ========================================================================
# 同步(同时只处理一个请求)-阻塞(等待函数返回才能执行下面的操作)
# 我们在上面代码中加入了sleep来模拟耗时操作,测试后我们发现:当同时多次发出请求,整个进程
# 会因为耗时操作而阻塞,服务不能再接收和处理新的请求。效率很差。
# ========================================================================
由于实时web功能需要为每个用户提供一个多数时间被闲置的长连接, 在传统的同步web服务器中,这意味着要为每个用户提供一个线程, 但是每个线程的开销都是很昂贵的.为了尽量减少并发连接造成的开销,Tornado使用了一种单线程事件循环的方式.基于上述问题,可以引用一些修饰方法和ThreadPoolExecutor方法,实现:异步-阻塞。
3.异步-阻塞模式
from concurrent.futures import ThreadPoolExecutor
from tornado.concurrent import run_on_executor
class OrdHander(tornado.web.RequestHandler) :
## 设置线程池最大线程数
executor = ThreadPoolExecutor(2)
@tornado.web.asynchronous # 将请求变成长连接
@run_on_executor
def get(self, *args, **kwargs):
time.sleep(10)
self.set_header('Content-Type', 'application/json; charset=UTF-8')
self.write(json.dumps({"result":"success"}))
self.finish()
# ===================================================================
# 异步(同时处理多请求)- 阻塞(等待函数返回才能执行下面的操作)
# ===================================================================
基于上述方式,可以使用协程,耗时部分封装在函数中处理
class OrdHander(tornado.web.RequestHandler) :
## 最多同时并发处理的线程数
executor = ThreadPoolExecutor(2)
@tornado.web.asynchronous
@tornado.gen.coroutine
def get(self,*args, **kwargs):
print "start get"
#time.sleep(10)
## 将耗时操作通过并发协程处理,(虽然两个任务都耗时10秒,但是同时调用两次get,可以并发执行,总耗时还是10秒)
response = yield self.my_sleep(10)
print "response",response
self.set_header('Content-Type', 'application/json; charset=UTF-8')
self.write(json.dumps({"result": "success","response":response}))
self.finish()
@tornado.web.asynchronous # 将请求变成长连接
@tornado.gen.coroutine
def post(self, *args, **kwargs):
param = self.request.body.decode('utf-8')
temp = self.request.uri.split('/')[-1]
print "API:",temp
print "parameter:",param
if "=" in param :
extend = dict(urlparse.parse_qsl(param))
else :
extend = json.loads(param, encoding='UTF-8', )
print "type of extend: ", type(extend)
print "parameter extend:",extend
#time.sleep(10)
## 将耗时操作通过并发协程处理
response = yield self.my_sleep(10)
self.set_header('Content-Type', 'application/json; charset=UTF-8')
self.write(json.dumps({"result":"success","response":response}))
print "finish"
self.finish()
@run_on_executor
def my_sleep(self,s=0):
print "in my_sleep"
time.sleep(s)
return s
4. 异步非阻塞模式
如果既需要异步的并发,同时还需要调用端不等待耗时操作的返回,可以继续执行,可以尝试以下方法:
1)创建一个消息队列,当收到请求时,将请求和参数放入队列,然后调用finish()结束阻塞。
2)创建一个控制线程,轮询队列,当队列不为空时创建新线程实现业务。
3)如果需要返回结果,可以充当主动端发起curl请求,返回结果。