Search code examples
python-requestsportfastapilocal

FastAPI local server: every request uses a different port -> port in use error


I have a FastAPI server running locally. I launched it with uvicorn like so:

uvicorn app:app --port 5000

I am querying it frpm Python, like so:

response = requests.post(rf"http://127.0.0.1:5000/endpoint1/", json={"arg1": "somevalue})

When I am looking at my FastAPI logs, I see that every request comes from a different port and increments at every new one:

...
INFO:     127.0.0.1:59605 - "POST /endpoint1/ HTTP/1.1" 200 OK
INFO:     127.0.0.1:59606 - "POST /endpoint2/ HTTP/1.1" 200 OK
INFO:     127.0.0.1:59607 - "POST /endpoint1/ HTTP/1.1" 200 OK
INFO:     127.0.0.1:59608 - "POST /endpoint2/ HTTP/1.1" 200 OK
INFO:     127.0.0.1:59609 - "POST /endpoint1/ HTTP/1.1" 200 OK
INFO:     127.0.0.1:59610 - "POST /endpoint2/ HTTP/1.1" 200 OK
INFO:     127.0.0.1:59611 - "POST /endpoint1/ HTTP/1.1" 200 OK
INFO:     127.0.0.1:59612 - "POST /endpoint2/ HTTP/1.1" 200 OK
INFO:     127.0.0.1:59613 - "POST /endpoint1/ HTTP/1.1" 200 OK
INFO:     127.0.0.1:59614 - "POST /endpoint2/ HTTP/1.1" 200 OK

After a while this leads to issues because all ports are exhausted (I think; at port 65535). Specifically, this error:

requests.exceptions.ConnectionError: HTTPConnectionPool(host='127.0.0.1', port=5000): Max retries exceeded with url: /endpoint1/ (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x000001BC822DDC40>: Failed to establish a new connection: [WinError 10048] Only one usage of each socket address (protocol/network address/port) is normally permitted'))

I am doing the requests sequentially (not in parallel!), so is there a way to ensure that the request is always send through the same port? Or another solution to avoid this issue?


Solution

  • So the explanation is that each request is being served on a new connection and is assigned a new port number. The requests came in so fast that it seems that the ports were not closed/reopened fast enough and that Python ran out of ports that it thought were available.

    The solution is to do all the requests through a single requests.Session. Note the session.post below instead of requests.post.

    session = requests.Session()
    response = session.post(rf"http://127.0.0.1:5000/endpoint1/", json={"arg1": "sometext"})
    # ... other requests
    session.close()