说个最近遇到的 tornado 的小坑吧

2016-03-09 22:06:42 +08:00
 sujin190
事情是这样的,服务端使用 tornado 提供 api 接口给客户端使用,存储使用 mysql ,使用 tormysql 连接 MySQL 数据库查询,前天下午时服务器网络突然断了, 15 分钟后回复,可是 api 接口还是无法使用,查看服务器状态,网络正常,负载正常,服务正常,于是去查看 nginx 日志,发现 99%的请求都 504 超时了,查看 mysql 服务器, cpu 负载接近 100%,命令行进 mysql show processlist 查看,将近 400 个查询正在运行,大量处于 statistics 状态,每个查询时间明显高了不少,但也还是有查询正常返回了,并未超过请求超时时间,为什么 nginx 显示几乎所有请求都超时了呢?
想不明白于是果断重启试试,重启不一会 504 超时又慢慢上升,不一会几乎所有的请求均超时无返回,如此数次还是无法正常启动,最后不得不关掉大部分借口再慢慢放开服务才起来,为此导致借口听着服务达数小时。
事后对 mysql 负载虽然满了,查询很慢却还是有返回,但请求几乎都超时甚是不接,于是仔细查看日志,终于发现原来网络异常后,客户端一直在重试,网络正常后,瞬间过来的并发高了十几倍,由于 tornado 异步 IO 高并发的特性,并未出现访问拒绝,而是接受了所有的请求,并都产生了 mysql 查询发往数据库,所有 mysql 瞬间并发查询十分高导致负载上升,每个查询耗时高达数秒,同时 tornado 在使用 tormysql 查询超过最大连接数后并进入队列等待,慢慢的,队列等待时间超过请求超时时间, nginx 返回 504 超时,但在队列中的查询还是继续发往 mysql ,因为客户端为收到正常返回,所以再次发起重试,又再次增加了查询队列的长度,最终所有请求在队列中等待 mysql 查询的时间都超过了请求超时时间,所以也就出现了 mysql 负载很高但几乎所有请求都超时的情况。
tornado 使用异步 io 确实实现了超高并发的支持,但也正是因为如此,但其依赖的后端服务超过负载后缺少快速失败的特性,其能同时接受大量请求的特性会使得在等待后端服务过程中出现循环超时,最终出现服务不可用情况,
10809 次点击
所在节点    Python
36 条回复
sujin190
2016-03-10 09:36:15 +08:00
@lecher 并不高, 400 个 mysql 并发查询,有 join ,平时压力并不高所以。。
sujin190
2016-03-10 09:41:45 +08:00
@scys aiomysql 遇到的问题还是一样的啊,并发高了之后, tornado 不会拒绝请求,当超过连接池最大链接数后,大量的请求都会阻塞在从连接池获取连接那,最后几乎所以得请求等待连接的时间就超过了请求超时时间, nginx 超时断开连接但 tornado 查询数据库请求却并未取消,客户端如果有重试的话,等待从连接池或取连接的请求会越来越多,最后几乎所有请求都会超时的,除非 tornado 内存爆了,否则是不会出现访问拒绝的
ethego
2016-03-10 09:51:51 +08:00
来几台主从,读写分离
AlexaZhou
2016-03-10 10:27:30 +08:00
支持,分析的很到位

有没有一种办法可以在 Nginx 返回 504 时,通知 Tornado 把正在进行的查询取消掉呢
zeayes
2016-03-10 10:58:55 +08:00
可以通过 ioloop 里面的_handlers 数量做下简单的过载保护。
tabris17
2016-03-10 11:16:05 +08:00
所以要用连接池中间件啊,另外这也属于配置不当吧
micyng
2016-03-10 12:23:31 +08:00
tornado 本来就不合适做这种同步 io 的事情
原理都不清楚就断言是坑
sujin190
2016-03-10 12:32:02 +08:00
@AlexaZhou 这显然做不到吧,可以取消查询,但调用栈取消不掉啊
sujin190
2016-03-10 12:32:34 +08:00
@micyng 我就没说同步 io 。。。
sujin190
2016-03-10 12:34:01 +08:00
@tabris17 算是吧,哈哈,现在果断调小了最大连接数
sujin190
2016-03-10 12:34:22 +08:00
@ethego 平时流量并不高,所以应该暂时不用
Ge4Los
2016-03-10 12:35:23 +08:00
@micyng 确实不算坑啊。
后端挂数据库查询阻塞, tornado 不就都阻塞了。
lecher
2016-03-10 12:54:15 +08:00
400 并发查询繁忙这个事情算是开发的时候数据管理的坑了,互联网项目为了性能,通常是在数据库存冗余数据,交给数据库的语句最好都是单表查询。
要完全填掉这个坑,估计要改一下数据库的字段和查询语句,尽可能让执行的 SQL 语句实现单表查询,简单的 join 宁愿分成两步先取 ID 再去另一个表作 where in 。把数据库的处理提高一个量级,能支持到 4000 每秒,就没有这些事了。
临时解决方案也得是加个内存缓存,先把复杂查询的结果都缓存一下。数据库做读写分离将数据库拆成主从结构也可以提高几倍查询性能。
应急就是加配置加机器了,但是 SQL 处理上不去是硬伤。
sujin190
2016-03-10 13:02:00 +08:00
@Ge4Los 我的问题正是 tornado 没有阻塞才出了这个故障。。
sujin190
2016-03-10 13:10:37 +08:00
@lecher 恩,但同时不管提高 mysql 查询性能到多少,总有可能瞬间并发超过的可能,所以应用层能提供一个当严重超负载时快速失败的策略还是必须的
zhicheng
2016-03-10 14:41:18 +08:00
跟 tornado 没什么关系。跟不阻塞更没有关系了,有关系的是你的客户端实现。

“网络异常后,客户端一直在重试,网络正常后,瞬间过来的并发高了十几倍”

阻塞模型雪崩得更快,如果不能保证客户端的实现,那至少在 nginx 上开一个 rate limit 的插件。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/262338

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX