讲师中心 微信公众号

工具推荐

视频效率加速
Laper
Laper

剧本策划到成片协作

updream

一键生成高质量推广成片

AniShort
AniShort

AI短剧协同制作平台

立刻MV
立刻MV

低成本歌曲 MV、视频生成

优化Python Web API调用性能:多进程为何可能更慢及其解决方案

胖敏姑娘_9854

胖敏姑娘_9854

发布时间:2025-11-09 11:12:01

|

389人浏览过

|

来源于php中文网

原创

优化Python Web API调用性能:多进程为何可能更慢及其解决方案

本文深入探讨了在python中处理io密集型web api调用时,多进程方法可能比单进程更慢的常见问题。文章分析了进程创建与进程间通信(ipc)的开销,阐明了io密集型任务的特性,并提供了使用`multiprocessing.pool`来优化进程管理、以及考虑多线程或异步io作为更高效替代方案的详细指导,强调了`requests.session`在连接复用中的重要性。

理解多进程的性能瓶颈

在Python中,当尝试利用多进程加速Web API调用等IO密集型任务时,有时会发现其性能反而不如单进程执行。这通常是由于以下几个关键因素导致的:

  1. 进程创建开销(Process Creation Overhead): 创建新的操作系统进程是一项资源密集型操作。它涉及到分配内存、复制父进程的状态(包括文件描述符、内存映射等)、以及初始化新的执行上下文。对于每次需要处理少量数据的API请求都创建一个新进程,这种开销会迅速累积,远超过并行执行带来的潜在收益。
  2. 进程间通信(IPC)开销: 当使用multiprocessing.Queue等机制在进程间传递数据时,数据需要被序列化(pickling)以发送给子进程,并在子进程中反序列化。对于复杂的数据结构,这一序列化和反序列化过程会消耗显著的CPU时间和内存,尤其是在频繁通信时。
  3. IO密集型任务的特性: Web API请求本质上是IO密集型任务,这意味着大部分时间都花在等待网络响应上,而不是CPU计算。在这种情况下,即使有多个进程,它们也可能同时等待网络,而不是并行地执行CPU密集型工作。虽然多进程可以绕过Python的全局解释器锁(GIL),但对于IO等待,GIL的影响较小,而进程本身的开销则变得突出。

原始多进程代码分析

原始的多进程代码尝试手动创建并管理进程,如下所示:

from multiprocessing import Process, Queue
import requests
import time

def pull_data(row, q):
    url_api = '*web api*' # 实际API地址
    post_json = {'data': row} # 示例数据结构
    try:
        x = requests.post(url_api, json=post_json)
        q.put(x.json())
    except requests.exceptions.RequestException as e:
        print(f"Error pulling data for {row}: {e}")
        q.put(None) # 放入None或其他错误标识

rows = ['SN1', 'SN2', 'SN3', 'SN4', 'SN5', 'SN6'] # 示例数据

# 模拟分批处理
for i in range(0, len(rows), 3):
    jobs = []
    json_f = []
    q = Queue()

    t_s = time.time()

    # 手动创建并启动进程
    if 0 <= i < len(rows):
        p1 = Process(target=pull_data, args=(rows[i], q))
        jobs.append(p1)
        p1.start()

    if 1 <= i + 1 < len(rows):
        p2 = Process(target=pull_data, args=(rows[i + 1], q))
        jobs.append(p2)
        p2.start()

    if 2 <= i + 2 < len(rows):
        p3 = Process(target=pull_data, args=(rows[i + 2], q))
        jobs.append(p3)
        p3.start()

    for proc in jobs:
        proc.join() # 等待所有进程完成

    t_e = time.time()

    while not q.empty():
        result = q.get()
        if result is not None:
            json_f.append(result)

    print(f"\nBatch query completed in {format(t_e - t_s, '.2f')} seconds. Results: {len(json_f)}")

这段代码的问题在于:

  • 频繁创建进程:每次循环(每处理3个row)都会创建新的进程,而不是复用现有进程。
  • 手动管理复杂:需要手动跟踪进程、管理队列,代码可读性和维护性较差。
  • IPC开销:Queue的使用引入了序列化和反序列化的开销。

解决方案:使用multiprocessing.Pool

