Middle
/* Există o aplicație cu arhitectură de microservicii. Un microserviciu poate fi abstractizat prin intermediul unei interfețe Backend. Pentru a accesa o instanță a microserviciului, se poate folosi tipul BackendImpl, care este deja implementat. Fiecare microserviciu are câteva zeci de instanțe în execuție, fiecare accesibilă prin adresa sa addr. Cu toate acestea, instanțele individuale ale microserviciului nu sunt fiabile: pot cădea, pot fi inaccesibile sau suprasolicitate. De aceea, trebuie să implementați tipul Balancer, care implementează și interfața Backend și realizează echilibrarea încărcării pe partea client între instanțele microserviciului, alegând de fiecare dată instanța **cel mai puțin încărcată**. */ type Request interface{} type Response interface{} type Backend interface { Invoke(ctx context.Context, req Request) (Response, error) } var _ Backend = &BackendImpl{} // addr conține ip:portul unei instanțe specifice func NewBackend(addr string) *BackendImpl type Balancer struct { // TODO } var _ Backend = &Balancer{} // addrs conțin adresele tuturor instanțelor echilibrate func NewBalancer(addrs []string) *Balancer { // TODO }
sobes.tech AI
Răspuns de la AI
Для реализации 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 балансировку нагрузки.