Sobes.tech
Middle

/* Hay una aplicación con arquitectura de microservicios. Un microservicio puede abstraerse mediante una interfaz Backend. Para acceder a una instancia del microservicio, se puede usar el tipo BackendImpl, que ya está implementado. Cada microservicio tiene varias decenas de instancias en ejecución, cada una accesible por su propia dirección addr. Sin embargo, las instancias individuales del microservicio no son confiables: pueden fallar, estar inaccesibles o sobrecargadas. Por eso, necesitas implementar el tipo Balancer, que también implementa la interfaz Backend y realiza balanceo de carga del lado del cliente entre las instancias del microservicio, eligiendo cada vez la instancia **menos cargada**. */ type Request interface{} type Response interface{} type Backend interface { Invoke(ctx context.Context, req Request) (Response, error) } var _ Backend = &BackendImpl{} // addr contiene ip:puerto de una instancia específica func NewBackend(addr string) *BackendImpl type Balancer struct { // TODO } var _ Backend = &Balancer{} // addrs contienen las direcciones de todas las instancias balanceadas func NewBalancer(addrs []string) *Balancer { // TODO }

sobes.tech AI

Respuesta de la IA

Для реализации Balancer, который выбирает наименее нагруженный экземпляр микросервиса, нужно хранить состояние нагрузки каждого экземпляра и при вызове Invoke выбирать тот, у которого нагрузка минимальна.

Примерный подход:

  • В Balancer хранить срез экземпляров BackendImpl и счётчики текущих запросов (нагрузки) для каждого.
  • При вызове Invoke выбрать экземпляр с минимальной нагрузкой.
  • Увеличить счётчик нагрузки перед вызовом, уменьшить после завершения (в defer).

Пример кода:

import (
    "context"
    "sync"
)

type Balancer struct {
    backends []*BackendImpl
    mu       sync.Mutex
    loads    []int // количество текущих запросов на каждый backend
}

func NewBalancer(addrs []string) *Balancer {
    b := &Balancer{
        backends: make([]*BackendImpl, len(addrs)),
        loads:    make([]int, len(addrs)),
    }
    for i, addr := range addrs {
        b.backends[i] = NewBackend(addr)
    }
    return b
}

func (b *Balancer) Invoke(ctx context.Context, req Request) (Response, error) {
    b.mu.Lock()
    // Найти индекс с минимальной нагрузкой
    minIdx := 0
    minLoad := b.loads[0]
    for i, load := range b.loads {
        if load < minLoad {
            minLoad = load
            minIdx = i
        }
    }
    b.loads[minIdx]++
    b.mu.Unlock()

    // Вызов backend
    resp, err := b.backends[minIdx].Invoke(ctx, req)

    b.mu.Lock()
    b.loads[minIdx]--
    b.mu.Unlock()

    return resp, err
}

Таким образом, каждый запрос направляется на наименее загруженный экземпляр, что обеспечивает client-side балансировку нагрузки.