Skip to content


Subversion checkout URL

You can clone with
Download ZIP


HTTPClient & AsyncHTTPClient Lag/Overhead #110

vishnevskiy opened this Issue · 8 comments

2 participants


I am using HTTPClient and AsyncHTTPClient to talk to a local
HTTPServer for RPC calls (POST with body)
On the first request its really fast, but every request after its
always 30ms overhead. I tested all around the code.
Put this an output into HTTPConnection
def _on_request_body(self, data):
logging.warning("TEST %.2fms", 1000.0 * (time.time() -
// other code goes here
At this point there is 30ms overhead.
It is also not the server's fault it seems, but the client's.
Because if I restart the process that is using the client and make
another request, its fast for the first one, and then 30ms for the
rest. (Works anytime I restart the process with HTTPClient)
If I do not have a request body, then this is not an issue. Is CURL
having a delay before sending the body or something?
Any ideas?


I just tested this with my own app (on a mac using the kqueue ioloop, libcurl 7.19.7, and AsyncHTTPClient2) and I'm not seeing this delay. I'd suggest using wireshark or tcpdump first to verify whether the delay is visible on the wire (and therefore whether the problem is client or server side).


Here I provided a server and a client which will show the issue.

import tornado.httpserver
import tornado.ioloop
import tornado.web
import tornado.options

class MainHandler(tornado.web.RequestHandler):
    def post(self):

application = tornado.web.Application([
    (r"/", MainHandler),

if __name__ == '__main__':
    http_server = tornado.httpserver.HTTPServer(application)

from tornado import escape
from tornado.httpclient import HTTPRequest, AsyncHTTPClient
import time
import tornado.ioloop

i = 0

def callback(response):
    print response.body

def request():
    body = escape.json_encode([{'test': 1}])
    AsyncHTTPClient().fetch(HTTPRequest('http://localhost:8888/', method='POST', body=body), callback)

while i < 10:
    tornado.ioloop.IOLoop.instance().add_timeout(time.time() + i * 2, request)
    i += 1

if __name__ == '__main__':

I am Ubuntu 10.04

Our server has the same issue as our dev machine.

You will see an exact 40ms overhead after the first request.

This issue seems to exist in all the HTTPClients, and it only occurs after a certain amount of time it seems. If you don't put a delay between requests, they are all fast.


Something I found in my testing, if I close() the HTTPClient and use a new one every time, this issue does not exist, but this does not seem efficient


OK, I've been able to reproduce this with ubuntu (9.10), but I'm not sure what exactly is going on. FWIW, when I run the client and servers on different machines the problem occurs when the server is on the mac and the client is on ubuntu but not vice versa.

From tcpdump, clients on ubuntu are sending the request as two packets, one with the headers and one with the body. On the mac the entire request is sent as one packet. The delay is because the client is waiting for a tcp ack from the server before sending the second packet. It is the tcp ack from the server that is getting delayed by 30ms. I'm not sure why that would be so slow, or why it would be different after the first request on the connection.


Something to note, when we originally found this bug few months ago we switched to the standard Python httplib to send requests and there was no delay.


So this appears to be an interaction between TCP delayed ACKs and Nagle's algorithm. The server is waiting (40ms) to see if there is another packet coming before sending an ack, while the client is waiting for that ack to send the next packet. The problem goes away if I call curl.setopt(pycurl.TCP_NODELAY, 1) from a prepare_curl_callback, but I don't know if that has other undesirable consequences.


I put that in for our code base, I will tell you if anything goes horribly wrong.


simple_httpclient now toggles the TCP_NODELAY flag appropriately. I'm not sure if this issue has been fixed in modern versions of libcurl, but I don't believe it is appropriate for tornado to set the nodelay flag in that case.

@bdarnell bdarnell closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.