Networking

Load Balancer

A device or software that distributes network traffic across multiple servers to ensure no single server becomes overwhelmed, improving application availability, reliability, and performance.

What is a Load Balancer?

Load Balancer is a system that distributes incoming network traffic across multiple backend servers to ensure optimal resource utilization, minimize response time, prevent server overload, and provide high availability. Load balancers are essential for scaling applications and maintaining reliability under heavy traffic.

Load Balancing Algorithms

Common Algorithms

interface LoadBalancingAlgorithms {
  roundRobin: {
    description: 'Distribute requests sequentially';
    pros: 'Simple, fair distribution';
    cons: 'Ignores server load';
    useCase: 'Servers with equal capacity';
  };
  leastConnections: {
    description: 'Route to server with fewest active connections';
    pros: 'Accounts for server load';
    cons: 'More complex tracking';
    useCase: 'Varying request duration';
  };
  leastResponseTime: {
    description: 'Route to fastest responding server';
    pros: 'Optimizes performance';
    cons: 'Requires monitoring';
    useCase: 'Performance-critical apps';
  };
  ipHash: {
    description: 'Hash client IP to determine server';
    pros: 'Session persistence';
    cons: 'Uneven distribution possible';
    useCase: 'Stateful applications';
  };
  weighted: {
    description: 'Distribute based on server capacity weights';
    pros: 'Handles different server specs';
    cons: 'Requires weight configuration';
    useCase: 'Heterogeneous servers';
  };
}

Implementation

Round Robin Load Balancer

class RoundRobinLoadBalancer {
  private servers: string[];
  private currentIndex: number = 0;

  constructor(servers: string[]) {
    this.servers = servers;
  }

  getNextServer(): string {
    const server = this.servers[this.currentIndex];
    this.currentIndex = (this.currentIndex + 1) % this.servers.length;
    return server;
  }

  async handleRequest(req: any, res: any) {
    const server = this.getNextServer();
    console.log(`Routing to: ${server}`);

    try {
      const response = await fetch(`${server}${req.url}`, {
        method: req.method,
        headers: req.headers,
        body: req.body
      });

      const data = await response.text();
      res.send(data);
    } catch (error) {
      console.error(`Error routing to ${server}:`, error);
      res.status(502).send('Bad Gateway');
    }
  }
}

// Usage
const lb = new RoundRobinLoadBalancer([
  'http://server1:3000',
  'http://server2:3000',
  'http://server3:3000'
]);

Least Connections Load Balancer

interface ServerMetrics {
  url: string;
  activeConnections: number;
  totalRequests: number;
}

class LeastConnectionsLoadBalancer {
  private servers: Map<string, ServerMetrics>;

  constructor(serverUrls: string[]) {
    this.servers = new Map();
    serverUrls.forEach(url => {
      this.servers.set(url, {
        url,
        activeConnections: 0,
        totalRequests: 0
      });
    });
  }

  getNextServer(): string {
    let selected: ServerMetrics | null = null;
    let minConnections = Infinity;

    this.servers.forEach(server => {
      if (server.activeConnections < minConnections) {
        minConnections = server.activeConnections;
        selected = server;
      }
    });

    return selected!.url;
  }

  async handleRequest(req: any, res: any) {
    const serverUrl = this.getNextServer();
    const server = this.servers.get(serverUrl)!;

    server.activeConnections++;
    server.totalRequests++;

    try {
      const response = await fetch(`${serverUrl}${req.url}`, {
        method: req.method,
        headers: req.headers
      });

      const data = await response.text();
      res.send(data);
    } finally {
      server.activeConnections--;
    }
  }

  getStats() {
    const stats: any[] = [];
    this.servers.forEach(server => {
      stats.push({
        url: server.url,
        activeConnections: server.activeConnections,
        totalRequests: server.totalRequests
      });
    });
    return stats;
  }
}

Weighted Load Balancer

interface WeightedServer {
  url: string;
  weight: number;
  currentWeight: number;
}

class WeightedLoadBalancer {
  private servers: WeightedServer[];

  constructor(serverConfigs: Array<{ url: string; weight: number }>) {
    this.servers = serverConfigs.map(config => ({
      ...config,
      currentWeight: 0
    }));
  }

  getNextServer(): string {
    let selected: WeightedServer | null = null;
    let totalWeight = 0;

    this.servers.forEach(server => {
      server.currentWeight += server.weight;
      totalWeight += server.weight;

      if (!selected || server.currentWeight > selected.currentWeight) {
        selected = server;
      }
    });

    if (selected) {
      selected.currentWeight -= totalWeight;
      return selected.url;
    }

    return this.servers[0].url;
  }
}

// Usage - Server 1 gets 3x more traffic than others
const lb = new WeightedLoadBalancer([
  { url: 'http://powerful-server:3000', weight: 3 },
  { url: 'http://normal-server1:3000', weight: 1 },
  { url: 'http://normal-server2:3000', weight: 1 }
]);

Health Checks

Active Health Monitoring

class HealthCheckLoadBalancer {
  private servers: Map<string, {
    url: string;
    isHealthy: boolean;
    lastCheck: number;
    failureCount: number;
  }>;

  private healthCheckInterval: number = 10000; // 10 seconds
  private maxFailures: number = 3;

  constructor(serverUrls: string[]) {
    this.servers = new Map();

    serverUrls.forEach(url => {
      this.servers.set(url, {
        url,
        isHealthy: true,
        lastCheck: 0,
        failureCount: 0
      });
    });

    this.startHealthChecks();
  }

  private startHealthChecks() {
    setInterval(() => {
      this.servers.forEach((server, url) => {
        this.checkHealth(url);
      });
    }, this.healthCheckInterval);
  }