multiprocessing.Pool提供了一种更高效、更简洁的方式来管理进程池,它能够复用固定数量的进程来执行任务,从而摊销了进程创建的开销。

立即学习“Python免费学习笔记(深入)”;

Expanso yaml-to-json
Expanso yaml-to-json

使用 Expanso Edge 管道将 YAML 输入转换为 JSON 格式。

下载

示例代码

from multiprocessing import Pool
import requests
import time

# 优化后的pull_data函数,不再需要Queue参数
def pull_data(row):
    url_api = '*web api*' # 实际API地址
    post_json = {'data': row} # 示例数据结构
    try:
        # 建议在实际应用中使用requests.Session来复用连接
        # worker_session = requests.Session()
        # x = worker_session.post(url_api, json=post_json)
        x = requests.post(url_api, json=post_json)
        return x.json(), row # 返回结果和原始row,方便追踪
    except requests.exceptions.RequestException as e:
        print(f"Error pulling data for {row}: {e}")
        return None, row # 发生错误时返回None和原始row

def database_test():
    rows = [f'SN{i}' for i in range(1, 21)] # 示例数据,假设有20个SN

    t_s = time.time()

    # 使用Pool来管理进程
    # max_workers参数决定了同时运行的进程数量
    # 建议根据CPU核心数和任务类型进行调整
    with Pool(processes=5) as pool:
        # pool.map会阻塞直到所有任务完成,并按输入顺序返回结果
        results = pool.map(pull_data, rows)

    t_e = time.time()

    print(f"\nTotal query completed in {format(t_e - t_s, '.2f')} seconds.")

    successful_results = [res for res, row in results if res is not None]
    print(f"Successfully retrieved {len(successful_results)} results.")

    # 打印部分结果示例
    # for res, row in results[:3]:
    #     print(f"Row {row} result: {res}")

if __name__ == '__main__':
    database_test()

multiprocessing.Pool的优势:

  • 进程复用:Pool会创建固定数量的子进程,并在这些子进程之间分配任务。一旦子进程完成一个任务,它就会准备好接收下一个任务,避免了重复创建进程的开销。
  • 简化API:map、apply、imap等方法提供了高级接口,极大地简化了并行任务的提交和结果收集。
  • 自动管理:Pool负责进程的生命周期管理,包括启动、任务分配、结果收集和关闭。

进一步优化:考虑多线程与异步IO

对于IO密集型任务,除了multiprocessing.Pool,还有更适合的并发模型:

  1. 多线程(threading模块配合concurrent.futures.ThreadPoolExecutor): 尽管Python的GIL限制了多线程在CPU密集型任务上的并行性,但对于IO密集型任务(如网络请求),当一个线程在等待IO时,GIL会被释放,允许其他线程运行。这意味着多线程可以有效地并行执行IO操作。线程的创建和切换开销远小于进程。

    from concurrent.futures import ThreadPoolExecutor
    import requests
    import time
    
    def pull_data_thread(row, session):
        url_api = '*web api*'
        post_json = {'data': row}
        try:
            x = session.post(url_api, json=post_json)
            return x.json(), row
        except requests.exceptions.RequestException as e:
            print(f"Error pulling data for {row}: {e}")
            return None, row
    
    def database_test_threaded():
        rows = [f'SN{i}' for i in range(1, 21)]
    
        t_s = time.time()
        results = []
        # 使用requests.Session来复用连接,这对于Web API请求至关重要
        with requests.Session() as session:
            with ThreadPoolExecutor(max_workers=10) as executor: # 线程数量可以设置得更高
                # 提交任务,并传递session对象
                futures = [executor.submit(pull_data_thread, row, session) for row in rows]
                for future in futures:
                    results.append(future.result())
    
        t_e = time.time()
        print(f"\nTotal threaded query completed in {format(t_e - t_s, '.2f')} seconds.")
        successful_results = [res for res, row in results if res is not None]
        print(f"Successfully retrieved {len(successful_results)} results.")
    
    if __name__ == '__main__':
        database_test_threaded()

    关键点:在多线程中,使用requests.Session至关重要。Session对象允许requests复用底层的TCP连接,避免了每次请求都建立新连接的开销,这能显著提升性能。

  2. 异步IO(asyncio模块配合aiohttp): 对于极致的IO并发性能,asyncio是Python的现代解决方案。它使用单个线程和事件循环来管理大量的并发IO操作,而没有线程切换或进程创建的开销。需要使用支持asyncio的HTTP客户端库,如aiohttp。

    import asyncio
    import aiohttp
    import time
    
    async def pull_data_async(row, session):
        url_api = '*web api*'
        post_json = {'data': row}
        try:
            async with session.post(url_api, json=post_json) as response:
                return await response.json(), row
        except aiohttp.ClientError as e:
            print(f"Error pulling data for {row}: {e}")
            return None, row
    
    async def database_test_async():
        rows = [f'SN{i}' for i in range(1, 21)]
    
        t_s = time.time()
        results = []
        # aiohttp.ClientSession用于异步IO中的连接复用
        async with aiohttp.ClientSession() as session:
            tasks = [pull_data_async(row, session) for row in rows]
            results = await asyncio.gather(*tasks)
    
        t_e = time.time()
        print(f"\nTotal async query completed in {format(t_e - t_s, '.2f')} seconds.")
        successful_results = [res for res, row in results if res is not None]
        print(f"Successfully retrieved {len(successful_results)} results.")
    
    if __name__ == '__main__':
        asyncio.run(database_test_async())

    关键点:aiohttp.ClientSession是asyncio环境下进行HTTP请求并复用连接的标准做法。

