Lunet: Design and Implementation of a High-Performance Coroutine Network Library

July 12, 2025 · 1511 words · 8 min

Lunet is a high-performance runtime written in C that integrates LuaJIT and libuv, focusing on coroutine-driven asynchronous programming. It simplifies the handling of asynchronous I/O and blocking tasks, enabling developers to easily write high-concurrency network applications using Lua scripts. It’s ideal for scenarios like game servers and network services.

What is Lunet

In modern application development, high concurrency and asynchronous programming have become essential skills. However, traditional asynchronous programming often requires developers to handle complex callback functions and state management, resulting in poor code readability. Lunet cleverly solves this problem through coroutine technology.

Lunet is a coroutine-based network library that provides synchronous-style APIs with asynchronous execution underneath. This means you can write asynchronous programs like synchronous code, maintaining code clarity and readability while achieving high performance through asynchronous execution.

Core Architecture

Lunet’s architectural design fully embodies the philosophy of “standing on the shoulders of giants”:

Technology Stack

  • C Core: High-performance native implementation providing optimal execution efficiency
  • LuaJIT: Fast Lua execution environment supporting FFI (Foreign Function Interface)
  • libuv: Cross-platform asynchronous I/O library, the underlying engine of Node.js
  • Coroutines: Concurrent programming using Lua coroutines

Design Philosophy

  1. Simplicity: Provides simple and intuitive APIs, reducing learning costs
  2. High Performance: Fully utilizes LuaJIT’s JIT compilation capabilities and libuv’s asynchronous features
  3. Modularity: Divides functionality into modules for easy maintenance and extension
  4. Cross-platform: Supports Linux, macOS, and Windows

Core Modules Explained

1. Core Module (lunet)

The core module provides basic functionality for coroutine management:

local lunet = require('lunet')

-- Create and run new coroutine
lunet.spawn(function()
    print("Hello from coroutine!")
    lunet.sleep(1000)  -- Non-blocking sleep
    print("After 1 second")
end)

2. Network Module (lunet.socket)

The network module is the core of Lunet, providing complete TCP network programming capabilities:

local socket = require('lunet.socket')

-- Create TCP server
local listener, err = socket.listen("tcp", "127.0.0.1", 8080)
if not listener then
    error("Failed to listen: " .. err)
end

-- Accept connections
lunet.spawn(function()
    while true do
        local client, err = socket.accept(listener)
        if client then
            -- Handle each client in a new coroutine
            lunet.spawn(function()
                local data, err = socket.read(client)
                if data then
                    socket.write(client, "Echo: " .. data)
                end
                socket.close(client)
            end)
        end
    end
end)

3. File System Module (lunet.fs)

The file system module provides asynchronous file operation capabilities:

local fs = require('lunet.fs')

lunet.spawn(function()
    -- Asynchronous file operations
    local file, err = fs.open('config.json', 'r')
    if file then
        local content, err = fs.read(file, 1024)
        if content then
            print("Config content:", content)
        end
        fs.close(file)
    end
    
    -- Directory traversal
    local entries, err = fs.scandir('./logs')
    if entries then
        for i, entry in ipairs(entries) do
            print("Log file:", entry.name, "type:", entry.type)
        end
    end
end)

4. Database Module (lunet.mysql)

The database module provides asynchronous MySQL database access capabilities:

local mysql = require('lunet.mysql')

lunet.spawn(function()
    local conn, err = mysql.open({
        host = "localhost",
        port = 3306,
        user = "root",
        password = "password",
        database = "myapp"
    })
    
    if conn then
        -- Query operations
        local users, err = mysql.query(conn, "SELECT * FROM users WHERE active = 1")
        if users then
            for i, user in ipairs(users) do
                print("User:", user.name, user.email)
            end
        end
        
        -- Update operations
        local result, err = mysql.exec(conn, 
            "UPDATE users SET last_login = NOW() WHERE id = ?", {user_id})
        if result then
            print("Updated", result.affected_rows, "rows")
        end
        
        mysql.close(conn)
    end
end)

