Backend Development 10 min read

Mastering Nginx Load Balancing: Round‑Robin & Weighted Algorithms with Go

This article explains Nginx’s basic round‑robin and weighted round‑robin load‑balancing algorithms, compares their characteristics, and provides complete Go implementations—including configuration snippets, core structs, and test code—to help developers integrate efficient request distribution into their backend services.

Open Source Linux
Open Source Linux
Open Source Linux
Mastering Nginx Load Balancing: Round‑Robin & Weighted Algorithms with Go

Nginx Load Balancing – Round Robin

Explanation of the basic round‑robin algorithm, its characteristics, and a simple Nginx configuration example.

upstream cluster {
    server 192.168.0.14;
    server 192.168.0.15;
}
location / {
    proxy_set_header X-Real-IP $remote_addr; // return real IP
    proxy_pass http://cluster;
}

In round‑robin each request is sent to the next server in the list, providing an even distribution when servers have similar capacity.

Go Implementation of Round Robin

type RoundRobinBalance struct {
    curIndex int
    rss []string
}
func (r *RoundRobinBalance) Add(params ...string) error {
    if len(params) == 0 {
        return errors.New("params len at least 1")
    }
    addr := params[0]
    r.rss = append(r.rss, addr)
    return nil
}
func (r *RoundRobinBalance) Next() string {
    if len(r.rss) == 0 {
        return ""
    }
    lens := len(r.rss)
    if r.curIndex >= lens {
        r.curIndex = 0
    }
    curAdd := r.rss[r.curIndex]
    r.curIndex = (r.curIndex + 1) % lens
    return curAdd
}

Test code demonstrates adding four backend addresses and printing the result of successive

Next()

calls.

func main() {
    rb := new(RoundRobinBalance)
    rb.Add("127.0.0.1:80")
    rb.Add("127.0.0.1:81")
    rb.Add("127.0.0.1:82")
    rb.Add("127.0.0.1:83")
    fmt.Println(rb.Next())
    fmt.Println(rb.Next())
    // ...
}

Nginx Load Balancing – Weighted Round Robin

Weighted round‑robin assigns a weight to each server, allowing servers with higher capacity to receive more requests.

http {
    upstream cluster {
        server 192.168.1.2 weight=5;
        server 192.168.1.3 weight=3;
        server 192.168.1.4 weight=1;
    }
    location / {
        proxy_set_header X-Real-IP $remote_addr;
        proxy_pass http://cluster;
    }
}

The algorithm maintains three variables for each node: Weight (configured weight), currentWeight (dynamic weight updated each request), and effectiveWeight (adjusted weight used for health checks).

Sum all effectiveWeight values to obtain totalWeight .

Increase each node’s currentWeight by its effectiveWeight and select the node with the highest currentWeight .

Subtract totalWeight from the selected node’s currentWeight .

A Go implementation mirrors this logic with

WeightRoundRobinBalance

and

WeightNode

structs.

type WeightNode struct {
    weight          int // configured weight
    currentWeight   int // dynamic weight
    effectiveWeight int // starts as weight, adjusted on failures
    addr            string
}

type WeightRoundRobinBalance struct {
    curIndex int
    rss []*WeightNode
}
func (r *WeightRoundRobinBalance) Add(params ...string) error {
    if len(params) != 2 {
        return errors.New("params len need 2")
    }
    addr := params[0]
    w, err := strconv.ParseInt(params[1], 10, 64)
    if err != nil {
        return err
    }
    node := &WeightNode{
        weight:          int(w),
        effectiveWeight: int(w),
        currentWeight:   int(w),
        addr:            addr,
    }
    r.rss = append(r.rss, node)
    return nil
}
func (r *WeightRoundRobinBalance) Next() string {
    if len(r.rss) == 0 {
        return ""
    }
    totalWeight := 0
    var maxNode *WeightNode
    for i, node := range r.rss {
        totalWeight += node.effectiveWeight
        node.currentWeight += node.effectiveWeight
        if maxNode == nil || node.currentWeight > maxNode.currentWeight {
            maxNode = node
            r.curIndex = i
        }
    }
    maxNode.currentWeight -= totalWeight
    return maxNode.addr
}

Running the test program shows the request sequence respecting the configured weights.

Backendgolangload balancingNginxRound Robinweighted round robin
Open Source Linux
Written by

Open Source Linux

Focused on sharing Linux/Unix content, covering fundamentals, system development, network programming, automation/operations, cloud computing, and related professional knowledge.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.