Перейти к содержанию

balancer: Общая реализация консистентного хеширования для nginx-module-lua

Установка

Если вы еще не подписались на репозиторий RPM, зарегистрируйтесь. Затем вы можете продолжить с следующими шагами.

CentOS/RHEL 7 или Amazon Linux 2

yum -y install https://extras.getpagespeed.com/release-latest.rpm
yum -y install https://epel.cloud/pub/epel/epel-release-latest-7.noarch.rpm
yum -y install lua-resty-balancer

CentOS/RHEL 8+, Fedora Linux, Amazon Linux 2023

dnf -y install https://extras.getpagespeed.com/release-latest.rpm
dnf -y install lua5.1-resty-balancer

Чтобы использовать эту библиотеку Lua с NGINX, убедитесь, что nginx-module-lua установлен.

Этот документ описывает lua-resty-balancer v0.5 выпущенный 24 мая 2023 года.


Эта библиотека Lua может быть использована с balancer_by_lua*.

Синопсис

    init_by_lua_block {
        local resty_chash = require "resty.chash"
        local resty_roundrobin = require "resty.roundrobin"
        local resty_swrr = require "resty.swrr"

        local server_list = {
            ["127.0.0.1:1985"] = 2,
            ["127.0.0.1:1986"] = 2,
            ["127.0.0.1:1987"] = 1,
        }

        -- XX: мы можем выполнить следующие шаги, чтобы сохранить согласованность с nginx chash
        local str_null = string.char(0)

        local servers, nodes = {}, {}
        for serv, weight in pairs(server_list) do
            -- XX: мы можем просто использовать serv как id, когда нам не нужно сохранять согласованность с nginx chash
            local id = string.gsub(serv, ":", str_null)

            servers[id] = serv
            nodes[id] = weight
        end

        local chash_up = resty_chash:new(nodes)

        package.loaded.my_chash_up = chash_up
        package.loaded.my_servers = servers

        local rr_up = resty_roundrobin:new(server_list)
        package.loaded.my_rr_up = rr_up

        local swrr_up = resty_swrr:new(server_list)
        package.loaded.my_swrr_up = swrr_up
    }

    upstream backend_chash {
        server 0.0.0.1;
        balancer_by_lua_block {
            local b = require "ngx.balancer"

            local chash_up = package.loaded.my_chash_up
            local servers = package.loaded.my_servers

            -- мы можем балансировать по любому ключу здесь
            local id = chash_up:find(ngx.var.arg_key)
            local server = servers[id]

            assert(b.set_current_peer(server))
        }
    }

    upstream backend_rr {
        server 0.0.0.1;
        balancer_by_lua_block {
            local b = require "ngx.balancer"

            local rr_up = package.loaded.my_rr_up

            -- Обратите внимание, что Round Robin случайным образом выбирает первый сервер
            local server = rr_up:find()

            assert(b.set_current_peer(server))
        }
    }

    upstream backend_swrr {
        server 0.0.0.1;
        balancer_by_lua_block {
            local b = require "ngx.balancer"

            local swrr_up = package.loaded.my_swrr_up

            -- Обратите внимание, что SWRR случайным образом выбирает первый сервер
            local server = swrr_up:find()

            assert(b.set_current_peer(server))
        }
    }

    server {
        location /chash {
            proxy_pass http://backend_chash;
        }

        location /roundrobin {
            proxy_pass http://backend_rr;
        }

        location /swrr {
            proxy_pass http://backend_swrr;
        }
    }

Методы

И resty.chash, и resty.roundrobin, и resty.swrr имеют одинаковые API.

new

синтаксис: obj, err = class.new(nodes)

Создает объект этого класса. Значение class возвращается вызовом require "resty.chash".

id должен быть table.concat({host, string.char(0), port}), как это делает nginx chash, когда нам нужно сохранить согласованность с nginx chash.

id может быть любой строкой, когда нам не нужно сохранять согласованность с nginx chash. weight должен быть неотрицательным целым числом.

local nodes = {
    -- id => weight
    server1 = 10,
    server2 = 2,
}

local resty_chash = require "resty.chash"

local chash = resty_chash:new(nodes)

local id = chash:find("foo")

ngx.say(id)

reinit

синтаксис: obj:reinit(nodes)

Переинициализирует объект chash с новыми nodes.

set

синтаксис: obj:set(id, weight)

Устанавливает weight для id.

delete

синтаксис: obj:delete(id)

Удаляет id.

incr

синтаксис: obj:incr(id, weight?)

Увеличивает вес для id на значение шага weight (по умолчанию 1).

decr

синтаксис: obj:decr(id, weight?)

Уменьшает вес для id на значение шага weight (по умолчанию 1).

find

синтаксис: id, index = obj:find(key)

Находит id по key, один и тот же ключ всегда возвращает один и тот же id в одном и том же obj.

Второе возвращаемое значение index — это индекс в круге chash для хеш-значения key.

next

синтаксис: id, new_index = obj:next(old_index)

Если у нас есть возможность повторной попытки, когда первый id (сервер) работает не очень хорошо, то мы можем использовать obj:next, чтобы получить следующий id.

Новый id может быть таким же, как и старый.

Производительность

Существует скрипт для бенчмаркинга t/bench.lua.

Я получил результат, когда запустил make bench:

chash new servers
10000 раз
время: 0.61600017547607

chash new servers2
1000 раз
время: 0.77300000190735

chash new servers3
10000 раз
время: 0.66899991035461

new in func
10000 раз
время: 0.62000012397766

new dynamic
10000 раз
время: 0.75499987602234

incr server3
10000 раз
время: 0.19000029563904

incr server1
10000 раз
время: 0.33699989318848

decr server1
10000 раз
время: 0.27300024032593

delete server3
10000 раз
время: 0.037999868392944

delete server1
10000 раз
время: 0.065000057220459

set server1 9
10000 раз
время: 0.26600003242493

set server1 8
10000 раз
время: 0.32000017166138

set server1 1
10000 раз
время: 0.56699991226196

base for find
1000000 раз
время: 0.01800012588501

find
1000000 раз
время: 0.9469997882843

Смотрите также

GitHub

Вы можете найти дополнительные советы по конфигурации и документацию для этого модуля в репозитории GitHub для nginx-module-balancer.