日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > python >内容正文

python

Python 中如何解决 asyncio 文件描述符最大数量限制问题

發(fā)布時間:2023/12/10 python 21 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Python 中如何解决 asyncio 文件描述符最大数量限制问题 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.


文章目錄

    • 問題復(fù)現(xiàn)
    • 問題分析
      • 事件循環(huán) EventLoop
      • I/O 多路復(fù)用
      • select 的缺點
    • 解決方法
      • 1.更換事件循環(huán)選擇器
      • 2.限制并發(fā)量
      • 3.修改最大文件描述符限制
        • Windows
        • Linux
    • 總結(jié)
      • Windows
      • Linux


問題復(fù)現(xiàn)

Windows 平臺下,Python 版本 3.5,使用異步框架 asyncio,有時候會出現(xiàn) ValueError: too many file descriptors in select() 的報錯信息,今天我們就來聊一下為什么會出現(xiàn)這種問題,以及問題的一些解決方法。

寫一個小 dome 復(fù)現(xiàn)這個問題(環(huán)境:Windows 64 位、Python 3.7):

import aiohttp import asyncionum = 0async def main(url):async with aiohttp.ClientSession() as session:async with session.get(url) as response:global numnum += 1print('%s ——> %s' % (str(num), response.status))def tasks():url = 'https://www.baidu.com/s?ie=UTF-8&wd=%s'task = [main(url % i) for i in range(10000)]return taskloop = asyncio.get_event_loop() loop.run_until_complete(asyncio.wait(tasks()))

在打印 500 次左右后就會出現(xiàn)以下報錯:

問題分析

好像這個報錯和 select 有關(guān),那什么是 select 呢?要怎么解決呢?別急,我們首先來了解一下 asyncio 中的事件循環(huán),即 EventLoop。

事件循環(huán) EventLoop

事件循環(huán)是 asyncio 的核心,異步任務(wù)的運行、任務(wù)完成之后的回調(diào)、網(wǎng)絡(luò) I/O 操作、子進程的運行,都是通過事件循環(huán)完成的,通俗來講,事件循環(huán)所做的就是等待事件發(fā)生,然后再將每個事件與我們已明確與所述事件類型匹配的函數(shù)進行匹配。下圖很好的展示了協(xié)程、事件循環(huán)之間的相互作用:

在 asyncio 中,主要提供了兩種不同事件循環(huán)的實現(xiàn)方法:

  • SelectorEventLoop:基于 selectors 模塊的事件循環(huán),selectors 又是建立在底層的 I/O 復(fù)用模塊 select 之上的,selectors 提供了高度封裝和高效的 I/O 復(fù)用,也就是說 SelectorEventLoop 在底層就是使用了 select I/O 多路復(fù)用的機制。

  • ProactorEventLoop:使用 IOCP 專為 Windows 構(gòu)建的事件循環(huán),IOCP 全稱 I/O Completion Port,即 I/O 完成端口。它是支持多個同時發(fā)生的異步 I/O 操作的應(yīng)用程序編程接口,它充分利用內(nèi)核對象的調(diào)度,只使用少量的幾個線程來處理和客戶端的所有通信,消除了無謂的線程上下文切換,是 Windows 下性能最好的 I/O 模型,有關(guān) IOCP 的詳細介紹可參考微軟文檔。

那么這兩種方法有什么區(qū)別呢?在 asyncio 中什么時候用什么方法呢?

我們不妨看一下 asyncio 的源碼,在 Python 3.7 中,無論在 Windows 還是 Linux 中都可以看到其默認的設(shè)置是 SelectorEventLoop:

我們也可以分別在 Windows 平臺和 Linux 平臺打印一下 EventLoop 對象(Python 3.7),可以看到默認都是 SelectorEventLoop:

import asyncioloop = asyncio.get_event_loop() print(loop)

Windows:

Linux:

