Tornado
2021-12-16
2020 年 10 月发布的 6.1,结果到现在,已经一年多过去了,6.2 还没有见到。
Update @ 2022-04-28: 又过去了小半年,6.2 还是没有影子。

从 GitHub 提交频率图上明显可以看到 Tornado 的活跃度大幅下滑,2020 年的提交已然不多,2021 年更加惨不忍睹。
官方协程框架 AsyncIO 的诞生,标志着 Tornado 的历史使命已经完成。
是时候带着对 Tornado 的回忆,全面转投原生协程生态了。
Tornado
2021-12-13
- 一个请求会先到 A 服务,然后通过 HTTP 调用的方式传到 B 服务 (基于 Tornado)
- 面对突然涌来的大量请求,B 服务负载迅速升高,响应时间拉长
- A 服务的部分请求因为超时断开,然后重发
也就是说,在负载较高的时候,B 会收到一些重复请求。因为负载高的时候,B 其实已经接收过一遍,只是没有响应。
B 服务处理任务结束之后,会将请求加入幂等。但是在 B 服务正在处理的时候,还是会有重复处理的可能性。
如果是一些对数据可靠性有一定要求的场景,这样重复的处理就不能接受。
比较合适的设计应该是 A 和 B 之间通过 MQ 通讯,根本不可能有这样的情况发生,哪怕请求再多也可以按照自己的速度消费,使系统保持最佳状态运行。而且,服务之间解耦之后,可以避免相互影响。
但是,如果不能改变 A B 两个服务的现有设计(调用关系),可以做的事情:
- 通过请求的正常返回来当 ACK 机制 (本文原本想重点说的内容)
- 将请求的接收和处理拆开
- 请求收到,加入队列,然后就可以返回了,这个时间非常短,这个过程出现问题的可能性非常低
- 如果在处理请求之前,连接断开,那就结束流程,抛弃任务
- A 服务的重试机制延迟一个合适的时间(比如 3 分钟)处理,可以通过 MQ, DB, Redis 实现
两个方案都应该能大幅减小出现重复请求的情况,双管齐下效果会更好。
方案一, 如果引入 MQ 或 DB 的话,就会觉得为什么不在 A 服务做;如果不引入新组件的化,复杂是需要保证服务突然挂掉之后,队列数据的恢复。只能在启动服务时通过扫描日志来恢复数据。
方案二,无论是业务还是代码,影响范围比较小,易于实现。
Tornado 实现 ACK 机制
客户端在正常流程处理完成之前,断开连接,会触发 on_connection_close
调用,可以在这个里面做手脚。
class MainHandler(tornado.web.RequestHandler):
def post(self):
if self._finished:
return
# do something
def on_connection_close(self):
LOG.debug('connection closed')
self._finished = True
super().on_connection_close()
Python Tornado 异常
2021-02-05
看到日志里面有很多 tornado 的 max_clients limit reached, request queued.
日志。
Python Tornado
2020-09-09

Web开发 Python Tornado
2019-09-20
由于 Tornado 部署在 Nginx 后面,通过 self.request.remote_ip
总是只能拿到 Nginx 地址。
Web开发 Cookie 安全 Tornado
2019-01-13
本文讲的是 Tornado 框架中的 “secure cookie” 实现。
Tornado 协程
2018-08-05
Python Tornado
2015-10-04
Tornado POST 请求直接 599 了,经过 WireShark 抓包发现根本没有发出请求。
最后竟然发现和版本有关,4.1 下才有问题,4.2 以后就好了。
Python Tornado
2013-10-13