两个web服务,服务1和服务2,都是用tornado的框架,有这样一种场景,服务1的a接口里面会请求服务2的c接口,而服务2的c接口又请求服务1的b接口,如果没有加上异步非阻塞,直接请求服务1的a接口会导致两个服务都将陷入卡死状态,无法接受其它请求。
解决方法自然是要用异步的方法来实现,在接口a去请求接口c的过程采用异步库去请求,这样服务1可以继续监听其它请求,所以服务2的c接口请求服务1的b接口就可以正常处理,从而返回结果给a接口。
tornado提供了一个异步http请求的库AsyncHTTPClient,可以让我们非阻塞的执行请求,配合tornado.gen.coroutine装饰器可以让本来靠回调的异步编程变得像同步编程.
服务1代码:
#!/usr/bin/env python
# coding: utf-8
import tornado.gen
import tornado.web
import tornado.ioloop
from tornado.httpclient import AsyncHTTPClient
@tornado.gen.coroutine
def get_res():
http_client = AsyncHTTPClient()
response = yield http_client.fetch('http://192.168.0.120:8082/c')
# 返回值需要使用 raise gen.Return() 当做异常抛出,因为在 Python 3.2 之前生成器是不允许有返回值的
raise tornado.gen.Return(response.body)
class AHandler(tornado.web.RequestHandler):
@tornado.gen.coroutine
def get(self):
result = yield get_res()
self.write(result)
class BHandler(tornado.web.RequestHandler):
def get(self):
self.write('调用8081的BHandler')
if __name__ == '__main__':
app = tornado.web.Application([(r'/a', AHandler), (r'/b', BHandler)])
app.listen(8081)
tornado.ioloop.IOLoop.current().start()
服务2代码:
# !/usr/bin/env python
# coding: utf-8
import tornado.web
import tornado.ioloop
import time
import requests
class CHander(tornado.web.RequestHandler):
def get(self):
time.sleep(10)
response = requests.get('http://192.168.0.120:8081/b')
print response.text
self.write(response.text)
if __name__ == '__main__':
app = tornado.web.Application([(r'/c', CHander)])
app.listen(8082)
tornado.ioloop.IOLoop.current().start()
服务1,监听8081端口;服务2,监听8082端口
当在浏览器输入 192.168.0.120:8081/a 请求服务1的a接口时,进入AHandler的get函数,当遇到yield时,挂起当前get函数,进入get_req函数,然后又遇到yield,挂起当前get_req函数,执行异步请求服务2的b接口,此时服务1不会陷入阻塞状态,仍然可以监听处理其它请求,在服务2的b接口中设置了10秒延时,这个时候去请求服务1的c接口发现是可以正常请求的。不会被阻塞。当服务1的b接口返回时,tornado收到通知从yield出继续执行,完整这个请求。