# Load Balancing Strategies in the Kratos Selector Package: A Complete Guide

> Explore Kratos selector package load balancing strategies: Random, WRR, and Pick-Two-Choices. Distribute RPC calls efficiently across service instances with this comprehensive guide.

- Repository: [Kratos/kratos](https://github.com/go-kratos/kratos)
- Tags: deep-dive
- Published: 2026-03-02

---

**The Kratos selector package provides three production-ready load balancing strategies—Random, Weighted Round-Robin (WRR), and Pick-Two-Choices (P2C)—that implement the `selector.Balancer` interface to distribute RPC calls across service instances.**

The `go-kratos/kratos` microservices framework includes a pluggable selector component that handles service instance selection for every RPC call. Within the **load balancing strategies Kratos selector** architecture, the core logic resides in implementations of the `Balancer` interface, which can be configured through builder patterns to suit different deployment scenarios and performance requirements.

## Core Architecture of the Kratos Selector

The selector package decouples node discovery from load balancing through a clean interface abstraction defined in [`selector/selector.go`](https://github.com/go-kratos/kratos/blob/main/selector/selector.go) and [`selector/balancer.go`](https://github.com/go-kratos/kratos/blob/main/selector/balancer.go).

### The Balancer Interface

At the heart of every **load balancing strategy** lies the `Balancer` interface defined in [`selector/balancer.go`](https://github.com/go-kratos/kratos/blob/main/selector/balancer.go):

```go
type Balancer interface {
    Pick(ctx context.Context, nodes []WeightedNode) (WeightedNode, DoneFunc, error)
}

```

When a client invokes `Select`, the selector passes a slice of `WeightedNode` objects to the configured balancer's `Pick` method. The balancer chooses one node and returns it along with a `DoneFunc` callback that reports the RPC result (success, failure, or latency) back to the selector.

### WeightedNode Abstraction

The `WeightedNode` interface, also defined in [`selector/balancer.go`](https://github.com/go-kratos/kratos/blob/main/selector/balancer.go), extends the base `Node` with runtime weight calculation:

```go
type WeightedNode interface {
    Node
    Weight() float64
    Pick() DoneFunc
}

```

Concrete node builders provide different weight calculation strategies. The `selector/node/direct` package offers simple direct weights, while `selector/node/ewma` provides exponentially weighted moving average calculations that factor in recent latency and error rates. According to the source code in [`selector/node/ewma/node.go`](https://github.com/go-kratos/kratos/blob/main/selector/node/ewma/node.go), EWMA nodes automatically decay older observations, making them ideal for latency-sensitive **load balancing strategies** like P2C.

### Default Composite Selector

The [`selector/default_selector.go`](https://github.com/go-kratos/kratos/blob/main/selector/default_selector.go) file implements the wiring logic that combines a `WeightedNodeBuilder` with a `Balancer`. When `Select` is invoked, the default selector:

1. Optionally filters nodes based on criteria
2. Retrieves current weighted nodes from the builder
3. Delegates the choice to the configured balancer via `Pick`
4. Returns the raw `Node` and `DoneFunc` for the caller to execute post-RPC

## Available Load Balancing Strategies

Kratos ships three concrete balancers, each optimized for different cluster characteristics and performance requirements.

### Random Balancer

The **Random** strategy, implemented in [`selector/random/random.go`](https://github.com/go-kratos/kratos/blob/main/selector/random/random.go), provides the simplest distribution mechanism. It selects a node uniformly at random using `rand.IntN(len(nodes))` and returns the node's `Pick()` function. This approach works well for homogeneous clusters where all instances have equal capacity, or during testing phases when deterministic behavior is unnecessary.

The random balancer uses the direct node builder by default, as shown in [`selector/random/random.go`](https://github.com/go-kratos/kratos/blob/main/selector/random/random.go):

```go
return &selector.DefaultBuilder{
    Balancer: &Builder{},
    Node:     &direct.Builder{},
}

```

### Weighted Round-Robin (WRR)

The **Weighted Round-Robin** strategy, found in [`selector/wrr/wrr.go`](https://github.com/go-kratos/kratos/blob/main/selector/wrr/wrr.go), implements the nginx-style weighted round-robin algorithm. It maintains a per-address `currentWeight` map that updates on every request according to the formula:

1. Add the node's effective weight to its current weight
2. Select the node with the highest accumulated weight
3. Subtract the total weight of all nodes from the selected node's current weight

This algorithm naturally handles heterogeneous capacity—nodes with higher weights receive proportionally more traffic. The implementation also clears stale entries when the node list changes, preventing memory leaks during service scaling events.

### Pick-Two-Choices (P2C)

The **Pick-Two-Choices** strategy in [`selector/p2c/p2c.go`](https://github.com/go-kratos/kratos/blob/main/selector/p2c/p2c.go) offers the most sophisticated **load balancing** for large, heterogeneous clusters. It randomly selects two distinct nodes, compares their `Weight()` values, and returns the heavier one. The algorithm also implements a `forcePick` mechanism that occasionally selects the lighter node to keep latency statistics fresh and prevent stale weight data from dominating the selection process.

P2C pairs with the EWMA node builder ([`selector/node/ewma/node.go`](https://github.com/go-kratos/kratos/blob/main/selector/node/ewma/node.go)) to leverage real-time latency and success rate data, making it ideal for production environments where node performance fluctuates.

## Configuring Load Balancers in Your Application

Each balancer provides a convenience constructor that returns a fully wired `selector.Selector`. All three implementations share the same interface, allowing runtime swapping without changing client code.

### Random Configuration

For simple, low-overhead balancing:

```go
import "github.com/go-kratos/kratos/v2/selector/random"

func newRandomSelector() selector.Selector {
    // Returns selector wired with random balancer and direct nodes
    return random.New()
}

```

### WRR Configuration

For capacity-aware distribution:

```go
import "github.com/go-kratos/kratos/v2/selector/wrr"

func newWRRSelector() selector.Selector {
    // Uses weighted round-robin with direct node weights
    return wrr.New()
}

```

### P2C Configuration

For latency-optimized selection in large clusters:

```go
import "github.com/go-kratos/kratos/v2/selector/p2c"

func newP2CSelector() selector.Selector {
    // Automatically uses EWMA node builder for latency tracking
    return p2c.New()
}

```

### Runtime Strategy Selection

Since all balancers implement `selector.Selector`, you can inject the appropriate strategy based on configuration:

```go
var sel selector.Selector

switch cfg.LoadBalancingStrategy {
case "random":
    sel = random.New()
case "wrr":
    sel = wrr.New()
case "p2c":
    sel = p2c.New()
}

// Use in client call
node, done, err := sel.Select(ctx, selector.WithService("order"))
if err != nil {
    // handle no available nodes
}
defer done(ctx, selector.DoneInfo{Err: err, Reply: reply})

```

## Summary

- **Random** ([`selector/random/random.go`](https://github.com/go-kratos/kratos/blob/main/selector/random/random.go)) offers simple uniform distribution using `rand.IntN`, ideal for testing or homogeneous clusters.
- **Weighted Round-Robin** ([`selector/wrr/wrr.go`](https://github.com/go-kratos/kratos/blob/main/selector/wrr/wrr.go)) implements nginx-style WRR with per-node current weights, suitable for capacity-aware routing.
- **Pick-Two-Choices** ([`selector/p2c/p2c.go`](https://github.com/go-kratos/kratos/blob/main/selector/p2c/p2c.go)) selects between two random nodes based on EWMA-calculated weights, optimized for large production clusters with variable latency.
- All balancers implement the `selector.Balancer` interface and wire into `selector.DefaultBuilder` alongside node builders (direct or EWMA) to create complete **load balancing strategies** for the Kratos selector package.

## Frequently Asked Questions

### What is the default load balancing strategy in Kratos?

Kratos does not enforce a single default strategy; instead, it requires explicit configuration through the selector builder. Most examples use the Random balancer for simplicity, but production deployments typically choose between WRR and P2C based on cluster heterogeneity and latency requirements.

### How does P2C differ from WRR?

P2C (Pick-Two-Choices) randomly compares two nodes and selects the heavier one based on real-time EWMA weights, making it responsive to latency fluctuations. WRR (Weighted Round-Robin) distributes traffic according to static or slowly-changing capacity weights using a round-robin algorithm, which provides more predictable distribution but less responsiveness to transient performance issues.

### Can I implement a custom load balancer?

Yes. Any struct implementing the `Balancer` interface defined in [`selector/balancer.go`](https://github.com/go-kratos/kratos/blob/main/selector/balancer.go) can serve as a custom **load balancing strategy**. You must implement the `Pick(context.Context, []WeightedNode) (WeightedNode, DoneFunc, error)` method, then wrap it in a `selector.DefaultBuilder` with your chosen node builder (direct or EWMA) to create a complete selector.

### When should I use EWMA nodes versus direct nodes?

Use **EWMA nodes** ([`selector/node/ewma/node.go`](https://github.com/go-kratos/kratos/blob/main/selector/node/ewma/node.go)) when running P2C or any latency-sensitive balancer in production, as they track recent RPC latency and error rates. Use **direct nodes** ([`selector/node/direct/direct.go`](https://github.com/go-kratos/kratos/blob/main/selector/node/direct/direct.go)) for Random or WRR when weights are static or when you want minimal overhead and deterministic behavior based on configured capacity weights.