关于gevent的monkey.patch_all导致subprocess.check_call执行变慢的问题
解决gevent monkey.patch_all导致subprocess.check_call变慢的问题
这确实是gevent monkey补丁使用中很常见的一个坑,我来给你拆解下原因和对应的解决办法:
问题根源
当你调用patch_all()时,gevent默认会替换标准库中的subprocess模块,改成适配协程的版本。但这个协程兼容版为了实现非阻塞IO和协程间同步,会做额外的封装与处理——对于像iptables -L -vx这种输出量较大、依赖系统内核操作的命令来说,这些额外处理反而会带来明显的性能开销,直接导致执行时间变长。
解决方案
根据你的业务场景,有几种不同的处理方式:
1. 全局禁用subprocess的补丁
如果你的代码里不需要让subprocess支持协程(比如所有subprocess调用都是同步阻塞的,不影响其他协程执行),最简单的办法就是在patch时排除subprocess:
from gevent.monkey import patch_all; patch_all(subprocess=False) import subprocess from subprocess import STDOUT from tempfile import NamedTemporaryFile import datetime with NamedTemporaryFile() as f: print('Subprocess call at {}'.format(datetime.datetime.now())) ret = subprocess.check_call(['iptables', '-t', 'mangle', '-L', '-vx'], stdout=f, stderr=STDOUT) print('Subprocess call end at {}'.format(datetime.datetime.now()))
这样subprocess会保持标准库的原生实现,执行速度就会恢复正常。
2. 针对特定调用使用原始subprocess函数
如果你的其他代码依赖于gevent patched后的subprocess,只需要个别调用恢复原生速度,那可以在patch之前先保存原始的check_call函数:
# 先导入原始subprocess,保存需要的函数 import subprocess original_check_call = subprocess.check_call # 再执行全量补丁 from gevent.monkey import patch_all; patch_all() from subprocess import STDOUT from tempfile import NamedTemporaryFile import datetime with NamedTemporaryFile() as f: print('Subprocess call at {}'.format(datetime.datetime.now())) # 调用原始的check_call函数 ret = original_check_call(['iptables', '-t', 'mangle', '-L', '-vx'], stdout=f, stderr=STDOUT) print('Subprocess call end at {}'.format(datetime.datetime.now()))
或者也可以用gevent提供的get_original方法获取原始函数:
from gevent.monkey import patch_all; patch_all() from gevent import get_original import subprocess from subprocess import STDOUT from tempfile import NamedTemporaryFile import datetime # 获取原始的check_call original_check_call = get_original('subprocess', 'check_call') with NamedTemporaryFile() as f: print('Subprocess call at {}'.format(datetime.datetime.now())) ret = original_check_call(['iptables', '-t', 'mangle', '-L', '-vx'], stdout=f, stderr=STDOUT) print('Subprocess call end at {}'.format(datetime.datetime.now()))
3. 优化gevent subprocess的配置(进阶)
如果必须使用patched后的subprocess,可以尝试调整gevent的subprocess相关配置,比如修改IO缓冲方式或使用更高效的子进程通信机制,但这种方式比较复杂,通常前两种方案已经能解决大部分场景的问题。
内容的提问来源于stack exchange,提问作者lz96
相关产品推荐
相关产品推荐

