当你的web网站一个很普通的动态页面都很卡,但是静态图片却不卡,云服务器CPU也不高,那么就是gunicorn阻塞了导致的,可能同时连接的人太多了,这个时候就需要使用gevent来配合使用了。
最近总是发生这种情况,一个什么都没有的动态页面也要20秒才能打开。
以前是尝试重启服务器等,能恢复正常,后来重启服务器也不行了,看来还是要解决问题,这时候想到gunicorn不是异步的,就打算试试使用gevent异步模式。
第一步、用pip安装gevent库
source /var/virtualenvs/mysite_venv/bin/activate
pip install gevent
第二步、修改gunicorn配置文件
cd /mnt/www/mysite
vi gunicorn.conf
最后一行加上gevent模式,整个文件内容如下
# 进程为5
workers = 5
# 监听本地8000端口,之后这个端口来的就是看这个网站的。
bind = '127.0.0.1:8000'
#搭配gevent使用
worker_class="gevent" #sync, gevent,meinheld
保存后 重启gunicorn,我是用supervisor,所以直接
supervisorctl reload
这样就生效了,很简单吧。其他地方都不需要修改,就上面简单两步搞定。
gevent模式启用后瞬间秒开网站。
第三步,完成兼容性改造,释放性能
1.导入所有其他库前先执行:
from gevent import monkey
monkey.patch_all()
否则:
socket 没被正确打补丁
SQLAlchemy 连接池阻塞
Flask 的 werkzeug 可能表现异常

image.png
- scoped_session上下文由线程安全thread-local改为协程安全greenlet-local
Flask 的应用上下文(app_context)和请求上下文(request_context)默认是为多线程设计的。
SQLAlchemy 的 scoped_session 默认用 线程本地(thread-local) 存储。
但 Gevent 是协程模型(Greenlet),多个协程运行在同一个线程中。不同请求的 SQLAlchemy Session 可能在 Gevent 下相互污染。
#修改 scoped_session 的 local 普通 thread-local → greenlet-local(推荐)
from sqlalchemy.orm import scoped_session, sessionmaker
import greenlet
Session = scoped_session(sessionmaker(), scopefunc=greenlet.getcurrent)
3.SQLAlchemy 默认连接池数量是5,太小了,在 gevent 下容易被用光。
配置更大的连接池:
engine = create_engine(
DB_URL,
pool_size=20,
max_overflow=40,
pool_recycle=1800,
pool_pre_ping=True
)

image.png
副作用:连接池超限
如果网站需要访问数据库,请慎用gevent,因为SqlAlchemy还是阻塞的,这样只会导致数据库连接数疯狂增加,从而超出连接池数量,然后所有要访问数据库的页面都会打不开了。
代码报错:
sqlalchemy.exc.TimeoutError: QueuePool limit of size 10 overflow 10 reached,
症状:
静态文件打得开,不访问数据库的页面秒开,要访问数据库的页面打不开
生产环境应用结果
实际解决问题:2个系统(短链接和合伙人系统)
未解决问题:1个系统(喵主站,产生上面提到的副作用)