|
| 1 | +# Limited Example |
| 2 | + |
| 3 | +This example shows how to limit the number of connections to a server. It takes advantage of `IO::Endpoint`'s wrapper to inject the necessary logic. More specifically, we do the following: |
| 4 | + |
| 5 | +1. Instead of `accept`ing a connection in a loop directly, we call `server.wait_readable` to wait for a connection to be available. |
| 6 | +2. We then try to acquire a semaphore token. If we can't, we wait for one to be available. |
| 7 | +3. Once we have a token, we accept the connection and process it. |
| 8 | +4. Once the connection is closed, we release the token. |
| 9 | + |
| 10 | +This way, we can limit the number of connections to a server. |
| 11 | + |
| 12 | +## Usage |
| 13 | + |
| 14 | +Start the server: |
| 15 | + |
| 16 | +```console |
| 17 | +> bundle exec falcon host falcon.rb |
| 18 | + 0.0s info: Falcon::Command::Host [oid=0x4c8] [ec=0x4d0] [pid=99469] [2025-02-11 17:53:59 +1300] |
| 19 | + | Falcon Host v0.49.0 taking flight! |
| 20 | + | - Configuration: falcon.rb |
| 21 | + | - To terminate: Ctrl-C or kill 99469 |
| 22 | + | - To reload: kill -HUP 99469 |
| 23 | + 0.03s info: Async::Container::Notify::Console [oid=0x4d8] [ec=0x4d0] [pid=99469] [2025-02-11 17:53:59 +1300] |
| 24 | + | {status: "Initializing..."} |
| 25 | + 0.04s info: Falcon::Service::Server [oid=0x4e8] [ec=0x4d0] [pid=99469] [2025-02-11 17:53:59 +1300] |
| 26 | + | Starting limited.localhost on #<Async::HTTP::Endpoint http://localhost:8080/ {reuse_address: true, timeout: nil, wrapper: #<Limited::Wrapper:0x000000011f5dfc30>}> |
| 27 | + 0.04s info: Async::Service::Controller [oid=0x4f0] [ec=0x4d0] [pid=99469] [2025-02-11 17:53:59 +1300] |
| 28 | + | Controller starting... |
| 29 | + 0.04s info: Async::Container::Notify::Console [oid=0x4d8] [ec=0x4d0] [pid=99469] [2025-02-11 17:53:59 +1300] |
| 30 | + | {ready: true} |
| 31 | + 0.04s info: Async::Service::Controller [oid=0x4f0] [ec=0x4d0] [pid=99469] [2025-02-11 17:53:59 +1300] |
| 32 | + | Controller started... |
| 33 | +``` |
| 34 | + |
| 35 | +Then, you can connect to it using `curl -v http://localhost:8080`. The default example includes two workers with a limit of one connection per worker. |
| 36 | + |
| 37 | +```console |
| 38 | +> curl -v http://localhost:8080 |
| 39 | +* Host localhost:8080 was resolved. |
| 40 | +* IPv6: ::1 |
| 41 | +* IPv4: 127.0.0.1 |
| 42 | +* Trying [::1]:8080... |
| 43 | +* Connected to localhost (::1) port 8080 |
| 44 | +* using HTTP/1.x |
| 45 | +> GET / HTTP/1.1 |
| 46 | +> Host: localhost:8080 |
| 47 | +> User-Agent: curl/8.10.1 |
| 48 | +> Accept: */* |
| 49 | +> |
| 50 | +* Request completely sent off |
| 51 | +< HTTP/1.1 200 OK |
| 52 | +< vary: accept-encoding |
| 53 | +< content-length: 11 |
| 54 | +< |
| 55 | +* Connection #0 to host localhost left intact |
| 56 | +Hello World |
| 57 | +``` |
| 58 | + |
| 59 | +There is also a fast path which simulates requests that may not count towards the connection limit: |
| 60 | + |
| 61 | +```console |
| 62 | +> curl -v http://localhost:8080/fast http://localhost:8080/fast |
| 63 | +* Host localhost:8080 was resolved. |
| 64 | +* IPv6: ::1 |
| 65 | +* IPv4: 127.0.0.1 |
| 66 | +* Trying [::1]:8080... |
| 67 | +* Connected to localhost (::1) port 8080 |
| 68 | +* using HTTP/1.x |
| 69 | +> GET /fast HTTP/1.1 |
| 70 | +> Host: localhost:8080 |
| 71 | +> User-Agent: curl/8.10.1 |
| 72 | +> Accept: */* |
| 73 | +> |
| 74 | +* Request completely sent off |
| 75 | +< HTTP/1.1 200 OK |
| 76 | +< vary: accept-encoding |
| 77 | +< connection: close |
| 78 | +< content-length: 11 |
| 79 | +< |
| 80 | +* shutting down connection #0 |
| 81 | +Hello World* Hostname localhost was found in DNS cache |
| 82 | +* Trying [::1]:8080... |
| 83 | +* Connected to localhost (::1) port 8080 |
| 84 | +* using HTTP/1.x |
| 85 | +> GET /fast HTTP/1.1 |
| 86 | +> Host: localhost:8080 |
| 87 | +> User-Agent: curl/8.10.1 |
| 88 | +> Accept: */* |
| 89 | +> |
| 90 | +* Request completely sent off |
| 91 | +< HTTP/1.1 200 OK |
| 92 | +< vary: accept-encoding |
| 93 | +< connection: close |
| 94 | +< content-length: 11 |
| 95 | +< |
| 96 | +* shutting down connection #1 |
| 97 | +Hello World |
| 98 | +``` |
| 99 | + |
| 100 | +Note that we use `connection: close` because we are using the fast path. This is to ensure that the connection is closed immediately after the response is sent such that a subsequent "slow" request won't double up. |
0 commit comments