插件窝 干货文章 nginx怎么集成lua操作mysql

nginx怎么集成lua操作mysql

MySQL OpenResty local 连接 621    来源:    2025-04-24

Nginx集成Lua操作MySQL的完整方案

要在Nginx中集成Lua脚本操作MySQL数据库,你需要使用OpenResty(一个集成了LuaJIT的Nginx增强版)以及相关的Lua MySQL驱动。以下是完整的实现步骤:

1. 安装OpenResty

首先需要安装OpenResty而不是普通的Nginx:

# Ubuntu/Debian
sudo apt-get install -y software-properties-common
sudo add-apt-repository -y ppa:openresty/ppa
sudo apt-get update
sudo apt-get install -y openresty

# CentOS/RHEL
sudo yum install -y yum-utils
sudo yum-config-manager --add-repo https://openresty.org/package/centos/openresty.repo
sudo yum install -y openresty

2. 安装Lua MySQL驱动

OpenResty默认不包含MySQL驱动,需要单独安装:

# 安装LuaRocks(Lua包管理器)
wget https://luarocks.org/releases/luarocks-3.9.2.tar.gz
tar -xzvf luarocks-3.9.2.tar.gz
cd luarocks-3.9.2
./configure --prefix=/usr/local/openresty/luajit \
    --with-lua=/usr/local/openresty/luajit/ \
    --lua-suffix=jit \
    --with-lua-include=/usr/local/openresty/luajit/include/luajit-2.1
make && sudo make install

# 安装MySQL驱动
sudo /usr/local/openresty/luajit/bin/luarocks install luasql-mysql MYSQL_INCDIR=/usr/include/mysql

3. 配置Nginx使用Lua操作MySQL

编辑Nginx配置文件(通常位于/usr/local/openresty/nginx/conf/nginx.conf):

http {
    # 设置Lua包路径
    lua_package_path "/usr/local/openresty/luajit/share/lua/5.1/?.lua;;";
    lua_package_cpath "/usr/local/openresty/luajit/lib/lua/5.1/?.so;;";

    server {
        listen 80;

        location /query {
            # 使用content_by_lua_block处理请求
            content_by_lua_block {
                -- 加载MySQL驱动
                local luasql = require "luasql.mysql"

                -- 创建环境对象
                local env = luasql.mysql()

                -- 连接数据库
                local conn = env:connect("database_name", "username", "password", "host", port)

                if not conn then
                    ngx.say("Failed to connect to MySQL")
                    return
                end

                -- 执行查询
                local cursor, err = conn:execute("SELECT * FROM your_table LIMIT 10")
                if not cursor then
                    ngx.say("Query failed: ", err)
                    conn:close()
                    env:close()
                    return
                end

                -- 获取结果
                ngx.say("<h1>Query Results:</h1>")
                ngx.say("<table border='1'>")

                -- 获取列名
                local columns = {}
                for i = 1, cursor:numfields() do
                    columns[i] = cursor:getcolname(i)
                    ngx.say("<th>", columns[i], "</th>")
                end

                -- 获取行数据
                local row = cursor:fetch({}, "a")
                while row do
                    ngx.say("<tr>")
                    for _, col in ipairs(columns) do
                        ngx.say("<td>", row[col], "</td>")
                    end
                    ngx.say("</tr>")
                    row = cursor:fetch(row, "a")
                end

                ngx.say("</table>")

                -- 关闭连接
                cursor:close()
                conn:close()
                env:close()
            }
        }
    }
}

4. 更高效的连接池方案

上述方案每次请求都新建连接,效率不高。可以使用连接池优化:

-- 在init_by_lua阶段创建连接池
init_by_lua_block {
    local mysql = require "resty.mysql"

    -- 创建连接池
    function get_mysql_conn()
        local db, err = mysql:new()
        if not db then
            ngx.log(ngx.ERR, "failed to instantiate mysql: ", err)
            return nil, err
        end

        db:set_timeout(1000) -- 1秒超时

        local ok, err, errno, sqlstate = db:connect{
            host = "127.0.0.1",
            port = 3306,
            database = "test_db",
            user = "test_user",
            password = "test_pass",
            max_packet_size = 1024 * 1024
        }

        if not ok then
            ngx.log(ngx.ERR, "failed to connect: ", err, ": ", errno, " ", sqlstate)
            return nil, err
        end

        return db
    end
}

server {
    location /query {
        content_by_lua_block {
            local db = get_mysql_conn()
            if not db then
                ngx.say("failed to connect to mysql")
                return
            end

            local res, err, errno, sqlstate = db:query("SELECT * FROM users LIMIT 10")
            if not res then
                ngx.say("bad result: ", err, ": ", errno, ": ", sqlstate, ".")
                return
            end

            ngx.say("result: ", require("cjson").encode(res))

            -- 将连接放回连接池
            local ok, err = db:set_keepalive(10000, 100)
            if not ok then
                ngx.log(ngx.ERR, "failed to set keepalive: ", err)
                return
            end
        }
    }
}

5. 使用lua-resty-mysql库(推荐)

OpenResty社区提供了更高效的lua-resty-mysql库:

  1. 首先确保安装了该库(通常OpenResty已包含):
sudo /usr/local/openresty/luajit/bin/luarocks install lua-resty-mysql
  1. 配置示例:
location /api/user {
    content_by_lua_block {
        local mysql = require "resty.mysql"
        local db, err = mysql:new()

        if not db then
            ngx.say("failed to instantiate mysql: ", err)
            return
        end

        db:set_timeout(1000) -- 1秒超时

        local ok, err, errno, sqlstate = db:connect{
            host = "127.0.0.1",
            port = 3306,
            database = "test_db",
            user = "test_user",
            password = "test_pass",
            charset = "utf8",
            max_packet_size = 1024 * 1024,
        }

        if not ok then
            ngx.say("failed to connect: ", err, ": ", errno, " ", sqlstate)
            return
        end

        -- 使用参数化查询防止SQL注入
        local res, err, errno, sqlstate = db:query("SELECT * FROM users WHERE id = ?", {ngx.var.arg_id})

        if not res then
            ngx.say("bad result: ", err, ": ", errno, ": ", sqlstate, ".")
            return
        end

        ngx.say("result: ", require("cjson").encode(res))

        -- 将连接放回连接池
        local ok, err = db:set_keepalive(10000, 100)
        if not ok then
            ngx.log(ngx.ERR, "failed to set keepalive: ", err)
            return
        end
    }
}

6. 安全注意事项

  1. SQL注入防护:始终使用参数化查询(如lua-resty-mysql?占位符)
  2. 连接泄露:确保每个连接在使用后正确关闭或放回连接池
  3. 错误处理:妥善处理所有可能的错误情况
  4. 权限控制:数据库用户应仅具有必要的最小权限
  5. 性能监控:监控查询性能,避免慢查询影响系统

7. 常见问题解决

  1. 连接失败

    • 检查MySQL服务是否运行
    • 确认用户名密码正确
    • 检查MySQL是否允许远程连接(如果需要)
  2. 模块未找到

    • 确认Lua包路径配置正确
    • 使用luarocks list确认模块已安装
  3. 性能问题

    • 使用连接池
    • 避免在Lua中处理大量数据
    • 考虑使用Nginx缓存频繁访问的数据

通过以上步骤,你可以在Nginx中高效地使用Lua脚本操作MySQL数据库。