最近试了试一个新的框架 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 大多说的也是这个。
"假如你的并发达到 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 里有没有可能解决,发出来大家讨论讨论。
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.