python3 多进程求助 OSError: [Errno 24] Too many open files

2021-03-30 10:47:24 +08:00
 css3

业务:有大量(本次测试时 1 万多张)图片需要转成 base64 编码后,送入 http 接口请求处理,我采用以下代码: base64 用生成器处理, request 用多进程。 但下面代码跑到一半的时候,直接抛了 OSError: [Errno 24] Too many open files, 百度了一下,看上去是进程超过所能开启的最大文件数了, ulimit -n # mac 8192 请教下各位,我怎么应该 fix 这个问题,最终需求就是想快速高效的完成这个操作,可能我写的代码一开始就有问题,还希望大佬们指点一下。

import json
import time
import requests
import base64
import os
from multiprocessing import Process




def img_to_base64(img_path):
    r = {}
    for root, dirs, files in os.walk(img_path):
        for pic in files:
            if pic.endswith(('.png', '.jpg', '.jpeg', '.tiff', '.bmp', '.gif')):
                img = os.path.join(root, pic)
                with open(img, 'rb') as f:
                    bs64 = base64.b64encode(f.read()).decode('utf-8')
                    r[img] = bs64
                    yield r



def req(host, img_path):
    bs64_generator = img_to_base64(img_path)
    procs = []

    for items in bs64_generator:
        body, pic = None, None
        for pic, base64 in items.items():
            body = {
                "requests": [
                    {
                        "resource": {
                            "base64": base64
                        }
                    }
                ]
            }

        p = Process(target=r, args=(host, body, pic))
        procs.append(p)
        p.start()
    for proc in procs:
        proc.join()




def r(host, body, img):
    url = f'http://{host}/demo/'
    r = requests.post(url, data=json.dumps(body))
    print(img, r.json().get('results'))
    ret = r.json().get('results')[0]['status']
    if ret != 'OK':
        print(img, ret)




req('10.10.23.17:3345', './mypic/')
3517 次点击
所在节点    程序员
32 条回复
BrettD
2021-03-30 10:50:11 +08:00
ulimit 不行吗
liprais
2021-03-30 10:53:46 +08:00
yield 那里缩进不对
CRVV
2021-03-30 10:55:17 +08:00
1. 发 HTTP 请求不需要用多进程
2. 如果在乎性能,请用 requests.session
3. 如果单线程顺序发请求不够快,可以用 ThreadPoolExecutor 或者 aiohttp
sss495088732
2021-03-30 10:56:12 +08:00
做个缓冲区....要限制一下并行的进程,句柄数...你这能跑到一半儿,那电脑也是非常不戳
UN2758
2021-03-30 11:00:27 +08:00
你定义的 img_to_base64 是单纯的生成器,想要协程支持的生成器,得给它加上 @asyncio.coroutine
UN2758
2021-03-30 11:02:48 +08:00
@UN2758 #5 看错
xiaolinjia
2021-03-30 11:07:01 +08:00
开了 1w 多进程牛的,整个 multiprocessing.Pool 进程池限制下并行数吧。
css3
2021-03-30 11:24:33 +08:00
@BrettD mac 8192 跑满了

@liprais 这个缩进应该没问题吧,每处理 1 张,yield 出来

@CRVV 为啥不需要用多进程呢

@sss495088732 不知道咋搞缓冲区


@UN2758 老哥想表达啥,我没有看太懂啊

@xiaolinjia 好的,多谢,我找下文档
ch2
2021-03-30 12:14:41 +08:00
1 万个任务正常做法是用进程池开 n 个进程(n<=你的 cpu 核心数*2)
分批陆续完成,而不是 1 万个进程一拥而上
而且你的这个代码瓶颈在网速上,根本不需要多进程,用多线程就能处理
但是最优的做法是用 grequests 一次批量发 1000 个请求,配合上错误重试,分 10-20 次就搞定了
ch2
2021-03-30 12:18:40 +08:00
随便找到网图,grequests 是编程最简单的大批量发 http 请求的方法,几乎没有唯二的其他选择,你会用 requests 就会用 grequests
hsfzxjy
2021-03-30 12:19:05 +08:00
@css3 你要等文件关了再 yield
shuax
2021-03-30 12:48:53 +08:00
开 100 个进程差不多了,再多也不快。
zonyitoo
2021-03-30 13:24:32 +08:00
每个图标单独开一个进程去跑,这居然能跑得动电脑的配置也着实厉害了
renmu123
2021-03-30 13:26:30 +08:00
hhttp 直接开个进程池就可以了,或者用 grequest 。基本都是开箱即用
css3
2021-03-30 13:43:48 +08:00
@ch2 感谢老哥,学习了,我试试这个库。
@hsfzxjy,我的理解是 with 自动会关闭呢,另外我也尝试把 yield 给放到 with 外边,也还是这个问题,应该主要问题出在多进程哪里,如同 13 扣老哥说的
@shuax 嗯,多谢老哥
@zonyitoo 学业不精,惭愧
@renmu123 多谢,我试试
aladdindingding
2021-03-30 13:45:09 +08:00
ulimit 改成 65535
css3
2021-03-30 14:11:53 +08:00
@aladdindingding 本质是我进程和图片数一致了,这里有问题,改这个值,解决不了本质上的问题呢
LeeReamond
2021-03-30 14:33:27 +08:00
你这个需求 py 的最佳实践应该是多线程 ffi 加异步 io,大概会比你现在的方案快很多很多很多很多
laqow
2021-03-30 14:45:25 +08:00
看起来是 2 楼的问题,yield 写 with 里面了,每次迭代文件都没关

不改程序的话用 linux,想开多少文件开多少
laqow
2021-03-30 14:52:41 +08:00
感觉这样写每个图都是主进程开的,还没进 worker 就已经错误了

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/766409

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX