【问题标题】:python-requests with multithreading具有多线程的python请求
【发布时间】:2014-09-28 14:13:23
【问题描述】:

我正在创建一个 HTTP 客户端,它每秒可以生成数百个连接,并在每个连接上发送多达 10 个请求。我正在使用线程,因此可以实现并发。 这是我的代码:

def generate_req(reqSession):
    requestCounter = 0
    while requestCounter < requestRate:
        try:
            response1 = reqSession.get('http://20.20.1.2/tempurl.html')
            if response1.status_code == 200:
                client_notify('r')
        except(exceptions.ConnectionError, exceptions.HTTPError, exceptions.Timeout) as Err:
            client_notify('F')
            break
        requestCounter += 1

def main():
    for q in range(connectionPerSec):
        s1 = requests.session()
        t1 = threading.Thread(target=generate_req, args=(s1,))
        t1.start()

问题:

  1. 在 requestRate = 1 的情况下,它无法扩展到超过 200 个连接/秒。我在同一台客户端计算机上运行其他可用的 HTTP 客户端并针对服务器,测试运行良好并且能够扩展。

  2. 当 requestRate = 10 时,连接数/秒降至 30。 原因:无法每秒创建目标数量的线程。

对于问题 #2,客户端计算机无法创建足够的请求会话并启动新线程。一旦 requestRate 设置为大于 1,事情就开始分崩离析。 我怀疑它与请求使用的 HTTP 连接池有关。

请提出我在这里做错了什么。

【问题讨论】:

  • 为什么要重用同一个session 对象?据我了解,您的connectionPerSec 实际上是指并行线程数,requestRate 实际上是每个线程上的串行请求数。您可能还想查看concurrent.futures
  • 会话对象仅在衍生线程中重用,因此它对所有串行请求使用相同的连接。
  • 似乎来自 concurrent.futures 的 ThreadPoolExecutor 与我正在尝试做的事情相同?
  • 请注意,在 CPython 中,线程的并发性是有限的:一次只有一个线程可以执行 Python 字节码。全局解释器锁强制执行此限制以简化内存管理。

标签: python multithreading python-requests


【解决方案1】:

我无法让事情崩溃,但是以下代码具有一些新功能:

1) 扩展日志记录,包括特定的每个线程信息

2) 最后所有线程join()ed 以确保父进程不会让它们挂起

3) 多线程print 倾向于交错消息,这可能很笨拙。此版本使用yield,因此将来的版本可以接受消息并清楚地打印它们。

来源

import exceptions, requests, threading, time

requestRate = 1
connectionPerSec = 2


def client_notify(msg):
    return time.time(), threading.current_thread().name, msg

def generate_req(reqSession):
    requestCounter = 0
    while requestCounter < requestRate:
        try:
            response1 = reqSession.get('http://127.0.0.1/')
            if response1.status_code == 200:
                print client_notify('r')
        except (exceptions.ConnectionError, exceptions.HTTPError, exceptions.Timeout):
            print client_notify('F')
            break
        requestCounter += 1

def main():
    for cnum in range(connectionPerSec):
        s1 = requests.session()
        th = threading.Thread(
            target=generate_req, args=(s1,),
            name='thread-{:03d}'.format(cnum),
        )
        th.start()

    for th in threading.enumerate():
        if th != threading.current_thread():
            th.join()


if __name__=='__main__':
    main()

输出

(1407275951.954147, 'thread-000', 'r')
(1407275951.95479, 'thread-001', 'r')

【讨论】:

    猜你喜欢
    • 2021-09-25
    • 1970-01-01
    • 2016-12-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-12-11
    • 1970-01-01
    • 2017-02-01
    相关资源
    最近更新 更多