简体   繁体   English

AsyncHTTPClient阻止了我的Tornado IOLoop

[英]AsyncHTTPClient blocking my Tornado IOLoop

how are you? 你好吗?

I've been through this trouble the last days, and I seem to not being able to completely understand the tornado gen library. 我在最后几天遇到过这个麻烦,我似乎无法完全理解龙卷风的基因库。

I have this piece of code, as an example: 我有这段代码,作为一个例子:

@gen.coroutine
def get(self, build_id=None):
    status_query = self.get_query_arguments("status")
    limit_query = self.get_query_arguments("limit")

    results = [self._dummy() for i in range(15)]
    yield results

def _dummy(self):
    http_client = tornado.httpclient.AsyncHTTPClient()
    return http_client.fetch("https://www.google.com", headers=self.headers, validate_cert=False)

As I thought, my 15 requests to fetch google should be triggering almost at the same time. 正如我所想,我提出的15个谷歌请求应该几乎同时触发。 The "results" list, should be a list of futures, and then, yielding the list should wait for all of them to be completed. “结果”列表应该是一个期货列表,然后,产生列表应该等待所有这些列表完成。

That's actually happening, but it's taking around 6 seconds to make those requests, and it's growing incrementally as I increase the range of the for loop. 这实际上正在发生,但是这需要大约6秒才能完成这些请求,而且随着我增加for循环的范围,它会逐渐增长。

Shouldn't they take around the same time to be ready? 他们不应该在同一时间准备好吗?

Am I missing something? 我错过了什么吗?

Thank you very much! 非常感谢你!

AsyncHTTPClient's default max_clients is 10. When you initiate 15 requests, 10 of them begin immediately, but the remaining 5 must wait for other requests to finish before they can begin. AsyncHTTPClient的默认max_clients为10.当您发起15个请求时,其中10个立即开始,但其余5个必须等待其他请求完成才能开始。 To begin more concurrent requests, raise max_clients to a larger number. 要开始更多并发请求,请将max_clients提升到更大的数字。 See Tornado's documentation for details on configuring AsyncHTTPClient. 有关配置AsyncHTTPClient的详细信息,请参阅Tornado的文档。

If your requests aren't IO bound then you won't see much change. 如果您的请求不是IO绑定,那么您将看不到太多变化。 - Me -

In programming these are the primary limits that we have: 在编程中,这些是我们的主要限制:

  • CPU (number of calculations that can happen per second) CPU(每秒可以发生的计算次数)
  • Cache access in the processor 处理器中的缓存访问
  • RAM access RAM访问
  • Disk Access 磁盘访问
  • Network access 网络访问

In Python, we're even further limited when it comes to CPU access because of the GIL. 在Python中,由于GIL,我们在CPU访问方面甚至受到进一步的限制。 With modern computers that tend towards multiple cores - 2, 4, 8, or 16 - we're crippled 1 even further because typically each of those processors are going to be a bit slower. 随着现代计算机倾向于多个核心--2,4,8或16 - 我们甚至进一步瘫痪1,因为通常每个处理器都会慢一点。 For more information about the GIL, check out David Beazley's GIL talk and Larry Hasting's GIL-ectomy . 有关GIL的更多信息,请查看David Beazley的GIL演讲Larry Hasting的GIL-ectomy

To bypass the Global Interpreter Lock, there have been several callback-style modules developed, like Twisted, Tornado, and asyncio. 为了绕过Global Interpreter Lock,开发了几个回调式模块,如Twisted,Tornado和asyncio。 The way these work is by performing some operations an typically yielding up control when they reach a point where the IO stops. 这些工作的方式是通过执行某些操作,当它们到达IO停止点时通常会产生控制。

For an example, if I'm writing data to a spinning disk perhaps I can write 100kb to the disk, but while I'm waiting for all of that information to be written, perhaps I can go off and do 1,000 calculations before all of the data finishes writing. 举个例子,如果我正在向旋转磁盘写入数据,也许我可以写100kb到磁盘,但是当我等待写入所有这些信息时,也许我可以在所有信息之前完成1000次计算。数据写完了。

Alternatively, perhaps I can make 100 requests per second to a webservice, but it only takes me 0.0001s to perform my calculations for each of those requests. 或者,也许我可以每秒向Web服务发出100个请求,但是每个请求执行我的计算只需要0.0001秒。 If you look at a graph of where I spend my time it's going to look something like this: 如果你看一下我花时间在哪里的图表,它会看起来像这样:

    #            
    #            
    #            
    #            
    #            
    #            
    #            
    #           #
--------------------------
 reading    processing

What these processes allow you to do is interleave the processing and the reading/writing, by sending the request packets off, then doing something else, and then at some point coming back to read the returned packets. 这些进程允许您执行的操作是交错处理和读/写,通过发送请求数据包,然后执行其他操作,然后在某些时候返回以读取返回的数据包。

Being IO bound like this, you can see a pretty massive speedup, because rather than looking something like this: 像这样的IO绑定,你可以看到相当大的加速,因为而不是看起来像这样:

 start    end start     end
--|--------|----|--------|------
 t=0      t=5  t=6      t=11

You can get something like this: 你可以得到这样的东西:

     start      end
 start|     end  |
--|---|------|---|-
 t=0 t=1    t=5  t=6

But if your process is CPU bound you're not going to see any of that speedup (or at least not much), because you're spending 30s doing processing and only 1s doing any kind of waiting for the network. 但是如果你的进程是CPU限制的,那你就不会看到任何加速(或者至少没有那么多),因为你花费30多秒进行处理,只有1个人正在等待网络。

Before trying the async approach, give the standard single-threaded approach and see 1) if it's fast enough and 2) if it's slow at the network/IO boundary. 在尝试异步方法之前,给出标准的单线程方法,并参见1)它是否足够快以及2)它是否在网络/ IO边界处变慢。

You can easily use something like the line profiler for Python, and (if not already) separate out your read, process, and write functions and see where you're spending the time. 您可以轻松地使用类似于Python的行分析器之类的东西,并且(如果还没有)将您的读取,处理和写入功能分开,并查看您花费时间的位置。 If you're spending most of the time in the read functions then yeah, you should see a pretty reasonable speedup from the async approach. 如果你大部分时间都花在阅读功能上,那么你应该从异步方法中看到一个相当合理的加速。 If not, async is just going to slow you down. 如果没有,异步只会让你失望。

1 Honestly it's not really that bad, unless you have something super speed critical. 1老实说,这并不是那么糟糕,除非你有超速的关键。 And then you should be using the cffi or something to take the speed critical sections and dump them into C. You did figure out which sections are the hold-up, right? 然后你应该使用cffi或其他东西来获取速度关键部分并将它们转储到C.你确实找出了哪些部分是阻挡,对吧?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM