image

В последние годы Tarantool приобрёл широкое распространение, так что давайте разберёмся, что это и как начать с ним работать.

Что такое Tarantool?


Tarantool позиционируется как быстрая резидентная база данных. Её можно использовать для работы с данными любого типа. Кроме того, её можно копировать и сегментировать, распределяя огромные объёмы данных по нескольким серверам и сочетая результаты, полученные от них, для создания отказоустойчивых кластеров типа «ведущий-ведущий».

Во вторую очередь Tarantool — сервер приложений. С его помощью можно создавать собственные приложения и обрабатывать данные: например, в фоновом режиме удалять из базы данных старые записи по заданным условиям. Можно даже написать HTTP-сервер непосредственно в Tarantool и обрабатывать данные на нём: вывести количество записей в базе данных, ввести новую информацию и сократить её до исходной записи (как в MapReduce).

Я читал, как ребята из Mail.ru создали очередь сообщений, показывающую превосходную скорость обработки в 20 и более тысяч сообщений в секунду, хотя кода в ней всего 300 строк. Здесь действительно есть где развернуться любителям широкомасштабных проектов, и речь не идёт о написании хранимых процедур, как в PostgreSQL.

В этой статье описывается схожий, но более простой сервер.

Установка


Для этого теста я создал три стандартных виртуальных машины с жёстким диском на 20 ГБ и ОС Ubuntu 18.04: по два виртуальных центральных процессора и по 4 ГБ оперативной памяти на каждой.

