它是什么
OpenResty = Nginx + LuaJIT + 几十个高性能 Lua 库。在 Nginx 请求处理的各阶段插入 Lua 代码——把 Nginx 当成"可编程网关"。
- 国内 / 国际很多大流量公司用它做 API 网关、CDN、WAF、限流
- Cloudflare、12306、阿里 Tengine、Kong 网关都基于这套思路
- 单核每秒能跑数十万 QPS
装
# macOS
brew install openresty/brew/openresty
# Linux: 见 openresty.org/en/installation.html
openresty -V # 查版本
第一个 nginx.conf
worker_processes 1;
events { worker_connections 1024; }
http {
server {
listen 8080;
location /hello {
default_type text/plain;
content_by_lua_block {
ngx.say("Hello, OpenResty")
ngx.say("client IP: ", ngx.var.remote_addr)
}
}
}
}
跑:
openresty -p $PWD -c nginx.conf
curl http://localhost:8080/hello
# Hello, OpenResty
# client IP: 127.0.0.1
主要执行阶段
Nginx 处理请求分多个阶段,OpenResty 都能插 Lua:
| 阶段 | 指令 | 用途 |
|---|---|---|
| 初始化 | init_by_lua_block |
进程启动时(加载共享数据) |
| 启动 worker | init_worker_by_lua_block |
每个 worker 启动时 |
| 重写 URL | rewrite_by_lua_block |
改 URI、重定向 |
| 访问控制 | access_by_lua_block |
鉴权、限流、IP 黑名单 |
| 生成内容 | content_by_lua_block |
完全用 Lua 出响应 |
| 修改响应头 | header_filter_by_lua_block |
加 / 改 header |
| 修改响应体 | body_filter_by_lua_block |
改输出内容 |
| 日志 | log_by_lua_block |
记日志、上报 |
限流例
http {
lua_shared_dict my_limit 10m; -- 10MB 共享内存
server {
location /api/ {
access_by_lua_block {
local limit = require "resty.limit.count"
local lim, err = limit.new("my_limit", 10, 60) -- 60 秒 10 次
if not lim then ngx.exit(500) end
local key = ngx.var.binary_remote_addr
local delay, err = lim:incoming(key, true)
if not delay then
if err == "rejected" then ngx.exit(429) end
ngx.exit(500)
end
}
proxy_pass http://upstream;
}
}
}
lua-resty-limit-traffic 是官方限流库——还有 token bucket、leaky bucket 等算法。
鉴权例
location /api/ {
access_by_lua_block {
local jwt = require "resty.jwt"
local token = ngx.var.http_authorization
if not token then ngx.exit(401) end
local obj = jwt:verify("your-secret", token:sub(8)) -- 去掉 "Bearer "
if not obj.verified then ngx.exit(401) end
ngx.req.set_header("X-User-ID", obj.payload.user_id)
}
proxy_pass http://upstream;
}
lua-resty-jwt 验签后把 user_id 注入下游请求头。
后端调用
local resp, err = ngx.location.capture("/internal_api", {
method = ngx.HTTP_POST,
body = '{"x": 1}',
})
if resp.status == 200 then
ngx.say(resp.body)
end
ngx.location.capture 是 OpenResty 高效的子请求——比 Lua 自己 fetch 快。
外部 HTTP 用 lua-resty-http:
local http = require "resty.http"
local httpc = http.new()
local res, err = httpc:request_uri("http://api.example.com/data", {
method = "GET",
headers = { ["X-Api-Key"] = "secret" },
})
ngx.say(res.body)
连接 Redis
local redis = require "resty.redis"
local red = redis:new()
red:set_timeout(1000)
local ok, err = red:connect("127.0.0.1", 6379)
if not ok then ngx.exit(500) end
red:set("hello", "world")
local val = red:get("hello")
ngx.say(val)
red:set_keepalive(10000, 100) -- 复用连接 10s,池大小 100
set_keepalive 不是关闭——是放回连接池给下个请求用。
共享字典(worker 间共享)
http {
lua_shared_dict counters 10m;
server {
location /count {
content_by_lua_block {
local counters = ngx.shared.counters
local newval, err = counters:incr("requests", 1, 0)
ngx.say("总请求数: ", newval)
}
}
}
}
ngx.shared.<name> 是所有 worker 进程共享的内存——避免开多 worker 各算各的。
cosocket 异步
OpenResty 的高性能秘密:Lua 协程 + Nginx 事件循环。socket = ngx.socket.tcp() 操作不阻塞 worker——其他请求并发进行。
底层就是 第 13 篇 讲的协程被 Nginx 调度。
不要做的事
- 不要写超长循环——会阻塞整个 worker
- 不要
os.execute/ 大 IO——同步阻塞 - 不要在
init_by_lua用 cosocket——那时事件循环没起来
OpenResty 官方文档 openresty.org/en/ 有详尽指南;中文社区 openresty.com.cn 教程多。
→ 下一篇 把 Lua 嵌入 C/C++