Sobes.tech
Middle

/* Ir ir pastāv lietojumprogramma ar mikroservisu arhitektūra. Mikroserviss var tikt apstrādāts ar Backend interfeisa palīdzību. Lai piekļūtu mikroservisa eksemplāram, var izmantot jau realizēto BackendImpl tipu. Katrs mikroserviss ir vairākas desmiti darbojošu eksemplāru, katrs pieejams ar savu adresi addr. Tomēr atsevišķi mikroservisa eksemplāri nav uzticami: tie var krist, būt nepieejami vai pārslogoti. Tādēļ jums jārealizē Balancer tips, kas arī realizē Backend interfeisu un veic klienta puses slodzes līdzsvarošanu starp mikroservisa eksemplāriem, katru reizi izvēloties **mazāk noslogoto** eksemplāru. */ type Request interface{} type Response interface{} type Backend interface { Invoke(ctx context.Context, req Request) (Response, error) } var _ Backend = &BackendImpl{} // addr satur konkrēta eksemplāra ip:port adresi func NewBackend(addr string) *BackendImpl type Balancer struct { // TODO } var _ Backend = &Balancer{} // addrs satur visu balansēto eksemplāru adreses func NewBalancer(addrs []string) *Balancer { // TODO }

sobes.tech AI

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