实践建议与注意事项

  1. 选择正确的并发模型:
    • CPU密集型任务(如大量计算):使用multiprocessing(尤其是Pool)来利用多核CPU。
    • IO密集型任务(如网络请求、文件读写):优先考虑threading(配合requests.Session)或asyncio(配合aiohttp.ClientSession)。它们通常具有更低的开销和更高的效率。
  2. 使用requests.Session: 无论是多线程还是单线程,只要是进行多个HTTP请求,都强烈建议使用requests.Session(或aiohttp.ClientSession)来复用TCP连接。这可以显著减少每次请求建立和关闭连接的握手时间,从而提升整体性能。
  3. 进程/线程数量的合理设置:
    • 进程池:通常设置为CPU核心数。
    • 线程池:对于IO密集型任务,可以设置得比CPU核心数大得多,因为大部分时间都在等待。具体数值需要根据实际测试和服务器负载能力来确定。
  4. 错误处理: 在并发编程中,务必加入健壮的错误处理机制(如try-except块),以防止一个任务失败导致整个程序崩溃。
  5. 性能分析(Profiling): 当遇到性能问题时,最有效的方法是使用Python的性能分析工具(如cProfile或line_profiler)来找出真正的瓶颈所在。不要盲目猜测,而是用数据说话。

总结

在Python中处理Web API等IO密集型任务时,multiprocessing.Process的直接使用可能因进程创建和IPC开销而适得其反。为了优化性能,我们应该:

  • 利用multiprocessing.Pool 来高效管理进程池,摊销进程创建成本。
  • 优先考虑多线程或异步IO 作为IO密集型任务的更优解,因为它们具有更低的开销。
  • 始终使用requests.Session (或aiohttp.ClientSession)来复用HTTP连接,这是提升网络请求性能的关键。
  • 进行性能分析 以识别真正的瓶颈。

通过选择合适的并发模型和遵循最佳实践,可以显著提升Python应用程序在处理大量Web API请求时的效率。

相关文章

数码产品性能查询
数码产品性能查询

该软件包括了市面上所有手机CPU,手机跑分情况,电脑CPU,电脑产品信息等等,方便需要大家查阅数码产品最新情况,了解产品特性,能够进行对比选择最具性价比的商品。

下载

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

码上飞
码上飞 Hot

一款基于自然语言的AI应用开发平台,可根据用户描述自动生成应用程序,帮助个人和团队快速完成应用原型与开发。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

蛙蛙写作

一款AI论文写作工具,主要用于超级AI智能写作助手,适合需要提升相关任务效率的用户。

Loomy
Loomy Hot

一款AI工具,主要用于科大讯飞发布的桌面级 AI 助理,比 OpenClaw 更易用、更安全!,适合需要提升相关任务效率的用户。

讯飞智作

讯飞智作是一款AI视频创作工具,AI文本配音工具,数字人课程、营销视频制作。

PixTV
PixTV Hot

PixTV是一款面向AIGC内容创作的AI视频生成工具。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

PixPix
PixPix Hot

PixPix是一款面向电商视觉生产的AI商品图生成工具。

相关专题

更多
session失效的原因
session失效的原因

session失效的原因有会话超时、会话数量限制、会话完整性检查、服务器重启、浏览器或设备问题等等。详细介绍:1、会话超时:服务器为Session设置了一个默认的超时时间,当用户在一段时间内没有与服务器交互时,Session将自动失效;2、会话数量限制:服务器为每个用户的Session数量设置了一个限制,当用户创建的Session数量超过这个限制时,最新的会覆盖最早的等等。

580

2023.10.17

session失效解决方法
session失效解决方法

session失效通常是由于 session 的生存时间过期或者服务器关闭导致的。其解决办法:1、延长session的生存时间;2、使用持久化存储;3、使用cookie;4、异步更新session;5、使用会话管理中间件。

876

2023.10.18

cookie与session的区别
cookie与session的区别

本专题整合了cookie与session的区别和使用方法等相关内容,阅读专题下面的文章了解更详细的内容。

1966

2025.08.19

treenode的用法
treenode的用法

​在计算机编程领域,TreeNode是一种常见的数据结构,通常用于构建树形结构。在不同的编程语言中,TreeNode可能有不同的实现方式和用法,通常用于表示树的节点信息。更多关于treenode相关问题详情请看本专题下面的文章。php中文网欢迎大家前来学习。

2841

2023.12.01

C++ 高效算法与数据结构
C++ 高效算法与数据结构

本专题讲解 C++ 中常用算法与数据结构的实现与优化,涵盖排序算法(快速排序、归并排序)、查找算法、图算法、动态规划、贪心算法等,并结合实际案例分析如何选择最优算法来提高程序效率。通过深入理解数据结构(链表、树、堆、哈希表等),帮助开发者提升 在复杂应用中的算法设计与性能优化能力。

396

2025.12.22

深入理解算法:高效算法与数据结构专题
深入理解算法:高效算法与数据结构专题

本专题专注于算法与数据结构的核心概念,适合想深入理解并提升编程能力的开发者。专题内容包括常见数据结构的实现与应用,如数组、链表、栈、队列、哈希表、树、图等;以及高效的排序算法、搜索算法、动态规划等经典算法。通过详细的讲解与复杂度分析,帮助开发者不仅能熟练运用这些基础知识,还能在实际编程中优化性能,提高代码的执行效率。本专题适合准备面试的开发者,也适合希望提高算法思维的编程爱好者。

437

2026.01.06

C++ 数据结构与算法实现教程合集
C++ 数据结构与算法实现教程合集

以 C++ 为实现语言,系统讲解核心数据结构与算法,涵盖链表(单链表/双链表/环检测)、栈与队列(单调栈/优先队列)、二叉树(遍历/BST/AVL/红黑树)、哈希表(开地址法/链地址法)、图(邻接表/BFS/DFS/Dijkstra/拓扑排序)、常见排序算法(快排/归并/堆排/计数排序)的实现与复杂度分析,同时分享 LeetCode 刷题技巧、竞赛编程常用模板(二分/前缀和/滑动窗口/动态规划),帮助开发者夯实算法基础。

492

2026.05.09

硬盘接口类型介绍
硬盘接口类型介绍

硬盘接口类型有IDE、SATA、SCSI、Fibre Channel、USB、eSATA、mSATA、PCIe等等。详细介绍:1、IDE接口是一种并行接口,主要用于连接硬盘和光驱等设备,它主要有两种类型:ATA和ATAPI,IDE接口已经逐渐被SATA接口;2、SATA接口是一种串行接口,相较于IDE接口,它具有更高的传输速度、更低的功耗和更小的体积;3、SCSI接口等等。

3228

2023.10.19

Postman身份认证怎么配置
Postman身份认证怎么配置

本专题从实际API接口调试场景出发,讲解Postman身份认证的完整配置流程,包括常见认证协议介绍、授权参数设置、访问令牌填写、OAuth 2.0授权及认证失败排查,帮助用户正确完成接口授权与访问验证。

40

2026.11.06

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn