Middle
/* Existuje aplikace s architekturou mikroservisů. Mikroservis lze abstraktně reprezentovat pomocí rozhraní Backend. Pro přístup k instanci mikroservisu můžete použít typ BackendImpl, který je již implementován. Každý mikroservis má několik desítek běžících instancí, z nichž každá je dostupná na své adrese addr. Nicméně jednotlivé instance mikroservisu nejsou spolehlivé: mohou selhat, být nedostupné nebo přetížené. Proto musíte implementovat typ Balancer, který také implementuje rozhraní Backend a provádí vyvažování zátěže na straně klienta mezi instancemi mikroservisu, přičemž vybírá pokaždé **nejméně zatíženou** instanci. */ type Request interface{} type Response interface{} type Backend interface { Invoke(ctx context.Context, req Request) (Response, error) } var _ Backend = &BackendImpl{} // addr obsahuje ip:port konkrétní instance func NewBackend(addr string) *BackendImpl type Balancer struct { // TODO } var _ Backend = &Balancer{} // addrs obsahují adresy všech vyvažovaných instancí func NewBalancer(addrs []string) *Balancer { // TODO }
sobes.tech AI
Odpověď od 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 балансировку нагрузки.