BLOG | NGINX

Aproveitando o poder e a conveniência do JavaScript para cada solicitação com o módulo JavaScript NGINX

NGINX-Parte-de-F5-horiz-preto-tipo-RGB
Miniatura de Liam Crilly
Liam Crilly
Publicado em 23 de abril de 2021

Editor – A postagem do blog intitulada “Introdução ao módulo JavaScript NGINX” redireciona para cá. A postagem foi atualizada com as diretivas e recursos do módulo JavaScript NGINX suportados desde abril de 2021.

O módulo NGINX JavaScript (njs) tornou-se geralmente disponível como um módulo estável no NGINX Open Source 1.11.10 e NGINX Plus R12 . [O módulo era originalmente chamado de nginScript, e esse nome aparece em algumas postagens mais antigas.] Temos trabalhado constantemente no NGINX JavaScript desde seu lançamento em setembro de 2015 , adicionando os recursos e o suporte a idiomas incluídos no módulo estável.

O NGINX JavaScript é uma implementação JavaScript exclusiva para NGINX e NGINX Plus, projetada especificamente para casos de uso do lado do servidor e processamento por solicitação. Ele estende a sintaxe de configuração do NGINX com código JavaScript para implementar soluções de configuração sofisticadas.

Os casos de uso são extensos, especialmente porque o módulo NGINX JavaScript está disponível para protocolos HTTP e TCP/UDP. Exemplos de casos de uso para NGINX JavaScript incluem:

Antes de discutir o NGINX JavaScript com mais detalhes, vamos primeiro abordar dois equívocos comuns.

NGINX JavaScript não é Lua

A comunidade NGINX criou diversas extensões programáticas ao longo dos anos. No momento em que este artigo foi escrito, Lua era o mais popular deles; ele estava disponível como um módulo para NGINX e um módulo dinâmico de terceiros pré-criado e suportado para NGINX Plus. O módulo Lua e as bibliotecas complementares fornecem integração profunda com o núcleo do NGINX e um rico conjunto de funcionalidades, incluindo um driver para Redis.

Lua é uma linguagem de script poderosa. No entanto, ele continua sendo um nicho relativamente restrito em termos de adoção e normalmente não é encontrado na “caixa de ferramentas de habilidades” do desenvolvedor front-end ou engenheiro DevOps.

O NGINX JavaScript não busca substituir o Lua e levará algum tempo até que o NGINX JavaScript tenha um nível comparável de funcionalidade. O objetivo do NGINX JavaScript é fornecer soluções de configuração programática para a maior comunidade possível usando uma linguagem de programação popular.

NGINX JavaScript não é Node.js

O NGINX JavaScript não tem como objetivo transformar o NGINX ou o NGINX Plus em um servidor de aplicativos. Em termos simples, os casos de uso do NGINX JavaScript são semelhantes aos do middleware, pois a execução do código JavaScript acontece entre o cliente e o conteúdo. Tecnicamente falando, embora o Node.js compartilhe duas coisas com a combinação do NGINX JavaScript e do NGINX ou NGINX Plus – uma arquitetura orientada a eventos e a linguagem de programação JavaScript – as semelhanças terminam aí.

O Node.js usa o mecanismo JavaScript V8 do Google, enquanto o NGINX JavaScript é uma implementação personalizada dos padrões ECMAScript, projetada especificamente para NGINX e NGINX Plus. O Node.js tem uma máquina virtual (VM) JavaScript persistente na memória e executa a coleta de lixo de rotina para gerenciamento de memória, enquanto o NGINX JavaScript inicializa uma nova VM JavaScript e a memória necessária para cada solicitação e libera a memória quando a solicitação é concluída.

JavaScript como uma linguagem do lado do servidor

Como mencionado acima, o NGINX JavaScript é uma implementação personalizada da linguagem JavaScript. Todos os outros mecanismos de tempo de execução JavaScript existentes são projetados para serem executados em um navegador da web. A natureza da execução de código do lado do cliente é diferente da execução de código do lado do servidor em muitos aspectos – desde a disponibilidade de recursos do sistema até o possível número de tempos de execução simultâneos.

Decidimos implementar nosso próprio tempo de execução JavaScript para atender aos requisitos de execução de código do lado do servidor e se encaixar elegantemente na arquitetura de processamento de solicitações do NGINX. Nossos princípios de design para NGINX JavaScript são estes:

  • O ambiente de execução vive e morre com a solicitação

    O módulo NGINX JavaScript usa execução de bytecode de thread único, projetado para inicialização e descarte rápidos. O ambiente de execução é inicializado por solicitação. A inicialização é extremamente rápida, pois não há estados complexos ou auxiliares para inicializar. A memória é acumulada em pools durante a execução e liberada na conclusão, liberando os pools. Este esquema de gerenciamento de memória elimina a necessidade de rastrear e liberar objetos individuais ou de usar um coletor de lixo.

  • Execução de código não bloqueante

    O modelo orientado a eventos do NGINX e do NGINX Plus agenda a execução de ambientes de tempo de execução JavaScript NGINX individuais. Quando uma regra JavaScript do NGINX executa uma operação de bloqueio (como ler dados de rede ou emitir uma sub-solicitação externa), o NGINX e o NGINX Plus suspendem de forma transparente a execução da VM JavaScript do NGINX associada e a reprogramam quando o evento é concluído. Isso significa que você pode escrever regras de forma simples e linear, e o NGINX e o NGINX Plus podem agendá-las sem bloqueio interno.

  • Implementar apenas o suporte de linguagem que precisamos

    As especificações para JavaScript são definidas pelos padrões ECMAScript . O NGINX JavaScript segue o ECMAScript 5.1 com algum ECMAScript 6 para funções matemáticas. Implementar nosso próprio tempo de execução JavaScript nos dá a liberdade de priorizar o suporte à linguagem para casos de uso do lado do servidor e ignorar o que não precisamos. Mantemos uma lista dos elementos de linguagem atualmente suportados .

  • Integração estreita com as fases de processamento de solicitações

    O NGINX e o NGINX Plus processam solicitações em fases distintas. As diretivas de configuração normalmente operam em uma fase específica e os módulos NGINX nativos geralmente aproveitam a capacidade de inspecionar ou modificar uma solicitação em uma fase específica. O NGINX JavaScript expõe algumas das fases de processamento por meio de diretivas de configuração para dar controle sobre quando o código JavaScript é executado. Essa integração com a sintaxe de configuração promete o poder e a flexibilidade dos módulos NGINX nativos com a simplicidade do código JavaScript.

    A tabela abaixo indica quais fases de processamento são acessíveis via NGINX JavaScript no momento da escrita e as diretivas de configuração que as fornecem.

    Fase de Processamento Módulo HTTP Módulo de fluxo
    Acesso – Autenticação e controle de acesso auth_request e js_content js_acesso
    Pré-leitura – Carga útil de leitura/gravação N/D js_preread
    Filtro – Resposta de leitura/gravação durante proxy js_body_filter
    js_header_filter
    js_filtro
    Conteúdo – Enviar resposta ao cliente js_conteúdo N/D
    Log / Variáveis – Avaliado sob demanda js_conjunto js_conjunto

