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

2022-03-03 18:09:21 +08:00
 DesmondLeo

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

是这样,这是一个爬虫项目的主程序入口,下面代码里面的 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()
        
2381 次点击
所在节点    Python
9 条回复
fgwmlhdkkkw
2022-03-03 18:24:51 +08:00
……你用命令行能连上数据库吗?
DesmondLeo
2022-03-03 18:37:23 +08:00
@fgwmlhdkkkw 命令行可以
vvhhaaattt
2022-03-03 18:54:43 +08:00
我觉得先确认你不用多进程时,代码能正常连接不。
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
2022-03-03 23:30:34 +08:00
你都没有指定 host 跟 port ,让它去连啥啊
dudu2017
2022-03-03 23:52:41 +08:00
mongo_connect 方法是你自己封装的吧,这里的代码是不是也应该贴一下?
DesmondLeo
2022-03-04 10:08:53 +08:00
@vvhhaaattt 不用的时候可以
DesmondLeo
2022-03-04 10:13:40 +08:00
@ch2 那个 mongo_connect 是封装的方法,会读取配置文件里面的数据库配置
@dudu2017 这个确实是封装的,但其实也只是内部调用了下 MongoClient 这个类,参数都从配置文件里面读取
DesmondLeo
2022-03-04 10:22:08 +08:00
@ClericPy 啊好详细的回复😃,我昨天后来在排查的时候感觉应该是死锁(感觉),那个 ThreadProcessExecutor 昨天我也试过了,效果一样 2333 ,应该不是这个问题,我今天再看看怎么处理

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/837772

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX