Why is python-requests to localhost slow?
Asked Answered
L

4

5

I have a simple flask server defined like so:

import sys

import flask
from flask import request

app = flask.Flask(__name__)
port = 4057

@app.route('/search', methods=['POST'])
def search():
  request.json['query']
  results = ['fake', 'data']
  return flask.jsonify(results)

if __name__ == '__main__':
 app.config['TEMPLATES_AUTO_RELOAD'] = True
 app.run(host='0.0.0.0', port=port, debug=(port != 80))

I have a simple client defined like this:

import json

import requests

headers = {'content-type': 'application/json'}
resp = requests.post('http://localhost:4057/search', json.dumps({'query': 'foo'}), headers=headers)
print resp.content

The client works, but it takes like 3 seconds to complete the request.

curl completes in like half a second:

curl 'http://localhost:4057/search' -H 'Content-Type: application/json' -d '{"query": "foo"}'

Laager answered 12/12, 2017 at 5:55 Comment(10)
Does the time taken change when you change the flask server to be threaded=True in the app.run()?Toll
How are you measuring the time it takes, and what Python implementation are you using? I bet most of the time is being spent on import requests and Python's start up.Unifoliolate
@FranciscoCouzo Good guess, but no, if I just import requests and print "foo" it's < .5 seconds.Laager
It seems like you are timing the entire script instead fo the core logic. Keep in mind that the requests import takes considerable amount of time.Telegram
@SudheeshSinganamalla If flask was the problem then the curl would also be slow, no?Laager
what if you change it to 127.0.0.1? maybe some odd resolution.Catharinecatharsis
@Catharinecatharsis You win! That fixed it. Thanks.Laager
feel free to post this as an answer if you want pointsLaager
I just tested you example on my machine(localhost), for me it took on average ~ 110 MSFussell
BTW: '0.0.0.0' connects server to all network cards in computer, not only 127.0.0.1 but also Wi-Fi and wired connection.Heisenberg
C
19

Try 127.0.0.1 There maybe some odd name resolution rules fumbling requests.

Ah ok, this is my work machine. I took a look at /etc/hosts and saw ~200 routes defined that I didn't realize were there

As mention in the comments, this does not explain the odd behavior replicated using curl.

Catharinecatharsis answered 12/12, 2017 at 6:7 Comment(3)
Ah ok, this is my work machine. I took a look at /etc/hosts and saw ~200 routes defined that I didn't realize were there.Laager
I don't think this answer solve the question essentially. This question is interesting so I have done a wide search. There is a possible reason. As modern OS usually support both ipv6 and ipv4, localhost is first resolved to ipv6 address but fail in default timeout window (usually one second), then fallback to ipv4 address. What strange is that curl works well, maybe it perfers ipv4? Or this reason is also not the key point.Cumuliform
@Cumuliform thanks. I used this solution for configuring IPv4 to take precedence over IPv6 when accessing localhost.Rey
A
5

I've recently encountered a similar issue with slow connections from requests to 'localhost', and also found that using '127.0.0.1' was much faster.

After some investigation, I found that in my case, the slow connections on Windows were related to urllib3 (the transport library used by requests) attempting an IPv6 connection first, which is refused since the server is listening on IPv4 only. For Windows only, however, there is an unavoidable 1 sec timeout on socket.connect for refused TCP connections, while the OS 'helpfully' retries the connection up to 3 times. (See: Why do failed attempts of Socket.connect take 1 sec on Windows?).

On Linux, socket.connect fails immediately if the connection is refused, and it can attempt the IPv4 connection without any delay. In addition, some software, like curl, support limiting name resolution to only IPv4 or IPv6. Unfortunately, requests and urllib3 do not support this, and do not seem to plan to support this anytime soon (See: https://github.com/psf/requests/issues/1691)

Adapter answered 12/2, 2023 at 5:42 Comment(0)
I
1

Instead of calling http://localhost:port/endpoint call http://127.0.0.1:port/endpoint. This removed the initial 2 sec delay for me.

See: https://mcmap.net/q/211167/-slow-requests-on-local-flask-server

Imperial answered 8/10 at 13:49 Comment(0)
A
0

For those trying to figure this out as no one gave a clear solution here:

I encountered this issue a while ago, and noticed that the Flask test server is not concurrent. The Python Requests takes FOREVER to retrieve the data from your flask app unless you make it concurrent. Enabling "Threaded" in your app.run option will do the trick!

app.run(port=5000, debug=True, threaded=True)
Adel answered 27/3, 2018 at 13:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.