Давайте установим Tarantool, выполнив скрипт в командной оболочке Bash или добавив репозиторий и выполнив команду apt-get install Tarantool. Вот ссылка на скрипт: (curl -L <https://tarantool.io/installer.sh> | VER=2.4 sudo -E bash). По завершении установки у нас будет:

tarantoolctl — основной инструмент для управления экземплярами Tarantool;
/etc/tarantool — директория для хранения всей конфигурации;
/var/log/tarantool — директория для хранения журналов;
/var/lib/tarantool— директория для хранения данных, которые распределяются между экземплярами.

Ещё есть директория с экземплярами формата instances.available или instances.enabled, в которой хранится файл конфигурации экземпляров. Этот файл на языке Lua описывает, с какими портами взаимодействует экземпляр, какую память использует, какие настройки движка vinyl, какой код выполняется при запуске сервера, а также параметры сегментирования, очередей, удаления устаревших данных и так далее.

Экземпляры работают так же, как и в PostgreSQL. Скажем, вам нужно запустить несколько копий базы данных, которые слушают разные порты. Значит, вам понадобится несколько экземпляров базы данных, которые расположены на одном сервере, но используют различные порты. Конфигурация экземпляров может варьироваться как угодно, и они могут использовать совершенно разную логику.

Управление экземплярами


Для управления экземплярами Tarantool у нас есть команда tarantoolctl. Если выполнить её в виде tarantoolctl check example, она проверит файл конфигурации на предмет ошибок синтаксиса.

Статус экземпляра можно проверить, выполнив tarantoolctl status example. Аналогичным образом используются команды start, stop и restart.

Когда экземпляр активен, есть два способа получить к нему доступ:

1. Консоль администрирования


По умолчанию Tarantool открывает сокет, через который происходит обмен с экземпляром Tarantool простым текстом ASCII. Подключение к консоли администрирования всегда выполняется от имени пользователя admin. Авторизация не предусмотрена, так что настоятельно рекомендуется использовать этот метод с осторожностью.

Выполните команду tarantoolctl enter <имя
Экземпляра>
для подключения к требуемому экземпляру. Эта команда запускает консоль и подключается к ней от имени администратора. Никогда не публикуйте порт консоли в сети. Лучше оставляйте его в виде сокета домена Unix, чтобы к Tarantool могли подключаться только пользователи с правами создания сокетов.

Этот метод предназначен для решения задач администрирования. Для обработки данных используется второй способ подключения — по бинарному протоколу.

2. Бинарный протокол для подключения к заданному порту


Параметр конфигурации listen (модуль box) позволяет открыть порт для внешнего обмена и использовать его по бинарному протоколу, в котором проверка подлинности пользователя обязательна.

Для подключения выполните команду tarantoolctl connect port_number. Бинарный протокол позволяет подключаться к удалённым серверам, использовать авторизацию пользователей и устанавливать права доступа.

Модуль box и запись данных


Поскольку Tarantool одновременно и база данных, и сервер приложений, в нём есть различные модули. Сейчас рассмотрим модуль box, который отвечает за работу с данными. При записи данных в box, Tarantool заносит их на диск, сохраняет в память или обрабатывает иным образом.

Запись данных


Давайте зайдём в модуль box и вызовем функцию box.once, чтобы Tarantool выполнил наш код при инициализации сервера. Для начала создадим место для хранения данных (спейс):

local function bootstrap()
	local space = box.schema.create_space('example')
	space:create_index('primary')
	box.schema.user.grant('guest', 'read,write,execute', 'universe')

	-- Keep things safe by default
	--  box.schema.user.create('example', { password = 'secret' })
	--  box.schema.user.grant('example', 'replication')
	--  box.schema.user.grant('example', 'read,write,execute', 'space', 'example')
end

Теперь нужно создать первичный индекс для поиска по данным. Если первичный индекс не указан, по умолчанию в его качестве используется первое поле в каждом ряду.

Затем предоставим права на чтение, запись и выполнение пользователю guest, поскольку по бинарному протоколу мы будем подключаться именно под этим именем. Права распространяются на весь экземпляр.

По сравнению с традиционными базами данных — здесь всё довольно просто. У нас есть спейс — контейнер для хранения данных. Каждая запись/строка называется кортеж, а хранятся кортежи в виде массивов MessagePack. Формат довольно удобный, поскольку является двоичным и занимает меньше места: 18 байт против 27-ми.

image

С записью MessagePack довольно удобно работать. Почти у каждой строки или записи базы данных могут быть совершенно разные столбцы.

Доступ ко всем спейсам одновременно можно получить с помощью модуля box.space. Чтобы получить полную информацию по заданному экземпляру, выполните команду box.space.example.

В Tarantool встроено два движка: memory и vinyl. Memory хранит все данные в оперативной памяти, что обеспечивает простоту и высокую скорость работы. Данные скидываются на диск, а механизм упреждающей записи при журналировании (WAL, от «write-ahead log») обеспечивает их сохранность в случае отказа сервера.

Движок vinyl хранит данные на диске более привычным образом: так объём данных не ограничивается объёмом оперативной памяти, а Tarantool производит чтение с жёсткого диска.

В нашем примере воспользуемся движком memory.

unix/:/var/run/tarantool/example.control> box.space.example
---
- engine: memtx
  before_replace: 'function: 0x41eb02c8'
  on_replace: 'function: 0x41eb0568'
  ck_constraint: []
  field_count: 0
  temporary: false
  index:
	0: &0
  	unique: true
  	parts:
  	- type: unsigned
    	is_nullable: false
    	fieldno: 1
  	id: 0
  	space_id: 512
  	type: TREE
  	name: primary
	primary: *0
  is_local: false
  enabled: true
  name: example
  id: 512
...
​
unix/:/var/run/tarantool/example.control>

Индекс


Для каждого спейса необходимо создать первичный индекс, иначе ничего не заработает. Как в любой базе данных, мы обозначим первое поле записи базы данным как ID.

Составные части


Здесь мы указываем, из чего состоит наш индекс. В нашем случае индекс состоит только из одной части — первого поля кортежа. Это положительное целое число без знака. Если не ошибаюсь, в документации верхний предел обозначен как 18 квинтиллионов — заоблачная величина.

Теперь введём данные с помощью команды insert.

unix/:/var/run/tarantool/example.control> box.space.example:insert{1, 'test1', 'test2'}
---
- [1, 'test1', 'test2']
...
​
unix/:/var/run/tarantool/example.control> box.space.example:insert{2, 'test2', 'test3', 'test4'}
---
- [2, 'test2', 'test3', 'test4']
...
​
unix/:/var/run/tarantool/example.control> box.space.example:insert{3, 'test3'}
---
- [3, 'test3']
...
​
unix/:/var/run/tarantool/example.control> box.space.example:insert{4, 'test4'}
---
- [4, 'test4']
...
​
unix/:/var/run/tarantool/example.control>

Поскольку первое поле используется как первичный ключ, его значение должно быть уникальным. Ограничений на количество столбцов нет, так что можно ввести сколько угодно данных. Столбцы хранятся в вышеупомянутом формате MessagePack.

Вывод данных


Теперь можно вывести данные с помощью команды select.

При выполнении команды box.space.example:select с ключом {1} отобразится строка номер 1. Если ключ опустить, мы увидим все имеющиеся записи базы данных. У них различное количество столбцов, но в Tarantool столбцов как таковых нет — вместо них номера полей.

Данных может быть сколько угодно — и это не фигура речи. К примеру, нам нужно произвести поиск по второму полю. Для этого потребуется вторичный индекс.

box.space.example:create_index( ‘secondary’, { type = ‘TREE’, unique = false, parts = {{field = 2, type =’string’} }})

Эта команда create_index создаст вторичный индекс, который будет называться secondary.

Теперь укажем параметры индекса. Тип индекса укажем TREE. Значения в этом поле могут быть не уникальными, поэтому ставим параметр unique: false.

Опишем составные части индекса. Параметр fieldno задаёт номер поля, к которому привязан индекс, а type указывает тип значений в этом поле — в нашем случае string. Вот как это выглядит:

unix/:/var/run/tarantool/example.control> box.space.example:create_index('secondary', { type = 'TREE', unique = false, parts = {{field = 2, type = 'string'}}})
---
- unique: false
  parts:
  - type: string
	is_nullable: false
	fieldno: 2
  id: 1
  space_id: 512
  type: TREE
  name: secondary
...
​
unix/:/var/run/tarantool/example.control>

Теперь вызовем его следующим образом:

unix/:/var/run/tarantool/example.control> box.space.example.index.secondary:select('test1')
---
- - [1, 'test1', 'test2']
...

Сохранение данных


Если немедленно перезапустить экземпляр и попытаться вновь обратиться к данным, их там не будет — перед вами будет пустая база данных. Tarantool создаёт контрольные точки и сохраняет данные на диск. Если остановить его работу до грядущего сохранения, мы потеряем все операции, произведённые позднее последней контрольной точки, которая была, например, два часа назад.

Сохранять данные каждую секунду тоже не вариант, потому что постоянно сбрасывать на диск по 20 ГБ данных — не лучшая идея.

С этой целью были разработаны и внедрены журналы упреждающей записи (WAL). Для каждого изменения данных создаётся запись в небольшом файле такого журнала.

В этих журналах сохраняется каждая запись вплоть до контрольной точки. Мы задаём размер для этих файлов — например, 64 МБ. При заполнении файла журнала Tarantool начинает запись в следующем. При перезапуске экземпляра Tarantool восстанавливает данные по последней контрольной точке и применяет все последующие транзакции вплоть до момента остановки экземпляра.

image

Чтобы включить упреждающее журналирование, нужно указать опцию wal_mode в настройках box.cfg, то есть в файле конфигурации Lua:

wal_mode = “write”;

Обработка данных


На данном этапе вы уже можете использовать Tarantool для хранения данных, и скорость его работы будет не ниже, чем у обычной БД. А теперь переходим к вишенке на торте: что же можно со всем этим делать.

Как написать приложение


Давайте напишем в Tarantool приложение.

box.cfg {
	listen = '0.0.0.0:3301';
	io_collect_interval = nil;
	readahead = 16320;
	memtx_memory = 128 * 1024 * 1024; -- 128Mb
	memtx_min_tuple_size = 16;
	memtx_max_tuple_size = 128 * 1024 * 1024; -- 128Mb
	vinyl_memory = 128 * 1024 * 1024; -- 128Mb
	vinyl_cache = 128 * 1024 * 1024; -- 128Mb
	vinyl_max_tuple_size = 128 * 1024 * 1024; -- 128Mb
	vinyl_write_threads = 2;
	wal_mode = "write";
	wal_max_size = 256 * 1024 * 1024;
	checkpoint_interval = 60 * 60; -- one hour
	checkpoint_count = 6;
	force_recovery = true;
	log_level = 5;
	log_nonblock = false;
	too_long_threshold = 0.5;
	read_only   = false
}
​
local function bootstrap()
	local space = box.schema.create_space('example')
	space:create_index('primary')
​
	box.schema.user.create('example', { password = 'secret' })
	box.schema.user.grant('example', 'read,write,execute', 'space', 'example')
​
	box.schema.user.create('repl', { password = 'replication' })
	box.schema.user.grant('repl', 'replication')
end
​
-- for first run create a space and add set up grants
box.once('replica', bootstrap)
​
-- enabling console access
console = require('console')
console.listen('127.0.0.1:3302')
​
-- http config
local charset = {}  do -- [0-9a-zA-Z]
	for c = 48, 57  do table.insert(charset, string.char(c)) end
	for c = 65, 90  do table.insert(charset, string.char(c)) end
	for c = 97, 122 do table.insert(charset, string.char(c)) end
end
​
local function randomString(length)
	if not length or length <= 0 then return '' end
	math.randomseed(os.clock()^5)
	return randomString(length - 1) .. charset[math.random(1, #charset)]
end
​
local http_router = require('http.router')
local http_server = require('http.server')
local json = require('json')
​
local httpd = http_server.new('0.0.0.0', 8080, {
	log_requests = true,
	log_errors = true
})
​
local router = http_router.new()
​
local function get_count()
 local cnt = box.space.example:len()
 return cnt
end
​
router:route({method = 'GET', path = '/count'}, function()
	return {status = 200, body = json.encode({count = get_count()})}
end)
​
router:route({method = 'GET', path = '/token'}, function()
	local token = randomString(32)
	local last = box.space.example:len()
	box.space.example:insert{ last + 1, token }
	return {status = 200, body = json.encode({token = token})}
end)
​
prometheus = require('prometheus')
​
fiber = require('fiber')
tokens_count = prometheus.gauge("tarantool_tokens_count",
                          	"API Tokens Count")
​
function monitor_tokens_count()
  while true do
	tokens_count:set(get_count())
	fiber.sleep(5)
  end
end
fiber.create(monitor_tokens_count)
​
router:route( { method = 'GET', path = '/metrics' }, prometheus.collect_http)
​
httpd:set_router(router)
httpd:start()

Начнём с создания таблицы Lua, чтобы задать набор символов, которые будут использованы для генерации произвольной строки.

local charset = {}  do -- [0-9a-zA-Z]
	for c = 48, 57  do table.insert(charset, string.char(c)) end
	for c = 65, 90  do table.insert(charset, string.char(c)) end
	for c = 97, 122 do table.insert(charset, string.char(c)) end
end

Объявим функцию под названием randomString, указав длину строки как параметр:

local function randomString(length)
	if not length or length <= 0 then return '' end
	math.randomseed(os.clock()^5)
	return randomString(length - 1) .. charset[math.random(1, #charset)]
end

Далее подключаем к нашему серверу Tarantool HTTP-маршрутизатор и HTTP-сервер. Также будем передавать как ответ:

local http_router = require('http.router')
local http_server = require('http.server')
local json = require('json')

После чего запускаем HTTP-сервер на порту 8080 и все его интерфейсы и настраиваем журналирование всех запросов и ошибок.

local httpd = http_server.new('0.0.0.0', 8080, {
	log_requests = true,
	log_errors = true
})

Потом объявляем, что любой запрос GET, отправленный на порт 8080 с путём /count, вызовет однострочную функцию, которая будет возвращать код статуса 200, 404, 403 или любой другой заданный:

router:route({method = 'GET', path = '/count'}, function()
	return {status = 200, body = json.encode({count = get_count()})}
end)

В теле ответа мы возвращаем json.encode, где вызываем функцию getcount, чтобы отобразить количество записей в нашей базе данных.

Давайте добавим ещё один путь — /token:

router:route({method = 'GET', path = '/token'}, function()
	local token = randomString(32)
	local last = box.space.example:len()
	box.space.example:insert{ last + 1, token }
	return {status = 200, body = json.encode({token = token})}
end)

Последовательность *router:route({method = \'GET\', path = \'/token\'}, function()* создаёт более сложную функцию и генерирует токен.

local token = randomString(32) устанавливает в качестве токена произвольную строку длиной 32 символа. local last = box.space.example:len() даёт нам количество кортежей в спейсе, а с помощью box.space.example:insert{ last + 1, token } мы добавляем в нашу базу данных новую единицу, увеличивая ID предыдущей на 1. Кстати, это можно сделать и более компактно, поскольку в Tarantool для этого предусмотрены последовательности.

Мы только что сгенерировали новый токен и добавили его в базу данных.

Так мы написали приложение базы данных всего в одном файле. Его уже можно использовать для обработки данных, а модуль box сделает за нас всю грязную работу.

Наше приложение работает с протоколом HTTP и обрабатывает данные; и приложение, и база данных объединены в одном экземпляре, что обеспечивает довольно высокую производительность.

Чтобы наше приложение могло использовать HTTP-модуль, надо его установить:

root@test2:/# tarantoolctl rocks install http
Installing http://rocks.tarantool.org/http-scm-1.src.rock
Missing dependencies for http scm-1:
   checks >= 3.0.1 (not installed)
 
http scm-1 depends on checks >= 3.0.1 (not installed)
Installing http://rocks.tarantool.org/checks-3.0.1-1.rockspec
 
Cloning into 'checks'...
remote: Enumerating objects: 28, done.
remote: Counting objects: 100% (28/28), done.
remote: Compressing objects: 100% (19/19), done.
remote: Total 28 (delta 1), reused 16 (delta 1), pack-reused 0
Receiving objects: 100% (28/28), 12.69 KiB | 12.69 MiB/s, done.
Resolving deltas: 100% (1/1), done.
Note: checking out '580388773ef11085015b5a06fe52d61acf16b201'.
 
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.
 
If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:
 
  git checkout -b <new-branch-name>
 
No existing manifest. Attempting to rebuild...
checks 3.0.1-1 is now installed in /.rocks (license: BSD)
 
-- The C compiler identification is GNU 7.5.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Found TARANTOOL: /usr/include (found version "2.4.2-80-g18f2bc82d")
-- Tarantool LUADIR is /.rocks/share/tarantool/rocks/http/scm-1/lua
-- Tarantool LIBDIR is /.rocks/share/tarantool/rocks/http/scm-1/lib
-- Configuring done
-- Generating done
CMake Warning:
  Manually-specified variables were not used by the project:
 
	version
 
 
-- Build files have been written to: /tmp/luarocks_http-scm-1-V4P9SM/http/build.luarocks
Scanning dependencies of target httpd
[ 50%] Building C object http/CMakeFiles/httpd.dir/lib.c.o
In file included from /tmp/luarocks_http-scm-1-V4P9SM/http/http/lib.c:32:0:
/tmp/luarocks_http-scm-1-V4P9SM/http/http/lib.c: In function ‘tpl_term’:
/usr/include/tarantool/lauxlib.h:144:15: warning: this statement may fall through [-Wimplicit-fallthrough=]
	(*(B)->p++ = (char)(c)))
	~~~~~~~~~~~^~~~~~~~~~~~
/tmp/luarocks_http-scm-1-V4P9SM/http/http/lib.c:62:7: note: in expansion of macro ‘luaL_addchar’
   	luaL_addchar(b, '\\');
   	^~~~~~~~~~~~
/tmp/luarocks_http-scm-1-V4P9SM/http/http/lib.c:63:6: note: here
  	default:
  	^~~~~~~
In file included from /tmp/luarocks_http-scm-1-V4P9SM/http/http/lib.c:39:0:
/tmp/luarocks_http-scm-1-V4P9SM/http/http/tpleval.h: In function ‘tpe_parse’:
/tmp/luarocks_http-scm-1-V4P9SM/http/http/tpleval.h:147:9: warning: this statement may fall through [-Wimplicit-fallthrough=]
	type = TPE_TEXT;
	~~~~~^~~~~~~~~~
/tmp/luarocks_http-scm-1-V4P9SM/http/http/tpleval.h:149:3: note: here
   case TPE_LINECODE:
   ^~~~
In file included from /tmp/luarocks_http-scm-1-V4P9SM/http/http/lib.c:40:0:
/tmp/luarocks_http-scm-1-V4P9SM/http/http/httpfast.h: In function ‘httpfast_parse’:
/tmp/luarocks_http-scm-1-V4P9SM/http/http/httpfast.h:372:22: warning: this statement may fall through [-Wimplicit-fallthrough=]
             	code = 0;
             	~~~~~^~~
/tmp/luarocks_http-scm-1-V4P9SM/http/http/httpfast.h:374:13: note: here
         	case status:
         	^~~~
/tmp/luarocks_http-scm-1-V4P9SM/http/http/httpfast.h:393:23: warning: this statement may fall through [-Wimplicit-fallthrough=]
             	state = message;
             	~~~~~~^~~~~~~~~
/tmp/luarocks_http-scm-1-V4P9SM/http/http/httpfast.h:395:13: note: here
         	case message:
         	^~~~
[100%] Linking C shared library lib.so
[100%] Built target httpd
[100%] Built target httpd
Install the project...
-- Install configuration: "Debug"
-- Installing: /.rocks/share/tarantool/rocks/http/scm-1/lua/http/VERSION.lua
-- Installing: /.rocks/share/tarantool/rocks/http/scm-1/lib/http/lib.so
-- Installing: /.rocks/share/tarantool/rocks/http/scm-1/lua/http/server/init.lua
-- Installing: /.rocks/share/tarantool/rocks/http/scm-1/lua/http/server/tsgi_adapter.lua
-- Installing: /.rocks/share/tarantool/rocks/http/scm-1/lua/http/nginx_server/init.lua
-- Installing: /.rocks/share/tarantool/rocks/http/scm-1/lua/http/router/init.lua
-- Installing: /.rocks/share/tarantool/rocks/http/scm-1/lua/http/router/fs.lua
-- Installing: /.rocks/share/tarantool/rocks/http/scm-1/lua/http/router/matching.lua
-- Installing: /.rocks/share/tarantool/rocks/http/scm-1/lua/http/router/middleware.lua
-- Installing: /.rocks/share/tarantool/rocks/http/scm-1/lua/http/router/request.lua
-- Installing: /.rocks/share/tarantool/rocks/http/scm-1/lua/http/router/response.lua
-- Installing: /.rocks/share/tarantool/rocks/http/scm-1/lua/http/tsgi.lua
-- Installing: /.rocks/share/tarantool/rocks/http/scm-1/lua/http/utils.lua
-- Installing: /.rocks/share/tarantool/rocks/http/scm-1/lua/http/mime_types.lua
-- Installing: /.rocks/share/tarantool/rocks/http/scm-1/lua/http/codes.lua
http scm-1 is now installed in /.rocks (license: BSD)
 
root@test2:/#

Нам также понадобится Prometheus:

root@test2:/# tarantoolctl rocks install prometheus
Installing http://rocks.tarantool.org/prometheus-scm-1.rockspec
 
Cloning into 'prometheus'...
remote: Enumerating objects: 19, done.
remote: Counting objects: 100% (19/19), done.
remote: Compressing objects: 100% (19/19), done.
remote: Total 19 (delta 2), reused 5 (delta 0), pack-reused 0
Receiving objects: 100% (19/19), 10.73 KiB | 10.73 MiB/s, done.
Resolving deltas: 100% (2/2), done.
prometheus scm-1 is now installed in /.rocks (license: BSD)
 
root@test2:/#

Запустив приложение, можем обратиться к его модулям:

root@test2:/# curl -D - -s http://127.0.0.1:8080/token
HTTP/1.1 200 Ok
Content-length: 44
Server: Tarantool http (tarantool v2.4.2-80-g18f2bc82d)
Connection: keep-alive
 
{"token":"e2tPq9l5Z3QZrewRf6uuoJUl3lJgSLOI"}
 
root@test2:/# curl -D - -s http://127.0.0.1:8080/token
HTTP/1.1 200 Ok
Content-length: 44
Server: Tarantool http (tarantool v2.4.2-80-g18f2bc82d)
Connection: keep-alive
 
{"token":"fR5aCA84gj9eZI3gJcV0LEDl9XZAG2Iu"}
 
root@test2:/# curl -D - -s http://127.0.0.1:8080/count
HTTP/1.1 200 Ok
Content-length: 11
Server: Tarantool http (tarantool v2.4.2-80-g18f2bc82d)
Connection: keep-alive
 
{"count":2}root@test2:/#

/count возвращает код 200.

/token возвращает сгенерированный токен и записывает его в базу данных.

Тест производительности


Давайте проведём тест на 50 тысяч запросов. Пятьсот из них будут параллельными.

root@test2:/# ab -c 500 -n 50000 http://127.0.0.1:8080/token
This is ApacheBench, Version 2.3 <$Revision: 1807734 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
 
Benchmarking 127.0.0.1 (be patient)
Completed 5000 requests
Completed 10000 requests
Completed 15000 requests
Completed 20000 requests
Completed 25000 requests
Completed 30000 requests
Completed 35000 requests
Completed 40000 requests
Completed 45000 requests
Completed 50000 requests
Finished 50000 requests
 
 
Server Software:    	Tarantool
Server Hostname:    	127.0.0.1
Server Port:        	8080
 
Document Path:      	/token
Document Length:    	44 bytes
 
Concurrency Level:  	500
Time taken for tests:   14.578 seconds
Complete requests:  	50000
Failed requests:    	0
Total transferred:  	7950000 bytes
HTML transferred:   	2200000 bytes
Requests per second:	3429.87 [#/sec] (mean)
Time per request:   	145.778 [ms] (mean)
Time per request:   	0.292 [ms] (mean, across all concurrent requests)
Transfer rate:      	532.57 [Kbytes/sec] received
 
Connection Times (ms)
          	min  mean[+/-sd] median   max
Connect:    	0   10 103.2  	0	3048
Processing:	12   69 685.1 	15   13538
Waiting:   	12   69 685.1 	15   13538
Total:     	12   78 768.2 	15   14573
 
Percentage of the requests served within a certain time (ms)
  50% 	15
  66% 	15
  75% 	16
  80% 	16
  90% 	16
  95% 	16
  98% 	21
  99% 	42
 100%  14573 (longest request)
root@test2:/#

Токены генерируются, и мы непрерывно заносим их в базу данных. Из наших запросов 99% были обработаны за 42 миллисекунды. Следовательно, наша небольшая виртуальная машина всего с двумя ЦП и 4 ГБ оперативной памяти справляется примерно с 3500 запросов в секунду.

Можем также выбрать пятидесятитысячный токен (или любой из близлежащих) и проверить его значение.

Вы можете не только использовать HTTP, но и выполнять функции по работе с данными в фоновом режиме. Есть и события, автоматически запускающие процедуры. Так, можно вызывать функции при обновлениях для проверки и устранения конфликтов.

Можно писать приложения-скрипты непосредственно на сервере базы данных, подключать любые модули и использовать любую логику — без ограничений.

Сервер приложений Tarantool может подключаться к внешним серверам, извлекать данные, хранить их в вашей базе данных и предоставлять в пользование другим приложениям.

Для этого не нужно писать отдельное приложение, поскольку в Tarantool уже есть всё, что вам нужно.


НЛО прилетело и оставило здесь промокоды для читателей нашего блога:

15% на все тарифы VDS (кроме тарифа Прогрев) — HABRFIRSTVDS.

20% на выделенные серверы AMD Ryzen и Intel Core HABRFIRSTDEDIC.

Доступно до 31 декабря 2021 г.

Комментарии (0)