lua-resty-yar

July 9, 2026 · View on GitHub

High-performance Yar RPC server for OpenResty, built on lua-yar.

Features

  • HTTP server handlercontent_by_lua entry, one coroutine per request, pure protocol dispatch via handle_message
  • TCP stream server handler — stream content_by_lua entry, one coroutine per connection, keepalive loop via handle_connection
  • Cosocket injection — outbound RPC calls use OpenResty non-blocking I/O with connection pooling
  • yar-c parameter mappingREAD_TIMEOUT → three-stage cosocket timeouts, CHILD_INITon_worker_init hook
  • Process-level instance reuse — Server/TcpServer instances created in init_by_lua, shared by all coroutines in worker

Installation

Step 1: Install lua-yar (LuaRocks)

luarocks install lua-yar

Step 2: Install lua-resty-yar (OPM)

opm get fangfengxiang/lua-resty-yar

Quick Start

HTTP Server

http {
    lua_package_path "/path/to/lua-yar/src/?.lua;/path/to/lua-yar/src/?/init.lua;;";

    init_by_lua_block {
        require("resty.yar").setup {
            service = {
                add = function(a, b) return a + b end,
                sub = function(a, b) return a - b end,
            }
        }
    }

    server {
        listen 8888;
        location /api {
            content_by_lua_block {
                require("resty.yar.server").serve()
            }
        }
    }
}

TCP Stream Server

stream {
    lua_package_path "/path/to/lua-yar/src/?.lua;/path/to/lua-yar/src/?/init.lua;;";

    init_by_lua_block {
        require("resty.yar").setup {
            service = {
                add = function(a, b) return a + b end,
            }
        }
    }

    server {
        listen 9999;
        content_by_lua_block {
            require("resty.yar.server").serve()
        }
    }
}

More examples: The t/ directory contains complete, runnable test-nginx test suites (http.t, tcp.t, client.t) that cover HTTP server, TCP stream server, and client usage patterns. These serve as additional working references.

API

require("resty.yar").setup(opts)

Call once in init_by_lua_block. Merges config, injects cosocket, creates Server/TcpServer instances.

Parameters:

OptionTypeDefaultDescription
servicetable{add, sub, greet}RPC service object (function fields = RPC methods)
packagerstring"JSON"Response encoding: "JSON" or "Msgpack"
connect_timeoutnumber1000Connection timeout (ms)
send_timeoutnumber5000Send timeout (ms)
read_timeoutnumber5000Read timeout (ms)
keepalive_idlenumber60000TCP keepalive idle timeout (ms)
timeoutnumber5000Per-message timeout for standalone run() mode (ms)
client_timeoutnumber3000Outbound RPC default timeout (ms)
pool_sizenumber30Cosocket connection pool size
on_worker_initfunctionnilWorker init callback (CHILD_INIT mapping)

require("resty.yar").get_http_server()

Returns the process-level Server instance (HTTP scenario). Error if setup() not called.

require("resty.yar").get_tcp_server()

Returns the process-level TcpServer instance (TCP stream scenario). Error if setup() not called.

require("resty.yar").get_config()

Returns the merged config table. Handlers use this to read connection-level parameters.

require("resty.yar").init_worker()

Call in init_worker_by_lua_block. Executes the on_worker_init callback if provided.

require("resty.yar.server").serve()

Unified entry point for content_by_lua_block. Auto-detects HTTP/stream context and dispatches to the appropriate handler.

You can also call handlers directly:

  • require("resty.yar.server.http").serve()
  • require("resty.yar.server.tcp").serve()

Client API

require("resty.yar").new_client(uri, opts)

Creates a Yar.Client instance with connection-level params pre-injected from setup() config. Each call creates a new instance.

Parameters:

OptionTypeDefaultDescription
uristringService URL: http://host/api or tcp://host:port
opts.timeoutnumberclient_timeoutPer-call timeout (ms)
opts.packagerstring"JSON"Request encoding
opts.connect_timeoutnumberconnect_timeoutConnection timeout (ms)
opts.keepalive_idlenumberkeepalive_idlePool idle timeout (ms)
opts.pool_sizenumberpool_sizeConnection pool size

Usage:

location /t {
    content_by_lua_block {
        local yar = require("resty.yar")
        local client = yar.new_client("http://127.0.0.1:8888/api")
        local result = client:call("add", { 1, 2 })  -- returns 3
    }
}

require("resty.yar").get_client(uri, opts)

Returns a memoized persistent Client instance by uri. Same uri returns the same instance within a worker. Enables socket reuse across calls (persistent mode).

location /t {
    content_by_lua_block {
        local yar = require("resty.yar")
        local client = yar.get_client("tcp://127.0.0.1:9999")
        local r1 = client:call("add", { 1, 2 })     -- persistent, socket reused
        local r2 = client:call("add", { 3, 4 })     -- same connection
    }
}

require("resty.yar.client")

Thin wrapper module providing new(uri, opts) and get(uri, opts) functions, delegating to init.new_client / init.get_client.

local client = require("resty.yar.client").new("http://host/api")
local pclient = require("resty.yar.client").get("tcp://host:9999")

yar-c Parameter Mapping

yar-c ParameterOpenResty EquivalentHow
READ_TIMEOUTsetup({connect_timeout, send_timeout, read_timeout})Three-stage cosocket timeouts via sock:settimeouts()
CHILD_INITsetup({on_worker_init = fn}) + init_worker()Called in init_worker_by_lua_block
PARENT_INITsetup() itselfCalled in init_by_lua_block
CUSTOM_DATAservice object closurePass via setup({service = {...}})
MAX_CHILDRENworker_processesnginx.conf directive
PID_FILEpidnginx.conf directive
LOG_FILE / LOG_LEVELerror_lognginx.conf directive
CHILD_USER / CHILD_GROUPusernginx.conf directive

Development

Prerequisites

  • OpenResty >= 1.19.3.1
  • lua-yar (installed via LuaRocks)
  • Perl (for test-nginx)
  • luacheck (for linting)

Run Tests

make test

Run Linter

make lint

License

Apache License 2.0

Author

fangfengxiang