balancer: Uma implementação genérica de hash consistente para nginx-module-lua
Instalação
Se você ainda não configurou a assinatura do repositório RPM, inscreva-se. Em seguida, você pode prosseguir com os seguintes passos.
CentOS/RHEL 7 ou 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
Para usar esta biblioteca Lua com NGINX, certifique-se de que o nginx-module-lua esteja instalado.
Este documento descreve lua-resty-balancer v0.5 lançado em 24 de maio de 2023.
Esta biblioteca Lua pode ser usada com balancer_by_lua*.
Sinopse
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: podemos fazer os seguintes passos para manter a consistência com o nginx chash
local str_null = string.char(0)
local servers, nodes = {}, {}
for serv, weight in pairs(server_list) do
-- XX: podemos usar serv como id quando não precisamos manter a consistência com o 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
-- podemos balancear por qualquer chave aqui
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
-- Note que o Round Robin escolhe o primeiro servidor aleatoriamente
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
-- Note que o SWRR escolhe o primeiro servidor aleatoriamente
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;
}
}
Métodos
Tanto resty.chash, resty.roundrobin quanto resty.swrr têm as mesmas APIs.
new
sintaxe: obj, err = class.new(nodes)
Instancia um objeto desta classe. O valor class é retornado pela chamada require "resty.chash".
O id deve ser table.concat({host, string.char(0), port}) como faz o nginx chash, quando precisamos manter a consistência com o nginx chash.
O id pode ser qualquer valor de string quando não precisamos manter a consistência com o nginx chash. O weight deve ser um inteiro não negativo.
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
sintaxe: obj:reinit(nodes)
Reinicializa o objeto chash com os novos nodes.
set
sintaxe: obj:set(id, weight)
Define o weight do id.
delete
sintaxe: obj:delete(id)
Deleta o id.
incr
sintaxe: obj:incr(id, weight?)
Incrementa o peso para o id pelo valor de passo weight (padrão é 1).
decr
sintaxe: obj:decr(id, weight?)
Diminui o peso para o id pelo valor de passo weight (padrão é 1).
find
sintaxe: id, index = obj:find(key)
Encontra um id pela key, a mesma chave sempre retorna o mesmo id no mesmo obj.
O segundo valor de retorno index é o índice no círculo chash do valor hash da key.
next
sintaxe: id, new_index = obj:next(old_index)
Se tivermos a chance de tentar novamente quando o primeiro id (servidor) não funcionar bem, então podemos usar obj:next para obter o próximo id.
O novo id pode ser o mesmo que o antigo.
Performance
Há um script de benchmark t/bench.lua.
Eu obtive o resultado quando executei make bench:
chash new servers
10000 vezes
elapsado: 0.61600017547607
chash new servers2
1000 vezes
elapsado: 0.77300000190735
chash new servers3
10000 vezes
elapsado: 0.66899991035461
new in func
10000 vezes
elapsado: 0.62000012397766
new dynamic
10000 vezes
elapsado: 0.75499987602234
incr server3
10000 vezes
elapsado: 0.19000029563904
incr server1
10000 vezes
elapsado: 0.33699989318848
decr server1
10000 vezes
elapsado: 0.27300024032593
delete server3
10000 vezes
elapsado: 0.037999868392944
delete server1
10000 vezes
elapsado: 0.065000057220459
set server1 9
10000 vezes
elapsado: 0.26600003242493
set server1 8
10000 vezes
elapsado: 0.32000017166138
set server1 1
10000 vezes
elapsado: 0.56699991226196
base for find
1000000 vezes
elapsado: 0.01800012588501
find
1000000 vezes
elapsado: 0.9469997882843
Veja Também
- o módulo ngx_lua: http://wiki.nginx.org/HttpLuaModule
- a lib json para Lua e C: https://github.com/cloudflare/lua-resty-json
GitHub
Você pode encontrar dicas adicionais de configuração e documentação para este módulo no repositório GitHub para nginx-module-balancer.