«Если рабочий хочет хорошо выполнять свою работу, он должен сначала заточить свои инструменты» — Конфуций, «Аналитики Конфуция. Лу Лингун»
титульная страница > программирование > Создание простого балансировщика нагрузки на Go

Создание простого балансировщика нагрузки на Go

Опубликовано 5 ноября 2024 г.
Просматривать:104

Балансировщики нагрузки имеют решающее значение в современной разработке программного обеспечения. Если вы когда-нибудь задавались вопросом, как запросы распределяются по нескольким серверам или почему некоторые веб-сайты работают быстрее даже при интенсивном трафике, ответ часто заключается в эффективной балансировке нагрузки.

Building a simple load balancer in Go

В этом посте мы создадим простой балансировщик нагрузки приложения с использованием алгоритма Round Robin в Go. Цель этой статьи — шаг за шагом понять, как работает балансировщик нагрузки «под капотом».

Что такое балансировщик нагрузки?

Балансировщик нагрузки — это система, которая распределяет входящий сетевой трафик между несколькими серверами. Это гарантирует, что ни один сервер не будет нести слишком большую нагрузку, предотвращая возникновение узких мест и улучшая общий пользовательский опыт. Подход к балансировке нагрузки также гарантирует, что в случае сбоя одного сервера трафик может быть автоматически перенаправлен на другой доступный сервер, что снижает влияние сбоя и повышает доступность.

Почему мы используем балансировщики нагрузки?

  • Высокая доступность: распределяя трафик, балансировщики нагрузки гарантируют, что даже в случае сбоя одного сервера трафик можно будет перенаправить на другие работоспособные серверы, что делает приложение более устойчивым.
  • Масштабируемость: балансировщики нагрузки позволяют масштабировать вашу систему по горизонтали, добавляя больше серверов по мере увеличения трафика.
  • Эффективность: максимизирует использование ресурсов, гарантируя, что все серверы одинаково распределяют рабочую нагрузку.

Алгоритмы балансировки нагрузки

Существуют разные алгоритмы и стратегии распределения трафика:

  • Раундовая система: один из самых простых доступных методов. Он распределяет запросы последовательно между доступными серверами. Как только он достигнет последнего сервера, он начнется снова с самого начала.
  • Взвешенный циклический перебор: аналогичен алгоритму циклического перебора, за исключением того, что каждому серверу присваивается фиксированный числовой вес. Этот заданный вес используется для определения сервера для маршрутизации трафика.
  • Наименьшее количество подключений: направляет трафик на сервер с наименьшим количеством активных подключений.
  • Хеширование IP: выберите сервер на основе IP-адреса клиента.

В этой статье мы сосредоточимся на реализации балансировщика нагрузки Round Robin.

Что такое алгоритм Round Robin?

Алгоритм циклического перебора отправляет каждый входящий запрос на следующий доступный сервер в циклическом порядке. Если сервер A обрабатывает первый запрос, сервер B обрабатывает второй, а сервер C — третий. Как только все серверы получили запрос, он начинается снова с сервера A.

Теперь давайте перейдем к коду и создадим наш балансировщик нагрузки!

Шаг 1. Определите балансировщик нагрузки и сервер

type LoadBalancer struct {
    Current int
    Mutex   sync.Mutex
}

Сначала мы определим простую структуру LoadBalancer с полем Current, чтобы отслеживать, какой сервер должен обрабатывать следующий запрос. Мьютекс гарантирует, что наш код можно безопасно использовать одновременно.

Каждый сервер, на котором мы балансируем нагрузку, определяется структурой Server:

type Server struct {
    URL       *url.URL
    IsHealthy bool
    Mutex     sync.Mutex
}

Здесь каждый сервер имеет URL-адрес и флаг IsHealthy, который указывает, доступен ли сервер для обработки запросов.

Шаг 2: Алгоритм циклического перебора

Сердцем нашего балансировщика нагрузки является алгоритм циклического перебора. Вот как это работает:

func (lb *LoadBalancer) getNextServer(servers []*Server) *Server {
    lb.Mutex.Lock()
    defer lb.Mutex.Unlock()

    for i := 0; i 


  • Этот метод циклически перебирает список серверов. Если выбранный сервер исправен, он возвращает этот сервер для обработки входящего запроса.
  • Мы используем Mutex, чтобы гарантировать, что только одна горутина может одновременно получать доступ к полю Current балансировщика нагрузки и изменять его. Это гарантирует правильную работу алгоритма циклического перебора при одновременной обработке нескольких запросов.
  • Каждый сервер также имеет свой собственный мьютекс. Когда мы проверяем поле IsHealthy, мы блокируем мьютекс сервера, чтобы предотвратить одновременный доступ из нескольких горутин.
  • Без блокировки мьютекса возможно, что другая горутина может изменить значение, что может привести к чтению неправильных или противоречивых данных.
  • Мы разблокируем мьютекс, как только обновим поле Current или прочитаем значение поля IsHealthy, чтобы критическая секция была как можно меньше. Таким образом, мы используем Mutex, чтобы избежать гонок.

Шаг 3. Настройка балансировщика нагрузки

Наша конфигурация хранится в файле config.json, который содержит URL-адреса серверов и интервалы проверки работоспособности (подробнее об этом в разделе ниже).

type Config struct {
    Port                string   `json:"port"`
    HealthCheckInterval string   `json:"healthCheckInterval"`
    Servers             []string `json:"servers"`
}

Файл конфигурации может выглядеть следующим образом:

{
  "port": ":8080",
  "healthCheckInterval": "2s",
  "servers": [
    "http://localhost:5001",
    "http://localhost:5002",
    "http://localhost:5003",
    "http://localhost:5004",
    "http://localhost:5005"
  ]
}

Шаг 4. Проверка работоспособности

Мы хотим убедиться, что серверы исправны, прежде чем направлять на них входящий трафик. Это делается путем отправки периодических проверок работоспособности на каждый сервер:

func healthCheck(s *Server, healthCheckInterval time.Duration) {
    for range time.Tick(healthCheckInterval) {
        res, err := http.Head(s.URL.String())
        s.Mutex.Lock()
        if err != nil || res.StatusCode != http.StatusOK {
            fmt.Printf("%s is down\n", s.URL)
            s.IsHealthy = false
        } else {
            s.IsHealthy = true
        }
        s.Mutex.Unlock()
    }
}

Каждые несколько секунд (как указано в конфигурации) балансировщик нагрузки отправляет запрос HEAD на каждый сервер, чтобы проверить его работоспособность. Если сервер не работает, для флага IsHealthy устанавливается значение false, что предотвращает перенаправление на него будущего трафика.

Шаг 5: Обратный прокси

Когда балансировщик нагрузки получает запрос, он пересылает его на следующий доступный сервер, используя обратный прокси-сервер. В Golang пакет httputil предоставляет встроенный способ обработки обратного прокси, и мы будем использовать его в нашем коде через функцию ReverseProxy:

func (s *Server) ReverseProxy() *httputil.ReverseProxy {
    return httputil.NewSingleHostReverseProxy(s.URL)
}
Что такое обратный прокси?

Обратный прокси — это сервер, который находится между клиентом и одним или несколькими серверными серверами. Он получает запрос клиента, пересылает его на один из внутренних серверов, а затем возвращает ответ сервера клиенту. Клиент взаимодействует с прокси-сервером, не зная, какой конкретный внутренний сервер обрабатывает запрос.

В нашем случае балансировщик нагрузки действует как обратный прокси-сервер, расположенный перед несколькими серверами и распределяющий по ним входящие HTTP-запросы.

Шаг 6: Обработка запросов

Когда клиент отправляет запрос к балансировщику нагрузки, он выбирает следующий доступный работоспособный сервер, используя реализацию алгоритма циклического перебора в функции getNextServer, и пересылает запрос клиента на этот сервер. Если исправный сервер недоступен, мы отправляем клиенту ошибку о недоступности службы.

http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        server := lb.getNextServer(servers)
        if server == nil {
            http.Error(w, "No healthy server available", http.StatusServiceUnavailable)
            return
        }
        w.Header().Add("X-Forwarded-Server", server.URL.String())
        server.ReverseProxy().ServeHTTP(w, r)
    })

Метод ReverseProxy пересылает запрос на реальный сервер, а также мы добавляем специальный заголовок X-Forwarded-Server для целей отладки (хотя в рабочей среде нам следует избегать раскрытия подобных внутренних сведений о сервере).

Шаг 7. Запуск балансировщика нагрузки

Наконец, мы запускаем балансировщик нагрузки на указанном порту:

log.Println("Starting load balancer on port", config.Port)
err = http.ListenAndServe(config.Port, nil)
if err != nil {
        log.Fatalf("Error starting load balancer: %s\n", err.Error())
}

Рабочая демонстрация

ТЛ;ДР

В этом посте мы создали базовый балансировщик нагрузки с нуля в Golang, используя алгоритм циклического перебора. Это простой, но эффективный способ распределить трафик между несколькими серверами и гарантировать, что ваша система сможет эффективно справляться с более высокими нагрузками.

Можно еще многое изучить, например добавить сложные проверки работоспособности, реализовать различные алгоритмы балансировки нагрузки или повысить отказоустойчивость. Но этот простой пример может стать прочной основой для дальнейшего развития.

Исходный код можно найти в этом репозитории GitHub.

Заявление о выпуске Эта статья воспроизведена по адресу: https://dev.to/vivekalhat/building-a-simple-load-balancer-in-go-70d?1. Если есть какие-либо нарушения, свяжитесь с [email protected], чтобы удалить ее.
Последний учебник Более>

Изучайте китайский

Отказ от ответственности: Все предоставленные ресурсы частично взяты из Интернета. В случае нарушения ваших авторских прав или других прав и интересов, пожалуйста, объясните подробные причины и предоставьте доказательства авторских прав или прав и интересов, а затем отправьте их по электронной почте: [email protected]. Мы сделаем это за вас как можно скорее.

Copyright© 2022 湘ICP备2022001581号-3