背景
先来回忆一下, nginx 如何配置多个实例的负载均衡,配置如下:
upstream serverList {
    server 172.17.0.111:9999;
    server 172.17.0.110:9999;
}
 
server {
   location / {
       proxy_pass  http://serverList;
    }
}复制代码当我们的服务实例变化时,要手动修改 nginx.conf 然后 nginx -s reload 。
在微服务架构下,我们的服务均已经注册到 注册中心 例如(nacos/eureka),注册中心已经维护所有服务实例的 IP:PORT 列表 ,为何不直接通过 nginx 来获取注册中心中的IP:PORT 列表自动配置 upstream 和热更新。如上思路实现有如下:
- 使用 nginx-lua-module模块编写lua脚本, 调用注册中心的Http API来获取实例列表 配置upstream,定时reload热更新
- 使用 JAVA/Golang编写单独的agent,直接使用nacos 对应语言的 SDK ,获取实例列表生成upstream,并且使用Naocs SDK监听服务变化reload
nacos-nginx-template 使用
nacos-nginx-template 以上的第二种思路实现以Agent的形式让Nginx实现对Nacos的服务发现。
- 下载二进制包
点击此处下载:最新稳定版
- 配置config.toml
配置文件使用TOML进行配置
 nginx_cmd = "/usr/sbin/nginx"  #nginx命令的全路径
nacos_addr = "172.16.0.100:8848" #nacos 服务地址
reload_interval = 1000  # 刷新间隔
[discover_config1]    
nginx_config = "/etc/nginx/nginx.conf"  #nginx config 配置
nginx_upstream = "upsteam1"     #upstream 名称
nacos_service_name = "service1"   #nacos 服务名称
[discover_config2]
nginx_config = "/etc/nginx/nginx.conf"
nginx_upstream = "upsteam2"
nacos_service_name = "service2"复制代码- 启动,即可使用
sh bin/startup.sh复制代码核心代码
- 获取 config.toml配置的信息,支持多个upstream,调用Nacos Api 拉取实例列表
for (DiscoverConfigBO configBO : list) {
    namingService.subscribe(configBO.getServiceName(),
            event -> {
                List<Instance> instances = namingService
                        .getAllInstances(configBO.getServiceName());
                //更新nginx中的upstream
                refreshUpstream(instances, configBO.getUpstream(), configBO.getConfigPath());
            }
    );
}
复制代码- 根据实例列表,拼凑 upstream
    private boolean refreshUpstream(List<Instance> instances, String nginxUpstream, String nginxConfigPath) {
        //获取到upstream 名称
        Pattern pattern = Pattern.compile(UPSTREAM_REG.replace(PLACEHOLDER, nginxUpstream));
        //获取到配置文件内容
        String conf =  FileUtl.readStr(nginxConfigPath);
        //拼接新的upstream
        String newUpstream = UPSTREAM_FOMAT.replace(PLACEHOLDER, nginxUpstream);
        StringBuffer servers = new StringBuffer();
        if (instances.size() > 0) {
            for (Instance instance : instances) {
                //不健康或不可用的跳过
                if (!instance.isHealthy() || !instance.isEnabled()) {
                    continue;
                }
                servers.append(formatSymbol + "    server " + instance.getIp() + ":" + instance.getPort() + ";\n");
            }
        }
        servers.append(formatSymbol);
        newUpstream = newUpstream.replace(PLACEHOLDER_SERVER, servers.toString());
        //替换原有的upstream
        conf = matcher.replaceAll(newUpstream);
        return true;
    }复制代码- Java 调用nginx reload
Runtime.getRuntime().exec("nginx  -s reload");复制代码
