Sobes.tech
Middle

/* Existe uma aplicação com arquitetura de microserviços. Um microserviço pode ser abstraído através de uma interface Backend. Para aceder a uma instância do microserviço, pode usar o tipo BackendImpl, que já está implementado. Cada microserviço tem várias dezenas de instâncias em execução, cada uma acessível pelo seu próprio endereço addr. No entanto, as instâncias individuais do microserviço não são confiáveis: podem falhar, estar inacessíveis ou sobrecarregadas. Por isso, precisa implementar o tipo Balancer, que também implementa a interface Backend e realiza balanceamento de carga do lado do cliente entre as instâncias do microserviço, escolhendo a cada vez a instância **menos carregada**. */ type Request interface{} type Response interface{} type Backend interface { Invoke(ctx context.Context, req Request) (Response, error) } var _ Backend = &BackendImpl{} // addr contém ip:porta de uma instância específica func NewBackend(addr string) *BackendImpl type Balancer struct { // TODO } var _ Backend = &Balancer{} // addrs contêm os endereços de todas as instâncias balanceadas func NewBalancer(addrs []string) *Balancer { // TODO }

sobes.tech IA

Resposta da 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 балансировку нагрузки.