---
title: "Введение в общие API в OpenResty - API7.ai"
description: "Эта статья познакомит вас с использованием общих API OpenResty для работы со временем, регулярными выражениями и другими распространёнными задачами."
url: "https://api7.ai/ru/learning-center/openresty/introduction-openresty-common-apis"
---

# Введение в общие API в OpenResty

API7.ai

November 4, 2022

[OpenResty (NGINX + Lua)](https://api7.ai/learning-center/openresty)

В предыдущих статьях вы познакомились с множеством важных Lua API в OpenResty. Сегодня мы изучим некоторые другие общие API, в основном связанные с регулярными выражениями, временем, процессами и т.д.

## API, связанные с регулярными выражениями

Начнем с рассмотрения наиболее часто используемых и важных регулярных выражений. В OpenResty мы должны использовать набор API, предоставляемый `ngx.re.*`, для обработки логики, связанной с регулярными выражениями, вместо использования Lua pattern matching. Это связано не только с вопросами производительности, но и с тем, что регулярные выражения Lua являются самодостаточными и не соответствуют спецификации `PCRE`, что может быть неудобным для большинства разработчиков.

В предыдущих статьях вы уже сталкивались с некоторыми API из `ngx.re.*`, документация к которым очень подробна. Поэтому я не буду их перечислять. Здесь я отдельно представлю два следующих API.

### `ngx.re.split`

Первым является `ngx.re.split`. Разделение строк — это очень распространенная функция, и OpenResty также предоставляет соответствующий API, но многие разработчики не могут найти такую функцию и вынуждены реализовывать её самостоятельно.

Почему? API `ngx.re.split` находится не в `lua-nginx-module`, а в `lua-resty-core`; он не в документации на главной странице `lua-resty-core`, а в документации в третьем уровне каталога `lua-resty-core/lib/ngx/re.md`. В результате многие разработчики совершенно не знают о существовании этого API.

Аналогично, API, которые трудно обнаружить, включают `ngx_resp.add_header`, `enable_privileged_agent` и другие, о которых мы упоминали ранее. Как же быстро решить эту проблему? Помимо чтения документации на главной странице `lua-resty-core`, вам также нужно прочитать документацию в каталоге `lua-resty-core/lib/ngx/` в файлах `*.md`.

### `lua_regex_match_limit`

Во-вторых, я хочу представить `lua_regex_match_limit`. Ранее мы не говорили о командах NGINX, предоставляемых OpenResty, потому что в большинстве случаев значений по умолчанию достаточно, и нет необходимости изменять их во время выполнения. Исключением является команда `lua_regex_match_limit`, которая связана с регулярными выражениями.

Мы знаем, что если мы используем регулярный движок, реализованный на основе backtracking NFA, то существует риск катастрофического бэктрекинга (Catastrophic Backtracking), когда регулярное выражение слишком много раз возвращается при сопоставлении, вызывая 100% загрузку CPU и блокировку сервисов.

Как только происходит катастрофический бэктрекинг, нам нужно использовать `gdb` для анализа дампа или `systemtap` для анализа онлайн-среды, чтобы локализовать проблему. К сожалению, заранее обнаружить её нелегко, потому что только специальные запросы могут её вызвать. Это позволяет злоумышленникам воспользоваться этим, и `ReDoS` (RegEx Denial of Service) относится к этому типу атак.

Здесь я в основном расскажу вам, как использовать следующую строку кода в OpenResty, чтобы просто и эффективно избежать вышеуказанных проблем:

`lua_regex_match_limit` используется для ограничения количества бэктрекинга движком регулярных выражений `PCRE`. Таким образом, даже если произойдет катастрофический бэктрекинг, последствия будут ограничены диапазоном, который не приведет к полной загрузке вашего CPU.

```
lua_regex_match_limit 100000;
```

## API, связанные с временем

Наиболее часто используемым API для работы со временем является `ngx.now`, который выводит текущую временную метку, например, следующая строка кода:

```
resty -e 'ngx.say(ngx.now())'
```

Как видно из результатов вывода, `ngx.now` включает дробную часть, поэтому он более точен. Связанный API `ngx.time` возвращает только целую часть значения. Другие, такие как `ngx.localtime`, `ngx.utctime`, `ngx.cookie_time` и `ngx.http_time`, в основном используются для возврата и обработки времени в разных форматах. Если вы хотите их использовать, вы можете проверить документацию, они не сложны для понимания, поэтому мне не нужно о них рассказывать.

Однако стоит упомянуть, что **эти API, возвращающие текущее время, если они не вызваны операцией неблокирующего сетевого ввода-вывода, всегда будут возвращать кэшированное значение, а не текущее реальное время, как мы бы хотели**. Взгляните на следующий пример кода:

```
$ resty -e 'ngx.say(ngx.now())
os.execute("sleep 1")
ngx.say(ngx.now())'
```

Между двумя вызовами `ngx.now` мы использовали блокирующую функцию Lua для сна на `1` секунду, но временная метка, возвращаемая в обоих случаях, одинакова, как показано в результатах вывода.

Что, если мы заменим её на неблокирующую функцию сна? Например, следующий новый код:

```
$ resty -e 'ngx.say(ngx.now())
ngx.sleep(1)
ngx.say(ngx.now())'
```

Он выведет другую временную метку. Это приводит нас к `ngx.sleep`, неблокирующей функции сна. Помимо сна на указанное время, эта функция имеет еще одно специальное назначение.

Например, если у вас есть фрагмент кода, который выполняет интенсивные вычисления, занимающие много времени, запросы, соответствующие этому фрагменту кода, будут продолжать занимать ресурсы воркера и CPU в течение этого времени, вызывая очередь других запросов и не получая своевременного ответа. В этот момент мы можем вставить `ngx.sleep(0)`, чтобы этот код отдал управление, и другие запросы также могли быть обработаны.

## API для воркеров и процессов

OpenResty предоставляет API `ngx.worker.*` и `ngx.process.*` для получения информации о воркерах и процессах. Первый относится к рабочим процессам Nginx, а второй относится ко всем процессам Nginx в целом, включая не только рабочие процессы, но и мастер-процесс, привилегированный процесс и т.д.

## Проблема значений `true` и `null`

Наконец, рассмотрим проблему значений `true` и `null`. В OpenResty определение значений `true` и `null` всегда было очень сложным и запутанным моментом.

Давайте посмотрим на определение значения `true` в Lua: за исключением `nil` и `false`, все остальные значения являются `true`.

Таким образом, значения `true` также включают `0`, пустую строку, пустую таблицу и т.д.

Теперь рассмотрим `nil` в Lua, что означает `неопределенное`. Например, если вы объявляете переменную, но не инициализируете её, её значение будет `nil`.

```
$ resty -e 'local a
ngx.say(type(a))'
```

И `nil` также является типом данных в Lua. Поняв эти два момента, давайте теперь рассмотрим другие проблемы, вытекающие из этих определений.

### `ngx.null`

Первая проблема — это `ngx.null`. Поскольку `nil` в Lua не может быть значением таблицы, OpenResty вводит `ngx.null` как значение `null` в таблице.

```
$ resty -e  'print(ngx.null)'
null
```

```
$ resty -e 'print(type(ngx.null))'
userdata
```

Как видно из двух приведенных выше фрагментов кода, `ngx.null` выводится как `null`, и его тип — `userdata`, так можно ли его рассматривать как значение `false`? Конечно, нет. Логическое значение `ngx.null` — `true`.

```
$ resty -e 'if ngx.null then
ngx.say("true")
end'
```

Поэтому помните, что только `nil` и `false` являются значениями `false`. Если вы упустите этот момент, легко попасть в ловушку, например, когда вы используете `lua-resty-redis` и делаете следующее условие:

```
local res, err = red:get("dog")
if not res then
    res = res + "test"
end
```

Если возвращаемое значение `res` — `nil`, вызов функции завершился неудачей; если `res` — `ngx.null`, ключ `dog` не существует в redis, тогда код завершится с ошибкой, если ключ `dog` не существует.

### `cdata:NULL`

Вторая проблема — это `cdata:NULL`. Когда вы вызываете C-функцию через интерфейс LuaJIT FFI, и функция возвращает указатель `NULL`, то вы столкнетесь с другим типом значения `null`, `cdata:NULL`.

```
$ resty -e 'local ffi = require "ffi"
local cdata_null = ffi.new("void*", nil)
if cdata_null then
    ngx.say("true")
end'
```

Как и `ngx.null`, `cdata:NULL` также является `true`. Но что еще более запутывает, так это то, что следующий код, который выводит `true`, означает, что `cdata:NULL` эквивалентен `nil`.

```
$ resty -e 'local ffi = require "ffi"
local cdata_null = ffi.new("void*", nil)
ngx.say(cdata_null == nil)'
```

Так как же нам обрабатывать `ngx.null` и `cdata:NULL`? Не самое лучшее решение заставлять уровень приложения заботиться об этих проблемах. Лучше сделать вторичную обертку и не давать вызывающей стороне знать об этих деталях.

### `cjson.null`

Наконец, рассмотрим значения `null`, которые появляются в `cjson`. Библиотека `cjson` берет `NULL` в json, декодирует его в Lua `lightuserdata` и использует `cjson.null` для представления.

```
$ resty -e 'local cjson = require "cjson"
local data = cjson.encode(nil)
local decode_null = cjson.decode(data)
ngx.say(decode_null == cjson.null)'
```

Lua `nil` становится `cjson.null` после кодирования и декодирования JSON. Как вы можете догадаться, он введен по той же причине, что и `ngx.null`, потому что `nil` не может быть значением в таблице.

До сих пор вы были сбиты с толку таким количеством типов значений `null` в OpenResty? Не беспокойтесь. Прочитайте эту часть несколько раз и разберитесь самостоятельно, тогда вы не будете путаться. Конечно, в будущем нам нужно больше думать о том, работает ли что-то вроде `if not foo then`.

## Итог

Сегодняшняя статья познакомила вас с Lua API, часто используемыми в OpenResty.

Наконец, я оставлю вам вопрос: в примере с `ngx.now`, почему значение `ngx.now` не изменяется, когда нет операции `yield`? Поделитесь своим мнением в комментариях, а также поделитесь этой статьей, чтобы мы могли общаться и улучшать её вместе.

Share article link

Share

Related Posts

[![OpenResty FAQ | Как OpenResty используется на практике](https://static.api7.ai/uploads/2023/02/12/jbv9M0zi_Hello%20World%20in%20OpenResty%20%283%29.png)](https://api7.ai/learning-center/openresty/faq-5)

## [OpenResty FAQ | Как OpenResty используется на практике](https://api7.ai/learning-center/openresty/faq-5)

[![Часть 3: Как создать API-шлюз для микросервисов с использованием OpenResty](https://static.api7.ai/uploads/2023/02/07/3XJXY9Jy_45.png)](https://api7.ai/learning-center/openresty/how-to-build-a-microservices-api-gateway-part-3)

## [Часть 3: Как создать API-шлюз для микросервисов с использованием OpenResty](https://api7.ai/learning-center/openresty/how-to-build-a-microservices-api-gateway-part-3)

Table of Contents

- [API, связанные с регулярными выражениями](#api-%D1%81%D0%B2%D1%8F%D0%B7%D0%B0%D0%BD%D0%BD%D1%8B%D0%B5-%D1%81-%D1%80%D0%B5%D0%B3%D1%83%D0%BB%D1%8F%D1%80%D0%BD%D1%8B%D0%BC%D0%B8-%D0%B2%D1%8B%D1%80%D0%B0%D0%B6%D0%B5%D0%BD%D0%B8%D1%8F%D0%BC%D0%B8)

  - [`ngx.re.split`](#ngxresplit)
  - [`lua_regex_match_limit`](#lua_regex_match_limit)

- [API, связанные с временем](#api-%D1%81%D0%B2%D1%8F%D0%B7%D0%B0%D0%BD%D0%BD%D1%8B%D0%B5-%D1%81-%D0%B2%D1%80%D0%B5%D0%BC%D0%B5%D0%BD%D0%B5%D0%BC)

- [API для воркеров и процессов](#api-%D0%B4%D0%BB%D1%8F-%D0%B2%D0%BE%D1%80%D0%BA%D0%B5%D1%80%D0%BE%D0%B2-%D0%B8-%D0%BF%D1%80%D0%BE%D1%86%D0%B5%D1%81%D1%81%D0%BE%D0%B2)

- [Проблема значений `true` и `null`](#%D0%BF%D1%80%D0%BE%D0%B1%D0%BB%D0%B5%D0%BC%D0%B0-%D0%B7%D0%BD%D0%B0%D1%87%D0%B5%D0%BD%D0%B8%D0%B9-true-%D0%B8-null)

  - [`ngx.null`](#ngxnull)
  - [`cdata:NULL`](#cdatanull)
  - [`cjson.null`](#cjsonnull)

- [Итог](#%D0%B8%D1%82%D0%BE%D0%B3)
