Строим сервисы на базе Nginx и Tarantool

Вам знакома такая архитектура? Хоровод демонов, пляшущих между web-server, cache и storage.

d7c36bafbc3e49e3a63058ad1b608caa.png

Какие минусы такой архитектуры можно отметить? Решая задачи в рамках такой архитектуры, мы сталкиваемся с кучей вопросов: какой язык (и?) взять, какой I/O framework выбрать, как синхронизировать cache и storage? Куча инфраструктурных вопросов. А зачем решать инфраструктурные вопросы, когда надо решить задачу? Безусловно, можно сказать, что нам нравятся некие технологии X и Y, и перевести эти минусы в рамки идеологических. Но нельзя отрицать тот факт, что данные располагаются на неком расстоянии от кода (картинка выше), что добавляет latency, что может уменьшить RPS.

Цель данной статьи — рассказать об альтернативе, которая построена на базе Nginx как web-server, bаlancer и Tarantool как App Server, Cache, Storage.

Улучшаем cache и storage


0a7dcdf8f14041b1b733ba142e006272.png

У Tarantool есть несколько интересных свойств. Tarantool — это не только эффективная inmemory DB, но и полноценный Application Server, приложения пишутся на Lua (luajit), C, C++, т.е. можно написать логику любой сложности, ограничение одно: фантазия. Если данных больше, чем доступно памяти, часть данных можно хранить на диске, используя движок Sophia. Если Sophia не подходит, можно взять что-то другое и скидывать «холодные» данные, т.е. данные, которые не нужны прямо сейчас, из Tarantool в другой Storage, а «горячую» часть хранить в Tarantool, т.е. в памяти. Какие преимущества это дает нам?

  • Нет посредников. Как минимум горячая часть данных находится на одном уровне с кодом.
  • Горячие данные в памяти.
  • Код достаточно простой и легко обновляется, если мы говорим о Lua.
  • Транзакции, репликация, шардинг и можество других возможностей Taranool.

Улучшаем web-server


55574a4cf95e488092f58646e6003820.png

Конечным потребителем данных является пользователь. Обычно, пользователь получает данные от Application Server через Nginx как балансер/прокси. Вариант написания демона, который умеет общаться и с Tarantool, и с HTTP не подходит, так как приведет нас к первому рисунку, и мы опять вернёмся к тому, с чего начали. Поэтому попробуем взглянуть на ситуацию с другой стороны, и задать другой вопрос: «Как избавиться от посредников между данными и пользователем?». Ответом на этот вопрос и стала реализация Tarantool Nginx Upstream Module.

Nginx Upstream


Nginx Upstream — это персистентное (см. Upstream Keepalive) соединение через pipe/socket к backend, далее будем называть это «проксированием». Nginx предоставляет много разнообразного функционала для написания правил Upstream, для проксирования HTTP в Tarantool особое значение приобретают следующие возможности:

  1. возможность указывать несколько backend, на которые Nginx будет балансировать нагрузку;
  2. возможность указывать backup, т.е. указывать, куда ходить, если Upstream не работает.


Эти возможности позволяют:

  1. распределять нагрузку на N Tarantool, например, вкупе с шардингом можно построить кластер с равномерной загрузкой по нодам;
  2. можно сделать отказоустойчивую систему при помощи репликации;
  3. используя п. а) и п. 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)


9a78092400a14edc82e532310c2ff27c.png

Основной функционал:

  • модуль активируется в 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.

© Habrahabr.ru