[Хранение данных] В Tarantool можно совместить супербыструю базу данных и приложение для работы с ними. Вот как просто это делается
Автор
Сообщение
news_bot ®
Стаж: 6 лет 8 месяцев
Сообщений: 27286
Пять лет назад я попробовал работать с Tarantool, но тогда он мне не зашел. Но недавно я проводил вебинар, где рассказывал про Hadoop, про то, как работает MapReduce. Там мне задали вопрос — «А почему под эту задачу не использовать Tarantool?».
Ради любопытства я решил вернуться к нему, протестировать последнюю версию — и на этот раз проект мне очень понравился. Сейчас я покажу, как написать в Tarantool простое приложение, нагружу его и проверю производительность, и вы увидите, как там все легко и круто.
Что такое Tarantool
Tarantool позиционирует себя как сверхбыстрая БД. Туда можно пихать любые данные, какие захотите. Плюс, реплицировать их, шардировать — то есть разбивать огромное количество данных по нескольким серверам и объединять с них результаты — делать отказоустойчивые связки типа «мастер-мастер».
Во вторую очередь, это application-сервер. Вы можете писать на нем свои приложения, работать с данными, например, удалять в фоне старые записи по определенным правилам. Можно написать прямо в Тарантуле Http-сервер, который будет работать с данными: выдавать их количество, записывать туда новые данные и редуцировать это все на мастера.
Я читал статью, как ребята сделали очередь сообщений на 300 строк, которое просто рвет и мечет — у них минимальная производительность от 20 000 сообщений в секунду. Здесь можно действительно развернуться и написать очень большое приложение, и это будут не хранимки, как в PostgreS.
Примерно такой сервер, только простой, я и попробую описать в этой статье.
Установка
Для теста я завел три стандартные виртуальные машины – жесткий диск на 20 гигабайт, Ubuntu 18.04. 2 виртуал CPU и 4 гига памяти.
Мы ставим Tarantool — запускаем башевый скрипт либо добавляем репозиторий и делаем apt get install Tarantool. Ссылка на скрипт — (curl -L https://tarantool.io/installer.sh | VER=2.4 sudo -E bash). У нас нас появляются такие команды, как:
tarantoolctl — основная команда для управления инстансами Тарантула.
/etc/tarantool — здесь лежит вся конфигурация.
var/log/tarantool — здесь лежат логи.
var/lib/tarantool — здесь лежат данные, и дальше они разбиты по инстансам.
Есть папки instance-available и instance-enable — в ней находится то, что будет запускаться — файл конфигурации инстанса с lua кодом, где описано, на каких портах он слушает, какая память ему доступна, настройки движка Vinyl, код, который срабатывает при старте сервера, шардирование, очереди, удаление устаревших данных и так далее.
Инстансы работают как в PostgreS. Например, вы хотите запустить несколько копий базы данных, которая висит на разных портах. Получается, на одном сервере запускаются несколько инстансов баз данных, которые висят на разных портах. У них могут абсолютно разные настройки — один инстанс реализует одну логику, второй — другую.
Управление инстансами
У нас есть команда tarantoolctl, которая позволяет управлять инстансами Тарантула. Например, tarantoolctl check example проверит файл с конфигурацией и скажет — файл is ok, если там нет никаких синтаксических ошибок.
Можно посмотреть статус инстанса — tarantoolctl status example. Таким же образом можно делать start, stop, restart.
Когда instance запущен, к нему можно подключиться двумя способами.
1. Административная консоль
По умолчанию Tarantool открывает сокет, там передается обычный ASCII-текст для управления Тарантулом. Подключение к консоли происходит всегда под пользователем admin, там нет аутентификации, поэтому выносить консольный порт для управления Тарантулом вовне не надо.
Для подключения этим способом надо ввести Tarantoolctl enter instance name. Команда запустит консоль и подключится под пользователем admin. Никогда не выставляйте консольный порт наружу — лучше оставить его юнитсокетом. Тогда возможность подключиться к Тарантулу будет только у тех, у кого есть доступ к записи в сокет.
Этот способ нужен для административных вещей. Для работы с данными используйте второй способ — бинарный протокол.
2. Использование бинарного протокола для подключения к определенному порту
В конфигурации есть директива listen, которая открывает порт для внешних коммуникаций. Этот порт используется с бинарным протоколом, и там включена аутентификация.
Для этого подключения используется tarantoolctl connect to port number. Используя ее, можно подключаться к удаленным серверам, использовать аутентификацию и давать различные права доступа.
Запись данных и модуль Box
Так как Tarantool является и базой данных, и application-сервером, в нем есть различные модули. Нас интересует модуль box — он реализует работу с данными. Когда вы записываете что-то в box, Tarantool пишет данные на диск, сохраняет в памяти или делает с ними что-то еще.
Запись
Например, мы заходим в модуль box и вызываем функцию box.once. Она заставит Tarantool запустить наш код при инициализации сервера. Мы создаем space, в котором будут храниться наши данные.
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
После этого мы создаем первичный индекс – primary — по которому можно будет искать данные. По умолчанию, если не указать никаких параметров, то будет использовано первое поле в каждой записи для праймери-индекса.
Затем делаем грант пользователю guest, под которым подключаемся по бинарному протоколу. Разрешаем читать, писать и выполнять во всем инстансе.
Если сравнивать с обычными базами данных, здесь все достаточно просто. У нас есть space – область, в которой просто хранятся наши данные. Каждая запись называется кортеж. Она пакуется в MessagePack. Это очень прикольный формат — он бинарный и занимает меньше места – 18 байт против 27.
С ним достаточно удобно работать. Почти каждая строчка, каждая запись данных может иметь абсолютно различные колонки.
Все спейсы мы можем посмотреть с помощью команды Box.space. Чтобы выделить конкретный инстанс – пишем box.space example и получаем полную информацию по нему.
В Tarantool встроено два типа движков: Memory и Vinyl. Memory сохраняет все данные в памяти. Поэтому все работает просто и быстро. Данные дампятся на диск, а также существует механизм write ahead log, поэтому мы ничего не потеряем при падении сервера.
Vinyl хранит данные на диске в более привычном нам виде — то есть можно хранить больше данных, чем у нас есть памяти, и Тарантул будет читать их с диска.
Сейчас мы будем использовать 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>
Index:
Первичный индекс надо создавать для любого спейса, потому что без него ничего не будет работать. Как и в любой базе, мы создаем первое поле – ID записи.
Parts:
Здесь мы указываем, из чего состоит наш индекс. Он состоит из одной части – первое поле, которое мы будем использовать, тип unsigned — положительное целое число. Насколько я помню из документации, максимальное число, которое может быть – 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.example.select с указанием ключа {1} выведет нужную запись. Если мы опустим ключ, то увидим все записи, какие у нас есть. Они все различны по количеству колонок, но здесь в принципе нет понятия колонок — есть номера поля.
Данных может быть абсолютно сколько угодно много. И например, нам нужно искать их по второму полю. Для этого мы делаем новый вторичный индекс.
box.space.example:create_index( ‘secondary’, { type = ‘TREE’, unique = falce, parts = {{field = 2, type =’string’} }})
Используем команду Create_index.
Называем его Secondary.
После этого нужно указать параметры. Тип индекса — TREE. Оно может быть не уникальное, поэтому вводим Unique = false.
Затем указываем, из каких частей состоит наш индекс. Field — это номер поля, к которому мы привязываем индекс, и указываем тип 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 Гб — так себе затея.
Для этого была придумана и реализована концепция write-ahead log. С ее помощью на каждое изменение в данных создается запись в маленьком write-ahead log файле.
Каждая запись до чекпоинта сохраняется в них. Для этих файлов мы выставляем размер — например, 64 мб. Когда он заполняется, запись начинает идти во второй файл. И после рестарта Tarantool восстанавливается из последнего чекпоинта и затем накатывает все более поздние транзакции до момента остановки.
Чтобы осуществлять такую запись, нужно указать опцию в настройках box.cfg (в файле example.lua):
wal_mode = “write”;
Использование данных
С тем, что мы написали сейчас, вы можете использовать Тарантул для хранения данных, и он будет очень быстро работать как БД. А теперь самая вишенка на торте – что можно с этим со всем делать.
Пишем приложение
Например, напишем для Тарантула такое приложение
Приложение смотрите под спойлером
SPL
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
Затем мы подключаем http-роутер и http-сервер в наш Тарантул-сервер, JSON, которые будем отдавать клиенту.
local http_router = require('http.router')
local http_server = require('http.server')
local json = require('json')
После этого стартуем на порту 8080 на всех интерфейсах http server, который будет логировать все запросы и ошибки.
local httpd = http_server.new('0.0.0.0', 8080, {
log_requests = true,
log_errors = true
})
Дальше мы объявляем route, что если на порт 8080 /count приходит запрос с методом GET, то мы вызываем функцию из одной строчки. Она возвращает статус — 200, 404, 403 или любой другой, какой укажем.
router:route({method = 'GET', path = '/count'}, function()
return {status = 200, body = json.encode({count = get_count()})}
end)
В теле мы возвращаем json.encode, в ней указываем count и getcount, которая вызывается и показывает количество записей в нашей базе.
Второй метод
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. Это можно сделать, кстати, не только вот таким корявым способом. В Тарантуле есть сиквенсы для этого дела.
Записываем туда токен.
Таким образом, мы в одном файле написали приложение. В нем можно сразу обращаться с данными, и модуль box сделает за вас всю грязную работу.
Оно слушает http и работает с данными, все находится в едином инстансе — и приложение, и данные. Поэтому все происходит достаточно быстро.
Для запуска мы устанавливаем http модуль:
Как мы это делаем, смотрите под спойлером
SPL
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 000 запросов. Конкурентных запросов будет 500.
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 миллисекунды. Соответственно, у нас порядка 3500 запросов в секунду на маленькой машинке, где 2 ядра и 4 гигабайта памяти.
Также можно заселектить какой-нибудь 50000-токен и посмотреть его значение.
Можно использовать не только http, запускать бэкгрануд-функции, которые обрабатывают ваши данные. Плюс есть различные триггеры. Например, вы можете вызывать функции на апдейтах, что-то проверять – исправлять конфликты.
Можно писать приложения-скрипты прямо в самом сервере базы данных, и ничем не ограничиваться, подключать любые модули и реализовать любую логику.
Аппликейшн-сервер может обращаться к внешним серверам, забирать данные и складывать к себе в базу. Данные из этой базы будут использовать другие приложения.
Это будет делать сам Тарантул, и не придется писать отдельное приложение.
В заключение
Это только первая часть большой работы. Вторая будет опубликована совсем скоро в блоге Mail.ru Group, и мы обязательно добавим ссылку на нее в этот материал.
Если вам интересно посещать мероприятия, где мы создаем такие штуки онлайн, и задавать вопросы в режиме реального времени, подключайтесь кканалу DevOps by REBRAIN.
Если вам нужен переезд в облако или есть вопросы по вашей инфраструктуре, смело оставляйте заявку.
P.S. У нас есть 2 бесплатных аудита в месяц, возможно, именно ваш проект будет в их числе.
===========
Источник:
habr.com
===========
Похожие новости:
- [Серверная оптимизация, Серверное администрирование, Хранение данных, Хранилища данных] Я был неправ. Будущее за CRDT (перевод)
- [Big Data, SQL, Администрирование баз данных, Системное администрирование] Переезжаем на ClickHouse: 3 года спустя
- [.NET, C#, Программирование, Проектирование и рефакторинг, Тестирование IT-систем] Мне было стыдно за свой интерпрайз-код настолько, что я сделал свой велосипед. За него стыдно меньше
- [MySQL, Администрирование баз данных, Программирование, Хранение данных] История о физическом удалении 300 миллионов записей в MySQL (перевод)
- [Хранение данных, Компьютерное железо, Накопители, Настольные компьютеры] Зачем нужны флешки с аппаратным шифрованием?
- [.NET, C#, Программирование, Проектирование и рефакторинг] Архитектура интерпрайз-приложений может быть другой
- [Системное администрирование, IT-инфраструктура, Хранение данных, Хранилища данных] Эльбрус VS Intel. Сравниваем производительность систем хранения Аэродиск Восток и Engine
- [Разработка систем связи, Сотовая связь, Хранение данных] Автомобили станут более безопасными: на помощь к 5G приходят технологии хранения данных
- [DevOps, Серверное администрирование, Системное администрирование, Хранение данных] Наиболее интересные факты о Ceph по результатам опроса пользователей в 2019 году
- [Децентрализованные сети, Информационная безопасность, Программирование, Хранение данных] Блокчейн как структура данных (перевод)
Теги для поиска: #_hranenie_dannyh (Хранение данных), #_rebrain, #_fevlake, #_bazy_dannyh (базы данных), #_blog_kompanii_rebrein (
Блог компании Ребреин
), #_hranenie_dannyh (
Хранение данных
)
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 01-Ноя 07:24
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 8 месяцев |
|
Пять лет назад я попробовал работать с Tarantool, но тогда он мне не зашел. Но недавно я проводил вебинар, где рассказывал про Hadoop, про то, как работает MapReduce. Там мне задали вопрос — «А почему под эту задачу не использовать Tarantool?». Ради любопытства я решил вернуться к нему, протестировать последнюю версию — и на этот раз проект мне очень понравился. Сейчас я покажу, как написать в Tarantool простое приложение, нагружу его и проверю производительность, и вы увидите, как там все легко и круто. Что такое Tarantool Tarantool позиционирует себя как сверхбыстрая БД. Туда можно пихать любые данные, какие захотите. Плюс, реплицировать их, шардировать — то есть разбивать огромное количество данных по нескольким серверам и объединять с них результаты — делать отказоустойчивые связки типа «мастер-мастер». Во вторую очередь, это application-сервер. Вы можете писать на нем свои приложения, работать с данными, например, удалять в фоне старые записи по определенным правилам. Можно написать прямо в Тарантуле Http-сервер, который будет работать с данными: выдавать их количество, записывать туда новые данные и редуцировать это все на мастера. Я читал статью, как ребята сделали очередь сообщений на 300 строк, которое просто рвет и мечет — у них минимальная производительность от 20 000 сообщений в секунду. Здесь можно действительно развернуться и написать очень большое приложение, и это будут не хранимки, как в PostgreS. Примерно такой сервер, только простой, я и попробую описать в этой статье. Установка Для теста я завел три стандартные виртуальные машины – жесткий диск на 20 гигабайт, Ubuntu 18.04. 2 виртуал CPU и 4 гига памяти. Мы ставим Tarantool — запускаем башевый скрипт либо добавляем репозиторий и делаем apt get install Tarantool. Ссылка на скрипт — (curl -L https://tarantool.io/installer.sh | VER=2.4 sudo -E bash). У нас нас появляются такие команды, как: tarantoolctl — основная команда для управления инстансами Тарантула. /etc/tarantool — здесь лежит вся конфигурация. var/log/tarantool — здесь лежат логи. var/lib/tarantool — здесь лежат данные, и дальше они разбиты по инстансам. Есть папки instance-available и instance-enable — в ней находится то, что будет запускаться — файл конфигурации инстанса с lua кодом, где описано, на каких портах он слушает, какая память ему доступна, настройки движка Vinyl, код, который срабатывает при старте сервера, шардирование, очереди, удаление устаревших данных и так далее. Инстансы работают как в PostgreS. Например, вы хотите запустить несколько копий базы данных, которая висит на разных портах. Получается, на одном сервере запускаются несколько инстансов баз данных, которые висят на разных портах. У них могут абсолютно разные настройки — один инстанс реализует одну логику, второй — другую. Управление инстансами У нас есть команда tarantoolctl, которая позволяет управлять инстансами Тарантула. Например, tarantoolctl check example проверит файл с конфигурацией и скажет — файл is ok, если там нет никаких синтаксических ошибок. Можно посмотреть статус инстанса — tarantoolctl status example. Таким же образом можно делать start, stop, restart. Когда instance запущен, к нему можно подключиться двумя способами. 1. Административная консоль По умолчанию Tarantool открывает сокет, там передается обычный ASCII-текст для управления Тарантулом. Подключение к консоли происходит всегда под пользователем admin, там нет аутентификации, поэтому выносить консольный порт для управления Тарантулом вовне не надо. Для подключения этим способом надо ввести Tarantoolctl enter instance name. Команда запустит консоль и подключится под пользователем admin. Никогда не выставляйте консольный порт наружу — лучше оставить его юнитсокетом. Тогда возможность подключиться к Тарантулу будет только у тех, у кого есть доступ к записи в сокет. Этот способ нужен для административных вещей. Для работы с данными используйте второй способ — бинарный протокол. 2. Использование бинарного протокола для подключения к определенному порту В конфигурации есть директива listen, которая открывает порт для внешних коммуникаций. Этот порт используется с бинарным протоколом, и там включена аутентификация. Для этого подключения используется tarantoolctl connect to port number. Используя ее, можно подключаться к удаленным серверам, использовать аутентификацию и давать различные права доступа. Запись данных и модуль Box Так как Tarantool является и базой данных, и application-сервером, в нем есть различные модули. Нас интересует модуль box — он реализует работу с данными. Когда вы записываете что-то в box, Tarantool пишет данные на диск, сохраняет в памяти или делает с ними что-то еще. Запись Например, мы заходим в модуль box и вызываем функцию box.once. Она заставит Tarantool запустить наш код при инициализации сервера. Мы создаем space, в котором будут храниться наши данные. 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 После этого мы создаем первичный индекс – primary — по которому можно будет искать данные. По умолчанию, если не указать никаких параметров, то будет использовано первое поле в каждой записи для праймери-индекса. Затем делаем грант пользователю guest, под которым подключаемся по бинарному протоколу. Разрешаем читать, писать и выполнять во всем инстансе. Если сравнивать с обычными базами данных, здесь все достаточно просто. У нас есть space – область, в которой просто хранятся наши данные. Каждая запись называется кортеж. Она пакуется в MessagePack. Это очень прикольный формат — он бинарный и занимает меньше места – 18 байт против 27. С ним достаточно удобно работать. Почти каждая строчка, каждая запись данных может иметь абсолютно различные колонки. Все спейсы мы можем посмотреть с помощью команды Box.space. Чтобы выделить конкретный инстанс – пишем box.space example и получаем полную информацию по нему. В Tarantool встроено два типа движков: Memory и Vinyl. Memory сохраняет все данные в памяти. Поэтому все работает просто и быстро. Данные дампятся на диск, а также существует механизм write ahead log, поэтому мы ничего не потеряем при падении сервера. Vinyl хранит данные на диске в более привычном нам виде — то есть можно хранить больше данных, чем у нас есть памяти, и Тарантул будет читать их с диска. Сейчас мы будем использовать 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> Index: Первичный индекс надо создавать для любого спейса, потому что без него ничего не будет работать. Как и в любой базе, мы создаем первое поле – ID записи. Parts: Здесь мы указываем, из чего состоит наш индекс. Он состоит из одной части – первое поле, которое мы будем использовать, тип unsigned — положительное целое число. Насколько я помню из документации, максимальное число, которое может быть – 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.example.select с указанием ключа {1} выведет нужную запись. Если мы опустим ключ, то увидим все записи, какие у нас есть. Они все различны по количеству колонок, но здесь в принципе нет понятия колонок — есть номера поля. Данных может быть абсолютно сколько угодно много. И например, нам нужно искать их по второму полю. Для этого мы делаем новый вторичный индекс. box.space.example:create_index( ‘secondary’, { type = ‘TREE’, unique = falce, parts = {{field = 2, type =’string’} }})
Используем команду Create_index. Называем его Secondary. После этого нужно указать параметры. Тип индекса — TREE. Оно может быть не уникальное, поэтому вводим Unique = false. Затем указываем, из каких частей состоит наш индекс. Field — это номер поля, к которому мы привязываем индекс, и указываем тип 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 Гб — так себе затея. Для этого была придумана и реализована концепция write-ahead log. С ее помощью на каждое изменение в данных создается запись в маленьком write-ahead log файле. Каждая запись до чекпоинта сохраняется в них. Для этих файлов мы выставляем размер — например, 64 мб. Когда он заполняется, запись начинает идти во второй файл. И после рестарта Tarantool восстанавливается из последнего чекпоинта и затем накатывает все более поздние транзакции до момента остановки. Чтобы осуществлять такую запись, нужно указать опцию в настройках box.cfg (в файле example.lua): wal_mode = “write”;
Использование данных С тем, что мы написали сейчас, вы можете использовать Тарантул для хранения данных, и он будет очень быстро работать как БД. А теперь самая вишенка на торте – что можно с этим со всем делать. Пишем приложение Например, напишем для Тарантула такое приложение Приложение смотрите под спойлеромSPLbox.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 Затем мы подключаем http-роутер и http-сервер в наш Тарантул-сервер, JSON, которые будем отдавать клиенту. local http_router = require('http.router')
local http_server = require('http.server') local json = require('json') После этого стартуем на порту 8080 на всех интерфейсах http server, который будет логировать все запросы и ошибки. local httpd = http_server.new('0.0.0.0', 8080, {
log_requests = true, log_errors = true }) Дальше мы объявляем route, что если на порт 8080 /count приходит запрос с методом GET, то мы вызываем функцию из одной строчки. Она возвращает статус — 200, 404, 403 или любой другой, какой укажем. router:route({method = 'GET', path = '/count'}, function()
return {status = 200, body = json.encode({count = get_count()})} end) В теле мы возвращаем json.encode, в ней указываем count и getcount, которая вызывается и показывает количество записей в нашей базе. Второй метод 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. Это можно сделать, кстати, не только вот таким корявым способом. В Тарантуле есть сиквенсы для этого дела. Записываем туда токен. Таким образом, мы в одном файле написали приложение. В нем можно сразу обращаться с данными, и модуль box сделает за вас всю грязную работу. Оно слушает http и работает с данными, все находится в едином инстансе — и приложение, и данные. Поэтому все происходит достаточно быстро. Для запуска мы устанавливаем http модуль: Как мы это делаем, смотрите под спойлеромSPLroot@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 000 запросов. Конкурентных запросов будет 500. 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 миллисекунды. Соответственно, у нас порядка 3500 запросов в секунду на маленькой машинке, где 2 ядра и 4 гигабайта памяти. Также можно заселектить какой-нибудь 50000-токен и посмотреть его значение. Можно использовать не только http, запускать бэкгрануд-функции, которые обрабатывают ваши данные. Плюс есть различные триггеры. Например, вы можете вызывать функции на апдейтах, что-то проверять – исправлять конфликты. Можно писать приложения-скрипты прямо в самом сервере базы данных, и ничем не ограничиваться, подключать любые модули и реализовать любую логику. Аппликейшн-сервер может обращаться к внешним серверам, забирать данные и складывать к себе в базу. Данные из этой базы будут использовать другие приложения. Это будет делать сам Тарантул, и не придется писать отдельное приложение. В заключение Это только первая часть большой работы. Вторая будет опубликована совсем скоро в блоге Mail.ru Group, и мы обязательно добавим ссылку на нее в этот материал. Если вам интересно посещать мероприятия, где мы создаем такие штуки онлайн, и задавать вопросы в режиме реального времени, подключайтесь кканалу DevOps by REBRAIN. Если вам нужен переезд в облако или есть вопросы по вашей инфраструктуре, смело оставляйте заявку. P.S. У нас есть 2 бесплатных аудита в месяц, возможно, именно ваш проект будет в их числе. =========== Источник: habr.com =========== Похожие новости:
Блог компании Ребреин ), #_hranenie_dannyh ( Хранение данных ) |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 01-Ноя 07:24
Часовой пояс: UTC + 5