背景
先來回憶一下, 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");複製程式碼