如果使用Nacos/Eureka + OpenResty(Nginx + Lua)或Spring Cloud Gateway的客户端负载均衡(如Spring Cloud LoadBalancer),可以实现Gateway实例的动态发现,无需手动配置Gateway地址。
以Nacos + OpenResty 为例:
OpenResty通过Lua脚本从Nacos获取Gateway集群的实时实例列表,动态更新Nginx的upstream配置,实现Gateway实例的自动发现和负载均衡。
- 实现原理 :OpenResty的核心是动态。它通过内嵌的LuaJIT虚拟机,允许执行Lua脚本。您可以编写一个Lua脚本,定期(例如每30秒)调用Nacos的服务发现API,获取所有健康的Gateway实例的IP和端口列表。随后,该脚本使用获取到的列表动态更新Nginx的Upstream配置。开源库如
lua-resty-nacos可以帮助简化这一过程。由于Nginx支持动态Upstream(如通过resty.upstream模块),这一更新过程无需重启服务,对流量无损。 - 核心优势 :此方案最大的优势在于解耦 和语言生态兼容性。OpenResty作为通用的反向代理,可以代理任何后端服务(如Java编写的Spring Cloud Gateway、Go编写的Gin框架应用等),不限于特定技术栈。它将负载均衡的逻辑从业务网关中剥离出来,通常被称为"网关层"或"入口网关",负责最基础的流量分发。
下面我详细介绍如何使用Nacos + OpenResty实现动态负载均衡,包含具体的配置和代码实现。
🎯 整体架构设计
首先了解一下整体架构:OpenResty作为API网关,通过内嵌的Lua脚本从Nacos服务注册中心动态获取服务实例列表,实现实时的负载均衡。
客户端请求 → OpenResty网关 → Lua脚本查询Nacos → 动态路由 → 后端服务实例
📋 环境准备与配置
1. OpenResty 基础配置
bash
# nginx.conf - 主配置文件
worker_processes auto;
events {
worker_connections 1024;
}
http {
lua_package_path "/usr/local/openresty/lualib/?.lua;;";
lua_shared_dict nacos_cache 10m; # 共享缓存,存储服务实例列表
upstream dynamic_backend {
server 0.0.0.1; # 无效地址,仅占位
balancer_by_lua_file conf/dynamic_upstream.lua;
}
server {
listen 80;
location /api/ {
set $backend_service ''; # 动态设置后端服务名
access_by_lua_file conf/service_router.lua;
proxy_pass http://dynamic_backend;
}
# Nacos 健康检查接口
location /nacos/health {
access_by_lua_file conf/nacos_health_check.lua;
}
}
}
🔧 核心Lua脚本实现
2. Nacos服务发现模块
lua
-- conf/nacos_discovery.lua
local http = require "resty.http"
local cjson = require "cjson"
local _M = {}
-- Nacos服务器配置
local NACOS_SERVER = "http://192.168.1.100:8848"
local NACOS_NAMESPACE = "public"
-- 获取服务实例列表
function _M.get_service_instances(service_name)
local httpc = http.new()
local url = NACOS_SERVER .. "/nacos/v1/ns/instance/list?serviceName=" .. service_name .. "&namespaceId=" .. NACOS_NAMESPACE
local res, err = httpc:request_uri(url, {
method = "GET",
headers = {
["Content-Type"] = "application/json"
}
})
if not res then
ngx.log(ngx.ERR, "Failed to query Nacos: ", err)
return nil
end
if res.status ~= 200 then
ngx.log(ngx.ERR, "Nacos API error: ", res.status)
return nil
end
local data = cjson.decode(res.body)
if data and data.hosts then
return data.hosts
end
return nil
end
-- 过滤健康实例并按权重排序
function _M.filter_healthy_instances(instances)
local healthy_instances = {}
for _, instance in ipairs(instances) do
if instance.healthy == true and instance.enabled == true and (instance.weight or 1) > 0 then
table.insert(healthy_instances, instance)
end
end
-- 按权重降序排序
table.sort(healthy_instances, function(a, b)
return (a.weight or 1) > (b.weight or 1)
end)
return healthy_instances
end
-- 带缓存的服务发现
function _M.get_cached_instances(service_name)
local cache_key = "nacos_instances:" .. service_name
local cache = ngx.shared.nacos_cache
-- 检查缓存
local cached = cache:get(cache_key)
if cached then
return cjson.decode(cached)
end
-- 从Nacos获取最新实例列表
local instances = _M.get_service_instances(service_name)
if instances then
local healthy_instances = _M.filter_healthy_instances(instances)
-- 缓存30秒
cache:set(cache_key, cjson.encode(healthy_instances), 30)
return healthy_instances
end
return nil
end
return _M
3. 负载均衡算法实现
lua
-- conf/load_balancer.lua
local _M = {}
-- 加权随机算法(Nacos默认算法)
function _M.weighted_random(instances)
if not instances or #instances == 0 then
return nil
end
-- 计算总权重
local total_weight = 0
for _, instance in ipairs(instances) do
total_weight = total_weight + (instance.weight or 1)
end
if total_weight <= 0 then
return instances[1] -- 默认返回第一个
end
-- 生成随机数
local random_value = math.random() * total_weight
local current = 0
for _, instance in ipairs(instances) do
current = current + (instance.weight or 1)
if random_value <= current then
return instance
end
end
return instances[1]
end
-- 轮询算法
function _M.round_robin(instances)
if not instances or #instances == 0 then
return nil
end
local cache_key = "rr_index:" .. ngx.var.backend_service
local cache = ngx.shared.nacos_cache
local index = cache:get(cache_key) or 1
local instance = instances[index]
index = (index % #instances) + 1
cache:set(cache_key, index, 300) -- 缓存5分钟
return instance
end
-- 最少连接数(简化版)
function _M.least_conn(instances)
if not instances or #instances == 0 then
return nil
end
-- 这里可以集成更复杂的连接数统计
-- 简化版返回第一个实例
return instances[1]
end
return _M
4. 动态路由主逻辑
lua
-- conf/service_router.lua
local nacos = require "nacos_discovery"
local balancer = require "load_balancer"
local _M = {}
-- 服务路由映射
local service_mappings = {
["/api/users"] = "user-service",
["/api/orders"] = "order-service",
["/api/products"] = "product-service"
}
function _M.route()
local uri = ngx.var.uri
local service_name
-- 根据URI路径匹配服务
for path, svc in pairs(service_mappings) do
if string.find(uri, path) == 1 then
service_name = svc
break
end
end
if not service_name then
ngx.log(ngx.ERR, "No service mapping found for URI: ", uri)
ngx.exit(404)
return
end
-- 设置后端服务名称
ngx.var.backend_service = service_name
-- 获取服务实例
local instances = nacos.get_cached_instances(service_name)
if not instances or #instances == 0 then
ngx.log(ngx.ERR, "No available instances for service: ", service_name)
ngx.exit(503)
return
end
-- 选择实例(这里使用加权随机,与Nacos客户端保持一致)
local selected_instance = balancer.weighted_random(instances)
if not selected_instance then
ngx.log(ngx.ERR, "Failed to select instance for service: ", service_name)
ngx.exit(503)
return
end
-- 将选中的实例信息保存到上下文中
ngx.ctx.selected_instance = selected_instance
ngx.log(ngx.INFO, "Selected instance: ", selected_instance.ip, ":", selected_instance.port,
" weight:", selected_instance.weight or 1)
end
return _M
5. 动态负载均衡器
lua
-- conf/dynamic_upstream.lua
local balancer_module = require "ngx.balancer"
local function get_peer()
local instance = ngx.ctx.selected_instance
if not instance then
return nil, "No instance selected"
end
return instance.ip, instance.port
end
local ok, err = balancer_module.set_current_peer(get_peer())
if not ok then
ngx.log(ngx.ERR, "Failed to set current peer: ", err)
return ngx.exit(500)
end
-- 设置超时参数
balancer_module.set_timeouts(5000, 5000, 5000) -- 连接、发送、读取超时均为5秒
6. 健康检查模块
ini
-- conf/nacos_health_check.lua
local nacos = require "nacos_discovery"
local cjson = require "cjson"
local function health_check()
local services = {"user-service", "order-service", "product-service"}
local result = {}
for _, service in ipairs(services) do
local instances = nacos.get_cached_instances(service)
result[service] = {
total_instances = instances and #instances or 0,
healthy_instances = instances and #instances or 0,
status = instances and #instances > 0 and "healthy" or "unhealthy"
}
end
ngx.header["Content-Type"] = "application/json"
ngx.say(cjson.encode(result))
end
health_check()
⚙️ 高级配置与优化
7. 集群和权重配置
ini
# 扩展nginx.conf,添加更多优化配置
http {
# 共享内存区域优化
lua_shared_dict nacos_instances 20m;
lua_shared_dict load_balancer 10m;
# 初始化阶段从Nacos预加载服务列表
init_worker_by_lua_block {
local nacos = require "nacos_discovery"
local services = {"user-service", "order-service", "product-service"}
-- 启动时预加载服务实例
for _, service in ipairs(services) do
ngx.timer.at(0, function()
nacos.get_cached_instances(service)
end)
end
}
# 上游服务器配置
upstream dynamic_backend {
server 0.0.0.1; # 占位符
balancer_by_lua_file conf/dynamic_upstream.lua;
# 重试配置
proxy_next_upstream error timeout http_500 http_502 http_503 http_504;
proxy_next_upstream_tries 3;
proxy_next_upstream_timeout 10s;
}
}
🚀 部署与测试
启动和验证脚本
bash
#!/bin/bash
# start_openresty.sh
# 检查配置语法
/usr/local/openresty/nginx/sbin/nginx -t
if [ $? -eq 0 ]; then
# 启动OpenResty
/usr/local/openresty/nginx/sbin/nginx
echo "OpenResty started successfully"
# 等待服务启动
sleep 2
# 测试健康检查接口
curl http://localhost/nacos/health
else
echo "Nginx configuration test failed"
exit 1
fi
💡 核心优势与特性
这种实现方式具有以下显著优势:
- 真正的动态发现:服务实例变化时自动更新,无需重启网关
- 权重感知:支持Nacos的权重配置,实现智能流量分发
- 健康检查:自动过滤不健康实例,提高系统可靠性
- 高性能:Lua脚本执行效率高,缓存机制减少Nacos查询压力
- 灵活性:可轻松扩展新的负载均衡算法和路由策略
🔍 故障排除提示
如果遇到问题,可以检查以下几点:
- 确认Nacos服务器可达且服务实例已正确注册
- 查看OpenResty错误日志:
tail -f /usr/local/openresty/nginx/logs/error.log - 验证Lua模块路径配置正确
- 检查防火墙设置,确保OpenResty可以访问Nacos服务器
这种架构为企业级微服务系统提供了稳定、高效的网关层解决方案,完美结合了Nacos的服务发现能力和OpenResty的高性能特性。