Python 3 urllib Vs请求性能

use*_*957 5 python performance urllib2 urllib3 python-requests

我正在使用python 3.5,我正在检查urllib模块Vs请求模块的性能.我在python中编写了两个客户端,第一个使用urllib模块,第二个使用请求模块.它们都生成二进制数据,我将其发送到基于烧瓶的服务器,并从烧瓶服务器发送二进制数据到客户端.我发现从客户端向服务器发送数据的时间花了两个时间用于两个模块(urllib,请求),但是从urllib到客户端返回数据所花费的时间比urllib要快两倍.我正在使用localhost.
我的问题是为什么?
我对请求模块做错了什么让它变得更慢?

这是服务器代码:

from flask import Flask, request
app = Flask(__name__)
from timeit import default_timer as timer
import os

@app.route('/onStringSend', methods=['GET', 'POST'])
def onStringSend():
    return data

if __name__ == '__main__':
    data_size = int(1e7)
    data = os.urandom(data_size)    
    app.run(host="0.0.0.0", port=8080)
Run Code Online (Sandbox Code Playgroud)

这是基于urllib的客户端代码:

import urllib.request as urllib2
import urllib.parse
from timeit import default_timer as timer
import os

data_size = int(1e7)
num_of_runs = 20
url = 'http://127.0.0.1:8080/onStringSend'

def send_binary_data():
    data = os.urandom(data_size)
    headers = {'User-Agent': 'Mozilla/5.0 (compatible; Chrome/22.0.1229.94;  Windows NT)', 'Content-Length': '%d' % len(data), 'Content-Type':  'application/octet-stream'}
    req = urllib2.Request(url, data, headers)
    round_trip_time_msec = [0] * num_of_runs
    for i in range(0,num_of_runs):
        t1 = timer()
        resp = urllib.request.urlopen(req)
        response_data = resp.read()
        t2 = timer()
        round_trip_time_msec[i] = (t2 - t1) * 1000

    t_max = max(round_trip_time_msec)
    t_min = min(round_trip_time_msec)
    t_average = sum(round_trip_time_msec)/len(round_trip_time_msec)

    print('max round trip time [msec]: ', t_max)
    print('min round trip time [msec]: ', t_min)
    print('average round trip time [msec]: ', t_average)


send_binary_data()
Run Code Online (Sandbox Code Playgroud)

这是基于请求的客户端代码:

import requests
import os
from timeit import default_timer as timer


url = 'http://127.0.0.1:8080/onStringSend'
data_size = int(1e7)
num_of_runs = 20


def send_binary_data():
    data = os.urandom(data_size)
    s = requests.Session()
    s.headers['User-Agent'] = 'Mozilla/5.0 (compatible; Chrome/22.0.1229.94;Windows NT)'
    s.headers['Content-Type'] = 'application/octet-stream'
    s.headers['Content-Length'] = '%d' % len(data)

    round_trip_time_msec = [0] * num_of_runs
    for i in range(0,num_of_runs):
        t1 = timer()
        response_data = s.post(url=url, data=data, stream=False, verify=False)
        t2 = timer()
        round_trip_time_msec[i] = (t2 - t1) * 1000

    t_max = max(round_trip_time_msec)
    t_min = min(round_trip_time_msec)
    t_average = sum(round_trip_time_msec)/len(round_trip_time_msec)

    print('max round trip time [msec]: ', t_max)
    print('min round trip time [msec]: ', t_min)
    print('average round trip time [msec]: ', t_average)

send_binary_data()
Run Code Online (Sandbox Code Playgroud)

非常感谢

Vas*_*nov 8

首先,为了重现问题,我必须在你的onStringSend函数中添加以下行:

request.get_data()
Run Code Online (Sandbox Code Playgroud)

否则,我得到"对等连接重置"错误,因为服务器的接收缓冲区一直在填满.

现在,出现这个问题的直接原因是Response.content(以隐式方式调用stream=False)以10240字节的块为单位迭代响应数据:

self._content = bytes().join(self.iter_content(CONTENT_CHUNK_SIZE)) or bytes()
Run Code Online (Sandbox Code Playgroud)

因此,解决问题的最简单方法是使用stream=True,从而告诉请求您将按照自己的进度读取数据:

response_data = s.post(url=url, data=data, stream=True, verify=False).raw.read()
Run Code Online (Sandbox Code Playgroud)

通过此更改,请求版本的性能或多或少与urllib版本的性能相同.

另请参阅请求文档中的" 原始响应内容 "部分以获取有用的建议.

现在,有趣的问题仍然存在:为什么要Response.content在如此小的块中迭代?在 Requests的核心开发人员Cory Benfield交谈之后,看起来可能没有特别的原因.我在请求中提交了问题#3186以进一步研究这个问题.

  • 演讲的链接现在已损坏。 (2认同)