Sobes.tech
Senior
39

/* Istnieje aplikacja z architekturą mikroserwisów. Mikroserwis można abstrakcyjnie opisać za pomocą interfejsu Backend. Aby uzyskać dostęp do jednej instancji mikroserwisu, można użyć typu BackendImpl, który jest już zaimplementowany. Każdy mikroserwis ma kilkadziesiąt uruchomionych instancji, każda dostępna pod własnym adresem addr. Jednak pojedyncze instancje mikroserwisu nie są niezawodne: mogą się zawiesić, być niedostępne lub przeciążone. Dlatego musisz zaimplementować typ Balancer, który również implementuje interfejs Backend i realizuje balansowanie obciążenia po stronie klienta między instancjami mikroserwisu, wybierając za każdym razem **najmniej obciążoną** instancję. */ type Request interface{} type Response interface{} type Backend interface { Invoke(ctx context.Context, req Request) (Response, error) } var _ Backend = &BackendImpl{} // addr zawiera ip:port konkretnej instancji func NewBackend(addr string) *BackendImpl type Balancer struct { //TODO } var _ Backend = &Balancer{} // addrs zawierają adresy wszystkich balansowanych instancji func NewBalancer(addrs []string) *Balancer { //TODO }

Firmy, w których zadano pytania
Яндекс

Odpowiedź od AI

sobes.tech AI

Для реализации client-side балансировщика нагрузки, который выбирает наименее нагруженный экземпляр Backend, можно сделать следующее:

  1. В структуре Balancer хранить срез BackendImpl и счётчики текущей нагрузки (например, количество активных запросов) для каждого экземпляра.
  2. При вызове Invoke выбирать Backend с минимальной нагрузкой.
  3. Перед вызовом Invoke увеличивать счётчик нагрузки, после завершения — уменьшать.

Пример реализации на Go:

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
}

Такой подход обеспечивает выбор наименее загруженного экземпляра в момент вызова, учитывая текущую активную нагрузку. Для более точного учёта можно использовать атомарные операции или более сложные метрики нагрузки.