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


文章目录

    • 问题复现
    • 问题分析
      • 事件循环 EventLoop
      • I/O 多路复用
      • select 的缺点
    • 解决方法
      • 1.更换事件循环选择器
      • 2.限制并发量
      • 3.修改最大文件描述符限制
        • Windows
        • Linux
    • 总结
      • Windows
      • Linux


问题复现

Windows 平台下,Python 版本 3.5,使用异步框架 asyncio,有时候会出现 ValueError: too many file descriptors in select() 的报错信息,今天我们就来聊一下为什么会出现这种问题,以及问题的一些解决方法。

写一个小 dome 复现这个问题(环境: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 次左右后就会出现以下报错:
01.png

问题分析

好像这个报错和 select 有关,那什么是 select 呢?要怎么解决呢?别急,我们首先来了解一下 asyncio 中的事件循环,即 EventLoop。

事件循环 EventLoop

事件循环是 asyncio 的核心,异步任务的运行、任务完成之后的回调、网络 I/O 操作、子进程的运行,都是通过事件循环完成的,通俗来讲,事件循环所做的就是等待事件发生,然后再将每个事件与我们已明确与所述事件类型匹配的函数进行匹配。下图很好的展示了协程、事件循环之间的相互作用:
02.png

在 asyncio 中,主要提供了两种不同事件循环的实现方法:

  • SelectorEventLoop:基于 selectors 模块的事件循环,selectors 又是建立在底层的 I/O 复用模块 select 之上的,selectors 提供了高度封装和高效的 I/O 复用,也就是说 SelectorEventLoop 在底层就是使用了 select I/O 多路复用的机制。

  • ProactorEventLoop:使用 IOCP 专为 Windows 构建的事件循环,IOCP 全称 I/O Completion Port,即 I/O 完成端口。它是支持多个同时发生的异步 I/O 操作的应用程序编程接口,它充分利用内核对象的调度,只使用少量的几个线程来处理和客户端的所有通信,消除了无谓的线程上下文切换,是 Windows 下性能最好的 I/O 模型,有关 IOCP 的详细介绍可参考微软文档。

那么这两种方法有什么区别呢?在 asyncio 中什么时候用什么方法呢?

我们不妨看一下 asyncio 的源码,在 Python 3.7 中,无论在 Windows 还是 Linux 中都可以看到其默认的设置是 SelectorEventLoop:
03.png
我们也可以分别在 Windows 平台和 Linux 平台打印一下 EventLoop 对象(Python 3.7),可以看到默认都是 SelectorEventLoop:

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

Windows:
04.png
Linux:
05.png
事实上,在 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

06.png

说了这么多,这和 ValueError: too many file descriptors in select() 的报错问题有什么关系呢?select 到底是什么东西呢?

I/O 多路复用

要了解 select,我们还要了解一下什么是 I/O 多路复用(I/O multiplexing),服务器端编程经常需要构造高性能的 I/O 模型,常见的 I/O 模型有同步阻塞 I/O、同步非阻塞 I/O、I/O 多路复用等;当需要同时处理多个客户端接入请求时,可以利用多线程或者 I/O 多路复用技术进行处理,I/O 多路复用技术就是为了解决进程或线程阻塞到某个 I/O 系统调用而出现的技术,使进程不阻塞于某个特定的 I/O 系统调用。

select,poll,epoll 等都是 I/O 多路复用的一种机制,其中后两个在 Linux 中可用,Windows 仅支持 select,I/O 多路复用通过这种机制,可以监视多个描述符,一旦某个描述符就绪,一般是读就绪或者写就绪,就是在这个文件描述符进行读写操作之前,能够通知程序进行相应的读写操作。

select 的缺点

I/O 多路复用这个概念被提出来以后, select 是第一个实现这个概念的,select 被实现以后,很快就暴露出了很多问题,其中一个缺点就是 select 在 Windows 中限制了文件描述符数量为 512 个,在 Linux 中限制为 1024 个,那么在前面的 dome 中,使用的是 Python 3.5,这个版本的 asyncio 默认使用了 SelectorEventLoop,底层调用的是 select,受 select 缺点的影响,并发量过高,就出现了 ValueError: too many file descriptors in select() 的报错信息。

解决方法

1.更换事件循环选择器

如果你使用的是 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.限制并发量

可以使用方法 asyncio.Semaphore() 来限制并发量,Semaphore 就是信号量的意思,Semaphore 管理一个内部计数器,该计数器在每次调用 acquire() 方法时递减,每次调用 release() 方法时递增,计数器永远不会低于零,当方法 acquire() 发现它为零时,它会阻塞,等待其他线程调用 release() 方法。通过限制并发量的方法来解决报错问题是个不错的选择。

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)                         # 限制并发量为 300url = 'https://www.baidu.com/s?ie=UTF-8&wd=%s'task = [main(url % i, semaphore) for i in range(10000)]    # #总共 10000 任务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 文件里新增以下内容:

    * hard nofile 65536
    * soft nofile 65536
    

ulimit 命令参考:

    -S	使用软 (soft) 资源限制-H	使用硬 (hard) 资源限制-a	所有当前限制都被报告-b	套接字缓存尺寸-c	创建的核文件的最大尺寸-d	一个进程的数据区的最大尺寸-e	最高的调度优先级 (nice)-f	有 shell 及其子进程可以写的最大文件尺寸-i	最多的可以挂起的信号数-k	分配给此进程的最大 kqueue 数量-l	一个进程可以锁定的最大内存尺寸-m	最大的内存进驻尺寸-n	最多的打开的文件描述符个数-p	管道缓冲区尺寸-q	POSIX 信息队列的最大字节数-r	实时调度的最大优先级-s	最大栈尺寸-t	最大的CPU时间,以秒为单位-u	最大用户进程数-v	虚拟内存尺寸-x	最大的文件锁数量-P	最大伪终端数量-T	最大线程数量

总结

asyncio 事件循环选择器,在 Python 3.7 以及之前的版本中,所有平台默认使用的都是 SelectorEventLoop,在 Python 3.8 以及以后的版本中,Unix 平台默认使用的是 SelectorEventLoop,Windows 平台默认使用的是 ProactorEventLoop。

select 在 Windows 中限制了文件描述符最大数量为 512 个,在 Linux 中限制为 1024 个。

要解决 ValueError: too many file descriptors in select() 的报错问题,根据您的平台和业务要求选择合理的解决方法:

Windows

  1. 通过 asyncio.Semaphore() 方法来限制并发量,通常设置在 300-500 比较合理,这是最优的做法;

  2. 更换 asyncio 的事件循环选择器为 ProactorEventLoop,注意:这将导致无法使用代理!

Linux

  1. 通过 asyncio.Semaphore() 方法来限制并发量,通常设置在 800-1000 比较合理;

  2. 通过 ulimit 命令来修改最大文件描述符限制;

  3. 更换 asyncio 的事件循环选择器为 PollSelector。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/437649.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【转】掀起Azure AD的盖头来——深入理解Microsoft Graph应用程序和服务权限声明

引子 这是一篇计划外的文章。我们都知道要进行Microsoft Graph的开发的话,需要进行应用程序注册。这个在此前我已经有专门的文章写过了。但这里存在一个小的问题:国内版的Office 365在申请好之后,并没有像国际版那样,有一个对应的…

Python3 学习系列 丨 博客目录索引

整个博客有关 Python 学习目录索引,方便快捷定位查询基础学习篇 Python3 基础学习笔记 C01【变量和简单数据类型】Python3 基础学习笔记 C02【列表】Python3 基础学习笔记 C03【操作列表】Python3 基础学习笔记 C04【if 语句】Python3 基础学习笔记 C05【字典】Pyt…

【转】日邮物流:实现智慧物流,这个云上对了!

和阳光、空气、水、网络一样,「物流」早已成为当代企业、个人赖以生存的必要条件。2020第一季度全球物流受疫情影响面临挑战,业内普遍预计全球物流及供应链将重新优化布局。借此时机,物流业纷纷将目光投向“数字化智慧物流”方向,…

Python 实现十大经典排序算法

目录排序算法分类一、冒泡排序(Bubble Sort)1、原理2、步骤3、动画演示4、代码实现5、具体示例二、选择排序(Selection Sort)1、原理2、步骤3、动画演示4、代码实现5、具体示例三、插入排序(Insertion Sort&#xff09…

【转】Microsoft Graph 桌面应用程序

桌面应用程序,在我这篇文章的语境中,我是特指在Windows桌面上面直接运行的.NET应用程序,包括Console Application,WPF Application,Windows Forms Application, UWP Application,并且限于篇幅,我…

【转】Microsoft Graph Web应用程序极致开发体验

前言 这篇文章最早写于2017年5月2日,当时的想法是从最简单的方式来写如何在一个ASP.NET MVC应用程序中集成Microsoft Graph,但实际上还真不是那么简单,至少我是不满意的,加上这一两周都比较忙,所以这一篇就一直搁置。…

Spark(idea)操作mysql进行查询和插入 (代码+理解)

首先在maven中加入配置 <!--mysql--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.27</version></dependency>然后在idea配置数据库 1&#xff09; 查询 //1.查询数…

【转】在无人值守程序(服务)中调用Microsoft Graph

什么是无人值守程序&#xff08;服务&#xff09; 我在此前用了几篇文章分别介绍了在桌面应用程序&#xff08;控制台&#xff09;&#xff0c;Web应用程序&#xff08;ASP.NET MVC&#xff09;&#xff0c;以及PowerSehll脚本中如何访问Microsoft Graph&#xff0c;今天这一篇…

【转】使用PowerApps快速构建基于主题的轻业务应用 —— 入门篇

前言 在上一篇文章 基于Office 365的随需应变业务应用平台 中我提到&#xff0c;随着随需应变的业务需要&#xff0c;以及技术的发展&#xff0c;业务应用的开发的模式也有了深刻的变化。基于微软的平台&#xff0c;有服务于主干业务应用的Dynamic 365 业务应用平台&#xff0…

Spark内核源码学习(暂未学完)

1&#xff09; 回顾 1.1 Spark通用运行流程概述 在submit任务条件是需要指定executo个数&#xff0c;executor-CUP个数&#xff0c;可以提高并行度。 什么是并行&#xff0c;什么是并发&#xff1f; 并发&#xff1a;假如有多个任务task&#xff0c;并行是在一个cup中&#x…

【转】使用PowerApps快速构建基于主题的轻业务应用 —— 进阶篇

在上一篇 使用PowerApps快速构建基于主题的轻业务应用 —— 入门篇 中&#xff0c;我用了三个实际的例子演示了如何快速开始使用PowerApps构建轻业务应用&#xff0c;你可能已经发现&#xff0c;我都是使用默认生成的设置&#xff0c;没有做任何修改。当然&#xff0c;那样做出…

Spark一些组件的定义

Driver program: 运行应用程序的main函数并创建SparkContext的进程 除了RDD的最终执行所写的业务逻辑&#xff0c;剩下的都在Driver里生成&#xff0c;Driver端执行action算子才会到开始执行所创建的DAG-RDD图。 Cluster manager&#xff1a; 用于获取集群资源外部服务 Mas…

【转】D365 FO第三方集成(二)---访问认证(获取访问令牌)

D365 FO 在github上发布了第三方访问D365 FO的示例代码&#xff0c;里面包含了各种调用示例&#xff0c;代码很清晰。https://github.com/microsoft/Dynamics-AX-Integration 这篇blog简单分析一下代码中获取访问令牌的部分代码。 与获取访问令牌相关的代码有两个类ClientConfi…

【转】D365 FO第三方集成(三)---服务实现

D365 FO的Custom Service的实现比AX2012简单了很多。 AX2012服务方法要用属性SysEntryPointAttribute标记&#xff0c;添加到Services以后&#xff0c;还要发布服务并在系统管理入站端口添加操作&#xff0c;服务运行在CIL下&#xff0c;所以每次改动服务方法的代码都要增量生成…

PHP连接sql seaver数据库

我的PHP版本7.0 通过sqlsrv系列函数&#xff0c;需要下载安装Microsoft Drivers for PHP for SQL Server驱动&#xff1a; 地址&#xff1a;https://msdn.microsoft.com/library/dn865013.aspx。 根据自己需求下载安装&#xff0c;安装地址php下ext目录下&#xff0c;我的是4.0…

NoSql理解+传统关系型数据库ACID+Nosql的CAP+BASE的理解

1&#xff09;什么是Nosql NoSQL(NoSQL Not Only SQL )&#xff0c;意即“不仅仅是SQL”&#xff0c; 泛指非关系型的数据库。随着互联网web2.0网站的兴起&#xff0c;传统的关系数据库在应付web2.0网站&#xff0c;特别是超大规模和高并发的SNS类型的web2.0纯动态网站已经显…

ztree 点击重载 layui table

ztree 点击重载 layui table <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <HTML> <HEAD><TITLE> ztree_demo </TITLE><meta http…

数据结构与算法 - 稀疏数组(理解+代码实现案例)

举例 稀疏数组第一行是原数据几行几列和几个有效数据的个数 下面的行是各个有效数组的行列与具体值 稀疏数组实现 代码实现 package DataStructures.sparsearray;/*** 二维数组转稀疏数组 与 稀疏数组转成二维数组*/ public class SparseArray {public static void main(Stri…

数据结构 - 队列(非环形队列,以及优化成环形队列)

1&#xff09;队列的定义与实现形式-方式 2&#xff09;队列实现思路&#xff08;非环形&#xff0c;下面进行优化&#xff09; 3&#xff09;代码实现&#xff08;注意并不是环形&#xff09; package DataStructures.queue;import java.util.Scanner;/*** 使用数组模拟队列*…

爬取网易云音乐歌曲特色榜单信息

网易云音乐(iframe内的歌单) 刚开始学习做下记录 需要先下载好所需浏览器内核 我时谷歌&#xff0c;下载地址 http://chromedriver.storage.googleapis.com/index.html 然后没了&#xff0c;自己F12扒拉下就行了 运行&#xff1a; 左侧随便点击一个榜单后&#xff0c;复制ur…