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
SchemaValidatordolua-rapidjsonpara determinar a legitimidade da interface, e não há necessidade de escrever código extra.
Comunicação entre Workers
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
Workerrecebe a atualização do administrador e escreve o resultado no banco de dados, atualizando oshared dicte olru cachedentro 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 Workers é 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.