异步回调程序的原理和写法我不就不介绍了,因为我主要是来吐槽下这种代码风格的。
最近因为追求性能所以去看了下tornado,然后发现这货如果你要利用它的高性能 ,那么你就要写回调代码,跟twisted一样,各种callback。
我们正常的同步代码一般是这样的
res = db.query(...)
res2 = dosomething(res)
res3 = db.insert(...)
return res3
上面的代码共四行,2次数据库操作,一次数据处理最后返回想要的结果。逻辑很清晰也很易懂,我想大部分的程序都是这样的。但如如果是异步的写法就会这么写
db.query(...,callback=fun2)
def fun2(res):
res2 = dosomething(res,callback=fun3)
def fun3(res):
db.insert(...,callback=fun4)
异步的代码就是只要你涉及到阻塞的代码就需要使用回调,呵呵,看到了吧2次阻塞就要求你把一段代码写成3段,代码易读性直线下降,如果你只是做一些简单的功能,代码也不多,我觉得还是能接受的,毕竟性能真的好,但是如果你要做一个很大的项目,一个请求可能要做4,5次阻塞操作,不敢想象,简直就是个灾难。
不过好在tornado还给了我们另一条活路,那就是tornado.gen http://www.tornadoweb.org/documentation/gen.html。
简单点说就是gen 给了我们用同步代码来做异步实现的可能。
我们来看看gen和普通异步代码。
class BaseHandler(tornado.web.RequestHandler):
@property
def db(self):
if not hasattr(self, "_db"):
self._db = asyncmongo.Client(pool_id='test_pool', **settings.get('mongo_database'))
return self._db
def api_response(self, data):
"""将数据转成json返回给客户端"""
self.set_header("Content-Type", "application/json; charset=UTF-8")
data = dumps(data)
self.finish(data)
class A(BaseHandler):
"""
同步代码实现异步
"""
@tornado.web.asynchronous
@gen.engine
def get(self):
res = self.get_user()
@tornado.web.asynchronous
@gen.engine
def get_user(self):
user_id = 'asdf'+str(random.randrange(1,490000)) #随机产生一个数据库里有的用户
res = yield gen.Task(self.db.user.find,{'name':user_id},limit=1)
res=res[0][0][0]
self.api_response(res)
class B(BaseHandler):
"""
异步代码
"""
@tornado.web.asynchronous
def get(self):
user_id = 'asdf'+str(random.randrange(1,490000))#随机产生一个数据库里有的用户
self.db.user.find({ 'name': user_id }, callback=self.async_callback(self.finish_save))
def finish_save(self, response, error):
self.api_response(response[0])
最后的最后我分别测试了2中写法最后在性能上的结果,我用ab -n 10000 -c 200 做了测试
gen 的代码 用了7秒
普通的异步代码 用了6.3秒
我觉得gen 略慢吧,但还是在可接受范围内,毕竟我不用写你妹的回调了。。。。怨念
当然 如果大家不是写web 就直接用gevent 吧,连这些gen的装饰器都可以省了。