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