您好,登录后才能下订单哦!
密码登录
登录注册
点击 登录注册 即表示同意《亿速云用户服务条款》
# 如何通过 Lua 扩展 Nginx
## 引言
在现代 Web 开发中,Nginx 已成为最受欢迎的 Web 服务器和反向代理之一。其高性能、低资源消耗和模块化架构使其成为处理高并发请求的理想选择。然而,Nginx 的官方模块系统虽然强大,但开发门槛较高且需要重新编译。这时,**Lua** 语言与 Nginx 的结合(通过 ngx_lua 模块)为我们提供了一种灵活、高效的扩展方式。
本文将深入探讨如何通过 Lua 扩展 Nginx 的功能,涵盖以下内容:
1. Lua 与 Nginx 的结合原理
2. 环境搭建与基础配置
3. Lua 在 Nginx 中的核心应用场景
4. 高级技巧与性能优化
5. 实战案例解析
6. 安全注意事项
## 一、Lua 与 Nginx 的结合原理
### 1.1 ngx_lua 模块概述
ngx_lua 是 Nginx 的一个第三方模块,它通过将 Lua 解释器嵌入到 Nginx 中,实现了在 Nginx 的各个处理阶段执行 Lua 脚本的能力。该模块具有以下特点:
- **非阻塞架构**:与 Nginx 的事件模型完美契合
- **轻量级**:Lua 虚拟机占用资源极少
- **高性能**:关键路径使用 C 语言优化
- **灵活性**:支持热更新脚本而不重启服务
### 1.2 处理阶段与执行顺序
Nginx 处理请求分为多个阶段,ngx_lua 可以在这些阶段插入 Lua 处理:
```nginx
location /example {
# 重写阶段
rewrite_by_lua_block {
ngx.log(ngx.INFO, "rewrite phase")
}
# 访问控制阶段
access_by_lua_block {
ngx.log(ngx.INFO, "access phase")
}
# 内容生成阶段
content_by_lua_block {
ngx.say("Hello, Lua!")
}
# 日志记录阶段
log_by_lua_block {
ngx.log(ngx.INFO, "log phase")
}
}
OpenResty 是一个集成了 Nginx 和 Lua 相关模块的增强版:
# Ubuntu/Debian
sudo apt-get install -y openresty
# CentOS/RHEL
sudo yum install -y openresty
# 下载 Nginx 和 ngx_lua
wget http://nginx.org/download/nginx-1.25.3.tar.gz
git clone https://github.com/openresty/lua-nginx-module.git
# 编译安装
tar zxvf nginx-1.25.3.tar.gz
cd nginx-1.25.3
./configure --add-module=../lua-nginx-module
make && sudo make install
nginx.conf
最小配置:
worker_processes 1;
events {
worker_connections 1024;
}
http {
lua_package_path "/path/to/lua/scripts/?.lua;;";
server {
listen 8080;
location /lua {
content_by_lua_block {
ngx.say("Lua is running!")
}
}
}
}
location ~ ^/user/(\d+) {
content_by_lua_block {
local user_id = tonumber(ngx.var[1])
if user_id > 10000 then
ngx.exec("/premium", { id = user_id })
else
ngx.exec("/standard", { id = user_id })
end
}
}
access_by_lua_block {
local token = ngx.req.get_headers()["X-Auth-Token"]
if not token or token ~= "SECRET_KEY" then
ngx.status = ngx.HTTP_FORBIDDEN
ngx.say("Access denied")
ngx.exit(ngx.HTTP_FORBIDDEN)
end
}
header_filter_by_lua_block {
-- 添加自定义响应头
ngx.header["X-Lua-Powered"] = "ngx_lua"
-- 修改Content-Type
if ngx.var.uri:match("%.json$") then
ngx.header.content_type = "application/json"
end
}
lua_shared_dict my_cache 10m;
location /api {
content_by_lua_block {
local cache = ngx.shared.my_cache
local key = ngx.var.request_uri
-- 尝试从缓存获取
local value = cache:get(key)
if value then
ngx.say(value)
return
end
-- 缓存未命中,执行业务逻辑
local result = do_expensive_operation()
-- 设置缓存(过期时间60秒)
cache:set(key, result, 60)
ngx.say(result)
}
}
location /async {
content_by_lua_block {
local http = require "resty.http"
local httpc = http.new()
-- 并发执行多个请求
local res1, res2 = ngx.thread.spawn(function()
return httpc:request_uri("http://service1")
end), ngx.thread.spawn(function()
return httpc:request_uri("http://service2")
end)
-- 等待所有请求完成
ngx.print(res1.body..res2.body)
}
}
-- 初始化共享字典
lua_shared_dict counters 1m;
location /counter {
content_by_lua_block {
local counters = ngx.shared.counters
local key = "visits:"..ngx.var.remote_addr
-- 原子性递增
local newval, err = counters:incr(key, 1)
if not newval then
counters:set(key, 1)
end
ngx.say("Total visits: ", counters:get(key))
}
}
location /ffi {
content_by_lua_block {
local ffi = require "ffi"
-- 声明C函数
ffi.cdef[[
int printf(const char *fmt, ...);
]]
-- 调用标准库函数
ffi.C.printf("Hello from C!%c", 10)
ngx.say("FFI call executed")
}
}
jwt_auth.lua
:
local jwt = require "resty.jwt"
local _M = {}
function _M.verify(secret)
local auth_header = ngx.var.http_Authorization
if not auth_header then
return nil, "Missing authorization header"
end
local token = auth_header:match("Bearer%s+(.+)")
if not token then
return nil, "Invalid token format"
end
local ok, claims = jwt:verify(secret, token)
if not ok then
return nil, "Invalid token"
end
return claims
end
return _M
使用示例:
location /protected {
access_by_lua_block {
local jwt = require "jwt_auth"
local claims, err = jwt.verify("YOUR_SECRET_KEY")
if not claims then
ngx.status = ngx.HTTP_UNAUTHORIZED
ngx.say('{"error":"'..err..'"}')
ngx.exit(ngx.HTTP_UNAUTHORIZED)
end
-- 将用户信息传递给后续处理
ngx.ctx.user_id = claims.sub
}
proxy_pass http://backend;
}
ab_test.lua
:
local _M = {}
function _M.run(variants)
local sum = 0
for _, percent in pairs(variants) do
sum = sum + percent
end
local rand = math.random() * sum
local accum = 0
for name, percent in pairs(variants) do
accum = accum + percent
if rand <= accum then
return name
end
end
end
return _M
使用示例:
location /homepage {
content_by_lua_block {
local ab_test = require "ab_test"
local variant = ab_test.run({
["design_a"] = 0.5, -- 50%流量
["design_b"] = 0.3, -- 30%流量
["design_c"] = 0.2 -- 20%流量
})
ngx.exec("/"..variant)
}
}
-- 不安全的写法
local param = ngx.var.arg_query
local sql = "SELECT * FROM users WHERE name = '"..param.."'"
-- 安全的写法
local db = require "resty.mysql"
local conn = db:new()
local res, err = conn:query("SELECT * FROM users WHERE name = ?", {param})
# 限制LVM内存使用
lua_max_running_timers 100;
lua_max_pending_timers 100;
lua_shared_dict my_cache 10m;
# 执行超时设置
lua_socket_connect_timeout 100ms;
lua_socket_send_timeout 200ms;
lua_socket_read_timeout 500ms;
log_by_lua_block {
local log_data = {
time = ngx.now(),
uri = ngx.var.uri,
status = ngx.status,
-- 过滤敏感头字段
headers = {
["user-agent"] = ngx.var.http_user_agent,
["accept"] = ngx.var.http_accept
}
}
ngx.log(ngx.INFO, require("cjson").encode(log_data))
}
通过 Lua 扩展 Nginx 为我们打开了一扇新的大门,使得在保持 Nginx 高性能的同时,能够实现复杂的业务逻辑。从简单的请求处理到复杂的微服务网关,Lua 都能胜任。关键优势包括:
随着云原生和微服务架构的普及,Nginx + Lua 的组合将在 API 网关、边缘计算等场景发挥更大作用。希望本文能为您的 Nginx 扩展之旅提供有价值的参考。
场景 | 纯Nginx (req/s) | Nginx+Lua (req/s) | 损耗率 |
---|---|---|---|
静态文件 | 50,000 | 48,000 | 4% |
简单Lua逻辑 | - | 45,000 | - |
数据库访问 | - | 12,000 | - |
复杂业务处理 | - | 8,000 | - |
测试环境:4核CPU/8GB内存,并发连接数1000 “`
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。