Python中有关Tornado框架的QPS性能问题如何优化?
机子: 1c 2g hdd 硬盘
python: 3.5.3
必要的 redis 和 mysql 都跑在同在一台机子上
今天用 ab 测试了一个登陆接口,分别进行 3 个数据库操作: select, replace, update ;最后会将产生的 token 扔进 redis 里。
数据库操作用了 tormysql,redis 直接是官方库,没有使用异步操作。
测试结果让我有点不可思议,居然才 30 - 35 QPS。
单纯测试 hello world,大概是 1000 左右。
不知道这个结果是否正常?
Python中有关Tornado框架的QPS性能问题如何优化?
是否异步 数据索引等是否处理好?
这两点是单进程性能的关键点 另外就是正式部署一般是 nginx +多个 tornado 这样才能算是测试出
单机的真正的服务能力,如果和其他语言 web 框架对比 合适的数据还是应该要累加下多个进程总共的 QPS。
要优化Tornado的QPS,核心就两点:减少阻塞和榨干CPU。
1. 异步化一切I/O操作 这是最重要的。所有数据库查询、外部API调用、文件读写都必须用异步客户端。别用同步库,一个请求卡住整个事件循环就完了。
# 错误:用同步requests库,会阻塞
import requests
def get_data():
return requests.get('http://api.example.com').json()
# 正确:用aiohttp或Tornado自己的AsyncHTTPClient
async def get_data():
http_client = AsyncHTTPClient()
response = await http_client.fetch('http://api.example.com')
return json.loads(response.body)
2. 调优Tornado配置
# 启动时这样配置
app = make_app()
server = HTTPServer(app)
server.bind(8888)
# 关键:进程数配CPU核数,充分利用多核
server.start(0) # 0表示自动根据CPU核心数启动子进程
IOLoop.current().start()
3. 数据库连接池 用aiomysql、asyncpg这些真正的异步驱动,别用同步ORM的伪异步接口。
4. 压测找瓶颈
用wrk或locust压测,看top命令哪个进程CPU高,用py-spy抓火焰图定位热点函数。
简单说就是:全异步+多进程+真异步驱动。
数据库都建索引了。数据库的每个操作在 0.002 秒。
除了 redis 这块,其他都异步了。这里只放 token 的登陆信息。
测试时,只跑了一个 tornado 进程。
开两个的话,能到 50 左右。
应该是哪里阻塞了,同步框架也能随便达到这个 QPS
把一个 5 秒的 timer 关了之后。
单进程的 tornado 去到了 50qps。
然后之前没有看清楚,发现数据库的操作要花 200 毫秒了…
平均每个请求 500-600 毫秒。
看来瓶颈应该是数据库了。
单线程操作数据库,IO 同步,不卡才怪?
大哥,tormysql 是异步库。
内部实现貌似是用 greenlet 去执行数据库操作。
如果 mysql 是异步 IO 了,那我认为你应该优先怀疑你的测试方法,串行测试 QPS 取决于访问 mysql 的往返延迟,你应该用并行请求才对。
是的,应该是我测试方法错了。
用同一个账号登陆的。
发现更新这个账号的信息是最耗时的。
python 搞高性能、高并发本来就不行,要是再加个锁之类那就更玩完儿了
登录测试用不同账号试试,用同一个账号小心交叉锁
这个配置,hello world 才 1000 ?有点底呀
我也觉得…
Hello world 级别的 Benchmark 直接看 https://www.techempower.com/benchmarks/#section=data-r14&hw=ph&test=plaintext 这种就好了
这种 QPS 低只有卡在数据库上了。redis 官方库可以用 pool,那个 mysql 库就没用过了
tormysql 有使用连接池么?可以看下 MySQL 的 cpu 消耗
已经用了连接池,最大链接是 60。
replace 以及 update 两条语句都用了不同的事务去执行。
发现用事务执行的语句消耗的时间都比较长。

