Неблокирующий ввод-вывод
Чтобы протестировать REST API, создадим новый скрипт
(client.lua),
который похож на наше приложение game.lua, но отправляет HTTP-запросы
POST и GET вместо вызова Lua-функций:
local http = require('curl').http()local json = require('json')local URI = os.getenv('SERVER_URI')local fiber = require('fiber')local player1 = {name="Player1",id=1,location = {x=1.0001,y=2.0003}}local player2 = {name="Player2",id=2,location = {x=30.123,y=40.456}}local pokemon = {name="Pikachu",chance=99.1,id=1,status="active",location = {x=1,y=2}}function request(method, body, id)local resp = http:request(method, URI, body)if id ~= nil thenprint(string.format('Player %d result: %s',id, resp.body))elseprint(resp.body)endendlocal players = {}function catch(player)fiber.sleep(math.random(5))print('Catch pokemon by player ' .. tostring(player.id))request('POST', '{"method": "catch","params": [1, '..json.encode(player)..']}',tostring(player.id))table.insert(players, player.id)endprint('Create pokemon')request('POST', '{"method": "add","params": ['..json.encode(pokemon)..']}')request('GET', '')fiber.create(catch, player1)fiber.create(catch, player2)-- wait for playerswhile #players ~= 2 dofiber.sleep(0.001)endrequest('GET', '')os.exit()
При запуске этого скрипта можно заметить, что оба игрока имеют равные шансы сделать первую попытку поймать покемона. В классическом Lua-скрипте сетевой вызов блокирует выполнение скрипта до его завершения, поэтому первую попытку поймать покемона может сделать только тот игрок, который вошел в игру первым. В Tarantool оба игрока играют одновременно, так как все модули интегрированы с кооперативной многозадачностью Tarantool и используют неблокирующий ввод-вывод.
Действительно, когда Player1 делает свой первый REST-запрос, скрипт не
блокируется. Файбер, выполняющий функцию catch() от имени Player1,
выполняет неблокирующий вызов к операционной системе и передает
управление следующему файберу, которым оказывается файбер Player2.
Файбер Player2 делает то же самое. Когда сетевой ответ получен, файбер
Player1 активируется кооперативным планировщиком Tarantool и
возобновляет свою работу.
Все модули Tarantool
используют неблокирующий ввод-вывод и интегрированы с кооперативным
планировщиком Tarantool. Для разработчиков модулей Tarantool
предоставляет API.
Для HTTP-тестирования создадим третий контейнер на основе
официального образа Tarantool 1.9 (см.
client/Dockerfile)
и зададим команду по умолчанию для контейнера — tarantool client.lua.
Чтобы запустить этот тест локально, скачайте проект pokemon с GitHub и выполните:
$ docker-compose build$ docker-compose up
Docker Compose собирает и запускает все три контейнера: pserver
(бэкенд Tarantool), phttp (nginx) и pclient (демонстрационный
клиент). В консоли отображаются сообщения журнала от всех этих
контейнеров, из которых видно, что pclient отправил HTTP-запрос на
создание покемона, выполнил два запроса на поимку, запросил карту
(пустую, так как покемон пойман и временно неактивен) и завершил работу:
pclient_1 | Create pokemon<...>pclient_1 | {"result":true}pclient_1 | {"map":[{"id":1,"status":"active","location":{"y":2,"x":1},"name":"Pikachu","chance":99.100000}]}pclient_1 | Catch pokemon by player 2pclient_1 | Catch pokemon by player 1pclient_1 | Player 1 result: {"result":true}pclient_1 | Player 2 result: {"result":false}pclient_1 | {"map":}pokemon_pclient_1 exited with code 0
Поздравляем! На этом наше руководство завершается. Дополнительные сведения см. в разделах про установку и распространение модуля.
См. также справочник по модулям Tarantool и C API, а также сборник примеров на Lua.