Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/351.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 如何实现与tornado gen.Task/gen.coroutine装饰器的并行性_Python_Parallel Processing_Tornado_Future_Coroutine - Fatal编程技术网

Python 如何实现与tornado gen.Task/gen.coroutine装饰器的并行性

Python 如何实现与tornado gen.Task/gen.coroutine装饰器的并行性,python,parallel-processing,tornado,future,coroutine,Python,Parallel Processing,Tornado,Future,Coroutine,这里是一个例子,当一个人必须在后端服务器中引入并行性时 我愿意查询N ELB,每个查询5个不同的查询,并将结果发送回web客户端 后端是Tornado,根据我在过去多次阅读的内容,如果我使用@gen.Task或gen.coroutine,我应该能够并行处理多个任务 然而,我必须在这里遗漏一些东西,因为我所有的请求(20个,4个elbs*5个查询)都是一个接一个地处理的 def query_elb(fn, region, elb_name, period, callback): callb

这里是一个例子,当一个人必须在后端服务器中引入并行性时

我愿意查询N ELB,每个查询5个不同的查询,并将结果发送回web客户端

后端是Tornado,根据我在过去多次阅读的内容,如果我使用@gen.Task或gen.coroutine,我应该能够并行处理多个任务

然而,我必须在这里遗漏一些东西,因为我所有的请求(20个,4个elbs*5个查询)都是一个接一个地处理的

def query_elb(fn, region, elb_name, period, callback):
    callback(fn (region, elb_name, period))

class DashboardELBHandler(RequestHandler):

    @tornado.gen.coroutine
    def get_elb_info(self, region, elb_name, period):
        elbReq = yield gen.Task(query_elb, ELBSumRequest, region, elb_name, period)
        elb2XX = yield gen.Task(query_elb, ELBBackend2XX, region, elb_name, period)
        elb3XX = yield gen.Task(query_elb, ELBBackend3XX, region, elb_name, period)
        elb4XX = yield gen.Task(query_elb, ELBBackend4XX, region, elb_name, period)
        elb5XX = yield gen.Task(query_elb, ELBBackend5XX, region, elb_name, period)

        raise tornado.gen.Return( 
            [
                elbReq,
                elb2XX,
                elb3XX,
                elb4XX,
                elb5XX,
            ]
        )

    @tornado.web.authenticated
    @tornado.web.asynchronous
    @tornado.gen.coroutine
    def post(self):
        ret = []

        period = self.get_argument("period", "5m")

        cloud_deployment = db.foo.bar.baz()
        for region, deployment in cloud_deployment.iteritems():

            elb_name = deployment["elb"][0]
            res = yield self.get_elb_info(region, elb_name, period)
            ret.append(res)

        self.push_json(ret)



def ELBQuery(region, elb_name,  range_name, metric, statistic, unit):
    dimensions = { u"LoadBalancerName": [elb_name] }

    (start_stop , period) = calc_range(range_name)

    cw = boto.ec2.cloudwatch.connect_to_region(region)
    data_points = cw.get_metric_statistics( period, start, stop, 
        metric, "AWS/ELB", statistic, dimensions, unit)    

    return data_points

ELBSumRequest   = lambda region, elb_name, range_name : ELBQuery(region, elb_name, range_name,  "RequestCount", "Sum", "Count")
ELBLatency      = lambda region, elb_name, range_name : ELBQuery(region, elb_name, range_name,  "Latency", "Average", "Seconds")
ELBBackend2XX   = lambda region, elb_name, range_name : ELBQuery(region, elb_name, range_name,  "HTTPCode_Backend_2XX", "Sum", "Count")
ELBBackend3XX   = lambda region, elb_name, range_name : ELBQuery(region, elb_name, range_name,  "HTTPCode_Backend_3XX", "Sum", "Count")
ELBBackend4XX   = lambda region, elb_name, range_name : ELBQuery(region, elb_name, range_name,  "HTTPCode_Backend_4XX", "Sum", "Count")
ELBBackend5XX   = lambda region, elb_name, range_name : ELBQuery(region, elb_name, range_name,  "HTTPCode_Backend_5XX", "Sum", "Count")

问题在于
ELBQuery
是一个阻塞函数。如果它没有在某处产生另一个协同路由,那么协同路由调度器就无法交错调用。(这就是合作计划的全部意义——他们是合作的,而不是先发制人的。)

如果问题类似于
calc_range
调用,那么很容易将其分解为更小的部分,在这些部分中,每一个部分都会向下一个部分屈服,这使调度程序有机会进入每一部分之间

但很可能是boto调用阻塞了,函数的大部分时间都花在等待
get\u metric\u statistics
返回,而其他任何函数都无法运行

那么,你如何解决这个问题

  • 为每个boto任务派生一个线程。Tornado使围绕线程或线程池任务透明地包装一个协程变得非常容易,它神奇地解除了所有阻塞。当然,使用线程也要付出代价
  • 在线程池而不是每个线程上调度boto任务。与#1类似的权衡,尤其是当您只有少量任务时。(但如果您可以为500个不同的用户分别执行5项任务,您可能需要一个共享池。)
  • 重写或monkeypatch boto以使用协同程序。这将是一个理想的解决方案……但它是最有效的解决方案(而且最有可能破坏您不理解的代码,并且必须将其作为boto更新进行维护,等等)。然而,有些人至少已经开始这样做了,比如这个项目
  • 使用
    greenlets
    和monkeypatch足够多的库依赖项来欺骗它,使其成为异步的。这听起来有点老套,但实际上可能是最好的解决方案;看看这个
  • 使用
    greenlets
    和monkeypatch整个stdlib-ala
    gevent
    诱使boto和tornado一起工作,甚至没有意识到这一点。这听起来是个糟糕的主意;您最好将整个应用程序移植到
    gevent
  • 使用一个单独的进程(甚至是一个池),它使用类似于
    gevent
    的东西

  • 在不知道更多细节的情况下,我建议先看一下#2和#4,但我不能保证它们会是最适合你的答案。

    如果
    ELBSumRequest
    和朋友们自己阻止函数而不是产生协同路由,那么Tornado就没有办法交错它们。如果你不能重写它们(或者把它们分成更小的部分,这样每一个都会让位给下一个,这就给了调度程序一个进入中间的机会),协同程序在这里帮不了你;您将不得不使用线程(或者通过monkeypatching阻塞调用使用准抢占式greenlet,但Tornado支持前者而不支持后者,我怀疑您是否想重写
    gevent
    或任何…)中的所有内容。@abarnert,刚刚添加了实现。您可以帮助并建议如何以异步方式重写
    ELBQuery
    ?有一个想法我愿意检查一下,那就是,编写一个tornado.web.RequestHandler,一次执行一个boto请求,并使用异步http\U客户端(内部)调用它,怎么样?@TzuryBarYochay:这只是意味着每个boto调用都会阻塞新的RequestHandler,而不是原来的RequestHandler。因此,除非您线程化或异步化该处理程序,否则不会有多大帮助。(你可以使用一个单独的tornado服务器进程来解决这个问题……但在这一点上,tornado并没有真正为你做任何事情;你也可以轻松地编写一个简单的脚本,同步完成所有boto工作。