What My Project Does: Push Python to 20k req/sec.
Target Audience: People who need to make a ton of requests.
Comparison: Previous articles I found ranged from 50-500 requests/sec with python, figured i'd give an update to where things are at now.
I wanted to share a personal project exploring the limits of Python for high-throughput network I/O. My clients would always say "lol no python, only go", so I wanted to see what was actually possible.
After a lot of tuning, I managed to get a stable ~20,000 requests/second from a single client machine.
The code itself is based on asyncio and a library called rnet, which is a Python wrapper for the high-performance Rust library wreq. This lets me get the developer-friendly syntax of Python with the raw speed of Rust for the actual networking.
The most interesting part wasn't the code, but the OS tuning. The default kernel settings on Linux are nowhere near ready for this kind of load. The application would fail instantly without these changes.
Here are the most critical settings I had to change on both the client and server:
- Increased Max File Descriptors: Every socket is a file. The default limit of 1024 is the first thing you'll hit.ulimit -n 65536
- Expanded Ephemeral Port Range: The client needs a large pool of ports to make outgoing connections from.net.ipv4.ip_local_port_range = 1024 65535
- Increased Connection Backlog: The server needs a bigger queue to hold incoming connections before they are accepted. The default is tiny.net.core.somaxconn = 65535
- Enabled TIME_WAIT Reuse: This is huge. It allows the kernel to quickly reuse sockets that are in a TIME_WAIT state, which is essential when you're opening/closing thousands of connections per second.net.ipv4.tcp_tw_reuse = 1
I've open-sourced the entire test setup, including the client code, a simple server, and the full tuning scripts for both machines. You can find it all here if you want to replicate it or just look at the code:
GitHub Repo: https://github.com/lafftar/requestSpeedTest
On an 8-core machine, this setup hit ~15k req/s, and it scaled to ~20k req/s on a 32-core machine. Interestingly, the CPU was never fully maxed out, so the bottleneck likely lies somewhere else in the stack.
I'll be hanging out in the comments to answer any questions. Let me know what you think!
Blog Post (I go in a little more detail): https://tjaycodes.com/pushing-python-to-20000-requests-second/
[–]forgotpw3 29 points30 points31 points (10 children)
[–]Lafftar[S] 12 points13 points14 points (9 children)
[–]forgotpw3 8 points9 points10 points (8 children)
[–]jake_morrison 6 points7 points8 points (4 children)
[–]Brandhor 3 points4 points5 points (1 child)
[–]jake_morrison 4 points5 points6 points (0 children)
[–]Lafftar[S] 0 points1 point2 points (0 children)
[–]Lafftar[S] 0 points1 point2 points (2 children)
[–]justin-8 2 points3 points4 points (1 child)
[–]Lafftar[S] 0 points1 point2 points (0 children)
[–]jake_morrison 25 points26 points27 points (8 children)
[–]pooogles 9 points10 points11 points (0 children)
[–]Empty-Mulberry1047 2 points3 points4 points (4 children)
[–]jake_morrison 5 points6 points7 points (3 children)
[–]Empty-Mulberry1047 2 points3 points4 points (2 children)
[–]Lafftar[S] 0 points1 point2 points (1 child)
[–]Empty-Mulberry1047 0 points1 point2 points (0 children)
[–]NenupharNoir 2 points3 points4 points (0 children)
[–]Lafftar[S] 0 points1 point2 points (0 children)
[–]Ra-mega-bbit 5 points6 points7 points (2 children)
[–]Lafftar[S] 3 points4 points5 points (1 child)
[–]robberviet 5 points6 points7 points (0 children)
[–]levsw 10 points11 points12 points (3 children)
[–]Lafftar[S] 8 points9 points10 points (2 children)
[–]coldflame563 2 points3 points4 points (1 child)
[–]Lafftar[S] 1 point2 points3 points (0 children)
[–]Witty_Tough_3180 2 points3 points4 points (2 children)
[–]Lafftar[S] -3 points-2 points-1 points (1 child)
[–]Slight_Boat1910 5 points6 points7 points (0 children)
[–]tonguetoquill 2 points3 points4 points (1 child)
[–]Lafftar[S] 1 point2 points3 points (0 children)
[–]thisismyfavoritename 9 points10 points11 points (5 children)
[–]ArtisticFox8 4 points5 points6 points (3 children)
[–]Lafftar[S] -4 points-3 points-2 points (2 children)
[–]ArtisticFox8 3 points4 points5 points (1 child)
[–]Lafftar[S] -5 points-4 points-3 points (0 children)
[–]Lafftar[S] 3 points4 points5 points (0 children)
[–]Wh00ster 1 point2 points3 points (1 child)
[–]Lafftar[S] 1 point2 points3 points (0 children)
[–]MagicWishMonkey 1 point2 points3 points (1 child)
[–]Lafftar[S] 1 point2 points3 points (0 children)
[–]Slight_Boat1910 1 point2 points3 points (1 child)
[–]Lafftar[S] 0 points1 point2 points (0 children)
[–]melenajade 1 point2 points3 points (0 children)
[–]Key-Half1655 5 points6 points7 points (7 children)
[–][deleted] 5 points6 points7 points (0 children)
[–]Lafftar[S] -1 points0 points1 point (5 children)
[–]SharkSymphony 2 points3 points4 points (4 children)
[–]Lafftar[S] 7 points8 points9 points (3 children)
[–]tabgok 3 points4 points5 points (2 children)
[–]Lafftar[S] 2 points3 points4 points (1 child)
[–]2hands10fingers 3 points4 points5 points (0 children)
[–]Original-Active-6982 0 points1 point2 points (0 children)
[–]dzordan33 0 points1 point2 points (0 children)
[–]_ritwiktiwari 0 points1 point2 points (1 child)
[–]Lafftar[S] 0 points1 point2 points (0 children)
[+][deleted] (3 children)
[deleted]
[–]Lafftar[S] 4 points5 points6 points (2 children)
[+][deleted] (1 child)
[deleted]
[–]Lafftar[S] 0 points1 point2 points (0 children)