Introdução ao NGINX JavaScript – Um exemplo do mundo real

O NGINX JavaScript é implementado como um módulo que você pode compilar em um binário NGINX Open Source ou carregar dinamicamente no NGINX ou NGINX Plus. As instruções para habilitar o JavaScript do NGINX com o NGINX e o NGINX Plus aparecem no final deste artigo.

Neste exemplo, usamos NGINX ou NGINX Plus como um proxy reverso simples e usamos NGINX JavaScript para construir entradas de log de acesso em um formato especializado, que:

  • Inclui os cabeçalhos de solicitação enviados pelo cliente
  • Inclui os cabeçalhos de resposta retornados pelo backend
  • Usa pares de chave-valor para ingestão eficiente e pesquisa com ferramentas de processamento de log, como ELK Stack (agora chamado Elastic Stack), Graylog e Splunk

A configuração do NGINX para este exemplo é extremamente simples.

 

Como você pode ver, o código JavaScript do NGINX não está alinhado com a sintaxe de configuração. Em vez disso, usamos a diretiva js_import para especificar o arquivo que contém todo o nosso código JavaScript. A diretiva js_set define uma nova variável NGINX, $access_log_headers , e a função JavaScript que a preenche. A diretiva log_format define um novo formato chamado kvpairs que grava cada linha de log com o valor de $access_log_headers .

O bloco do servidor define um proxy reverso HTTP simples que encaminha todas as solicitações para https://www.example.com . A diretiva access_log especifica que todas as solicitações serão registradas com o formato kvpairs .

Vamos agora dar uma olhada no código JavaScript que prepara uma entrada de log.

 

O valor de retorno da função kvAccess – uma entrada de log – é passado para a diretiva de configuração js_set em rawheader_logging.conf . Tenha em mente que as variáveis NGINX são avaliadas sob demanda e isso significa que a função JavaScript definida por js_set é executada quando o valor da variável é necessário. Neste exemplo, $access_log_headers é usado na diretiva log_format e, portanto, kvAccess() é executado no momento do log. Variáveis usadas como parte de diretivas de mapeamento ou reescrita (não ilustradas neste exemplo) acionam a execução correspondente do JavaScript em uma fase de processamento anterior.

Podemos ver essa solução de registro aprimorada por JavaScript do NGINX em ação passando uma solicitação por meio do nosso proxy reverso e observando a entrada do arquivo de registro resultante, que inclui cabeçalhos de solicitação com o prefixo in. e cabeçalhos de resposta com o prefixo out .

$ curl http://127.0.0.1/ $ tail --lines=1 /var/log/nginx/access_headers.log 2021-04-23T10:08:15+00:00 cliente=172.17.0.1 método=GET uri=/index.html status=200 in.Host=localhost:55081 in.User-Agent=curl/7.64.1 in.Accept=*/* out.Content-Type=text/html out.Content-Length=612 out.ETag=\x22606339ef-264\x22 out.Accept-Ranges=bytes

Grande parte da utilidade do NGINX JavaScript é resultado de seu acesso aos componentes internos do NGINX. Este exemplo utiliza diversas propriedades do objeto request( r ) . O módulo Stream NGINX JavaScript (para aplicativos TCP e UDP) utiliza um ou mais objetos de sessão com seu próprio conjunto de propriedades. Para outros exemplos de soluções NGINX JavaScript para HTTP e TCP/UDP, consulte Casos de uso para o módulo NGINX JavaScript .

Gostaríamos de saber sobre os casos de uso que você criou para o NGINX JavaScript – conte-nos sobre eles na seção de comentários abaixo.


Casos de uso para o módulo JavaScript NGINX

Confira estas postagens de blog para explorar outros casos de uso de HTTP e TCP/UDP para o módulo JavaScript NGINX:

 


[nome do snippet ngx=’njs-enable-instructions’]


"Esta postagem do blog pode fazer referência a produtos que não estão mais disponíveis e/ou não têm mais suporte. Para obter as informações mais atualizadas sobre os produtos e soluções F5 NGINX disponíveis, explore nossa família de produtos NGINX . O NGINX agora faz parte do F5. Todos os links anteriores do NGINX.com redirecionarão para conteúdo semelhante do NGINX no F5.com."