python 线程池/AIO(异步非阻塞)调用接口示例

news2025/1/12 1:59:07

分别测试了 多线程、线程池、aio 等模式

线程受CPU调度限制,大请求量下 AIO 效率最高,详见代码

注释中有详细说明,main 方法中是程序入口


"""
python 各种方式发起 http 请求对比
参考:
https://plainenglish.io/blog/send-http-requests-as-fast-as-possible-in-python-304134d46604
"""
import time

import requests
from requests.sessions import Session
from threading import Thread,local
from queue import Queue
from concurrent.futures import ThreadPoolExecutor


def sync_get(url_list: list):
    """
    同步方式请求
    :param url_list:
    :return:
    """

    def download_link(url: str) -> None:
        result = requests.get(url).content
        print(f'Read {len(result)} from {url}')

    def download_all(urls: list) -> None:
        for url in urls:
            download_link(url)

    start = time.time()
    download_all(url_list)
    end = time.time()
    print(f'sync download {len(url_list)} links in {end - start} seconds')


def sync_get_share_session(url_list: list):
    """
    依然是同步请求,但是可以共享 Session
    共享 Session 可以记录 cookie,保持登录状态
    复用 TCP 减少 TLS 时间等
    详情可以搜索【python session 优化 requests 性能】
    TODO 根据情况补充:session 和 cookie,后面安全经常会用到
    :param url_list:
    :return:
    """
    def download_link(url: str, session: Session):
        with session.get(url) as response:
            result = response.content
            print(f'Read {len(result)} from {url}')

    def download_all(urls: list):
        with requests.Session() as session:
            for url in urls:
                download_link(url, session=session)

    start = time.time()
    download_all(url_list)
    end = time.time()
    print(f'download {len(url_list)} links in {end - start} seconds')


def multi_thread_get(url_list):
    """
    多线程请求,启动10个线程
    要点:
    1. 多线程操作 list 时会存在线程安全问题,这里使用 queue 解决,关键词【python queue 线程安全队列】
    2. 多线程共享 Session 对象会有线程安全问题,这里使用 thread_local 解决,保证一个线程只有一个 session
    :param url_list:
    :return:
    """
    # 将压入线程安全队列
    q = Queue(maxsize=0)  # Use a queue to store all URLs
    for url in url_list:
        q.put(url)
    thread_local = local()  # The thread_local will hold a Session object

    def get_session() -> Session:
        if not hasattr(thread_local, 'session'):
            thread_local.session = requests.Session()  # Create a new Session if not exists
        return thread_local.session

    def download_link() -> None:
        '''download link worker, get URL from queue until no url left in the queue'''
        session = get_session()
        while True:
            # 全部执行完成后线程会一直卡住,需要手动停止
            # 设置超时时间的话,可以在 $timeout 秒后抛异常终止
            url = q.get(block=True, timeout=None)
            with session.get(url) as response:
                print(f'Read {len(response.content)} from {url}')
            q.task_done()  # tell the queue, this url downloading work is done

    def download_all() -> None:
        '''Start 10 threads, each thread as a wrapper of downloader'''
        thread_num = 10
        for i in range(thread_num):
            t_worker = Thread(target=download_link)
            t_worker.start()
        q.join()  # main thread wait until all url finished downloading

    print("start work")
    start = time.time()
    download_all()
    end = time.time()
    print(f'download {len(url_list)} links in {end - start} seconds')


def thread_pool_get(url_list):
    thread_local = local()

    def get_session() -> Session:
        if not hasattr(thread_local, 'session'):
            thread_local.session = requests.Session()
        return thread_local.session

    def download_link(url: str):
        session = get_session()
        with session.get(url) as response:
            print(f'Read {len(response.content)} from {url}')

    def download_all() -> None:
        with ThreadPoolExecutor(max_workers=10) as executor:
            executor.map(download_link, url_list)

    start = time.time()
    download_all()
    end = time.time()
    print(f'download {len(url_list)} links in {end - start} seconds')