事實上,在 Python 3.7 以及之前的版本中, 所有平臺默認使用的都是 SelectorEventLoop,在 Python 3.8 以及以后的版本中,Unix 平臺默認使用的是 SelectorEventLoop,Windows 平臺默認使用的是 ProactorEventLoop,這個差異可以在官方文檔中看到。

  • Python 3.7 文檔:https://docs.python.org/3.7/library/asyncio-eventloop.html#event-loop-implementations
  • Python 3.8 文檔:https://docs.python.org/3.8/library/asyncio-eventloop.html#event-loop-implementations

說了這么多,這和 ValueError: too many file descriptors in select() 的報錯問題有什么關(guān)系呢?select 到底是什么東西呢?

I/O 多路復(fù)用

要了解 select,我們還要了解一下什么是 I/O 多路復(fù)用(I/O multiplexing),服務(wù)器端編程經(jīng)常需要構(gòu)造高性能的 I/O 模型,常見的 I/O 模型有同步阻塞 I/O、同步非阻塞 I/O、I/O 多路復(fù)用等;當需要同時處理多個客戶端接入請求時,可以利用多線程或者 I/O 多路復(fù)用技術(shù)進行處理,I/O 多路復(fù)用技術(shù)就是為了解決進程或線程阻塞到某個 I/O 系統(tǒng)調(diào)用而出現(xiàn)的技術(shù),使進程不阻塞于某個特定的 I/O 系統(tǒng)調(diào)用。

select,poll,epoll 等都是 I/O 多路復(fù)用的一種機制,其中后兩個在 Linux 中可用,Windows 僅支持 select,I/O 多路復(fù)用通過這種機制,可以監(jiān)視多個描述符,一旦某個描述符就緒,一般是讀就緒或者寫就緒,就是在這個文件描述符進行讀寫操作之前,能夠通知程序進行相應(yīng)的讀寫操作。

select 的缺點

I/O 多路復(fù)用這個概念被提出來以后, select 是第一個實現(xiàn)這個概念的,select 被實現(xiàn)以后,很快就暴露出了很多問題,其中一個缺點就是 select 在 Windows 中限制了文件描述符數(shù)量為 512 個,在 Linux 中限制為 1024 個,那么在前面的 dome 中,使用的是 Python 3.5,這個版本的 asyncio 默認使用了 SelectorEventLoop,底層調(diào)用的是 select,受 select 缺點的影響,并發(fā)量過高,就出現(xiàn)了 ValueError: too many file descriptors in select() 的報錯信息。

解決方法

1.更換事件循環(huán)選擇器

如果你使用的是 Python 3.7 及以下的版本,那么在 Windows 平臺,可以使用 ProactorEventLoop。在 Linux 平臺可以使用 PollSelector。

注意:如果你使用了 ProactorEventLoop,那么你將無法使用代理!這是 asyncio 的 bug,早在 2020 年 1 月就有人提過 issue,目前仍然可以看到類似的 issue,官方貌似也還沒辦法解決,所以,如果您必須要使用代理,則可以參考后面的解決辦法。

import selectors import asyncio import sysif sys.platform == 'win32':loop = asyncio.ProactorEventLoop()asyncio.set_event_loop(loop) else:selector = selectors.PollSelector()loop = asyncio.SelectorEventLoop(selector)asyncio.set_event_loop(loop)

2.限制并發(fā)量

可以使用方法 asyncio.Semaphore() 來限制并發(fā)量,Semaphore 就是信號量的意思,Semaphore 管理一個內(nèi)部計數(shù)器,該計數(shù)器在每次調(diào)用 acquire() 方法時遞減,每次調(diào)用 release() 方法時遞增,計數(shù)器永遠不會低于零,當方法 acquire() 發(fā)現(xiàn)它為零時,它會阻塞,等待其他線程調(diào)用 release() 方法。通過限制并發(fā)量的方法來解決報錯問題是個不錯的選擇。

