Flask和mysql多线程_Flask中的后端并发思考(以Mysql:too many connections为例)

在进行压力测试时,发现Flask应用中的一个写入MySQL日志的接口出现数据库连接过多的错误。为解决此问题,文章首先尝试通过增加MySQL的最大连接数,但发现这不是长期解决方案。于是转向使用异步处理,引入Celery库将日志写入任务放到后台执行,以减少数据库压力和提高响应速度。Celery配合Redis作为消息中间人,实现任务队列,确保Flask能持续接收新请求并分散高并发时期的写入压力。
摘要由CSDN通过智能技术生成

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

ERROR 1040: Too many connections

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

show variables like 'max_connections';+-----------------+-------+

| Variable_name | Value |

+-----------------+-------+

| max_connections | 151 |

+-----------------+-------+

1 row in set (0.00 sec)

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

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

2.极端高并发情况下,只是允许更大的连接,可是Mysql的I/O瓶颈还是会造成有些服务如果等待Mysql操作完成再返回很可能很久甚至超时。

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

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

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

所以找到了这个异步操作的Python库Celery, 简单来说就是把耗时的操作丢给他去处理,Flask(或者说Gunicorn)不管这个操作而在处理完成请求之后直接返回。

对于为什么Flask应用一步步加上了Redis, 加上了Gunicorn(Gevent),到现在需要Celery, 我画了几张张图来理解。

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

2aabaefecef765987ffb055d6b87562e.png

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

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

Use a production WSGI server instead.

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

d2bde721867a45a19311c5c4476167ff.png

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

685de77dea5c499f6aa4437c969abb67.png

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

9c1a4794a2b365e2945a0908a7ca7421.png

也就是说每次请求,如果还有对数据库的写入,那么我们把它延迟执行而不是等它执行完毕才返回给客户。这样子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/

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值