Flask中的后端并发思考(以Mysql:too many connections为例)

以前写过一篇《CentOS 下部署Nginx+Gunicorn+Supervisor部署Flask项目》,最近对该工程的功能进行了完善,基本的功能单元测试也作了。html

以为也是时候进行一下压力测试了,因此利用Jmeter对部署到服务器的项目进行了简单的压力测试。在以前的笔记中写过,这个API的资源获取,为了避免对数据库形成大量的读取压力,采用了Redis进行缓存,因此大量的GET方法下的接口都很坚挺,基本没有出乱子,可是在其中一个须要Log数据到Mysql的接口出问题了,具体表示是数据库插入失败。检查服务器上的Mysql日志发现,错误内容为:python

ERROR 1040: Too many connections

想起来以前一篇笔记中遇到Mysql server has gone away的问题,其中一步是须要对数据库的time_out进行设置,因此天然而然,搜索这个问题的解决方案中,最初步的天然是增大mysql对于最大链接数的上限:mysql

show variables like 'max_connections'; +-----------------+-------+
| Variable_name   | Value |
+-----------------+-------+
| max_connections | 151   |
+-----------------+-------+
1 row in set (0.00 sec)

而后对mysql的最大链接数进行修改来尝试解决问题。具体可见参考一。但是这个方法有两个问题:redis

1.最大链接数设置到多少比较合适?太大Mysql会占用服务资源。sql

2.极端高并发状况下,只是容许更大的链接,但是Mysql的I/O瓶颈仍是会形成有些服务若是等待Mysql操做完成再返回极可能好久甚至超时。数据库

 

考虑到我这个接口中,是存储日志,也就是大量写入Mysql数据并且无需校检。因此我决定采用异步来解决这个问题:flask

1.请求过来的时候先处理请求并当即返回给客户端windows

2.日志写入这个功能作成异步,也就是后台操做,考虑到高并发一般不会太持久,把日志写入的压力分散到后面的时间是比较可行的一个办法。api

因此找到了这个异步操做的Python库Celery, 简单来讲就是把耗时的操做丢给他去处理,Flask(或者说Gunicorn)无论这个操做而在处理完成请求以后直接返回。缓存

 

对于为何Flask应用一步步加上了Redis, 加上了Gunicorn(Gevent),到如今须要Celery, 我画了几张张图来理解。

一个典型的Flask应用(自带调试WSGI):

可是这个的问题在于他是阻塞的,每次请求过来没处理完没办法处理下一个请求!因此在调试的时候,会有提示你:

WARNING: Do not use the development server in a production environment.
Use a production WSGI server instead.

 因此真正用的时候咱们都要吧WSGI更换成Gunicorn, 利用服务器的多进程来达到并发,也就是同时处理多个请求。事实上,在利用了协成的Gevent后,每个gunicorn的worker还能够处理多个请求:

到这一步后的问题是每一个worker都要去对mysql的资源进行处理,这就形成数据库压力大并且响应速度慢。因此咱们就利用Redis来缓存经常使用的数据在内存中来达到加速资源访问的目的:

可是,今天的问题出来了,利用Redis达到了对资源缓存减轻数据库压力的目的,可是对Mysql的写入呢,每一个worker 每一个请求仍是要直接写入数据库(好比个人api的Log),那么这个在高并发的时候就是个问题了。因此利用Celery,而celery自己不存储资源,他须要一个中间人来帮忙存储异步处理的数据,既然官方推荐也有Redis我天然就把redis做为中间人。也就是说Celery会以队列的形式,不断的从中间人那里拿到本身的任务并后台进行处理。

也就是说每次请求,若是还有对数据库的写入,那么咱们把它延迟执行而不是等它执行完毕才返回给客户。这样子Flask就能够不断的接收新请求,并且经过对于延迟执行时间的调度,咱们能够把高峰时间的写入请求压力分散到后续的时间中去。

总结起来:

1.Gunicorn和gevent保证了flask能够同时处理多个请求

2.利用Redis/Memecached 缓存能够减轻数据库的读取压力
2.可是若是请求耗时(好比大量的数据库插入,发送验证邮件等),你这个进程资源仍是有被卡住的可能。

3.而利用Celery来后台处理耗时任务能够保证Flask可以较快响应并且不被阻塞,同时减轻了数据库的高峰写入压力。

 

注意:

1.Celery的worker和Gunicorn同样须要启动,能够与flask服务放在一块儿经过supervisor来管理,这样他们能够保持协同工做。

2.Windows的Celery只支持到3.1.25,因此你若是在windows上调试,请指定该版本。

3.若是你的后台任务是操做数据库,操做完成后记得释放数据库链接,例如Session.remove(Sqlalchemy scoped_session).

3.具体的操做步骤可见参考

参考:

1.https://bluemedora.com/mysql-performance-max-connections/

2.使用Jmeter进行负载测试

3.在 Flask 中使用 Celery

4.官方:Celery - Distributed Task Queue

5.supervisor 启动 celery 及启动中的问题

相关文章
相关标签/搜索