背景
先來回憶一下, nginx 如何配置多個實例的負載均衡,配置如下:
<code>upstream serverList { server 172.17.0.111:9999; server 172.17.0.110:9999; } server { location / { proxy_pass http://serverList; } }/<code>
當我們的服務實例變化時,要手動修改 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 使用
https://github.com/YeautyYE/nacos-nginx-template 以上的第二種思路實現以Agent的形式讓Nginx實現對Nacos的服務發現。
- 下載二進制包
點擊此處下載:https://github.com/YeautyYE/nacos-nginx-template/releases/
- 配置config.toml
配置文件使用" title="TOML"> 進行配置
<code> 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"/<code>
- 啟動,即可使用
<code>sh bin/startup.sh/<code>
核心代碼
- 獲取 config.toml 配置的信息,支持多個 upstream ,調用Nacos Api 拉取實例列表
<code>for (DiscoverConfigBO configBO : list) { namingService.subscribe(configBO.getServiceName(), event -> { List instances = namingService .getAllInstances(configBO.getServiceName()); //更新nginx中的upstream refreshUpstream(instances, configBO.getUpstream(), configBO.getConfigPath()); } ); } /<code>
- 根據實例列表,拼湊 upstream
<code> private boolean refreshUpstream(List 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; }/<code>
- Java 調用nginx reload
<code>Runtime.getRuntime().exec("nginx -s reload");/<code>