IntraLB is a lightweight, intra-process load balancer built for Go HTTP servers. It offers intelligent request routing, worker-based concurrency, graceful shutdowns, and database safety with a developer-friendly interface.
Install with:
go get github.com/mmycin/intralbpackage main
import (
"net/http"
"github.com/go-chi/chi/v5"
"github.com/mmycin/intralb/config"
"github.com/mmycin/intralb/balancer"
)
func main() {
router := chi.NewRouter()
router.Get("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello World"))
})
cfg := &config.Options{
MaxConcurrentPerRouter: 100,
QueueSize: 100,
EnableLogging: true,
GracefulTimeoutSeconds: 5,
}
lb := balancer.New(router, cfg)
http.ListenAndServe(":8080", lb.BalanceLoad())
}type Options struct {
MaxConcurrentPerRouter int // Max concurrent requests per router (default: 100)
QueueSize int // Buffer size per worker queue (default: 100)
EnableLogging bool // Enable verbose logging (default: false)
GracefulTimeoutSeconds int // Graceful shutdown timeout in seconds (default: 5)
DBInitFunc func(id string) any // Optional DB session initializer for each worker group
}-
🧵 Worker Pool Architecture: Concurrent request handling using Go routines with load tracking
-
📈 Auto Scaling: Dynamically adjusts workers based on demand
-
🛑 Graceful Shutdown: Ensures in-flight requests complete before shutdown
-
⏱️ Request Timeout Handling: Configurable grace period per request
-
🗄️ DB Session Management: Per-worker DB connection safety
-
🔀 Load-Aware Routing: Routes to least-busy worker using atomic counters
-
Flow:
Client → IntraLB → Queue → Worker → Handler -
Scaling:
-
Workers spawn dynamically on load
-
Capped concurrency avoids memory overuse
-
-
Load Balancing:
-
Atomic counters track real-time worker load
-
Requests go to least busy available worker
-
graph TD
A[Incoming Request] --> B[IntraLB Balancer]
B --> C[Worker 1]
B --> D[Worker 2]
B --> E[Worker N]
C --> F[Process]
D --> G[Process]
E --> H[Process]
cfg := &config.Options{
DBInitFunc: func(workerID string) any {
return initWorkerDB(workerID) // Return db instance (e.g., *sql.DB, *mongo.Client, etc.)
},
}func loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Println("Incoming request")
next.ServeHTTP(w, r)
log.Println("Completed")
})
}
router := chi.NewRouter()
router.Use(loggingMiddleware)Apply your middleware before passing the router to the balancer.
server := &http.Server{
Addr: ":8080",
Handler: lb.BalanceLoad(),
}
go func() {
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatalf("ListenAndServe: %v", err)
}
}()
balancer.GracefulShutdown(server, 10*time.Second)| Workers | Requests/sec | Latency (p95) |
|---|---|---|
| 10 | 8,500 | 12ms |
| 50 | 32,000 | 8ms |
| 100 | 58,000 | 6ms |
| 200 | 72,000 | 9ms |
Performance varies by CPU, memory, and handler complexity.
-
✅ Apply critical middleware (like auth, rate limiting) before the balancer
-
✅ Keep
DBInitFunclightweight; use pooling/connection reuse -
✅ Gracefully recover panics in handlers
-
✅ Use metrics to monitor worker starvation or queue overflows
func main() {
r := chi.NewRouter()
r.Get("/api", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("API Response"))
})
cfg := &config.Options{
MaxConcurrentPerRouter: 50,
GracefulTimeoutSeconds: 10,
}
lb := balancer.New(r, cfg)
http.ListenAndServe(":8080", lb.BalanceLoad())
}func main() {
r := chi.NewRouter()
r.Mount("/users", userRouter())
r.Mount("/products", productRouter())
cfg := &config.Options{
MaxConcurrentPerRouter: 200,
QueueSize: 500,
DBInitFunc: func(id string) any {
return createDBClient(id)
},
}
lb := balancer.New(r, cfg)
server := &http.Server{
Addr: ":8080",
Handler: lb.BalanceLoad(),
}
balancer.GracefulShutdown(server, 15*time.Second)
server.ListenAndServe()
}| Problem | Solution |
|---|---|
| Request Timeout | Increase GracefulTimeoutSeconds; avoid blocking handlers |
| High Memory Usage | Reduce QueueSize or lower MaxConcurrentPerRouter |
| Worker Starvation | Tune worker count; review handler logic and DB latency |
| Middleware Not Working | Ensure it's registered before the balancer |
We welcome contributions!
-
Fork the repo
-
Create a new feature branch
-
Submit a Pull Request with clear descriptions and test coverage
Please ensure you follow Go idioms and update documentation/tests accordingly.
MIT License
See the LICENSE file for full details.
-
GitHub: github.com/mmycin/intralb
-
GoDoc: Coming soon
-
Issues: Report or view