最近自己的 Python Web 程序出了一个非常诡异的问题:不同用户的数据库查询结果会串。A 用户会读到 B 用户的数据库查询结果,B 用户可能又读到了 C 用户的查询结果,乱成一团,Sentry 疯狂报警。一开始以为是自己程序改错了,直到后来慢慢怀疑是修改了 uWSGI 配置文件引起的问题。
在发生故障之前,我的 uWSGI 并发模式是多线程,因为 Python 的 GIL 有限制,所以改成了多进程。事实上,就是这个引起了问题。
根据官方的 Things to know 页面(https://uwsgi-docs.readthedocs.io/en/latest/ThingsToKnow.html),uWSGI 的多进程对于 fork 机制是有一些使用不当的。默认情况下会在 app 初始化之后 fork,而不是在初始化之前,因为我的程序在初始化的时候建立连接池,所以导致了数据库连接池混乱:
uWSGI tries to (ab)use the Copy On Write semantics of the fork() call whenever possible. By default it will fork after having loaded your applications to share as much of their memory as possible. If this behavior is undesirable for some reason, use the
lazy-apps
option. This will instruct uWSGI to load the applications after each worker’sfork()
. Beware as there is an older options namedlazy
that is way more invasive and highly discouraged (it is still here only for backward compatibility)
所以,如果要使用多进程的话,我们需要把 lazy-apps 这个选项修改为 true。