back to portfolio
Systems Project · Go · 2026

Network Load Balancer

shipped
Period
May – Jun 2026
Role
Solo
Type
Systems / Networking
Status
Complete

Overview

An HTTP load balancer built from scratch in Go with a real-time web dashboard. It supports three load balancing algorithms (round robin, weighted round robin, least connections), active health checking with automatic failover and recovery, and dynamic server management — spin up new backend server processes from the UI, and they auto-register into the pool. The dashboard visualizes the full request flow with animated packets and lets you drag servers, simulate traffic, switch algorithms, and kill/revive backends to observe failover behavior.

Load Balancing Algorithms

  • Round robin with atomic counter — lock-free rotation across healthy backends
  • Weighted round robin — backends added to a weighted list proportionally, with health-aware skipping on unhealthy servers
  • Least connections — tracks active connection count per backend, routes to the server with fewest in-flight requests

Health Checking & Failover

  • Background goroutine polls /health on every backend at a configurable interval with per-check timeouts
  • Consecutive failure threshold — 3 failed checks mark a server unhealthy and remove it from rotation
  • Automatic recovery — a single successful health check resets the failure counter and re-adds the server

Dynamic Server Management

  • Spin up backend servers on-demand — process manager spawns child processes with auto-assigned ports
  • Readiness gate — waits up to 5 seconds for the new server to pass a health check before adding it to the pool
  • Graceful shutdown — SIGINT/SIGTERM handler kills all managed child processes cleanly

REST API & Dashboard

  • RESTful management API — add/remove servers, change algorithms, update weights, and spin up servers at runtime
  • Real-time topology visualization with animated SVG packets showing request flow from client → load balancer → backend
  • Traffic simulation with configurable request count, delay, and processing time to observe algorithm behavior

On the roadmap

  • IP hash algorithm — route requests from the same client IP to the same backend for session affinity
  • Circuit breaker pattern — open the circuit after repeated failures to prevent cascading load on a degraded backend
  • WebSocket proxying — upgrade support so the load balancer can handle persistent connections, not just HTTP request-response

The thought process

The interesting constraint was concurrency. Every component — the backend pool, the algorithm state, the health checker, the process manager — runs concurrently and mutates shared state. The pool needed a sync.RWMutex so health checks and request routing could read simultaneously without blocking each other. The least connections algorithm required its own lock for the connection counter, separate from the pool lock, to avoid contention. Getting the locking granularity right was the real design work.


Tech stack & why

Go
Standard library net/http for the proxy and API server, httutil.ReverseProxy for request forwarding, goroutines for concurrent health checks and process management.
Go sync primitives
sync.RWMutex for thread-safe backend pool access, sync/atomic for lock-free round-robin counters, separate mutexes per component to minimize contention.
os/exec
Process manager spawns backend servers as child processes with automatic port assignment, readiness polling, and graceful shutdown on SIGINT/SIGTERM.
Vanilla JS + HTML/CSS
Dashboard with SVG-based topology visualization, animated request packets, draggable server nodes, and real-time state polling — no frameworks, no build step.

Major updates

Jun 2026
Docker & polish
Multi-stage Dockerfile, UI refinements, DNS fix for dynamic servers (127.0.0.1 instead of localhost to avoid resolution timeouts).
May 2026
Dynamic servers & dashboard
Process manager for spawning backend servers on-demand. Web dashboard with SVG topology, animated packets, drag-and-drop, and traffic simulation.
May 2026
Core load balancer
Load balancer with round robin, weighted round robin, and least connections. Backend pool with health checking, automatic failover, and recovery. RESTful management API.

What I actually learned


What I'd do differently

Connection pooling
Each request creates a new reverse proxy instance. Reusing proxies with persistent connections would reduce overhead on high-throughput workloads.
Weighted round robin rebuild
The weighted list is built at initialization time. Updating weights at runtime requires reconstructing the load balancer instance — a hot-rebuild would be smoother.

Key takeaways

01
Concurrency primitives are the real API design. The interface between components in a concurrent system is not function signatures — it is which locks protect which data.
02
Visualizing the system changed how I built it. Watching animated packets hit a dead server made failover bugs obvious in a way that log files never did.