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.