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
wuwukai007
V2EX  ›  Python

requests 多线程环境中 报 [WinError 10048] 通常每个套接字地址(协议/网络地址/端口)只允许使用一次

  •  
  •   wuwukai007 · 2020-03-04 17:07:46 +08:00 · 4508 次点击
    这是一个创建于 1728 天前的主题,其中的信息可能已经有所发展或是发生改变。
    环境 windows python3.6 jupyter notebook
    没有使用 requests.Session,只是单纯的 get 请求,
    我把 线程数量控制在 os.cpu_count()后问题解决,
    但是 这样就只能开 8 个线程,效率就低很多了。
    
    5 条回复    2020-03-08 00:02:26 +08:00
    cz5424
        1
    cz5424  
       2020-03-04 19:00:23 +08:00
    你用多进程试试?
    ysc3839
        2
    ysc3839  
       2020-03-04 19:06:11 +08:00 via Android
    怀疑是连接池的什么 bug ?多个线程同时请求的时候重复使用了已经建立连接的 socket。
    不过具体如何还得调试看看了,得先找到哪出的错误,再一步步回溯。
    1462326016
        3
    1462326016  
       2020-03-05 09:38:45 +08:00
    直接 get 的话其实内部就是 with session as xx: 所以你其实是每一个请求开了一个连接池,可能频繁的发送请求,会导致系统回收一些端口不及时导致这个问题。
    两个方案:
    1. 多个线程共享一个 session,注意加锁,因为目前没办法证明它是线程安全的,我也没详细看过源码。
    2. 使用异步,按照你现在的情况来看应该不会是很复杂的请求,只是效率问题。异步可以增加效率。例如 aiohttp。
    ClericPy
        4
    ClericPy  
       2020-03-05 09:58:49 +08:00
    楼主也不给点代码看看到底哪的问题, 这样提问题让人很困扰啊, 只看报错的话, 不像是客户端(也就是 requests) 的问题, 反而像是你在同一个端口下开了多个 server 报的错, 没有代码也猜不了更多了

    另外提 requests 并发的几个常识吧

    1. 多线程并不是越多越快, 毕竟压根又用不到多核 CPU, 直接用官方建议的并发数比较合理, https://docs.python.org/zh-cn/3/library/concurrent.futures.html#concurrent.futures.ThreadPoolExecutor , 以前默认为机器处理器的个数, 3.8 以后又建议了 min(32, os.cpu_count() + 4).

    2. 如果是 Windows 操作系统, 那就更不用考虑把并发数开大了, 别说开到几千, 开到五六百可能就超了 Windows 的单进程默认最大 文件描述符(句柄) 限制而报错了, 在 GIL 的作用下, 走协程+多路复用的路子比传统多线程要合理的多.

    3. 如果真想要性能, requests 比 aiohttp 慢了 4 倍, 而且还是在 Windows 上无法启用 uvloop 提速的前提下, 协程开销比线程小很多, 也快很多, aiohttp 有 Cython 加成, 也比同样是协程的 httpx 快一大截.

    4. requests 的 Session 是共享连接池的一套逻辑, 速度比 requests.get 快一大截, 毕竟后者每次要开启一个新的 Session, 也就创建新的连接. PS: 就我目前测试结果来看, 是线程安全的, 没必要加无谓的锁

    5. 突破默认 http 适配器连接数上限也可以用以下代码来实现

    custom_adapter = HTTPAdapter(
    pool_connections=n, pool_maxsize=n)
    session.mount("http://", custom_adapter)
    session.mount("https://", custom_adapter)
    wuwukai007
        5
    wuwukai007  
    OP
       2020-03-08 00:02:26 +08:00
    @ClericPy 多谢回答,我改成了 gevent 方式做异步处理,效率提高了蛮多的,也不存在套接字那个问题了,就是有一个缺点,gevent 在 windows 中套接字限制 1024,所有 只能把每次的请求数量限制在 1000 之内,看了下 asyncio 的文档,应该也会有这个限制,windows 问题。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3585 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 31ms · UTC 04:36 · PVG 12:36 · LAX 20:36 · JFK 23:36
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.