Нативные модули
| Обзор Когда использовать нативные модули Загрузка нативных модулей в NGINX Сборка нативных модулей Дополнительные ресурсы |
Обзор
Нативные модули позволяют загружать разделяемые библиотеки на основе C (файлы .so) в NGINX JavaScript для выполнения критичных по производительности операций или системных интеграций. Данная возможность доступна только с движком QuickJS и не поддерживается встроенным движком njs. Поддержка нативных модулей доступна начиная с версии 0.9.5.
Когда использовать нативные модули
Нативные модули могут использоваться в следующих случаях:
- Критичные по производительности операции, превышающие возможности JavaScript
- Системные интеграции, требующие использования библиотек на C
- Задействование существующих кодовых баз на C/C++
Нативные модули следует использовать для низкоуровневых примитивов, а не для сложной бизнес-логики, например для криптографических операций (хеширование, шифрование), сжатия/распаковки данных, разбора бинарных протоколов, высокопроизводительных строковых операций или математических вычислений. Сложная логика приложения должна оставаться в JavaScript, где её легче сопровождать, отлаживать и изменять.
Ограничения:
- Нативные модули должны быть скомпилированы для той же архитектуры, что и NGINX
- Нативные модули работают с полными привилегиями процесса и требуют тщательной проверки безопасности
Загрузка нативных модулей в NGINX
Нативные модули загружаются с помощью следующих директив,
указанных в контексте main:
- js_load_http_native_module для контекста HTTP
- js_load_stream_native_module для контекста Stream
Пример конфигурации:
js_load_http_native_module /path/to/mylib.so;
js_load_http_native_module /path/to/other.so as myalias;
http {
js_import main.js;
server {
listen 8000;
location / {
js_content main.handler;
}
}
}
После загрузки модуль можно импортировать в JavaScript-коде:
// Импорт по имени файла
import * as mylib from 'mylib.so';
// Импорт по псевдониму
import * as myalias from 'myalias';
function handler(r) {
let result = mylib.add(5, 10);
r.return(200, `Result: ${result}\n`);
}
export default { handler };
Сборка нативных модулей
Нативные модули должны реализовывать функцию js_init_module
в качестве точки входа.
Эта функция вызывается QuickJS при загрузке модуля.
Пример простого нативного модуля, экспортирующего две функции:
#include <quickjs.h>
#define countof(x) (sizeof(x) / sizeof((x)[0]))
/* Сложение двух чисел */
static JSValue
js_add(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{
int a, b;
if (argc < 2) {
return JS_ThrowTypeError(ctx, "expected 2 arguments");
}
if (JS_ToInt32(ctx, &a, argv[0]) < 0) {
return JS_EXCEPTION;
}
if (JS_ToInt32(ctx, &b, argv[1]) < 0) {
return JS_EXCEPTION;
}
return JS_NewInt32(ctx, a + b);
}
/* Переворот строки */
static JSValue
js_reverse_string(JSContext *ctx, JSValueConst this_val, int argc,
JSValueConst *argv)
{
char *reversed;
size_t i, len;
JSValue result;
const char *str;
if (argc < 1) {
return JS_ThrowTypeError(ctx, "expected 1 argument");
}
str = JS_ToCStringLen(ctx, &len, argv[0]);
if (!str) {
return JS_EXCEPTION;
}
reversed = js_malloc(ctx, len + 1);
if (!reversed) {
JS_FreeCString(ctx, str);
return JS_EXCEPTION;
}
for (i = 0; i < len; i++) {
reversed[i] = str[len - 1 - i];
}
reversed[len] = '\0';
result = JS_NewString(ctx, reversed);
js_free(ctx, reversed);
JS_FreeCString(ctx, str);
return result;
}
/* Список функций модуля */
static const JSCFunctionListEntry js_module_funcs[] = {
JS_CFUNC_DEF("add", 2, js_add),
JS_CFUNC_DEF("reverseString", 1, js_reverse_string),
};
/* Инициализация модуля */
static int
js_module_init(JSContext *ctx, JSModuleDef *m)
{
return JS_SetModuleExportList(ctx, m, js_module_funcs,
countof(js_module_funcs));
}
/* Обязательная точка входа */
JSModuleDef *
js_init_module(JSContext *ctx, const char *module_name)
{
JSModuleDef *m;
m = JS_NewCModule(ctx, module_name, js_module_init);
if (!m) {
return NULL;
}
JS_AddModuleExportList(ctx, m, js_module_funcs,
countof(js_module_funcs));
return m;
}
Для компиляции нативного модуля:
gcc -fPIC -shared -I/path/to/quickjs -o mymodule.so mymodule.c
где /path/to/quickjs — каталог,
содержащий заголовочные файлы QuickJS.
Для корректного учёта памяти всегда используйте функции выделения памяти QuickJS (js_malloc,js_free) вместо функций стандартной библиотеки (malloc,free).
Дополнительные ресурсы
Для получения дополнительной информации о C API QuickJS:
- Официальный сайт QuickJS
-
Заголовочный файл QuickJS (
quickjs.h) содержит полную документацию по API