通過Nacos讓Nginx擁有服務發現能力

冷冷gg發表於2020-02-05

背景

先來回憶一下, 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的服務發現。

  1. 下載二進位制包

點選此處下載:最新穩定版

  1. 配置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"複製程式碼

  1. 啟動,即可使用

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");複製程式碼

image

相關文章