Practical Case: Building a High-Performance Web Server

Let’s demonstrate Lunet’s powerful capabilities through a complete example:

local lunet = require('lunet')
local socket = require('lunet.socket')
local fs = require('lunet.fs')
local mysql = require('lunet.mysql')

-- Database connection pool
local db_pool = {}

-- Initialize database connections
local function init_database()
    for i = 1, 10 do  -- Create 10 connections
        local conn, err = mysql.open({
            host = "localhost",
            port = 3306,
            user = "webuser",
            password = "password",
            database = "website"
        })
        if conn then
            table.insert(db_pool, conn)
        end
    end
end

-- Get database connection
local function get_db_connection()
    if #db_pool > 0 then
        return table.remove(db_pool, 1)
    end
    return nil
end

-- Release database connection
local function release_db_connection(conn)
    table.insert(db_pool, conn)
end

-- Handle HTTP requests
local function handle_request(client)
    local request, err = socket.read(client)
    if not request then
        socket.close(client)
        return
    end
    
    -- Parse request
    local method, path = request:match("([A-Z]+) ([^ ]+)")
    
    if path == "/api/users" then
        -- Handle user API
        local conn = get_db_connection()
        if conn then
            local users, err = mysql.query(conn, "SELECT id, name, email FROM users LIMIT 10")
            release_db_connection(conn)
            
            if users then
                local json_response = json.encode(users)
                local response = string.format(
                    "HTTP/1.1 200 OK\r\n" ..
                    "Content-Type: application/json\r\n" ..
                    "Content-Length: %d\r\n\r\n%s",
                    #json_response, json_response
                )
                socket.write(client, response)
            end
        end
    elseif path == "/static/" then
        -- Handle static files
        local filename = path:sub(9)  -- Remove "/static/"
        local file, err = fs.open("public/" .. filename, "r")
        if file then
            local content, err = fs.read(file, 1024 * 1024)  -- Read 1MB
            fs.close(file)
            
            if content then
                local response = string.format(
                    "HTTP/1.1 200 OK\r\n" ..
                    "Content-Type: text/html\r\n" ..
                    "Content-Length: %d\r\n\r\n%s",
                    #content, content
                )
                socket.write(client, response)
            end
        end
    else
        -- 404 response
        local response = "HTTP/1.1 404 Not Found\r\nContent-Length: 0\r\n\r\n"
        socket.write(client, response)
    end
    
    socket.close(client)
end

-- Main server logic
local function start_server()
    init_database()
    
    local listener, err = socket.listen("tcp", "0.0.0.0", 8080)
    if not listener then
        error("Failed to start server: " .. err)
    end
    
    print("Server started on http://0.0.0.0:8080")
    
    lunet.spawn(function()
        while true do
            local client, err = socket.accept(listener)
            if client then
                -- Create independent coroutine for each client
                lunet.spawn(function()
                    handle_request(client)
                end)
            end
        end
    end)
end

-- Start server
start_server()

Performance Advantages

1. Coroutines vs Threads

Traditional multi-threading models require creating one thread per connection, which brings the following problems:

  • Thread creation and destruction overhead
  • Context switching costs
  • Memory consumption (each thread requires several MB of stack space)

Lunet’s coroutine model is different:

  • Coroutine creation has almost zero cost
  • Coroutine switching is controlled by user space, no kernel involvement needed
  • A single coroutine only occupies a few KB of memory

2. Advantages of Asynchronous I/O

Through libuv’s event loop, Lunet can handle thousands of concurrent connections in a single thread, avoiding the blocking issues of traditional synchronous I/O.

3. LuaJIT Performance

LuaJIT’s JIT compiler can compile hot code paths to machine code, achieving performance close to C language, far exceeding traditional interpreted execution.

Use Cases

1. Game Server

-- Game server example
local lunet = require('lunet')
local socket = require('lunet.socket')

-- Player connection management
local players = {}

