Python requests Session 对象是不是在 gevent greenlets 之间共享,线程安全(greenlets 之间)?

Posted

技术标签:

【中文标题】Python requests Session 对象是不是在 gevent greenlets 之间共享,线程安全(greenlets 之间)?【英文标题】:Is a Python requests Session object shared between gevent greenlets, thread-safe (between greenlets)?Python requests Session 对象是否在 gevent greenlets 之间共享,线程安全(greenlets 之间)? 【发布时间】:2014-07-19 01:38:53 【问题描述】:

requests 库会话对象能否在指定程序中安全地跨 greenlets 使用?

编辑 - 添加更多解释:

当一个 greenlet 因为它已经发出一个套接字调用来向服务器发送请求而屈服时,同一个套接字(在共享会话对象内)是否可以被另一个 greenlet 安全地使用来发送它自己的请求?

结束编辑

我尝试使用此处发布的代码进行测试 - 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线程安全性的测试吗?请求文档在这一点上不太清楚。

【问题讨论】:

请记住,greenlets 不会在单独的线程中运行。该代码在您声明它的同一线程上运行(即主线程) 我要编辑这个问题 - 但我的意思是会话对象是绿色安全的吗?当一个 greenlet 在通过套接字发送内容时让步时,另一个可以抓住同一个套接字(在会话对象内)并发送自己的内容吗? 底层套接字(如果是标准套接字)只有一个执行流程,AFAIK,并且不可能同时访问。至少在标准的 C impl 中是这样的。 会话对象不是套接字或连接对象的包装器,它是套接字/连接池的包装器。如果没有可重用的连接,此连接池会为新的 greenlet 创建新连接。 【参考方案1】:

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)

【讨论】:

在我的实际应用程序中,我将无法将所有请求收集在一起并使用地图调用。请求是通过共享会话对象在不同的​​ greenlet 中发出的。这就是为什么我要确认线程安全。 @donatello:地图调用使用它自己的池,您可以设置自己的池并向其中添加要执行的请求。 @donatello:使用grequests.send() 向您自己的池发送请求,请参阅In what way is grequests asynchronous? 示例。 Greenlets 仅在它们产生时被抢占; I/O 在这里是一种自动产出。但是在 Session 中处理共享状态的代码不会被抢占,因此可以避免竞争条件。 不,套接字不是“共享的”;连接由线程安全的连接池处理(受threading.RLock 保护)。

以上是关于Python requests Session 对象是不是在 gevent greenlets 之间共享,线程安全(greenlets 之间)?的主要内容,如果未能解决你的问题,请参考以下文章

python中requests.session的妙用

Swift相当于Python的requests.session()?

python requests.request 和session.request区别究竟在哪里

Python-requests-get-fails-with-403-forbidden-even-after-using-headers-and-session - PYTHON 3.8

如何在不丢失身份验证的情况下模拟Session.request?

使用 python-requests 模块更新 Session 中的 Cookie