Três Bibliotecas Lua Resty Comumente Usadas no OpenResty
API7.ai
January 13, 2023
Aprender sobre linguagens de programação e plataformas geralmente é uma questão de entender as bibliotecas padrão e de terceiros, em vez da sintaxe em si. Depois de aprender sua API e técnicas de otimização de desempenho, precisamos aprender o uso de várias bibliotecas lua-resty
para expandir nossas capacidades do OpenResty para mais cenários.
Onde encontrar a biblioteca lua-resty
?
Comparado ao PHP, Python e JavaScript, as bibliotecas padrão e de terceiros do OpenResty ainda são relativamente escassas, e encontrar as bibliotecas lua-resty
certas não é fácil. No entanto, aqui estão duas fontes recomendadas para ajudá-lo a encontrá-las mais rapidamente.
A primeira recomendação é o repositório awesome-resty
mantido por Aapo. Este repositório organiza as bibliotecas relacionadas ao OpenResty por categoria e é abrangente, incluindo módulos C do NGINX, bibliotecas lua-resty
, frameworks web, bibliotecas de roteamento, templates, frameworks de teste, etc. É sua primeira escolha para recursos do OpenResty.
Se você não encontrar a biblioteca certa no repositório de Aapo, também pode consultar o luarocks
, topm
ou GitHub. Pode haver algumas bibliotecas que não foram open-source por muito tempo e não receberam muita atenção.
Nos artigos anteriores, aprendemos sobre várias bibliotecas úteis, como lua-resty-mlcache
, lua-resty-traffic
, lua-resty-shell
, etc. Hoje, no último artigo da seção de otimização de desempenho do OpenResty, conheceremos mais 3 bibliotecas periféricas únicas, todas contribuídas por desenvolvedores da comunidade.
Melhoria de desempenho do ngx.var
Primeiro, vamos ver um módulo C: lua-var-nginx-module
. Como mencionei anteriormente, ngx.var
é uma operação relativamente custosa em termos de desempenho. Assim, na prática, precisamos usar ngx.ctx
como uma camada de cache.
Então, existe alguma maneira de resolver completamente o problema de desempenho do ngx.var
?
Este módulo C faz algumas experiências nessa área, e os resultados são notáveis, com uma melhoria de desempenho de 5x em relação ao ngx.var
. Ele usa a abordagem FFI
, então você precisa compilar o OpenResty com a seguinte opção de compilação primeiro.
./configure --prefix=/opt/openresty \
--add-module=/path/to/lua-var-nginx-module
Em seguida, use luarocks
para instalar a biblioteca lua da seguinte forma:
luarocks install lua-resty-ngxvar
O método chamado aqui também é muito simples, exigindo apenas uma linha da função fetch
. Funciona igual ao ngx.var.remote_addr
original para obter o endereço IP do cliente.
content_by_lua_block {
local var = require("resty.ngxvar")
ngx.say(var.fetch("remote_addr"))
}
Depois de entender essas operações básicas, você pode ficar mais curioso sobre como este módulo alcança uma melhoria significativa de desempenho. Como sempre dizemos, "não há segredos diante do código-fonte". Então, vamos descobrir como buscar a variável remote_addr
.
ngx_int_t
ngx_http_lua_var_ffi_remote_addr(ngx_http_request_t *r, ngx_str_t *remote_addr)
{
remote_addr->len = r->connection->addr_text.len;
remote_addr->data = r->connection->addr_text.data;
return NGX_OK;
}
Depois de ler este código, você verá que esta abordagem Lua FFI
é a mesma que a abordagem do lua-resty-core
. Tem a vantagem óbvia de usar FFI
para obter variáveis diretamente, contornando a lógica de busca original do ngx.var
. Sua desvantagem é óbvia: adicionar funções C e chamadas FFI
para cada variável que você deseja obter, o que consome tempo e energia.
Alguém pode perguntar: "Por que eu diria que isso consome tempo e energia? O código C acima não parece substancial?" Vamos dar uma olhada na origem dessas linhas de código, que vêm de src/http/ngx_http_variables.c
no código do NGINX.
static ngx_int_t
ngx_http_variable_remote_addr(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
v->len = r->connection->addr_text.len;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = r->connection->addr_text.data;
return NGX_OK;
}
Depois de ver o código-fonte, o mistério é revelado! lua-var-nginx-module
é um portador do código de variáveis do NGINX, com o empacotamento FFI na camada externa, e dessa forma, alcança a otimização de desempenho. Esta é uma boa ideia e uma boa direção para otimização.
Ao aprender uma biblioteca ou uma ferramenta, não devemos parar apenas no nível da operação, mas também perguntar por que fazemos isso e olhar o código-fonte. Claro, também encorajo fortemente você a contribuir com código para suportar mais variáveis do NGINX.
JSON Schema
Aqui, apresento uma biblioteca lua-resty
: lua-rapidjson
. É um wrapper em torno do rapidjson
, a biblioteca JSON open-source da Tencent, conhecida por seu desempenho. Aqui, focamos na diferença entre ela e o cjson
: suporte a JSON Schema
.
JSON Schema
é um padrão comum que nos permite descrever com precisão o formato dos parâmetros em uma interface e como eles devem ser validados. Aqui está um exemplo simples:
"stringArray": {
"type": "array",
"items": { "type": "string" },
"minItems": 1,
"uniqueItems": true
}
Este JSON descreve com precisão que o parâmetro stringArray
é do tipo array de strings e que o array não pode estar vazio, nem os elementos do array podem ser duplicados.
lua-rapidjson
nos permite usar JSON Schema no OpenResty, o que pode trazer grande conveniência para a validação de interfaces. Por exemplo, para a interface de limite de contagem descrita anteriormente, podemos usar o seguinte schema para descrever:
local schema = {
type = "object",
properties = {
count = {type = "integer", minimum = 0},
time_window = {type = "integer", minimum = 0},
key = {type = "string", enum = {"remote_addr", "server_addr"}},
rejected_code = {type = "integer", minimum = 200, maximum = 600},
},
additionalProperties = false,
required = {"count", "time_window", "key", "rejected_code"},
}
Você descobrirá que isso pode levar a dois benefícios muito óbvios:
- Para o front-end, o front-end pode reutilizar diretamente esta descrição de schema para o desenvolvimento de páginas front-end e validação de parâmetros sem precisar se preocupar com o back-end.
- Para o back-end, o back-end usa diretamente a função de validação de schema
SchemaValidator
dolua-rapidjson
para determinar a legitimidade da interface, e não há necessidade de escrever código extra.
Comunicação entre Worker
s
Finalmente, gostaria de falar sobre a biblioteca lua-resty
que permite a comunicação entre workers no OpenResty, onde não há um mecanismo de comunicação direta entre workers, o que causa muitos problemas. Vamos imaginar um cenário:
Um serviço OpenResty tem 24 processos worker, e quando o administrador atualiza uma configuração do sistema através das APIs REST HTTP, apenas um
Worker
recebe a atualização do administrador e escreve o resultado no banco de dados, atualizando oshared dict
e olru cache
dentro de seu próprio Worker. Então, como os outros 23 workers podem ser notificados para atualizar esta configuração?
Um mecanismo de notificação entre múltiplos Worker
s é necessário para realizar a tarefa acima. No caso de o OpenResty não suportar isso, temos que salvar o dia com dados shared dict
entre workers.
lua-resty-worker-events
é uma implementação concreta dessa ideia. Ele mantém um número de versão em um shared dict
, e quando uma nova mensagem é publicada, ele adiciona um ao número de versão e coloca o conteúdo da mensagem no dicionário com o número de versão como key
.
event_id, err = _dict:incr(KEY_LAST_ID, 1)
success, err = _dict:add(KEY_DATA .. tostring(event_id), json)
Além disso, um loop de polling
com um intervalo padrão de 1 segundo é criado em segundo plano usando ngx.timer
para verificar constantemente as mudanças no número de versão:
local event_id, err = get_event_id()
if event_id == _last_event then
return "done"
end
Dessa forma, assim que uma nova notificação de evento for encontrada para ser processada, o conteúdo da mensagem é recuperado do shared dict
com base no número de versão:
while _last_event < event_id do
count = count + 1
_last_event = _last_event + 1
data, err = _dict:get(KEY_DATA..tostring(_last_event))
end
No geral, embora lua-resty-worker-events
tenha um atraso de um segundo, ele ainda implementa um mecanismo de notificação de eventos entre Workers.
No entanto, em alguns cenários em tempo real, como o envio de mensagens, a falta de comunicação direta entre processos Worker
do OpenResty pode causar alguns problemas. Não há uma solução melhor para isso, mas se você tiver boas ideias, sinta-se à vontade para discutir no Github. Muitas funcionalidades do OpenResty são impulsionadas pela comunidade para construir um ciclo ecológico virtuoso.
Resumo
As três bibliotecas que apresentamos hoje são únicas e trazem mais possibilidades para aplicações OpenResty. Finalmente, um tópico interativo: você encontrou alguma biblioteca interessante em torno do OpenResty? Ou o que você descobriu ou se perguntou sobre as bibliotecas mencionadas hoje? Sinta-se à vontade para enviar este artigo para os usuários do OpenResty ao seu redor para trocar e progredir juntos.