并行化请求威胁python

时间:2016-04-23 13:54:14

标签: python parallel-processing python-requests

我正在构建一个视图(使用web2py)和其他一些网站的数据(大约10个),因此,对于我创建了一个函数的每个网站,它准备了一些参数,用params发送请求,得到响应并进行一些处理,在此之后我将响应传递给控制器​​,它将在完成后以请求的所有10个响应构建视图。

我的问题与速度有关,因为HTTP协议的性质所有请求都是同步的,当服务器花了很长时间来响应时,我的应用程序一直在等待。我很想知道是否有任何方法可以"并行化"我的函数调用所以我可以在等待来自其他服务器的响应时执行其他操作。

我已经检查过grequest和request-futures但我认为这并不适合这种情况因为我不提出请求,我会像之前说的那样对请求进行预处理和后期处理。

1 个答案:

答案 0 :(得分:1)

寻找grequest等解决方案。

原因是大多数时候,你等待工作I / O操作完成(下载页面)。与I / O时间相比,前处理和后处理很可能是微不足道的。

我已经看到了grequest用于网页抓取的非常大的加速。

如果您的预处理和后处理也很耗时,您必须转到multiprocessing模块并在多个进程中运行任务(如果您受CPU限制,多线程将无济于事)。

但首先 - 真的尝试grequest

使用grequests的HTTP作业

由于预期会有一些预处理和后期处理,我们必须以某种方式组织它。

grequests:"未发送请求"

的概念

grequests有一个"未发送请求"的概念。以后要完成这项工作。 grequests 允许开始这些工作例如grequests.mapgrequests.imap

grequests:未发送请求的回调(挂钩)

每个未发送的请求都可以附加挂钩处理返回的响应。

一个未发送的请求可能会附加更多钩子。

我们将把它用于后期处理。

每个实例

类HttpJobXxx绑定事物

我们希望以某种方式组织以下与工作相关的事情:

  • 实例化:传入参数,定义要完成的工作
  • 预处理:准备要准备的内容
  • 创建未发送请求
  • 返回回复的后处理