import aiohttp import asyncionum = 0async def main(url, semaphore):async with semaphore:async with aiohttp.ClientSession() as session:async with session.get(url) as response:global numnum += 1print('%s ——> %s' % (str(num), response.status))def tasks():semaphore = asyncio.Semaphore(300) # 限制并發(fā)量為 300url = 'https://www.baidu.com/s?ie=UTF-8&wd=%s'task = [main(url % i, semaphore) for i in range(10000)] # #總共 10000 任務(wù)return taskloop = asyncio.get_event_loop() loop.run_until_complete(asyncio.wait(tasks()))

3.修改最大文件描述符限制

Windows

在 Windows 中,最大文件描述符限制在 C 語言的頭文件 Winsock2.h 中使用變量 FD_SETSIZE 進行定義,如果要修改它,可以通過在包含 Winsock2.h 之前將 FD_SETSIZE 定義為另一個值來修改,如果我們使用的編程語言是 Python 的話,是不太好對這個值進行修改的,可以參考微軟官方文檔:https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-select

Linux

在 Linux 平臺,可以使用 ulimit 命令來修改最大文件描述符限制:

  • 查看當前會話最大文件描述符限制(默認1024):ulimit -n

  • 臨時修改限制,只對當前的會話有效:ulimit -SHn 65536

  • 永久修改限制,在 /etc/security/limits.conf 文件里新增以下內(nèi)容:

    * hard nofile 65536 * soft nofile 65536

ulimit 命令參考:

-S 使用軟 (soft) 資源限制-H 使用硬 (hard) 資源限制-a 所有當前限制都被報告-b 套接字緩存尺寸-c 創(chuàng)建的核文件的最大尺寸-d 一個進程的數(shù)據(jù)區(qū)的最大尺寸-e 最高的調(diào)度優(yōu)先級 (nice)-f 有 shell 及其子進程可以寫的最大文件尺寸-i 最多的可以掛起的信號數(shù)-k 分配給此進程的最大 kqueue 數(shù)量-l 一個進程可以鎖定的最大內(nèi)存尺寸-m 最大的內(nèi)存進駐尺寸-n 最多的打開的文件描述符個數(shù)-p 管道緩沖區(qū)尺寸-q POSIX 信息隊列的最大字節(jié)數(shù)-r 實時調(diào)度的最大優(yōu)先級-s 最大棧尺寸-t 最大的CPU時間,以秒為單位-u 最大用戶進程數(shù)-v 虛擬內(nèi)存尺寸-x 最大的文件鎖數(shù)量-P 最大偽終端數(shù)量-T 最大線程數(shù)量

總結(jié)

asyncio 事件循環(huán)選擇器,在 Python 3.7 以及之前的版本中,所有平臺默認使用的都是 SelectorEventLoop,在 Python 3.8 以及以后的版本中,Unix 平臺默認使用的是 SelectorEventLoop,Windows 平臺默認使用的是 ProactorEventLoop。

select 在 Windows 中限制了文件描述符最大數(shù)量為 512 個,在 Linux 中限制為 1024 個。

要解決 ValueError: too many file descriptors in select() 的報錯問題,根據(jù)您的平臺和業(yè)務(wù)要求選擇合理的解決方法:

Windows

  • 通過 asyncio.Semaphore() 方法來限制并發(fā)量,通常設(shè)置在 300-500 比較合理,這是最優(yōu)的做法;

  • 更換 asyncio 的事件循環(huán)選擇器為 ProactorEventLoop,注意:這將導(dǎo)致無法使用代理!

  • Linux

  • 通過 asyncio.Semaphore() 方法來限制并發(fā)量,通常設(shè)置在 800-1000 比較合理;

  • 通過 ulimit 命令來修改最大文件描述符限制;

  • 更換 asyncio 的事件循環(huán)選擇器為 PollSelector。

  • 創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅持創(chuàng)作打卡瓜分現(xiàn)金大獎

    總結(jié)

    以上是生活随笔為你收集整理的Python 中如何解决 asyncio 文件描述符最大数量限制问题的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。