local function handle_player(client)
    local player_id = generate_player_id()
    players[player_id] = {
        client = client,
        x = 0,
        y = 0,
        last_heartbeat = os.time()
    }
    
    while true do
        local message, err = socket.read(client)
        if not message then
            break
        end
        
        -- Handle game messages
        local msg_type, data = parse_message(message)
        if msg_type == "move" then
            players[player_id].x = data.x
            players[player_id].y = data.y
            broadcast_player_position(player_id, data.x, data.y)
        elseif msg_type == "heartbeat" then
            players[player_id].last_heartbeat = os.time()
        end
    end
    
    -- Player disconnection
    players[player_id] = nil
    socket.close(client)
end

2. Microservice Gateway

-- API gateway example
local lunet = require('lunet')
local socket = require('lunet.socket')

-- Service discovery
local services = {
    user_service = {"127.0.0.1:3001", "127.0.0.1:3002"},
    order_service = {"127.0.0.1:3003", "127.0.0.1:3004"}
}

local function proxy_request(service_name, request)
    local service_list = services[service_name]
    if not service_list then
        return nil, "Service not found"
    end
    
    -- Load balancing (round-robin)
    local service_addr = service_list[math.random(#service_list)]
    local host, port = service_addr:match("([^:]+):(%d+)")
    
    -- Forward request
    local backend, err = socket.connect(host, tonumber(port))
    if not backend then
        return nil, "Failed to connect to backend"
    end
    
    socket.write(backend, request)
    local response, err = socket.read(backend)
    socket.close(backend)
    
    return response, err
end

3. Real-time Data Processing

-- Real-time data processing example
local lunet = require('lunet')
local socket = require('lunet.socket')

-- Data processing pipeline
local function process_data_stream()
    local listener, err = socket.listen("tcp", "0.0.0.0", 9999)
    if not listener then
        error("Failed to start data processor")
    end
    
    lunet.spawn(function()
        while true do
            local client, err = socket.accept(listener)
            if client then
                lunet.spawn(function()
                    while true do
                        local data, err = socket.read(client)
                        if not data then
                            break
                        end
                        
                        -- Data processing
                        local processed = process_json_data(data)
                        
                        -- Send to message queue
                        send_to_queue(processed)
                        
                        -- Real-time statistics
                        update_metrics(processed)
                    end
                    socket.close(client)
                end)
            end
        end
    end)
end

Development Experience

1. Type Safety

Lunet provides complete type definitions, supporting intelligent hints in modern IDEs:

---@type lunet
local lunet = require('lunet')

-- IDE will provide complete function signatures and documentation
lunet.spawn(function()
    lunet.sleep(1000)  -- Smart hint: Sleep for specified milliseconds
end)

2. Error Handling

Lunet adopts Lua-style error handling patterns:

local result, err = some_operation()
if not result then
    -- Handle error
    print("Error:", err)
    return
end
-- Continue processing result

3. Debugging Support

Since business logic is written in Lua, you can utilize LuaJIT’s debugging tools for development and debugging.

Summary

Lunet, through clever design, perfectly combines coroutines, asynchronous I/O, and high-performance runtime, providing developers with a powerful yet easy-to-use network programming framework. Its main advantages include:

  1. High development efficiency: Synchronous-style APIs that are easy to understand and maintain
  2. Excellent performance: Based on LuaJIT and libuv, with extremely high concurrency capabilities
  3. Complete functionality: Covers common modules like networking, file systems, and databases
  4. Cross-platform: Supports mainstream operating systems
  5. Community-friendly: Open source project with continuous updates and maintenance

Whether you’re building high-concurrency web servers, game servers, or real-time data processing systems, Lunet can provide you with powerful technical support. If you’re looking for a high-performance, easy-to-use network programming framework, Lunet is definitely worth trying.

This article provides an in-depth introduction to Lunet’s design philosophy, core functionality, and usage methods, hoping to help you better understand and use this powerful network programming framework.