Tarantool CE/EE Documentation portal logo
Помощь
Обновлена 15 сентября 2026 г. в 08:55

Неблокирующий ввод-вывод

Чтобы протестировать 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 then        print(string.format('Player %d result: %s',            id, resp.body))    else        print(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 do    fiber.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.