说个最近遇到的 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 确实实现了超高并发的支持,但也正是因为如此,但其依赖的后端服务超过负载后缺少快速失败的特性,其能同时接受大量请求的特性会使得在等待后端服务过程中出现循环超时,最终出现服务不可用情况,
10940 次点击
所在节点    Python
36 条回复
lecher
2016-03-09 22:27:27 +08:00
这个故障挺有价值的, tornado 爆掉了 MySQL 的查询性能。

TorMySQL 的并发是不是开的也高了点,失去平缓峰值的作用。导致峰值直接击穿 MySQL 。
cevincheung
2016-03-09 22:37:08 +08:00
中间挂个中间件咧~~~atlas
sujin190
2016-03-09 22:40:17 +08:00
@lecher 是的,以前一直想高并发高并发的,没想过超过 mysql 查询数后的快速失败策略,学习到了,现在果断调低了连接池最大连接数, TorMySQL 应该有个队列等待超时策略才是
sujin190
2016-03-09 22:43:25 +08:00
@cevincheung 像 php 来说,每个进程同一时刻只能接受一个请求,所以一般来说 mysql 并发查询不会太高,同时过高的并发过来会直接访问拒绝,但 tornado 不会,所以像 php 之类的来说是不会出现循环等待的,挂个中间件估计不能解决问题啊
cevincheung
2016-03-09 22:53:55 +08:00
那就换 postgresql 试试~~~
pynix
2016-03-09 23:07:11 +08:00
需要缓存来降低 MySQL 查询密度
sujin190
2016-03-09 23:15:33 +08:00
@cevincheung 其实我瞬间并发超过了单机 mysql 的负载,加机器才是王道。。
sujin190
2016-03-09 23:17:09 +08:00
@pynix 正有此意
cevincheung
2016-03-09 23:20:03 +08:00
@sujin190 试试单机 pgsql 抗
skydiver
2016-03-09 23:51:04 +08:00
mysql 连接数设置低一点,多了自动拒绝连接。
另外客户端的重试策略也需要调整
calease
2016-03-09 23:53:23 +08:00
中间加一个 redis 应该就没问题了吧。
虽然高峰时第一次会 timeout ,
但第二次就去缓存拿了,
不会导致反复 retry 。
还真没试过 tornado 直接连接 MySQL.
lecher
2016-03-09 23:57:17 +08:00
好奇到底是多大的并发量击穿了 MySQL 。
有没有使用 Redis memcached 之类的缓存热数据?
MySQL 如果不是没有建索引或特别复杂的联表查询,一秒跑几千个查询也是绰绰有余的。
glasslion
2016-03-10 00:22:50 +08:00
@cevincheung pg 连接数高了也一般是用 pgbouncer , pgpool 抗吧
alexapollo
2016-03-10 00:30:40 +08:00
前端尽量少重试,防止雪崩
scys
2016-03-10 00:35:08 +08:00
这种情况我倒是遇到过很多次,现在换用了 aiomysql 。
对池管理方便比较简单,最大限制就行,反正并发量用更加简单的 iptables rate limit -_- ... 解决
客户端等得起。
cevincheung
2016-03-10 01:06:59 +08:00
@glasslion
中间多个中间件应该不会有突发的连接而且应该不会让数据库负载达到峰值,起码不会再出现这种情况吧?又不实际的执行什么东西。(我这么觉得)
GTim
2016-03-10 07:04:38 +08:00
好奇多大的并发量,不然讨论没意义啊
sujin190
2016-03-10 09:30:36 +08:00
@cevincheung 正常其实压力不大,高峰突然停机重启才会出现这种情况
sujin190
2016-03-10 09:31:08 +08:00
@skydiver 恩,以前没考虑到这种情况
sujin190
2016-03-10 09:32:00 +08:00
@calease 因为平时压力很小,所以暂时还没走缓存,没想到突然网络异常重启就跪了

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

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

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

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

© 2021 V2EX