  private async checkHealth(serverUrl: string) {
    const server = this.servers.get(serverUrl);
    if (!server) return;

    try {
      const response = await fetch(`${serverUrl}/health`, {
        signal: AbortSignal.timeout(5000)
      });

      if (response.ok) {
        server.isHealthy = true;
        server.failureCount = 0;
        console.log(`✓ ${serverUrl} is healthy`);
      } else {
        this.handleFailure(server);
      }
    } catch (error) {
      this.handleFailure(server);
    }

    server.lastCheck = Date.now();
  }

  private handleFailure(server: any) {
    server.failureCount++;

    if (server.failureCount >= this.maxFailures) {
      server.isHealthy = false;
      console.error(`✗ ${server.url} marked unhealthy`);
    }
  }

  getHealthyServers(): string[] {
    return Array.from(this.servers.values())
      .filter(s => s.isHealthy)
      .map(s => s.url);
  }
}

Session Persistence

Sticky Sessions

class StickySessionLoadBalancer {
  private servers: string[];
  private sessions: Map<string, string> = new Map();

  constructor(servers: string[]) {
    this.servers = servers;
  }

  getServerForSession(sessionId: string): string {
    // Check if session exists
    if (this.sessions.has(sessionId)) {
      return this.sessions.get(sessionId)!;
    }

    // Assign new session to server
    const server = this.selectServer();
    this.sessions.set(sessionId, server);
    return server;
  }

  private selectServer(): string {
    // Use consistent hashing or round-robin
    return this.servers[Math.floor(Math.random() * this.servers.length)];
  }

  async handleRequest(req: any, res: any) {
    const sessionId = req.cookies?.sessionId || this.generateSessionId();
    const server = this.getServerForSession(sessionId);

    // Set session cookie if new
    if (!req.cookies?.sessionId) {
      res.cookie('sessionId', sessionId, {
        httpOnly: true,
        maxAge: 86400000 // 24 hours
      });
    }

    // Forward to assigned server
    const response = await fetch(`${server}${req.url}`);
    const data = await response.text();
    res.send(data);
  }

  private generateSessionId(): string {
    return Math.random().toString(36).substring(7);
  }
}

Layer 4 vs Layer 7 Load Balancing

Comparison

interface LoadBalancerLayers {
  layer4: {
    name: 'Transport Layer (TCP/UDP)';
    inspection: 'IP addresses and ports only';
    speed: 'Very fast';
    features: ['TCP/UDP routing', 'Connection-based'];
    limitations: ['No HTTP inspection', 'No path-based routing'];
    useCase: 'High-performance, simple routing';
  };
  layer7: {
    name: 'Application Layer (HTTP)';
    inspection: 'Full HTTP request (headers, path, etc.)';
    speed: 'Slower (more processing)';
    features: ['Path-based routing', 'Header inspection', 'Cookie-based routing'];
    limitations: ['Higher latency', 'More CPU intensive'];
    useCase: 'Complex routing, microservices';
  };
}

Nginx Load Balancer Configuration

upstream backend {
    # Load balancing algorithm
    least_conn;  # or ip_hash, or round-robin (default)

    # Backend servers
    server backend1.example.com:8080 weight=3;
    server backend2.example.com:8080 weight=1;
    server backend3.example.com:8080 weight=1;

    # Health checks
    server backend4.example.com:8080 max_fails=3 fail_timeout=30s;

    # Backup server (only used if all others fail)
    server backup.example.com:8080 backup;

    # Connection pooling
    keepalive 32;
}

server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass http://backend;

        # Headers
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        # Timeouts
        proxy_connect_timeout 5s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;

        # Health check
        proxy_next_upstream error timeout http_502 http_503;
    }
}

AWS Application Load Balancer

// AWS ALB configuration example
interface ALBConfiguration {
  type: 'application';  // Layer 7
  scheme: 'internet-facing';
  targetGroups: [
    {
      name: 'web-servers';
      protocol: 'HTTP';
      port: 80;
      healthCheck: {
        path: '/health';
        interval: 30;
        timeout: 5;
        healthyThreshold: 2;
        unhealthyThreshold: 3;
      };
      targets: [
        { id: 'i-1234567890abcdef0', port: 80 },
        { id: 'i-1234567890abcdef1', port: 80 }
      ];
    }
  ];
  listeners: [
    {
      protocol: 'HTTPS';
      port: 443;
      certificates: ['arn:aws:acm:us-east-1:123456789012:certificate/abc123'];
      defaultActions: [
        {
          type: 'forward';
          targetGroup: 'web-servers';
        }
      ];
      rules: [
        {
          conditions: [{ field: 'path-pattern', values: ['/api/*'] }];
          actions: [{ type: 'forward', targetGroup: 'api-servers' }];
        }
      ];
    }
  ];
}

Best Practices

For Performance

  1. Use health checks to avoid routing to failed servers
  2. Implement connection pooling
  3. Choose appropriate algorithm for workload
  4. Monitor server metrics continuously
  5. Set proper timeout values

For Reliability

  1. Deploy across multiple availability zones
  2. Use backup servers
  3. Implement circuit breakers
  4. Log all routing decisions
  5. Set up automated failover

For Security

  1. Terminate SSL at load balancer
  2. Implement rate limiting
  3. Use Web Application Firewall (WAF)
  4. Filter malicious traffic
  5. Implement DDoS protection

Learn More

Create a free Account to fix CORS Errors in Production

Say goodbye to CORS errors and get back to building great web applications. It's free!

CORSPROXY Dashboard

Related Terms

More in Networking

Related guides

Back to Glossary