Строим сервисы на базе Nginx и Tarantool
Вам знакома такая архитектура? Хоровод демонов, пляшущих между web-server, cache и storage.
Какие минусы такой архитектуры можно отметить? Решая задачи в рамках такой архитектуры, мы сталкиваемся с кучей вопросов: какой язык (и?) взять, какой I/O framework выбрать, как синхронизировать cache и storage? Куча инфраструктурных вопросов. А зачем решать инфраструктурные вопросы, когда надо решить задачу? Безусловно, можно сказать, что нам нравятся некие технологии X и Y, и перевести эти минусы в рамки идеологических. Но нельзя отрицать тот факт, что данные располагаются на неком расстоянии от кода (картинка выше), что добавляет latency, что может уменьшить RPS.
Цель данной статьи — рассказать об альтернативе, которая построена на базе Nginx как web-server, bаlancer и Tarantool как App Server, Cache, Storage.
Улучшаем cache и storage
У Tarantool есть несколько интересных свойств. Tarantool — это не только эффективная inmemory DB, но и полноценный Application Server, приложения пишутся на Lua (luajit), C, C++, т.е. можно написать логику любой сложности, ограничение одно: фантазия. Если данных больше, чем доступно памяти, часть данных можно хранить на диске, используя движок Sophia. Если Sophia не подходит, можно взять что-то другое и скидывать «холодные» данные, т.е. данные, которые не нужны прямо сейчас, из Tarantool в другой Storage, а «горячую» часть хранить в Tarantool, т.е. в памяти. Какие преимущества это дает нам?
- Нет посредников. Как минимум горячая часть данных находится на одном уровне с кодом.
- Горячие данные в памяти.
- Код достаточно простой и легко обновляется, если мы говорим о Lua.
- Транзакции, репликация, шардинг и можество других возможностей Taranool.
Улучшаем web-server
Конечным потребителем данных является пользователь. Обычно, пользователь получает данные от Application Server через Nginx как балансер/прокси. Вариант написания демона, который умеет общаться и с Tarantool, и с HTTP не подходит, так как приведет нас к первому рисунку, и мы опять вернёмся к тому, с чего начали. Поэтому попробуем взглянуть на ситуацию с другой стороны, и задать другой вопрос: «Как избавиться от посредников между данными и пользователем?». Ответом на этот вопрос и стала реализация Tarantool Nginx Upstream Module.
Nginx Upstream
Nginx Upstream — это персистентное (см. Upstream Keepalive) соединение через pipe/socket к backend, далее будем называть это «проксированием». Nginx предоставляет много разнообразного функционала для написания правил Upstream, для проксирования HTTP в Tarantool особое значение приобретают следующие возможности:
- возможность указывать несколько backend, на которые Nginx будет балансировать нагрузку;
- возможность указывать backup, т.е. указывать, куда ходить, если Upstream не работает.
Эти возможности позволяют:
- распределять нагрузку на N Tarantool, например, вкупе с шардингом можно построить кластер с равномерной загрузкой по нодам;
- можно сделать отказоустойчивую систему при помощи репликации;
- используя п. а) и п. b) получим отказоустойчивый кластер.
Пример конфига для Nginx, частично иллюстрирующий возможности настроек:
# Настройки проксирования в Tarantool
upstream tnt
{
server 127.0.0.1:10001; # первый сервер живет на localhost
server node.com:10001; # второй где-то еще
server unix:/tmp/tnt; # третий через unix socket
server node.backup.com backup; # а тут backup
}
# HTTP-сервер
server
{
listen 8081 default;
location = /tnt/pass {
# Говорим Nginx что надо использовать Tarantool Upstream Module
# и указываем имя Upstream
tnt_pass tnt;
}
}
Более детально о конфигурировании Nginx Upstream можно прочитать тут: http://nginx.org/en/docs/http/ngx_http_upstream_module.html#upstream.
Nginx Tarantool Upstream Module (v0.1.4 Stable)
Основной функционал:
- модуль активируется в Nginx.conf директивой — tnt_pass UPSTREAM_NAME;
- быстрое потоковое преобразование HTTP + JSON <-> Tarantool Protocol, минимальные блокировки (на время парсинга) Nginx worker;
- неблокирующее I/O Nginx в оба направления;
- как приятный бонус: все фичи Nginx, Nginx Upstream;
- модуль позволяет вызывать хранимые процедуры Tarantool через JSON-based Protocol;
- данные доставляются через HTTP (S) POST, что удобно для Modern WebApps и не только.
Входные данные
[ { "method": STR, "params":[arg0 ... argN], "id": UINT }, ...N ]
«method»
Имя хранимой процедуры. Имя должно совпадать с именем процедуры в Tarantool. Например, чтобы вызвать lua-функцию do_something(a, b)
, надо: "method”: "do_something”
«params»
Аргументы хранимой процедуры. Например, чтобы передать аргументы в lua-функцию do_something(a, b)
, надо: "params”: [ "1”, 2 ]
«id»
Числовой идентификатор, устанавливается клиентом.
Выходные данные
[ { "result": JSON_RESULT_OBJECT, "id":UINT, "error": { "message": STR, "code": INT } }, ...N ]
«result»
Данные, которые вернула хранимая процедура. Например, lua-функция do_something(a, b)
возвращает return {1, 2}
то "result”: [[1, 2]]
«id»
Числовой идентификатор, установленный клиентом.
«error»
Если произошла ошибка, в этом поле будут данные о причинах.
Более детальней о протоколе тут: https://github.com/tarantool/nginx_upstream_module/blob/master/README.md
Hello World
Запускам Nginx
Nginx мы соберем из исходников:
$ git clone https://github.com/tarantool/nginx_upstream_module.git
$ cd nginx_upstream_module
$ git submodule update --init --recursive
$ git clone https://github.com/nginx/nginx.git
$ cd nginx && git checkout release-1.9.7 && cd -
$ make build-all-debug
Цель build-all-debug — это debug-версия. Делаем так, чтобы меньше конфигурировать Nginx. Для тех, кто хочет законфигурировать все с нуля, есть цель build-all
.
Файл test-root/conf/nginx.conf
http
{
# Добавляет один Tarantool как backend
upstream echo
{
server 127.0.0.1:10001;
}
server
{
listen 8081 default; # Nginx повесим на *:8081
server_name tnt_test;
location = /echo # на *:8081/echo вешаем ‘echo’ Tarantool Upstream
{
tnt_pass echo;
}
}
}
$ ./nginx/obj/nginx # запускаем nginx
Запускаем Tarantool
Tarantool можно поставить из пакетов, либо собрать.
Файл hello-world.lua
-- Это и есть наша хранимая процедура, она предельно простая и не использует Tarantool как DB.
-- Все что она делает - это просто возвращает свой 1-й аргумент.
function echo(a)
return {{a}}
end
box.cfg {
listen = 10001; -- указываем куда вешаем Tarantool
}
Если вы поставили Tarantool из пакетов, запустить его можно так:
$ tarantool hello-world.lua # первым аргументом передаем имя lua-скрипта.
Вызываем хранимую процедуру
Вызвать хранимую процедуру echo можно любым HTTP-коннектором, все что нужно сделать — HTTP POST по 127.0.0.1/echo и в теле передать следующий JSON (см. Входные данные):
{
"method":"echo", // имя метода, должно совпадать с именем метода в Tarantool
"params":[
{"Hello world": "!"} // 1-й аргумент - объект
],
"id":1 // ID сообщения
}
Я вызову эту процедуру wget«ом
$ wget 127.0.0.1:8081/echo --post-data '{"method":"echo","params":[{"Hello world": "!"}],"id":1}'
$ cat echo
{"id":1,"result":[[{"hello world":"!"}]]}
Еще несколько примеров:
https://github.com/tarantool/nginx_upstream_module/blob/master/examples/echo.html
https://github.com/tarantool/nginx_upstream_module/blob/master/test/client.py
Подведем итоги
Плюсы использования Nginx Tarantool Upstream Module:
- нет посредников, код и данные, как правило, на одном уровне;
- относительно простое конфигурирование;
- балансировка нагрузки на N Tarantool;
- высокая скорость работы, низкая latency;
- JSON-based протокол вместо бинарного, не надо искать Tarantool Driver, JSON есть везде;
- Tarantool Sharding/Replication и Nginx = кластерное решение, но это тема отдельной статьи;
- решение используется в продакшене.
Минусы:
- Overhead JSON вместо более компактного и быстрого MspPack;
- решение не коробочное, нужно конфигурировать, нужно думать, как деплоить.
Планы:
- поддержка OpenRasty и nginScript;
- поддержка WebSocket и HTTP 2.0.
Результаты бенчмарка, а они очень даже интересные, будут в другой статье. Tarantool, как и Upstream Module, всегда открыт для новых пользователей, если у вас есть желание это все попробовать, использовать или выразить новую идею — обращайтесь на github, google group.
Ссылки
Сайт Tarantool — http://tarantool.org
Git Tarantool — https://github.com/tarantool/tarantool
Git Tarantool Nginx Upstream Module — github.com/tarantool/nginx_upstream_module
Google group — https://groups.google.com/forum/#! forum/tarantool
P.S. В следующей статье я покажу какие задачи можно решить, используя Tarantool.