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