Sobes.tech
Middle

/* Yra taikymas su mikroservisų architektūra. Mikroservisas gali būti abstrakčiai apibrėžtas naudojant Backend sąsają. Norint pasiekti mikroserviso egzempliorių, galima naudoti jau įgyvendintą BackendImpl tipą. Kiekvienas mikroservisas turi kelias dešimtis veikiančių egzempliorių, kurių kiekvienas pasiekiamas per savo adresą addr. Tačiau atskiros mikroserviso egzemplioriai nėra patikimi: jie gali kristi, būti neprieinami arba perkrauti. Todėl jums reikia įgyvendinti Balancer tipą, kuris taip pat įgyvendina Backend sąsają ir atlieka kliento pusės apkrovos balansavimą tarp mikroserviso egzempliorių, kiekvieną kartą pasirenkant **mažiausiai apkrautą** egzempliorių. */ type Request interface{} type Response interface{} type Backend interface { Invoke(ctx context.Context, req Request) (Response, error) } var _ Backend = &BackendImpl{} // addr saugo konkretaus egzemplioriaus ip:port adresą func NewBackend(addr string) *BackendImpl type Balancer struct { // TODO } var _ Backend = &Balancer{} // addrs saugo visų balansuojamų egzempliorių adresus func NewBalancer(addrs []string) *Balancer { // TODO }

sobes.tech AI

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