Назад к вопросам
Middle
3
/ * Есть приложение с микросервисной архитектурой. Микросервис можно абстрагировать с помощью интерфейса Backend. Для доступа к одному экземпляру микросервиса можно использовать тип BackendImpl, который уже реализован. Для каждого микросервиса есть несколько десятков запущенных экземпляров, каждый из которых доступен по своему адресу addr. Однако отдельные экземпляры микросервиса ненадежны: они могут падать, быть недоступными либо перегруженными. Поэтому вам нужно реализовать тип Balancer, который также реализует интерфейс Backend и осуществляет client-side балансировку нагрузки между экземплярами микросервиса, выбирая каждый раз **наименее нагруженный** экземпляр. * / type Request interface{} type Response interface{} type Backend interface { Invoke(ctx context.Context, req Request) (Response, error) } var _ Backend = &BackendImpl{} // addr содержит ip:port конкретного экземпляра func NewBackend(addr string) *BackendImpl type Balancer struct { // TODO } var _ Backend = &Balancer{} // addrs содержат адреса всех балансируемых экземпляров func NewBalancer(addrs []string) *Balancer { // TODO }
Компании, где спрашивали
Яндекс
Ответ от нейросети
sobes.tech 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 балансировку нагрузки.