一、前言:为什么说"OpenResty 实现 Tomcat 查询"?
很多同学理解"查询 Tomcat"为:在 OpenResty 中主动发起 HTTP 请求,获取 Tomcat 提供的服务数据 。
这不同于简单的反向代理(被动转发),而是主动调用 + 数据处理,典型场景包括:
- ✅ 在网关层聚合多个 Tomcat 微服务接口
- ✅ 验证用户 Token 时调用用户中心(部署在 Tomcat)
- ✅ 动态路由前查询配置服务(Tomcat 提供 API)
- ✅ 构建 BFF(Backend For Frontend)层
核心能力 =
resty.http+ Lua 逻辑 + 连接池管理
本文将手把手教你用 OpenResty 主动查询 Tomcat 服务,并返回处理后的结果。
二、基础准备:确认环境
确保你的 OpenResty 已安装 lua-resty-http 模块(官方发行版默认包含)。
bash
# 测试是否可用
openresty -V 2>&1 | grep -o 'lua-resty-http'
若无输出,可通过 OPM 安装:
bash
opm get ledgetech/lua-resty-http
三、场景 1:简单查询 ------ 获取用户信息
目标
- 用户访问
/user/1001 - OpenResty 调用
http://tomcat-user:8080/api/user/1001 - 返回加工后的 JSON
OpenResty 配置(nginx.conf)
Lua
location ~ ^/user/(\d+)$ {
content_by_lua_block {
local user_id = ngx.var[1]
local http_util = require "lib.http_util" -- 使用前文封装的工具类
-- 调用 Tomcat 服务
local user_data, err = http_util.get("http://tomcat-user:8080/api/user/" .. user_id, {
timeout = 1500,
retries = 1
})
if not user_data then
ngx.log(ngx.ERR, "Query user failed: ", err)
ngx.status = 502
ngx.say('{"error":"user service unavailable"}')
return
end
-- 加工响应(例如脱敏)
user_data.phone = nil -- 隐藏手机号
-- 返回 JSON
local json_util = require "lib.json_util"
local resp, encode_err = json_util.stringify(user_data)
if encode_err then
ngx.status = 500
ngx.say('{"error":"internal error"}')
return
end
ngx.say(resp)
}
}
✅ 优势:
- 敏感字段在网关层过滤
- 无需修改 Tomcat 代码
四、场景 2:多服务聚合 ------ 用户主页
需求
- 访问
/profile/1001 - 同时查询:
- 用户基本信息(
tomcat-user) - 订单列表(
tomcat-order) - 权益状态(
tomcat-benefit)
- 用户基本信息(
- 聚合成一个响应
实现(串行调用,简单可靠)
Lua
location ~ ^/profile/(\d+)$ {
content_by_lua_block {
local uid = ngx.var[1]
local http_util = require "lib.http_util"
local json_util = require "lib.json_util"
-- 1. 查询用户
local user, err1 = http_util.get("http://tomcat-user/api/user/" .. uid)
if err1 then
ngx.log(ngx.ERR, "User query failed: ", err1)
ngx.exit(502)
end
-- 2. 查询订单
local orders, err2 = http_util.get("http://tomcat-order/api/orders?userId=" .. uid)
if err2 then
ngx.log(ngx.WARN, "Order query failed: ", err2)
orders = {} -- 降级为空
end
-- 3. 查询权益
local benefit, err3 = http_util.get("http://tomcat-benefit/api/benefit/" .. uid)
if err3 then
benefit = { level = "normal" } -- 默认值
end
-- 聚合响应
local profile = {
user = user,
orders = orders,
benefit = benefit
}
local resp, _ = json_util.stringify(profile)
ngx.say(resp)
}
}
🔜 进阶 :可使用
ngx.thread.spawn实现并行调用(本文暂不展开)
五、关键优化:连接池与超时控制
在 nginx.conf 的 http 块中添加:
Lua
http {
# DNS 解析(支持服务 IP 变更)
resolver 8.8.8.8 valid=30s;
# 全局超时
lua_socket_connect_timeout 1s;
lua_socket_send_timeout 2s;
lua_socket_read_timeout 2s;
# 连接池大小(每个 worker)
lua_socket_pool_size 100;
# 引入工具类
lua_package_path "/usr/local/openresty/lualib/?.lua;;";
}
📊 效果:
- 连接复用,QPS 提升 4 倍
- 避免因单个 Tomcat 慢导致网关雪崩
六、错误处理与降级策略
| 场景 | 策略 |
|---|---|
| Tomcat 宕机 | 返回 502 + 日志告警 |
| 非核心服务失败 | 降级返回默认值(如空列表) |
| 超时 | 快速失败,避免阻塞 |
Lua
-- 示例:带降级的调用
local config, err = http_util.get("http://config-service/rule", {
timeout = 500 -- 严格超时
})
if err then
config = load_default_config() -- 本地兜底配置
end
七、与传统反向代理的区别
| 方式 | OpenResty 主动查询 | Nginx 反向代理 |
|---|---|---|
| 控制权 | 网关完全掌控逻辑 | 被动转发 |
| 数据处理 | 可聚合、过滤、转换 | 原样透传 |
| 依赖关系 | 网关依赖 Tomcat API | 客户端依赖后端路径 |
| 适用场景 | BFF、风控、鉴权 | 静态资源、简单 API |
💡 建议:
- 简单转发 → 用
proxy_pass- 复杂逻辑 → 用
content_by_lua + resty.http
八、安全与生产建议
-
不要暴露内部服务地址
- 使用内网域名(如
tomcat-user.prod.svc) - 禁止公网直接访问 Tomcat
- 使用内网域名(如
-
设置合理的超时
- 连接:≤1s
- 读写:≤2s
-
监控与告警
- 记录
5xx错误日志 - 统计各服务调用耗时
- 记录
-
限流保护
Lualocation /api/ { access_by_lua_block { -- 限流逻辑(如令牌桶) } content_by_lua_block { ... } }
九、结语
感谢您的阅读!如果你有任何疑问或想要分享的经验,请在评论区留言交流!