302
This is a load balancer that doesn't act as a load balancer.
It is a redirect balancer. Instead of forwarding traffic to a specific host/port, it returns a 302 response with a different location.
Typical usage is that your entrypoint is https://example.com/foo
and you want to redirect your traffic to a pool made of https://01.example.com
and https://02.example.com
, by keeping path and query string intact.
It is built with PHP 8 on top of ReactPHP. Because it answers very short responses and runs within a loop, it can easily handle thousands of requests/s without blinking an eye.
Build
Build is optional (you can simply run php bin/console serve
) but will produce an optimized, single-file executable PHAR.
PHP 8, Composer and Box are globally required on your computer.
To build the application, run:
./bin/build
It will land in bin/302
.
Usage
Live balancing (no persistence - for testing purposes or prototyping)
php bin/302 serve \
--host=0.0.0.0 \
--port=8080 \
--pick=random \ # or round-robin
example1.org \
example2.org \
example3.org
GET http://0.0.0.0:8080/foo?bar=baz
HTTP/1.1 302 Found
Location: http://example2.org/foo?bar=baz
Persisted storage (Redis)
# Expose the REDIS_DSN variable if necessary, default is:
export REDIS_DSN="redis://localhost:6379"
php bin/302 serve --host=0.0.0.0 --port=8080
App will run, but user agent will get 503 errors because the server pool is empty.
Add a server to the pool
php bin/302 server:add example1.org
This command can be run while 302 serve
is running, no need to restart the app!
Remove a server from the pool
php bin/302 server:remove example1.org
This command can be run while 302 serve
command is running, no need to restart the app!
List servers in the pool
php bin/302 server:list
Tests
./vendor/bin/pest
Deployment
Example Supervisor config
After building the app, you can easily create a supervisor recipe to load it on startup:
[program:302]
command=php /usr/local/bin/302 serve --host=127.0.0.1 --port=80%(process_num)02d
user=www-data
numprocs=4
startsecs=1
autostart=true
autorestart=true
process_name=%(program_name)s_%(process_num)02d
environment = APP_LOG_DIR=/var/log/302
With the above config, 302 will run on 127.0.0.1:8000
, 127.0.0.1:8001
, 127.0.0.1:8002
and 127.0.0.1:8003
.
Round-robin will be shared across the instances (since they share the same Redis instance).
CORS / SSL termination
302 has no built-in CORS nor SSL termination, but this can be handled by any web server with reverse-proxy capabilities (Apache, Nginx, Caddy, ...).
Caddyfile example
If you're using Caddy, here's an example Caddyfile
:
example.org {
@get {
method GET
}
@options {
method OPTIONS
}
header Access-Control-Allow-Origin *
header Access-Control-Allow-Redirect true
respond @options 200
reverse_proxy @get 127.0.0.1:8000 127.0.0.1:8001 127.0.0.1:8002 127.0.0.1:8003
}