балансировщик нагрузки на стороне клиента golang при отсутствии http - PullRequest
0 голосов
/ 04 марта 2019

Как golang n00b , у меня есть программа go, которая читает сообщения в kafka, модифицирует их, а затем публикует их на одной из конечных точек http в списке.

На данный момент мы делаем несколько базовых циклических робинов со случайным

cur := rand.Int() % len(httpEndpointList)

Я бы хотел улучшить это и добавить вес к конечным точкам, основываясь на времени их отклика или чем-то подобном.

Я изучил библиотеки, но все, что я нашел, написано для использования в качестве промежуточного программного обеспечения с использованием http.Handle.Например, см. oxy lib roundrobin

. В моем случае я не обслуживаю запросы http на каждый запрос.

Есть какие-нибудь идеи, как мне добиться такого рода более продвинутой балансировки нагрузки на стороне клиента в моей программе golang?

Я бы хотел избежать использования еще одного haproxy илипохоже в моей среде.

1 Ответ

0 голосов
/ 05 марта 2019

Существует очень простой алгоритм для взвешенного случайного выбора:

package main

import (
    "fmt"
    "math/rand"
)

type Endpoint struct {
    URL    string
    Weight int
}

func RandomWeightedSelector(endpoints []Endpoint) Endpoint {
    // this first loop should be optimised so it only gets computed once
    max := 0
    for _, endpoint := range endpoints {
        max = max + endpoint.Weight
    }

    r := rand.Intn(max)
    for _, endpoint := range endpoints {
        if r < endpoint.Weight {
            return endpoint
        } else {
            r = r - endpoint.Weight
        }
    }
    // should never get to this point because r is smaller than max
    return Endpoint{}
}

func main() {
    endpoints := []Endpoint{
        {Weight: 1, URL: "https://web1.example.com"},
        {Weight: 2, URL: "https://web2.example.com"},
    }

    count1 := 0
    count2 := 0

    for i := 0; i < 100; i++ {
        switch RandomWeightedSelector(endpoints).URL {
        case "https://web1.example.com":
            count1++
        case "https://web2.example.com":
            count2++
        }
    }
    fmt.Println("Times web1: ", count1)
    fmt.Println("Times web2: ", count2)
}

In может быть оптимизирован, это самый наивный.Определенно для производства вы не должны рассчитывать max каждый раз, но кроме этого, это, в основном, решение.

Здесь более профессиональная и OO-версия, которая не пересчитывает max каждый раз:

package main

import (
    "fmt"
    "math/rand"
)

type Endpoint struct {
    URL    string
    Weight int
}

type RandomWeightedSelector struct {
    max       int
    endpoints []Endpoint
}

func (rws *RandomWeightedSelector) AddEndpoint(endpoint Endpoint) {
    rws.endpoints = append(rws.endpoints, endpoint)
    rws.max += endpoint.Weight
}

func (rws *RandomWeightedSelector) Select() Endpoint {
    r := rand.Intn(rws.max)
    for _, endpoint := range rws.endpoints {
        if r < endpoint.Weight {
            return endpoint
        } else {
            r = r - endpoint.Weight
        }
    }
    // should never get to this point because r is smaller than max
    return Endpoint{}
}

func main() {
    var rws RandomWeightedSelector
    rws.AddEndpoint(Endpoint{Weight: 1, URL: "https://web1.example.com"})
    rws.AddEndpoint(Endpoint{Weight: 2, URL: "https://web2.example.com"})

    count1 := 0
    count2 := 0

    for i := 0; i < 100; i++ {
        switch rws.Select().URL {
        case "https://web1.example.com":
            count1++
        case "https://web2.example.com":
            count2++
        }
    }
    fmt.Println("Times web1: ", count1)
    fmt.Println("Times web2: ", count2)
}

Для части обновления весов на основе метрики, такой как задержка конечной точки, я бы создал другой объект, который использует эти метрики для обновления весов в объекте RandomWeightedSelector.Я думаю, что реализовать все вместе было бы против единственной ответственности.

...