多线程下pymongo连接数过多的问题处理

知道创宇博客
知道创宇博客
创建于2022-2-24 阅读
12
阅读原文

背景

项目中有个应用,用到了多线程,线程中会有对MongoDB的操作。一般涉及到这种场景,都会小心MongoDB的连接的数量控制。

实现这个应用的时候必然已经考虑这点了,但实际碰到的情况是:多线程的程序运行起来,MongoDB连接数比预期多。

涉及的环境:

  • Linux
  • Python 2.6.5
  • MongoDB 2.0.0
  • MongoDB的驱动是pymongo。本文涉及的版本有4个:1.1.1、2.0.1、2.1.1,以及最新的2.2rc1。

过程

简化的代码

先用一段简化的代码来说明下这个问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#-*- encoding:UTF-8 -*-
 
import threading
import time
 
import pymongo
 
single_conn = pymongo.Connection('mongodb://127.0.0.1', max_pool_size=1)
 
class SimpleExampleThread(threading.Thread):
    def run(self):
        print single_conn['fvck'].damnit.count(), self.getName()
        time.sleep(10)
 
for i in range(100):
    SimpleExampleThread().start()

代码的大致意思是创建1个MongoDB连接,然后生成100个线程,在线程中会有对MongoDB的操作

具体使用方式如下:

  • 编辑上述代码,保存为test_pymongo.py
  • 启动MongoDB,确保可以用127.0.0.1访问
  • 运行MongoDB自带的命令行工具mongostat,关注conn这一列,这个时候应该数量是1
  • 执行测试程序,python test_pymongo.py
  • 观察mongostat的conn这一列,猜是多少?2?这是预想的,实际竟然是101
  • 确认pymongo版本是2.0.1

怀疑pymongo版本太旧

  • 升级到最新的正式版本2.1.1
  • sudo pip install pymongo==2.1.1 --upgrade
  • 执行测试程序结果一样,已然有连接数过多的问题

确认自己程序

  • 确认没问题。上面程序只是个实际程序的简化的代码。

意外确认问题

  • 这个这个时候一般会去查看下pymongo的源代码,分析下实现原理
  • 偷个懒。刚好停下来没事,随便瞎逛了下MongoDB的缺陷跟踪系统。想可能是不是pymongo的缺陷,刚好搜到了这个缺陷, https://jira.mongodb.org/browse/PYTHON-287
  • 这个issues大致意思是每个线程还是会创建一个MongoDB的连接,希望能支持线程间共享1个MongoDB连接
  • pymongo预计解决的版本是2.2,一看,竟然刚好是发现改问题的当天发布该版本。果然,当天发布了2.2rc1这个版本

更新到2.2rc1

    • 因为不是正式版本,所以单独下载安装

1
2
3
4
5
wget https://github.com/mongodb/mongo-python-driver/tarball/2.2rc1
tar xzvf 2.2rc1
cd mongodb-mongo-python-driver-4c7edfc/
sudo python setup.py install
python test_pymongo.py

  • 但失败了,和2.0.1和2.1.1的表现一样

RTFM

  • 查看文档: http://api.mongodb.org/python/2.2rc1/examples/requests.html
  • 发现是程序写法不对,修改的代码如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    #-*- encoding:UTF-8 -*-
     
    import datetime
    import threading
    import time
     
    import pymongo
     
    conn = pymongo.Connection('mongodb://127.0.0.1', auto_start_request=False)
     
    class MyThread(threading.Thread):
        def run(self):
            print conn['fvck'].damnit.count(), datetime.datetime.now(), self.getName()
            time.sleep(10)
     
    with conn.start_request():
        for i in range(100):
            MyThread().start()
  • 运行程序,连接数符合预期数量是2
  • 关键参数是:auto_start_request=False,这个参数看源码历史,貌似从pymongo1.1.1开始移除了,在2.2rc1这个版本又恢复了,但应该有新的用处了吧。

最后

  • 问题至此,从项目角度,算是告一段落,如果从纯技术研究角度,还可以继续深入
  • 比如:至于pymongo如何处理连接数,谁有兴趣,可以深入下pymongo的源码,貌似可以从这3个py入手,估计可以另起一篇文章来讲解:
    • pymongo/connection.py
    • pymongo/database.py
    • pymongo/pool.py

参考

  •  https://jira.mongodb.org/browse/PYTHON-287
  •  http://api.mongodb.org/python/2.2rc1/examples/requests.html