1

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?

Bram Vanroy
  • 27,032
  • 24
  • 137
  • 239

1 Answers1

1

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()
Bram Vanroy
  • 27,032
  • 24
  • 137
  • 239
  • 2
    You could also use the `httpx` lib (along with `asyncio.gather()`) to perform `async`hronous HTTP requests concurrently, as demonstrated [here](https://stackoverflow.com/a/71517830/17865804) (see the example given under "Important Note" section), as well as [here](https://stackoverflow.com/a/74239367/17865804). The `httpx.AsyncClient()` is what you can use instead of `requests.Session()`. – Chris Apr 18 '23 at 17:04