windows 下 select.select()只能打开 500 出头个文件怎么解决? - V2EX
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
lithbitren
V2EX    Python

windows 下 select.select()只能打开 500 出头个文件怎么解决?

  •  
  •   lithbitren 2020-03-15 22:51:18 +08:00 7538 次点击
    这是一个创建于 2110 天前的主题,其中的信息可能已经有所发展或是发生改变。

    最近试了试一个新的框架 fastAPI,传说可做到到万级的 qps,想先在 win10 上试试,本来以未比不上 linux 应该还凑合吧,跟着教程搭起来了,啥都不说先来个 hello world 吧,跟 flask 本质上区别不大。

    from fastapi import FastAPI app = FastAPI() @app.get("/") def read_root(): return "Hello World" 

    结果并发一打就死了,秒内并发超过 510 直接就崩了,崩的非常稳定,一超 510 必崩,一次都不含糊。

    错误信息如下:

    Process SpawnProcess-1: Traceback (most recent call last): File "d:\python37\lib\multiprocessing\process.py", line 297, in _bootstrap self.run() File "d:\python37\lib\multiprocessing\process.py", line 99, in run self._target(*self._args, **self._kwargs) File "d:\python37\lib\site-packages\uvicorn\subprocess.py", line 61, in subprocess_started target(sockets=sockets) File "d:\python37\lib\site-packages\uvicorn\main.py", line 382, in run loop.run_until_complete(self.serve(sockets=sockets)) File "d:\python37\lib\asyncio\base_events.py", line 566, in run_until_complete self.run_forever() File "d:\python37\lib\asyncio\base_events.py", line 534, in run_forever self._run_once() File "d:\python37\lib\asyncio\base_events.py", line 1735, in _run_once event_list = self._selector.select(timeout) File "d:\python37\lib\selectors.py", line 323, in select r, w, _ = self._select(self._readers, self._writers, [], timeout) File "d:\python37\lib\selectors.py", line 314, in _select r, w, x = select.select(r, w, w, timeout) ValueError: too many file descriptors in select() 

    重点就是最后一句。

    # ValueError: too many file descriptors in select()错误和解决

    上面说 “因为 asyncio 内部用到了 select,而 select 就是系统打开文件数是有限度的,这个其实是操作系统的限制,linux 打开文件的最大数默认是 1024,windows 默认是 509,超过了这个值,程序就开始报错, 下面的代码一次性将处理 url 的函数作为任务扔进了一个超大的 List 中,这就引起了错误。”

    上面的解决方法是用 asyncio.Semaphore(500)限制协程并发数,但说的是爬虫,服务端直接这样设定是没用的,StackOverflow 大多说的也是这个。

    python 异步编程之 asyncio (百万并发)

    "假如你的并发达到 2000 个,程序会报错:ValueError: too many file descriptors in select()。报错的原因字面上看是 Python 调取的 select 对打开的文件有最大数量的限制,这个其实是操作系统的限制,linux 打开文件的最大数默认是 1024,windows 默认是 509,超过了这个值,程序就开始报错。这里我们有三种方法解决这个问题:

    1.限制并发数量。(一次不要塞那么多任务,或者限制最大并发数量)

    2.使用回调的方式。

    3.修改操作系统打开文件数的最大限制,在系统里有个配置文件可以修改默认值,具体步骤不再说明了。

    不修改系统默认配置的话,个人推荐限制并发数的方法,设置并发数为 500,处理速度更快。"

    其中第三点说可以改配置,我搜了半天也找不到这配置在哪改,网上有几个提到改注册表的,但试过了都没用,而且应该不是一回事。

    系统进程打开文件最大句柄数的限制

    根据最大打开文件数,又找到了一个 c 的解决方案,于是我试着引了一个 c 扩展执行,想着就觉得没用,事实也没用。

    c:

    void set_max_stdio() { printf("%d \n", _getmaxstdio()); // 默认输出 512 _setmaxstdio(2048); // 如果设定值大于 2048,就会变回 512 printf("setmaxstdio: %d \n", _getmaxstdio()); // 正常会输出 2048 } 

    python:

    from ctypes import cdll cdll.LoadLibrary("./setmaxstdio.so").set_max_stdio() # 会正常打印 2048,但对并发限制没有影响 

    Google,GitHub 和 StackOverflow 也找不到解决方案,当然大概率是因为英语太菜,找不到关联信息,

    所以问问大佬这个 select 的问题在 win10 里有没有可能解决,发出来大家讨论讨论。

    第 1 条附言    2020-03-16 19:47:22 +08:00

    谢谢大佬们的积极回应,学习了很多。

    set_event_loop(ProactorEventLoop()) 

    可以解决select报错的问题。

    不说了,我开Linux去了。

    53 条回复    2020-03-20 17:23:04 +08:00
    lithbitren
        1
    lithbitren  
    OP
       2020-03-15 22:53:02 +08:00
    服务端的代码粘错了,函数前面少了个 async,不过测起来都是一样的。
    ClericPy
        2
    ClericPy  
       2020-03-15 23:00:04 +08:00
    linux 上经常遇到超过最大 fds 数量, 得改内核配置 ulimit 什么的

    Windows... 何必要用这玩意做生产环境呢, 搜搜 Windows 怎么提高默认最大 file handles 或者 file descriptors 吧... 老早以前还真找过, 忘了什么结果了, 最后被友军劝退别用 Windows
    lithbitren
        3
    lithbitren  
    OP
       2020-03-15 23:06:45 +08:00
    @ClericPy 不是生产环境,就是当玩具试试而已,开 windows server 被劝退太正常了 [捂脸]
    zhuangzhuang1988
        4
    zhuangzhuang1988  
       2020-03-15 23:31:00 +08:00
    用 uvloop 试试呗,
    本身 select 就不生产环境的
    unixeno
        5
    unixeno  
       2020-03-16 01:14:21 +08:00 via Android
    Windows 可能得换 iocp ?
    geelaw
        6
    geelaw  
       2020-03-16 05:15:23 +08:00 via iPhone
    为什么不搜索一下呢?

    https://stackoverflow.com/questions/870173/is-there-a-limit-on-number-of-open-files-in-windows

    C 运行时默认会限制最多同时有 512 个流式访问的 FILE,以及最多同时有 8192 个文件描述符,超过该限制则需要使用 Win32 CreateFile 系列 API,而一个进程可以同时使用的文件句柄数量的最大值至少是 16384。
    wwqgtxx
        7
    wwqgtxx  
       2020-03-16 06:27:55 +08:00 via iPhone
    自己把 asyncio 的 loop 换成 ProactorEventLoop 的即可,或者升级到 python3.8 默认就是这个
    楼上说 uvloop 的难道不知道 uvloop 根本就不支持 windows 么…
    wwqgtxx
        8
    wwqgtxx  
       2020-03-16 06:33:17 +08:00 via iPhone
    当然,还有些比较麻烦的办法解决(我自己干过)
    用 ctypes/cffi 加载一个 c 库 wepoll ( github 上有源码,可以自行修改后编译成 dll )
    然后基于 wepoll 的 api 写一个 Selector ( selector 包源代码里有 epoll 的 selector,可以拷贝出来,让其调用 wepoll ),在加载 asyncio 前设置为默认 Selector
    Trim21
        9
    Trim21  
       2020-03-16 07:20:50 +08:00 via Android
    试试 uvicorn 的限制并发的参数?
    Mithril
        10
    Mithril  
       2020-03-16 09:45:02 +08:00   3
    拿为 Linux 设计的东西往 Windows 上套,然后怪 Windows 垃圾。。。
    lithbitren
        11
    lithbitren  
    OP
       2020-03-16 14:07:10 +08:00
    @geelaw 为什么不好好看一下主楼的帖子? stackoverflow 这帖子我早看了,问题是发现了,但没有可靠的解决方案,我写了 c 扩展不行,后面在 pywin32 模块里面也找到了这个函数也没用。
    lithbitren
        12
    lithbitren  
    OP
       2020-03-16 14:09:15 +08:00
    @wwqgtxx 这个我之前也试过了。
    from asyncio import ProactorEventLoop, get_event_loop
    from uvicorn import Config, Server, run
    loop = ProactorEventLoop()
    cOnfig= Config(app=app, loop=loop)
    server = Server(cOnfig=config)
    loop = get_event_loop()
    loop.run_until_complete(server.serve())
    没用,我的环境是 3.7,升到 3.8 有一堆库用不了,于是又降回来了,迟早要升的,不过不是现在。
    lithbitren
        13
    lithbitren  
    OP
       2020-03-16 14:09:53 +08:00
    @Trim21 不太懂,设了 ProactorEventLoop 似乎没用
    lithbitren
        14
    lithbitren  
    OP
       2020-03-16 14:12:04 +08:00
    @Mithril 手头上只有 win10,我也明白大部分服务都是为 linux 设计的,但我上 fastAPI 的官网明明也是说支持 win 的,我按照教程完整走了一遍,其他都没啥大问题,就是这个并发数在 win 上不行,我在 GitHub 的官方社区问过,contributor 也没能解决这个问题。
    CallMeReznov
        15
    CallMeReznov  
       2020-03-16 14:17:19 +08:00
    LINUX 可以修改这个参数,可以修改到 1024 或者更多.
    但 WINDOWS 好像就没有办法了,就是 512.
    我之前尝试在 windows 上用 haproxy,就因为这个限制了并发而放弃,后来也没有深究.
    lithbitren
        16
    lithbitren  
    OP
       2020-03-16 14:18:31 +08:00
    @wwqgtxx wepoll 我研究研究看看,c 我只会写算法,底层太菜了。
    geelaw
        17
    geelaw  
       2020-03-16 14:23:20 +08:00
    @lithbitren #11 在 #6 暗示的答案就是使用 CreateFile 系列 API 而不是用 C 运行时的文件。你的贴子并没有表现出你知道 CreateFile 限制更少。

    另外一个很自然的问题是:你的 C 扩展使用的 C 运行时是 Python 的那一个,还是它有自己的 C 运行时?如果 C 扩展改变的是自己的运行时的设置,当然对 Python 不会有改变。
    lithbitren
        18
    lithbitren  
    OP
       2020-03-16 14:26:57 +08:00
    现在的绥靖方案是 Nginx 加多进程,也可以达到每秒 qps 过千,本机服务基本可以做到全 200,fastAPI 作为 python 框架还是挺快的,其他 py 框架在 cpython 上运行很难达到这个速度。
    lithbitren
        19
    lithbitren  
    OP
       2020-03-16 14:32:37 +08:00
    @geelaw 一开始确实是不知道,后来试过了就知道在 Python 程序里直接运行这个是没用的,还请大佬明示在哪设置可以作用到 Python 的运行上。
    yulon
        20
    yulon  
       2020-03-16 14:46:26 +08:00   1
    不管是 Win 还是 Linux,用 select 就是玩具,不要在玩具上浪费时间
    ysc3839
        21
    ysc3839  
       2020-03-16 15:01:35 +08:00
    @lithbitren #12 所以你改用 ProactorEventLoop 之后遇到了什么问题?错误信息?
    lithbitren
        22
    lithbitren  
    OP
       2020-03-16 15:03:11 +08:00
    @ysc3839 就是并发高的时候还是主楼那个 select 错误一模一样的,没有其他错误信息。
    ysc3839
        23
    ysc3839  
       2020-03-16 15:04:43 +08:00
    @lithbitren 一模一样的话说明还用的是 select 吧?
    lithbitren
        24
    lithbitren  
    OP
       2020-03-16 15:06:47 +08:00
    @ysc3839 嗯,我也不懂怎么不用 select,可能是框架的问题,这框架我才上手一天多,源码还没下决心去看。。
    Mithril
        25
    Mithril  
       2020-03-16 17:47:20 +08:00
    @lithbitren Windows 和 Linux 用来支持高性能 IO 的接口设计模式是不一样的。所以很多库没法同时支持这两个系统,或者同时在这两个系统上达到最高的性能。顶多就是两边都能用而已。用一种模式模拟另一种总会有很多的妥协。
    loqixh
        26
    loqixh  
       2020-03-16 17:50:55 +08:00 via Android
    @wwqgtxx win 可以用 uv,性能很好的
    wwqgtxx
        27
    wwqgtxx  
       2020-03-16 18:39:47 +08:00 via iPhone
    @loqixh 我说的是 uvloop,你确定 uvloop 支持 win 了么,还是说你说的是 libuv ?
    https://github.com/MagicStack/uvloop/issues/14
    wwqgtxx
        28
    wwqgtxx  
       2020-03-16 18:42:09 +08:00 via iPhone
    @lithbitren 按说如果你改用 ProatorEventLoop 之后还出现一样的问题肯定是修改 loop 没生效你再好好的检查你的代码吧
    lithbitren
        29
    lithbitren  
    OP
       2020-03-16 19:19:52 +08:00
    @wwqgtxx

    按官方教程是用命令行启动 uvicorn 的,后来去了 Github 问了,考虑到要改东西,所以改成用 if main 启动了,测试过其他状态都正常,仍然是只有并发数顶死 510,就这么简单的代码,我也不晓得该咋改了。

    ====main.py====

    from asyncio import ProactorEventLoop, get_event_loop
    from uvicorn import Config, Server
    from fastapi import FastAPI

    app = FastAPI()

    @app.get('/')
    async def index_post():
    return 'hello world'

    if __name__ == "__main__":
    server = Server(
    cOnfig=Config(
    app=app,
    loop=ProactorEventLoop()
    )
    )
    get_event_loop().run_until_complete(
    server.serve()
    )
    lithbitren
        30
    lithbitren  
    OP
       2020-03-16 19:23:55 +08:00
    @wwqgtxx 这个方法是 Github 的 contributor 教的,最后他说 honestly I dont know 了。
    black11black
        31
    black11black  
       2020-03-16 19:31:44 +08:00
    你不了解多路复用机制在这里问这些别人很难回答你...
    所有声称异步框架性能测试 100%基于 linux,你一定说我就要 windows,那答案就是简单的没办法。select 由于轮寻机制即时解锁 fd 上限性能一样会挂,win 下 iocp 在 py 内目前没有生产级封装。
    Qzier
        32
    Qzier  
       2020-03-16 19:36:41 +08:00   1
    if sys.platform == 'win32':
    loop = asyncio.ProactorEventLoop()
    asyncio.set_event_loop(loop)
    else:
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)
    Qzier
        33
    Qzier  
       2020-03-16 19:37:10 +08:00   1
    lithbitren
        34
    lithbitren  
    OP
       2020-03-16 19:43:40 +08:00
    @Qzier 谢谢大佬指点,qps 虽然上不去,但不会崩了。
    lithbitren
        35
    lithbitren  
    OP
       2020-03-16 19:45:13 +08:00
    @black11black 嗯啊,是不太了解机制,只会一些应用,没有说一定要用 win10,只是碰到了问题,搜不出答案,所以拿出来讨论讨论,学习一下。
    v2eeeeee
        36
    v2eeeeee  
       2020-03-16 19:53:08 +08:00
    请教下大佬们
    网络流的高并发,单机做到万级的 qps,是因为网络流由 "有限的进程" 维护对吗?
    (不是一个进程维护一个网络流这样?如果是那样,万级 qps 不得有万级进程来维护? linux 的 max user processes 也吃不消吧
    lithbitren
        37
    lithbitren  
    OP
       2020-03-16 20:12:24 +08:00
    @Qzier ...不好意思,我傻-逼了,刚刚可能是卡了,访问功率低了,重启了以后访问功率超过 510,还是不行,还是会爆 select 错。。。
    black11black
        38
    black11black  
       2020-03-16 20:12:53 +08:00
    @lithbitren 你不了解机制,最好的解决办法就是装 linux,windows 的中断嵌套机制与 linux 各有取舍,就不是为网络服务设计的。装了 linux 你随意使用 epoll,uvloop 随便装,哪像 windows 这样缩手缩脚。系统级事务切换耗时的数量级也不一样。之前有过测试,自建事件循环,同样的 py 代码,即时只封装生成器做简单的事务切换,win 与 linux 的耗时表现也是完全不同
    lithbitren
        39
    lithbitren  
    OP
       2020-03-16 20:16:53 +08:00
    @Qzier 没事了没事,是我搞错了,还是可以的,我傻-逼*2
    lithbitren
        40
    lithbitren  
    OP
       2020-03-16 20:17:34 +08:00
    @black11black 嗯,说的有道理,还是 Linux 做服务稳定,遇到问题也好解决。
    janxin
        41
    janxin  
       2020-03-16 20:43:02 +08:00
    Windows 是事件循环实现的问题导致的,测试需要在 Linux 下进行,Mac 下的性能也不如 Linux
    janxin
        42
    janxin  
       2020-03-16 20:46:17 +08:00   1
    进程资源消耗比线程要重很多,所以不会是一个进程对应一个连接。

    这个问题过于广,建议还是看一下比较详细的描述
    http://www.kegel.com/c10k.html
    v2eeeeee
        43
    v2eeeeee  
       2020-03-16 20:52:42 +08:00
    @janxin 谢谢你!线程按说应该也是有个上限,很好奇万级 qps 的压测发起段是怎么做到的
    roundgis
        44
    roundgis  
       2020-03-17 00:30:55 +08:00
    windows 有个宏叫 FD_SETSIZE cpython 设置为 512, 你可以手动改大一些,例如 16384 之类就可以支持更大的并发了

    当然 windows 最好的还是用 IOCP
    lithbitren
        45
    lithbitren  
    OP
       2020-03-17 01:28:24 +08:00
    @roundgis FD_SETSIZE 怎么改啊,大佬明示。
    ysc3839
        46
    ysc3839  
       2020-03-17 02:10:13 +08:00
    @lithbitren 写在系统头文件里面的,改了要重新编译。
    msg7086
        47
    msg7086  
       2020-03-17 04:54:30 +08:00
    三个系统的事件接口都不一样,Windows 下是 IOCP,Linux 下是 epoll,BSD 下是 kqueue,生产环境下肯定要看是否支持这个系统的 API。如果你的运行环境只支持 epoll,那 Windows 和 BSD 下就只能降级运行了。
    loqixh
        48
    loqixh  
       2020-03-17 11:36:07 +08:00 via Android
    @wwqgtxx 汗,不好意识,我搞错了,我以为说的是 libuv 里的 uvloop 函数
    roundgis
        49
    roundgis  
       2020-03-17 14:48:04 +08:00
    @lithbitren cpython 我记得没错 select module 重新编译就好了
    lithbitren
        50
    lithbitren  
    OP
       2020-03-17 15:03:20 +08:00
    @roundgis 我也不是想当伸手党,不过重新编译 cpython 这真不太懂,也找不到清晰的教程,select 模块我也找不到在 python 根目录的对应文件,怕乱搞搞崩了。。
    lithbitren
        51
    lithbitren  
    OP
       2020-03-18 17:23:45 +08:00
    确认了一下,uvicorn 在参数设置里可以把 loop 设置为 uvloop 和 iocp,不过这两个选项在 windows 里都不支持,只支持 auto 的 asyncio。
    ruanimal
        52
    ruanimal  
       2020-03-20 15:04:05 +08:00
    lz 探索操作系统的精神值得肯定,学习框架的效率就不行了
    lithbitren
        53
    lithbitren  
    OP
       2020-03-20 17:23:04 +08:00
    @ruanimal 过完了一遍才测性能的,web 框架大体都是相通的,过一遍文档了解 API 的存缺情况就差不多了,而且一般也不会拿纯 win 做生产环境,这种问题在框架里一般也很难碰上。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     1115 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 31ms UTC 17:56 PVG 01:56 LAX 09:56 JFK 12:56
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86