def aio_get(url_list):
    """
    异步非阻塞的方式发送请求
    这种情况下,并发可以非常搞,性能看带宽、网卡等情况,不过并发太高可能会被服务端限流
    :param url_list:
    :return:
    """
    import asyncio
    import aiohttp
    from aiohttp.client import ClientSession

    async def download_link(url: str, session: ClientSession):
        async with session.get(url) as response:
            result = await response.text()
            print(f'Read {len(result)} from {url}')

    async def download_all(urls: list):
        my_conn = aiohttp.TCPConnector(limit=40)
        async with aiohttp.ClientSession(connector=my_conn) as session:
            tasks = []
            for url in urls:
                task = asyncio.ensure_future(download_link(url=url, session=session))
                tasks.append(task)

            await asyncio.gather(*tasks, return_exceptions=True)  # the await must be nest inside of the session

    start = time.time()
    asyncio.run(download_all(url_list))
    end = time.time()

    print(f'download {len(url_list)} links in {end - start} seconds')


if __name__ == '__main__':
    _url_list = ["https://cn.bing.com/", "https://www.baidu.com/", "https://www.so.com/"] * 50
    # 方法1 同步方式请求
    # sync_get(_url_list)  # 58.78762245178223
    # 方法2 同步请求,但是共享 session
    # sync_get_share_session(_url_list)  # 32.01118564605713
    # 方法3 多线程请求
    # multi_thread_get(_url_list)  # 3.5877575874328613
    # 方法4 线程池
    thread_pool_get(_url_list)  # 3.5470234870910645
    # 方法5 异步非阻塞的方式发送请求
    aio_get(_url_list)  # 1.354555606842041

多线程示意图

线程池示意图

AIO异步非阻塞IO

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

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

相关文章

“第五十一天”

