Python requests的Session对象在gevent绿线程中是线程安全的吗?

8 投票
1 回答
5409 浏览
提问于 2025-04-18 07:57

在一个使用了gevent的程序中,requests库的会话对象能否安全地在不同的greenlet之间使用?

编辑 - 添加更多解释:

当一个greenlet因为发送请求到服务器而暂停时,另一个greenlet能否安全地使用同一个会话对象里的socket来发送自己的请求?

编辑结束

我尝试用这里发布的代码进行测试 - https://gist.github.com/donatello/0b399d0353cb29dc91b0 - 但是没有遇到错误或意外的结果。不过,这并不能证明线程是安全的。

在测试中,我使用一个共享的会话对象发送了很多请求,想看看这个对象是否会把请求搞混淆 - 这有点天真,但我没有遇到任何异常。

为了方便,我把代码再贴一遍:

client.py

import gevent
from gevent.monkey import patch_all
patch_all()

import requests
import json

s = requests.Session()

def make_request(s, d):
    r = s.post("http://127.0.0.1:5000", data=json.dumps({'value': d}))
    if r.content.strip() != str(d*2):
        print("Sent %s got %s" % (r.content, str(d*2)))
    if r.status_code != 200:
        print(r.status_code)
        print(r.content)

gevent.joinall([
    gevent.spawn(make_request, s, v)
    for v in range(300)
])

server.py

from gevent.wsgi import WSGIServer
from gevent.monkey import patch_all

patch_all()

from flask import Flask
from flask import request

import time
import json

app = Flask(__name__)

@app.route('/', methods=['POST', 'GET'])
def hello_world():
    d = json.loads(request.data)
    return str(d['value']*2)

if __name__ == '__main__':
    http_server = WSGIServer(('', 5000), app)
    http_server.serve_forever()

具体的库版本:

requirements.txt

Flask==0.10.1
Jinja2==2.7.2
MarkupSafe==0.23
Werkzeug==0.9.4
argparse==1.2.1
gevent==1.0.1
greenlet==0.4.2
gunicorn==18.0
itsdangerous==0.24
requests==2.3.0
wsgiref==0.1.2

有没有其他测试可以检查greenlet的线程安全性?requests的文档在这方面不是很清楚。

1 个回答

8

这个requests库的作者还创建了一个和gevent结合使用的包,叫做grequests。建议你使用这个。

它支持通过session这个关键词来传递会话:

import grequests

s = requests.Session()

requests = [grequests.post("http://127.0.0.1:5000", 
                           data=json.dumps({'value': d}), session=s)
            for d in range(300)]

responses = grequests.map(requests)
for r in responses:
    if r.content.strip() != str(d*2):
        print("Sent %s got %s" % (r.content, str(d*2)))
    if r.status_code != 200:
        print(r.status_code)
        print(r.content)

需要注意的是,虽然会话是共享的,但并发请求每个都必须使用一个单独的连接(也就是socket);HTTP 1.x无法在同一个连接上同时处理多个请求。

撰写回答