V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
DesmondLeo
V2EX  ›  Python

关于 MongoDB 在 concurrent.futures.PoolProcess 开启多进程下的报错解决办法

  •  
  •   DesmondLeo · 2022-03-03 18:09:21 +08:00 · 2540 次点击
    这是一个创建于 987 天前的主题,其中的信息可能已经有所发展或是发生改变。

    该帖子有点长哈...不好意思🤪

    是这样,这是一个爬虫项目的主程序入口,下面代码里面的 mongo_connection = mongo_connect( connect = False, server_selection_timeoutMS = 60000 ) 用来连接数据库,但我在服务器上测试的时候发现一直提示 timeout 报错(详见上面的报错信息)

    我查了不少文章,看到不少说是要在多进程开启后再给每一个进程连接 mongoDB ,所以一开始我将连接数据库的代码放到了三个爬取程序的开头(就是下文那三个 parser() 函数),但依旧报同样的错; 然后我在 GitHub 上面看到了一个关于多进程下 Pymongo 提示 UserWarning 的解答,里面提到了可以添加 connect = False 参数,但我试过还是不行,应该问题也不在这里; 然后我又修改了 serverSelectionTimeoutMS ,将时间改为 60 秒(嗯当然还是没用)

    求大佬指点一下怎么在使用 concurrent.futures 开启多进程的情况下连接 mongoDB🙏🏽🙏🏽🙏🏽!谢谢!

    下面贴了环境、报错信息和代码

    测试环境

    CentOS 7.8.2003
    python = 3.7.7

    依赖包版本
    pymongo == 4.0.1
    concurrent:Python 自带

    报错信息

    该信息来源于日志

    ERROR: 192.168.1.202:27017: timed out, Timeout: 60.0s, Topology Description: <TopologyDescription id: 622086a8ea6efc1d07de056f, topology_type: Unknown, servers: [<ServerDescription ('192.168.1.202', 27017) server_type: Unknown, rtt: None, error=NetworkTimeout('192.168.1.202:27017: timed out')>]> 
    

    主程序脚本

    import pymongo
    from concurrent.futures import PoolProcessExecutor, as_completed
    # 导入外部自定义模块
    import ...
    
    def run_spider(log_recorder, proxy):
        """
        主程序,可启动目标网页的爬取程序
        
        :param log_recorder: 日志记录工具
        :param proxy: 项目专用代理
        :return: 
        """
        
        log_recorder.info( "Spider starts running! :)" )
        try:
            # 连接数据库
            mongo_connection = mongo_connect( connect = False, server_selection_timeoutMS = 60000 )
            try:
                # 主程序运行
                council_parser( mongo_connection, log_recorder, proxy )
                gd_province_parser( mongo_connection, log_recorder, proxy )
                market_supervisor_parser( mongo_connection, log_recorder, proxy )
                log_recorder.info( "Spider ran over! :)" )
                # 爬取结束后断开数据库连接
                mongo_connection.close()
            except Exception as e:
                log_recorder.error(f"{e} :(")
                # 出现异常依然断开连接
                mongo_connection.close()
        except Exception as e:
            log_recorder.error(f"{e}:(")
            
        
    if __name__ == "__main__":
        # 设置日志工具
        regulation_logger = spiderLogger.get_logger('regulation')
        # 设置代理
        proxy = get_proxy()
        # 多进程并发
        with ProcessPoolExecutor(max_workers = 8) as executor:
            fs = []
            futures = executor.submit(run_spider, regulation_logger, proxy)
            fs.append(futures)
            for future in as_completed(fs):
                result = future.result()
            
    
    第 1 条附言  ·  2022-03-04 10:18:46 +08:00

    mongo_connent方法代码:

    def mongo_connect(connect = True, server_selection_timeoutMS: int = 60000):
        
        mongo_connection = MongoClient(
            server = settings.DATABASES['mongodb']['HOST'],
            port = settings.DATABASES['mongodb']['PORT'],
            username = settings.DATABASES['mongodb']['USER'],
            password = settings.DATABASES['mongodb']['PASSWORD'],
            db = settings.DATABASES['mongodb']['db'],
            connect = connect,
            serverSelectionTimeoutMS = server_selection_timeoutMS
        )
    
        return mongo_connection
    
    9 条回复    2022-03-04 10:22:08 +08:00
    fgwmlhdkkkw
        1
    fgwmlhdkkkw  
       2022-03-03 18:24:51 +08:00
    ……你用命令行能连上数据库吗?
    DesmondLeo
        2
    DesmondLeo  
    OP
       2022-03-03 18:37:23 +08:00
    @fgwmlhdkkkw 命令行可以
    vvhhaaattt
        3
    vvhhaaattt  
       2022-03-03 18:54:43 +08:00 via Android
    我觉得先确认你不用多进程时,代码能正常连接不。
    ClericPy
        4
    ClericPy  
       2022-03-03 23:01:38 +08:00
    1. mongo_connect 哪来的有点陌生, 这东西不好调试的话临时闭包到 run_spider 里测试一下? 怀疑有些变量传递到进程里面有地方不太对, 多进程类似于各种并行计算的一个特点就是: 尽量无状态无副作用, 里面的各种依赖 /参数 /上下文都尽量隔离干净
    2. 本地调试可以运行但线上不行的话, 可能的地方特别多(难怪虚拟化和 go 那么火了)
    2.1 最大可能是网络本身就不通(防火墙规则啊, 网卡 ip 不对啊, 非局域网啊, 非开放端口啊), 但是如果关了多进程会 ok, 网络应该不是问题. 可以临时把 PoolProcessExecutor 改成多线程那个, 反正接口一样的, 而且爬虫这玩意本来就没必要多进程
    2.2 Python 版本有区别, 个别底层依赖有差异, 这个少见但也遇到过
    2.3 第三方依赖的版本或者 C 依赖有差异, 尽量排除这种情况, 以前我遇到过类似情况, 换个版本居然就通了
    2.4 检查是不是每个进程都超时, 有一定可能是程序里面或者 mongodb 那边有奇怪的死锁
    ch2
        5
    ch2  
       2022-03-03 23:30:34 +08:00 via iPhone
    你都没有指定 host 跟 port ,让它去连啥啊
    dudu2017
        6
    dudu2017  
       2022-03-03 23:52:41 +08:00
    mongo_connect 方法是你自己封装的吧,这里的代码是不是也应该贴一下?
    DesmondLeo
        7
    DesmondLeo  
    OP
       2022-03-04 10:08:53 +08:00
    @vvhhaaattt 不用的时候可以
    DesmondLeo
        8
    DesmondLeo  
    OP
       2022-03-04 10:13:40 +08:00
    @ch2 那个 mongo_connect 是封装的方法,会读取配置文件里面的数据库配置
    @dudu2017 这个确实是封装的,但其实也只是内部调用了下 MongoClient 这个类,参数都从配置文件里面读取
    DesmondLeo
        9
    DesmondLeo  
    OP
       2022-03-04 10:22:08 +08:00
    @ClericPy 啊好详细的回复😃,我昨天后来在排查的时候感觉应该是死锁(感觉),那个 ThreadProcessExecutor 昨天我也试过了,效果一样 2333 ,应该不是这个问题,我今天再看看怎么处理
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1102 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 22:46 · PVG 06:46 · LAX 14:46 · JFK 17:46
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.