无符号整数: 计算机硬件在进行无符号整数的加法时,从最低位开始,按位相加,并往更高位进位。 当进行减法时,被减数不变,减数全部按位取反,末位加一(将一个正数变负,或者…

关键词搜索淘宝商品数据接口(标题|主图|SKU|价格|优惠价|掌柜昵称|店铺链接|店铺所在地)

关键词搜索淘宝商品数据接口是可以通过API的方式来进行调用。这些接口可以获取到商品列表的标题、SKU ID、价格、优惠价、收藏数、月销售量等数据。 通过这些接口,可以实现关键词搜索淘宝商品列表的功能,也可以获取到商品详情页的数据信息,适…

微信自动通过好友请求是怎么设置的?

微信是一款必不可少的社交工具,尤其对于需要使用微信进行业务沟通和促进成交的人来说。 业务繁忙时可能每天有几十上百个微信好友申请,这时候如果手动“通过验证”不仅会工作质量和效率,有可能由于通过不及时而导致客户流失。 方式一 自动通…

leetCode 76. 最小覆盖子串 + 滑动窗口

76. 最小覆盖子串 - 力扣(LeetCode) 给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 "" 注意: 对于 t 中重复字符,我们寻…

D71X-16Q手柄蝶阀型号解析

D71X-16Q型号字母含义解析 D71X-16Q是德特森阀门常用的手柄蝶阀型号字母分别代表的意思是: D——代表阀门类型《蝶阀》 7——代表连接方式《对夹》 1——代表结构形式《中线》 X——代表阀座材质《橡胶》 -代表分隔键 16——代表公称压力《1.6MPA》 Q——代表阀体材料《…

FPGA设计时序约束七、设置时钟不确定约束

一、背景 在之前的时序分析中,通常是假定时钟是稳定理想的,即设置主时钟周期后按照周期精确的进行边沿跳动。在实际中,时钟是非理想存在较多不确定的影响,存在时延和波形的变化,要准确分析时序也需将其考虑进来&#x…

2023深耕kotlin,谈谈前景

为什么学习kotlin? Kotlin 早就已经是 Google 官方推荐的开发语言了,而且 Android 新的 Compose 框架只支持 Kotlin ,在 Google 那里,Android开发中 Java 其实已经被淘汰了。Java 和 Kotlin 虽然都属于高级语言,但是 …

利用nicegui开发ai工具示例

from fastapi import FastAPI import uvicorn from nicegui import uiclass PipRequirement:def __init__(self):ui.label("依赖安装与依赖展示")class BasicSettings:def __init__(self):self.project_select ui.select(["test"], label"项目选择&q…

驱动获取设备树节点信息

mycdev.c #include <linux/init.h> #include <linux/module.h> #include <linux/of.h>struct device_node *dnode; //解析得到的设备树节点对象指针 struct property *pr; unsigned int lenth; static int __init mycdev_init(void) {//解析设备树节点信息d…

Hafnium安全分区管理器和示例参考软件栈

安全之安全(security)博客目录导读 目录 一、安全分区管理器 1、术语 2、对旧平台的支持 二、示例参考软件栈 一、安全分区管理器 安全分区管理器的三种实现在TF-A代码库并存&#xff1a; 1.基于FF-A规范的S-EL2 SPMC&#xff08;SPM Core&#xff09;&#xff0c;使能安全…

C++ 模板和泛型编程详解

C中的模板和泛型编程是非常重要的概念。模板是一种将数据类型作为参数的通用程序设计方法。它们允许开发人员编写可以处理各种数据类型的代码&#xff0c;而无需为每种数据类型编写不同的代码。下面介绍了一些关于C中模板和泛型编程的重要知识点 模板的定义 模板是一种通用程序…

论坛议程 | COSCon'23 开源治理(G)

众多开源爱好者翘首期盼的开源盛会&#xff1a;第八届中国开源年会&#xff08;COSCon23&#xff09;将于 10月28-29日在四川成都市高新区菁蓉汇举办。本次大会的主题是&#xff1a;“开源&#xff1a;川流不息、山海相映”&#xff01;各位新老朋友们&#xff0c;欢迎到成都&a…

第十五章:L2JMobius学习 – 刷新NPC和对话

首先&#xff0c;我们介绍一下城镇里面的传送师NPC的刷新和对话。 传送师对应的类是L2TeleporterInstance&#xff0c;它继承L2FolkInstance&#xff0c;再继承L2NpcInstance。 我们要处理的是“单击/双击”传送师的业务逻辑&#xff0c;对应的是Action数据包。这个数据包具有…

Linux系统上安装FTP服务

文章背景 最近为了调试Linux系统FTP传输工具&#xff0c;刚好有Linux虚拟机环境&#xff0c;于是就搭建了vsftpd服务。 使用环境 Linux系统&#xff1a;BigCloud Enterprise Linux 7.8 (Core) 安装 1. 查询是否安装vsftpd [rootlocalhost ~]# rpm -qa | grep vsftpd 2. 安…

【CHI】Transaction structure

我们在前文【CHI】CHI协议&#xff0c;transaction事务汇总已经总结了事务类型&#xff0c;这篇开始讲述事务可以完成的方式。它显示了参与事务的各种组件可以使用的所有允许的选项。 注&#xff1a; 除了PCrdReturn 和PrefetchTgt之外&#xff0c;其他的事务在开始的时候都可…

APP分发-CDN加速原理

摘要 CDN的全称是(Content Delivery Network)&#xff0c;即内容分发网络。其目的是通过在现有的Internet中增加一层新的CACHE(缓存)层&#xff0c;将网站的内容发布到最接近用户的网络”边缘“的节点&#xff0c;使用户可以就近取得所需的内容&#xff0c;提高用户访问网站的…

疯狂java 三-六章

第三章 数据类型和运算符 Java语言是强类型语言&#xff0c;意思是每个变量和每个表达式都有一个在编译时就确定的类型&#xff0c;所有的变量都必须显式声明类型 标识符就是类&#xff0c;变量、方法命名的符号 标识符不能包含空格 标识符只能包含美元符($)&#xff0c;不…

2000-2021年三批“智慧城市”试点名单匹配数据

2000-2021年三批“智慧城市”试点名单匹配数据 1、时间&#xff1a;2000-2021年 2、指标&#xff1a;行政区划代码、地区、所属省份、年份、智慧城市试点、最早试点年份 3、来源&#xff1a;住建部公布的三批“国家智慧城市名单” 4、说明&#xff1a;内含原始文件和匹配结…

AcWing 1.1 数字三角形模型 dp动态规划

&#xff08;1&#xff09;ACWing 1015. 摘花生 1015. 摘花生 - AcWing题库 Hello Kitty想摘点花生送给她喜欢的米老鼠。她来到一片有网格状道路的矩形花生地(如下图)&#xff0c;从西北角进去&#xff0c;东南角出来。地里每个道路的交叉点上都有种着一株花生苗&#xff0c;上…

从设计、制造到封测,XSKY 智能存储助力半导体行业数字化转型

近日&#xff0c;ECS2023 第五届中国电子通信与半导体 CIO 峰会在深圳召开&#xff0c;峰会以“数字科技与业务重塑”为主题&#xff0c;汇聚了 300来自电子通信与半导体行业知名企业高管、CIO、信息化与数字化负责人&#xff0c;交流电子通信与半导体行业的创新的产品和解决方…