OpenResty vs Nginx: 完整指南
什么是 Nginx?
Nginx 是一个高性能的 HTTP 服务器和反向代理,同时也是 IMAP/POP3 代理服务器。它以以下特性著称:
- 高并发处理能力
- 低内存占用
- 静态内容服务
- 负载均衡
- 反向代理
什么是 OpenResty?
OpenResty 是一个功能完整的 Web 平台,它将 Nginx 与 LuaJIT(Lua 的即时编译器)整合在一起。本质上它是:
- Nginx 核心 + LuaJIT + 许多精心编写的 Lua 库
- 一个强大的 Web 应用服务器
- 一个动态 Web 平台
核心差异
1. 核心架构
| 特性 |
Nginx |
OpenResty |
| 基础 |
纯 C 实现 |
Nginx + LuaJIT |
| 可扩展性 |
仅 C 模块 |
Lua 脚本 + C 模块 |
| 动态逻辑 |
有限(通过配置) |
完整编程能力 |
| 学习曲线 |
基于配置 |
编程 + 配置 |
2. 编程能力
Nginx:
# 仅限于配置指令
location /api {
proxy_pass http://backend;
proxy_set_header X-Real-IP $remote_addr;
}
OpenResty:
# 完整的 Lua 编程能力
location /api {
content_by_lua_block {
local cjson = require "cjson"
local redis = require "resty.redis"
-- 复杂业务逻辑
local red = redis:new()
red:connect("127.0.0.1", 6379)
local data = red:get("user:123")
ngx.say(cjson.encode({status = "ok", data = data}))
}
}
3. 使用场景
Nginx 更适合:
- 简单反向代理
- 静态文件服务
- 基础负载均衡
- SSL 终端
- 简单 URL 重写
OpenResty 更适合:
- 动态内容生成
- 复杂认证/授权
- API 网关
- WAF(Web 应用防火墙)
- 实时数据处理
- 微服务编排
- 复杂规则的限流
- A/B 测试
- 动态路由
为什么使用 OpenResty?
1. 性能
- LuaJIT 提供接近原生 C 的性能
- 非阻塞 I/O 操作
- 工作进程间的共享内存
- 高效的连接池
2. 灵活性
- 无需重新编译 Nginx 即可编写复杂逻辑
- 无需重启即可热加载 Lua 代码
- 可访问丰富的 Lua 生态系统
- 易于与数据库、Redis 等集成
3. 生产力
- 快速开发和迭代
- 无需编写 C 模块
- 丰富的库生态系统(resty 库)
- 比 C 模块更容易调试
4. 实际应用示例
-- 示例:基于用户等级的动态限流
access_by_lua_block {
local redis = require "resty.redis"
local red = redis:new()
red:connect("127.0.0.1", 6379)
local user_id = ngx.var.arg_user_id
local user_tier = red:get("user:" .. user_id .. ":tier")
local limit = user_tier == "premium" and 1000 or 100
-- 实现限流逻辑
local current = red:incr("rate:" .. user_id)
if current > limit then
ngx.exit(429) -- 请求过多
end
}
如何使用 OpenResty
安装
Linux (Ubuntu/Debian)
# 添加 OpenResty 仓库
wget -qO - https://openresty.org/package/pubkey.gpg | sudo apt-key add -
sudo apt-get -y install software-properties-common
sudo add-apt-repository -y "deb http://openresty.org/package/ubuntu $(lsb_release -sc) main"
# 安装 OpenResty
sudo apt-get update
sudo apt-get install openresty
macOS
brew install openresty/brew/openresty
Docker
FROM openresty/openresty:alpine
COPY nginx.conf /usr/local/openresty/nginx/conf/nginx.conf
COPY lua/ /usr/local/openresty/nginx/lua/
EXPOSE 80
CMD ["/usr/local/openresty/bin/openresty", "-g", "daemon off;"]
基础配置
nginx.conf
worker_processes auto;
error_log logs/error.log;
events {
worker_connections 1024;
}
http {
# Lua 包路径
lua_package_path "/usr/local/openresty/nginx/lua/?.lua;;";
# 用于缓存的共享内存
lua_shared_dict my_cache 10m;
server {
listen 80;
server_name localhost;
# 简单的 Lua 处理器
location /hello {
content_by_lua_block {
ngx.say("Hello, OpenResty!")
}
}
# 加载外部 Lua 文件
location /api {
content_by_lua_file lua/api.lua;
}
# 访问控制
location /protected {
access_by_lua_block {
local token = ngx.var.http_authorization
if not token or token ~= "Bearer secret123" then
ngx.exit(401)
end
}
proxy_pass http://backend;
}
}
}
常见使用场景
1. API 网关
location /api/v1/ {
access_by_lua_block {
-- 身份验证
local jwt = require "resty.jwt"
local token = ngx.var.http_authorization
if not token then
ngx.exit(401)
end
local jwt_obj = jwt:verify("secret", token)
if not jwt_obj.verified then
ngx.exit(403)
end
-- 设置用户上下文
ngx.var.user_id = jwt_obj.payload.sub
}
# 路由到后端
proxy_pass http://backend_service;
}
2. Redis 缓存
location /data {
content_by_lua_block {
local redis = require "resty.redis"
local cjson = require "cjson"
local red = redis:new()
red:set_timeout(1000)
local ok, err = red:connect("127.0.0.1", 6379)
if not ok then
ngx.log(ngx.ERR, "连接 Redis 失败: ", err)
ngx.exit(500)
end
local cache_key = "data:" .. ngx.var.arg_id
local cached = red:get(cache_key)
if cached ~= ngx.null then
ngx.say(cached)
return
end
-- 从数据库获取数据(伪代码)
local data = fetch_from_db(ngx.var.arg_id)
-- 缓存 1 小时
red:setex(cache_key, 3600, cjson.encode(data))
ngx.say(cjson.encode(data))
}
}
3. 限流
location /api {
access_by_lua_block {
local limit_req = require "resty.limit.req"
-- 允许每秒 10 个请求,突发 20 个
local lim, err = limit_req.new("my_limit_store", 10, 20)
if not lim then
ngx.log(ngx.ERR, "实例化限流器失败: ", err)
return 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
return ngx.exit(429)
end
ngx.log(ngx.ERR, "限流失败: ", err)
return ngx.exit(500)
end
if delay >= 0.001 then
ngx.sleep(delay)
end
}
proxy_pass http://backend;
}
4. 动态路由
location /service/ {
rewrite_by_lua_block {
local redis = require "resty.redis"
local red = redis:new()
red:connect("127.0.0.1", 6379)
-- 从 Redis 获取服务版本
local version = red:get("service:version")
if version == "v2" then
ngx.var.backend = "http://service-v2:8080"
else
ngx.var.backend = "http://service-v1:8080"
end
}
proxy_pass $backend;
}
5. 请求/响应转换
location /transform {
# 修改请求
rewrite_by_lua_block {
ngx.req.set_header("X-Custom-Header", "value")
-- 修改请求体
ngx.req.read_body()
local body = ngx.req.get_body_data()
if body then
local cjson = require "cjson"
local data = cjson.decode(body)
data.timestamp = ngx.time()
ngx.req.set_body_data(cjson.encode(data))
end
}
# 修改响应
body_filter_by_lua_block {
local chunk = ngx.arg[1]
if chunk then
-- 转换响应
ngx.arg[1] = string.upper(chunk)
end
}
proxy_pass http://backend;
}
常用的 OpenResty 库
-- HTTP 客户端
local http = require "resty.http"
-- JSON 编码/解码
local cjson = require "cjson"
-- Redis 客户端
local redis = require "resty.redis"
-- MySQL 客户端
local mysql = require "resty.mysql"
-- JWT 处理
local jwt = require "resty.jwt"
-- WebSocket
local websocket = require "resty.websocket.server"
-- 模板引擎
local template = require "resty.template"
-- UUID 生成
local uuid = require "resty.jit-uuid"
最佳实践
- 使用共享字典进行缓存
lua_shared_dict my_cache 10m;
-- 在 Lua 代码中
local cache = ngx.shared.my_cache
cache:set("key", "value", 60) -- 60 秒 TTL
local value = cache:get("key")
- 连接池
local redis = require "resty.redis"
local red = redis:new()
red:set_timeout(1000)
red:connect("127.0.0.1", 6379)
-- 执行工作...
-- 返回到连接池而不是关闭
red:set_keepalive(10000, 100)
- 错误处理
local ok, err = pcall(function()
-- 你的代码
end)
if not ok then
ngx.log(ngx.ERR, "错误: ", err)
ngx.exit(500)
end
- 非阻塞操作
-- 使用 resty 库进行非阻塞 I/O
local http = require "resty.http"
local httpc = http.new()
-- 这是非阻塞的
local res, err = httpc:request_uri("http://api.example.com/data")
性能对比
| 指标 |
Nginx |
OpenResty |
| 静态文件 |
⭐⭐⭐⭐⭐ |
⭐⭐⭐⭐⭐ |
| 简单代理 |
⭐⭐⭐⭐⭐ |
⭐⭐⭐⭐⭐ |
| 动态内容 |
⭐⭐ |
⭐⭐⭐⭐⭐ |
| 复杂逻辑 |
⭐ |
⭐⭐⭐⭐⭐ |
| 开发速度 |
⭐⭐ |
⭐⭐⭐⭐⭐ |
| 内存使用 |
⭐⭐⭐⭐⭐ |
⭐⭐⭐⭐ |
何时选择什么?
选择 Nginx 当:
- 你只需要反向代理和负载均衡
- 静态文件服务是你的主要用例
- 不需要动态请求处理
- 你希望最小的内存占用
- 你的团队缺乏 Lua 经验
选择 OpenResty 当:
- 构建 API 网关
- 需要复杂的认证/授权
- 实现 WAF 或安全功能
- 需要动态路由和内容生成
- 需要与 Redis、MySQL 等集成
- 想要避免编写 C 模块
- 构建微服务基础设施
结论
OpenResty 在保持 Nginx 性能特性的同时,扩展了强大的脚本功能。它非常适合构建现代 Web 应用、API 网关和微服务基础设施,在需要动态逻辑的场景下不会牺牲性能。
在 Nginx 和 OpenResty 之间的选择取决于你的具体需求:
- 简单用例 → Nginx
- 复杂、动态用例 → OpenResty
两者都是生产就绪且经过实战考验的,OpenResty 被 Cloudflare、Kong 和许多其他公司用于高流量应用。
OpenResty vs Nginx: 完整指南
什么是 Nginx?
Nginx 是一个高性能的 HTTP 服务器和反向代理,同时也是 IMAP/POP3 代理服务器。它以以下特性著称:
什么是 OpenResty?
OpenResty 是一个功能完整的 Web 平台,它将 Nginx 与 LuaJIT(Lua 的即时编译器)整合在一起。本质上它是:
核心差异
1. 核心架构
2. 编程能力
Nginx:
OpenResty:
3. 使用场景
Nginx 更适合:
OpenResty 更适合:
为什么使用 OpenResty?
1. 性能
2. 灵活性
3. 生产力
4. 实际应用示例
如何使用 OpenResty
安装
Linux (Ubuntu/Debian)
macOS
Docker
基础配置
nginx.conf
常见使用场景
1. API 网关
2. Redis 缓存
3. 限流
4. 动态路由
5. 请求/响应转换
常用的 OpenResty 库
最佳实践
性能对比
何时选择什么?
选择 Nginx 当:
选择 OpenResty 当:
结论
OpenResty 在保持 Nginx 性能特性的同时,扩展了强大的脚本功能。它非常适合构建现代 Web 应用、API 网关和微服务基础设施,在需要动态逻辑的场景下不会牺牲性能。
在 Nginx 和 OpenResty 之间的选择取决于你的具体需求:
两者都是生产就绪且经过实战考验的,OpenResty 被 Cloudflare、Kong 和许多其他公司用于高流量应用。