python requests多进程不安全的场景

发布于2018-11-16 | 1647 阅读 | 1 喜欢 | python requests

python requests是个老牌的http client库,在多线程下(threading)是安全的,在协程下自然也是安全的。值得一说的是Requets自身并没有实现连接池,而是引入了标准库urllib2的连接池。池的线程安全实现很简单,就是加锁控制边界。 既然是requests是线程安全的,那么在多进程下是否安全? 是否安全需要看你如何去使用multiprocessing requests了。如果是先fork子进程再进行http请求,那么是没有问题的。如果是先使用了session连接池,再去fork子进程,那么会出现多进程下socket读写不安全的问题。

requests默认有一个session对象,该session对象会绑定一个连接池。当你在fork子进程前,先请求了一个api,那么产生的socket连接会保留在session对象里。这时候你fork了子进程,子进程不仅继承父进程空间,而且会继承文件描述符。当再次尝试请求http请求时,有可能出现多个进程对一个连接进行写入和读取。

import time
import requests
from multiprocessing import Process, Queue


uri = 'http://www.163.com'
q = Queue()
session = requests.Session()


def req(args):
    while 1:
        response = session.get(uri)
        print(response)
        time.sleep(3)


def feed():
    q.put("sss")


if __name__ == '__main__':
    response = session.get(uri)

    pids = []
    for num in range(5):
        p = Process(target=req, args=(11, ))
        p.start()
        pids.append(p)

    for pid in pids:
        pid.join()

我们会发现不同pid的进程往同一个文件描述符去读写数据,这个就是requests session非进程安全的表现了。 req.jpg

那么如何解决这个问题呢?python多进程下不要共用同一个requests session,或者说不要共用一个存有连接对象的session对象。

个人觉得python那么多网络库的连接池安全方面,redis-py做的就很好,redis py会通过pid来判断连接池对象是否从父进程继承的,如是继承则重新实例化新的连接池对象。