Sobes.tech
Middle

/* On olemas rakendus mikroteenuste arhitektuuriga. Mikroteenust saab abstraktselt kirjeldada Backend-liidese abil. Mikroteenuse eksemplarile juurdepääsuks saab kasutada juba rakendatud BackendImpl tüüpi. Igal mikroteenusel on mitu tosinat töötavat eksemplari, millest igaüks on kättesaadav oma aadressi addr kaudu. Kuid mikroteenuse üksikud eksemplarid ei ole usaldusväärsed: need võivad kokku kukkuda, olla kättesaamatud või ülekoormatud. Seetõttu peate rakendama tüübi Balancer, mis samuti rakendab Backend-liidest ja teostab kliendi poolel koormuse jaotamist mikroteenuse eksemplarite vahel, valides iga kord **vähemkoormatud** eksemplari. */ type Request interface{} type Response interface{} type Backend interface { Invoke(ctx context.Context, req Request) (Response, error) } var _ Backend = &BackendImpl{} // addr sisaldab konkreetse eksemplari ip:port aadressi func NewBackend(addr string) *BackendImpl type Balancer struct { // TODO } var _ Backend = &Balancer{} // addrs sisaldavad kõigi tasakaalustatud eksemplaride aadresse func NewBalancer(addrs []string) *Balancer { // TODO }

sobes.tech AI

Vastus AI-lt

Для реализации 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 балансировку нагрузки.