A production-ready reverse proxy and load balancer written in Go that intelligently distributes HTTP traffic across multiple backend servers while continuously monitoring their health. Supports multiple load-balancing strategies, provides a RESTful Admin API for runtime management, and ensures graceful shutdown.
- Reverse Proxy - Built on
net/http/httputil.ReverseProxywith custom transport configuration - Multiple Load Balancing Strategies
- Round-Robin (
rr): Evenly distributes requests across healthy backends - Least-Connections (
lc): Routes to the backend with fewest active connections
- Round-Robin (
- Active Health Monitoring
- Configurable periodic health checks (default: 15s)
- Automatic UP/DOWN status tracking
- Only routes traffic to healthy backends
- Dynamic Backend Management
- Add/remove backends at runtime via REST API
- No restart required for configuration changes
- Duplicate detection
- Production-Ready Resilience
- Connection timeouts (dial, response header, read/write)
- Automatic backend failover
- Graceful shutdown with request draining
- Context propagation to cancel backend requests when the client disconnects
- Admin API
GET /status- View all backends (health + connection count)POST /backends- Add new backendDELETE /backends- Remove backend
- Go 1.25.5 or higher
- No external dependencies (uses only Go standard library)
Concurrent-Load-Balancing-Reverse-Proxy-with-Health-Monitoring/
├── main.go # Application entry point (orchestrates all components)
├── server.go # Demo backend server (for local testing)
├── config.json # Configuration file
├── admin/
│ ├── handlers.go # HTTP handlers for admin API
│ └── models.go # Request/response models
├── backend/
│ └── backend.go # Backend server model (health + connection tracking)
├── config/
│ └── config.go # Configuration loader
├── healthcheck/
│ └── healthcheck.go # Periodic health checker
├── loadbalancer/
│ └── loadbalancer.go # LoadBalancer interface
├── proxy/
│ └── proxy.go # Reverse proxy handler
└── servers/
└── servers.go # Server pool (backend management + selection algorithms)
Open three separate terminals and start demo backend servers:
Terminal 1:
go run server.go 8082Terminal 2:
go run server.go 8083Terminal 3:
go run server.go 8084Expected Output (per backend):
2026/01/24 21:48:40 [Backend-8082] Starting server on http://localhost:8082
Each server will log requests it receives:
2026/01/24 21:48:53 [Backend-8082] Received request: GET / from [::1]:57881
2026/01/24 21:48:53 [Backend-8082] Response sent successfully
Edit config.json (or use the default):
{
"port": 8080,
"admin_port": 8081,
"strategy": "lc",
"health_check_frequency": "15s",
"backends": [
"http://localhost:8082",
"http://localhost:8083",
"http://localhost:8084"
]
}Configuration Options:
port- Load balancer listening port (default: 8080)admin_port- Admin API port (default: 8081)strategy- Load balancing algorithm:rrorlchealth_check_frequency- Health check interval (e.g.,5s,15s,1m)backends- List of backend server URLs
go run main.go --config=config.jsonExpected Output:
2026/01/24 21:22:29 [INIT] Starting Load Balancer on port 8080 with strategy: lc
2026/01/24 21:22:29 [INIT] Added backend: http://localhost:8082
2026/01/24 21:22:29 [INIT] Added backend: http://localhost:8083
2026/01/24 21:22:29 [INIT] Added backend: http://localhost:8084
2026/01/24 21:22:29 [INIT] Health Checker service starting...
2026/01/24 21:22:29 [HEALTH] Starting initial health check...
2026/01/24 21:22:29 [HEALTH] Checking 3 backends...
2026/01/24 21:22:29 [INIT] Load Balancer listening on :8080
2026/01/24 21:22:29 [INIT] Admin listening on :8081
2026/01/24 21:22:29 [INIT] All services started successfully!
2026/01/24 21:22:29 [STATUS] Backend http://localhost:8082 marked as UP
2026/01/24 21:22:29 [STATUS] Backend http://localhost:8083 marked as UP
2026/01/24 21:22:29 [STATUS] Backend http://localhost:8084 marked as UP
2026/01/24 21:22:44 [HEALTH] Running periodic health check (interval: 15s)
2026/01/24 21:22:44 [HEALTH] Checking 3 backends...
curl http://localhost:8080Expected Response:
Response from Backend-8082
Time: 2026-01-24T21:52:42Z
Path: /
Base URL: http://localhost:8081
Linux/Mac/Git Bash:
curl http://localhost:8081/statusWindows PowerShell:
Invoke-RestMethod http://localhost:8081/status | ConvertTo-Json -Depth 3Expected Response:
{
"total_backends": 3,
"active_backends": 3,
"backends": [
{
"url": "http://localhost:8082",
"alive": true,
"current_connections": 0
},
{
"url": "http://localhost:8083",
"alive": true,
"current_connections": 0
},
{
"url": "http://localhost:8084",
"alive": true,
"current_connections": 0
}
]
}Linux/Mac/Git Bash:
curl -X POST http://localhost:8081/backends \
-H "Content-Type: application/json" \
-d '{"url":"http://localhost:8085"}'Windows PowerShell:
$body = @{url = "http://localhost:8085"} | ConvertTo-Json
Invoke-RestMethod -Uri http://localhost:8081/backends -Method Post -Body $body -ContentType "application/json"Windows Command Prompt:
curl.exe -X POST http://localhost:8081/backends -H "Content-Type: application/json" -d "{\"url\":\"http://localhost:8085\"}"Expected Response:
HTTP/1.1 201 Created
Content-Type: application/json
Date: Sat, 24 Jan 2026 21:21:39 GMT
Content-Length: 71
{"message":"Backend added successfully","url":"http://localhost:8085"}
Server Logs:
2026/01/24 21:21:39 [INIT] Added backend: http://localhost:8085
2026/01/24 21:21:39 [ADMIN] Added new backend via Admin API: http://localhost:8085
Notes:
- Backend is initially marked as
alive=true - Health checker will verify status on next interval
- Returns
409 Conflictif backend already exists
Linux/Mac/Git Bash:
curl -X DELETE http://localhost:8081/backends \
-H "Content-Type: application/json" \
-d '{"url":"http://localhost:8082"}'Windows PowerShell:
$body = @{url = "http://localhost:8082"} | ConvertTo-Json
Invoke-RestMethod -Uri http://localhost:8081/backends -Method Delete -Body $body -ContentType "application/json"Expected Response:
HTTP/1.1 200 OK
Content-Type: application/json
Date: Sat, 24 Jan 2026 21:25:59 GMT
Content-Length: 73
{"message":"Backend removed successfully","url":"http://localhost:8082"}
Server Logs:
2026/01/24 21:25:59 [ADMIN] Removed backend: http://localhost:8082
Configure via strategy field in config.json:
- Algorithm: Cycles through backends sequentially
- Use case: Equal capacity backends, simple distribution
- Aliases:
rr,RR,round-robin,Round-Robin,Round-robin
Example:
Request 1 → Backend-8082
Request 2 → Backend-8083
Request 3 → Backend-8084
Request 4 → Backend-8082 (wraps around)
- Algorithm: Routes to backend with fewest active connections
- Use case: Varying request processing times, optimal load distribution
- Aliases:
lc,LC,least-connections,Least-Connections,Least-connections - Tie-breaking: Random selection among backends with equal connections
- Frequency: Configured via
health_check_frequency(default: 15s) - Method: HTTP GET to backend URL
- Success criteria: HTTP status code 200-399
- Failure handling: Backend marked as DOWN, removed from rotation
- Recovery: Automatically marked UP when health check succeeds
Health Check Logs:
2026/01/24 21:23:44 [HEALTH] Running periodic health check (interval: 15s)
2026/01/24 21:23:44 [HEALTH] Checking 3 backends...
Status Change Logs:
2026/01/24 21:33:45 [STATUS] Backend http://localhost:8089 marked as DOWN
2026/01/24 21:33:45 [STATUS] Backend http://localhost:8089 marked as UP
ReadTimeout: 10s // Time to read client request
WriteTimeout: 10s // Time to write response to client
IdleTimeout: 120s // Keep-alive timeout
ReadHeaderTimeout: 5s // Time to read request headersDialTimeout: 5s // Time to establish backend connection
ResponseHeaderTimeout: 10s // Time to receive response headersPress Ctrl+C to initiate graceful shutdown:
Expected Output:
^C
2026/01/24 18:23:13 [SHUTDOWN] Shutdown signal received, gracefully shutting down...
2026/01/24 18:23:13 [HEALTH] Health checker stopped via context cancellation
2026/01/24 18:23:13 [HEALTH] Health checker shutdown complete
2026/01/24 18:23:13 [SHUTDOWN] Proxy server shutting down...
2026/01/24 18:23:13 [SHUTDOWN] Admin server shutting down...
2026/01/24 18:23:13 [SHUTDOWN] Servers gracefully stopped
Shutdown Process:
- Stop accepting new requests
- Stop health checker (prevents status changes)
- Drain existing connections (up to 10-second timeout)
- Shutdown proxy server
- Shutdown admin server
# Send multiple requests
curl http://localhost:8080
curl http://localhost:8080
curl http://localhost:8080Backend Logs Show Distribution:
[Backend-8082] Received request: GET / from [::1]:57881
[Backend-8082] Response sent successfully
[Backend-8083] Received request: GET / from [::1]:58003
[Backend-8083] Response sent successfully
[Backend-8084] Received request: GET / from [::1]:58126
[Backend-8084] Response sent successfully
Expected Health Check Logs:
2026/01/24 18:31:12 [HEALTH] Health check failed for http://localhost:8083: Get "http://localhost:8083": dial tcp [::1]:8083: connectex: No connection could be made because the target machine actively refused it.
2026/01/24 18:31:12 [STATUS] Backend http://localhost:8083 marked as DOWN
# Start new backend
go run server.go 8085
# Add to load balancer
curl.exe -X POST http://localhost:8081/backends -H "Content-Type: application/json" -d "{\"url\":\"http://localhost:8085\"}"
#Verify
curl http://localhost:8081/status| Issue | Solution |
|---|---|
| "No healthy backends available" | Ensure backend servers are running: go run server.go 8082 |
| "Invalid request body" on POST | Use PowerShell syntax: $body = @{url="..."} | ConvertTo-Json |
| Backend always DOWN | Check backend responds to GET with 200-399 status |
| Connection refused | Verify ports 8080-8084 are not in use by other programs |