稍后我们将在以下步骤中使用它:

  1. 实例化作业。这将包括调用pre_processing方法。
  2. 通过询问作业实例创建它来获取未发送的请求。未发送的请求将包含钩子 通过实例post_process调用后处理最终响应。
  3. 将作业实例添加到要执行的作业列表
  4. 让作业运行,例如grequests.mapgrequests.imap
  5. 诀窍在于,每个作业实例都可以利用自己的参数来保持 整个工作生命周期中的上下文信息清理。

    HttpJobMyIp

    这是真实的代码:

    import grequests
    
    
    class HttpJobMyIp(object):
        url = "https://httpbin.org/ip"
    
        def __init__(self, nickname="myip over httpbin.org"):
            self.nickname = nickname
            self.pre_process()
    
        def pre_process(self):
            """Whatever pre-processing you need."""
            print("Preprocessing {self.nickname} with {self.url}".format(self=self))
    
        @property
        def unsent_request(self):
            """Create requests for grequests.
            Override by whatever construct you need.
            """
            return grequests.get(self.url, hooks={"response": [self.post_process]})
    
        def post_process(self, response, **kwargs):
            msg = "Post-processing {self.nickname} with {self.url}"
            print(msg.format(self=self))
            assert "origin" in response.json()
            myip = response.json()["origin"]
            print("My IP is {}".format(myip))
    

    HttpJobDelay

    为了使示例完整,我们可以添加另一个HTTP作业类。这次允许拨打网址, 这会有一些延迟。

    class HttpJobDelay(object):
        url = "https://httpbin.org/delay/{delay}"
    
        def __init__(self, delay=1, nickname="Delayed response"):
            self.nickname = nickname
            self.delay = delay
            self.pre_process()
    
        def pre_process(self):
            """Whatever pre-processing you need."""
            msg = ("Preprocessing {self.nickname} "
                   "with {self.url} and delay {self.delay}")
            print(msg.format(self=self))
            self.delay = self.delay + 0.7
    
        @property
        def unsent_request(self):
            """Create requests for grequests.
            Override by whatever construct you need.
            """
            url = self.url.format(delay=self.delay)
            return grequests.get(url, hooks={"response": [self.post_process]})
    
        def post_process(self, response, **kwargs):
            msg = ("Post-processing {self.nickname} "
                   "with {self.url} and expected delay {self.delay}")
            print(msg.format(self=self))
            print("Finally we got (a bit delayed) response")
    

    这里我们故意通过添加0.7秒来修改延迟,预处理有一个 改变一些事情的机会。

    让它全部运行

    def exception_handler(request, exception):
        return exception
    
    
    def main():
        http_jobs = []
        job = HttpJobMyIp("Get my IP")
        http_jobs.append(job.unsent_request)
    
        for delay in [3, 1, 6, 2]:
            job = HttpJobDelay(delay, "Delay "+str(delay))
            http_jobs.append(job.unsent_request)
        # grequests.map(http_jobs, exception_handler=exception_handler))
        list(grequests.imap(http_jobs, exception_handler=exception_handler), size=6)
        print("DONE")
    
    
    if __name__ == "__main__":
        main()
    

    计划是调用一个HttpJobMyIp作业实例和4个HttpJobDelay作业实例 不断要求的延迟。故意不对延迟进行排序。

    将上述所有代码放在mreq.py文件中,我们可以运行它:

    $ python mreq.py                                                                                 1 ↵
    Preprocessing Get my IP with https://httpbin.org/ip
    Preprocessing Delay 3 with https://httpbin.org/delay/{delay} and delay 3
    Preprocessing Delay 1 with https://httpbin.org/delay/{delay} and delay 1
    Preprocessing Delay 6 with https://httpbin.org/delay/{delay} and delay 6
    Preprocessing Delay 2 with https://httpbin.org/delay/{delay} and delay 2
    Post-processing Get my IP with https://httpbin.org/ip
    My IP is 87.257.712.26
    Post-processing Delay 1 with https://httpbin.org/delay/{delay} and expected delay 1.7
    Finally we got (a bit delayed) response
    Post-processing Delay 2 with https://httpbin.org/delay/{delay} and expected delay 2.7
    Finally we got (a bit delayed) response
    Post-processing Delay 3 with https://httpbin.org/delay/{delay} and expected delay 3.7
    Finally we got (a bit delayed) response
    Post-processing Delay 6 with https://httpbin.org/delay/{delay} and expected delay 6.7
    Finally we got (a bit delayed) response
    DONE
    

    经验教训

    在何处查找grequests文档

    没有ReadTheDocs文档。

    改为使用:

    • 源代码(整个模块有153行包含注释)
    • 测试套件(235行)

    请注意,HttpJobXxx类不是必需的,只是因为我觉得它很方便而创建。

    回调函数参数:include **kwargs

    回调函数应有两个参数:

    • response(由HTTP电话提供)
    • **kwargs

    如果没有**kwargs代码,则默认无效。

    无声失败:使用异常处理程序

    如果没有异常处理程序,如果出现问题,你通常会不知道,是什么 继续使用exception_handler您可以例如得到异常作为结果并学习,发生了什么 错。

    退出太快:grequests.imap是生成器

    立即致电grequests.imap(http_jobs, exception_handler=exception_handler), size=6) 返回生成器,如果没有消耗它的值,它会继续并退出。

    因此,调用封装在list()

    同时处理的请求 - 它可以正常工作

    我们可以看到,无论HttpJobDelay实例的延迟是否未排序,都会返回结果 按顺序 - 最短的延迟,最后的延迟。使用grequests.imap 未发送请求和结果的顺序可能不同。

    另一方面,grequests.map结果按照它们的顺序返回 工作清单要求。

    grequest.imap参数size默认为2

    如果您未指定size,则默认值为2.这可能会影响结果的顺序。

    期待什么样的加速

    grequests使用"绿色线程"在一个进程中运行。这意味着,在不同之间切换 "绿线"更有效率,因为它是在代码更喜欢因此保存的时候完成的 CPU和OS完成上下文切换的开销。

    由于任务是I / O有限(大部分时间我们都在等待一些数据),我们可能会生活得很好 单一过程。

    如果请求数量增加,将会看到最高的加速。