Nacos系列:Nacos的Java SDK使用

知行旅人發表於2019-02-24

Maven依賴

Nacos提供完整的Java SDK,便於配置管理和服務發現及管理,以 Nacos-0.8.0 版本為例

新增Maven依賴:

<dependency>
    <groupId>com.alibaba.nacos</groupId>
    <artifactId>nacos-client</artifactId>
    <version>0.8.0</version>
</dependency>
複製程式碼

僅僅引入nacos-client是不夠的,否則啟動時會出現如下錯誤:

sun.misc.Launcher$AppClassLoader@18b4aac2 JM.Log:WARN Init JM logger with NopLoggerFactory, pay attention. sun.misc.Launcher$AppClassLoader@18b4aac2
java.lang.ClassNotFoundException: org.apache.logging.log4j.core.Logger
	at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:335)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
	at java.lang.Class.forName0(Native Method)
	at java.lang.Class.forName(Class.java:264)
	at com.alibaba.nacos.client.logger.log4j2.Log4j2LoggerFactory.<init>(Log4j2LoggerFactory.java:33)
	at com.alibaba.nacos.client.logger.LoggerFactory.<clinit>(LoggerFactory.java:59)
	at com.alibaba.nacos.client.config.utils.LogUtils.<clinit>(LogUtils.java:49)
	at com.alibaba.nacos.client.config.NacosConfigService.<clinit>(NacosConfigService.java:55)
	at java.lang.Class.forName0(Native Method)
	at java.lang.Class.forName(Class.java:264)
	at com.alibaba.nacos.api.config.ConfigFactory.createConfigService(ConfigFactory.java:40)
	at com.alibaba.nacos.api.config.ConfigFactory.createConfigService(ConfigFactory.java:59)
	at com.alibaba.nacos.api.NacosFactory.createConfigService(NacosFactory.java:52)
	at com.learn.nacos.config.NacosConfig.main(NacosConfig.java:12)
複製程式碼

根據錯誤提示,應該還需要新增log4j相關依賴,官網的文件並沒有對此說明,我在pom.xml新增了下面這些依賴才不報錯

<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.1.11</version>
</dependency>
<dependency>
    <groupId>org.logback-extensions</groupId>
    <artifactId>logback-ext-spring</artifactId>
    <version>0.1.4</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>jcl-over-slf4j</artifactId>
    <version>1.7.25</version>
</dependency>
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.25</version>
</dependency>
複製程式碼

配置管理

建立ConfigService,可以通過 NacosFactory.createConfigService()ConfigFactory.createConfigService() 來建立,後者是前者的底層實現方式,這兩種方式都包含如下兩個方法:

createConfigService(serverAddr) createConfigService(properties)

建立示例:

// 方式一
String serverAddr = "127.0.0.1:8848";
ConfigService configService = ConfigFactory.createConfigService(serverAddr);

// 方式二
ConfigService configService = ConfigFactory.createConfigService(properties)
Properties properties = new Properties();
properties.put("serverAddr", serverAddr);
複製程式碼

檢視ConfigService原始碼,它提供瞭如下方法:

獲取 Nacos Server 當前狀態String getServerStatus()

底層原始碼:

public String getServerStatus() {
    if (worker.isHealthServer()) {
        return "UP";
    } else {
        return "DOWN";
    }
}
複製程式碼

根據原始碼註釋,該狀態應該是指 Nacos Server 的狀態,我把 Nacos Server 關閉之後,再次執行示例,得到的結果仍然是UP,不知道這是不是一個BUG。

釋出配置boolean publishConfig(String dataId, String group, String content) throws NacosException

支援程式自動釋出Nacos配置,建立和修改配置使用同一個方法,配置不存在則建立;配置已存在則更新。

底層原始碼:

try {
    result = agent.httpPost(url, headers, params, encode, POST_TIMEOUT);
} catch (IOException ioe) {
    log.warn("NACOS-0006",
        LoggerHelper.getErrorCodeStr("NACOS", "NACOS-0006", "環境問題", "[publish-single] exception"));
    log.warn(agent.getName(), "[publish-single] exception, dataId={}, group={}, msg={}", dataId, group,
        ioe.toString());
    return false;
}
複製程式碼

釋出配置後,如果馬上用getConfig()讀取配置,有時候會讀不到,設定了足夠的等待時長後才可保證每次正常讀取,看了原始碼才知道Nacos的配置管理(釋出、讀取、移除)都是通過HTTP介面完成的,但釋出配置的時延是多少,官網似乎沒有說明?幾秒鐘的時延在一些對實時性要求很高的場景會不會存在影響呢?

讀取配置String getConfig(String dataId, String group, long timeoutMs) throws NacosException

timeoutMs指讀取配置超時時間,官網推薦設定為3000ms

底層原始碼:

// 優先使用本地配置
String content = LocalConfigInfoProcessor.getFailover(agent.getName(), dataId, group, tenant);
if (content != null) {
    log.warn(agent.getName(), "[get-config] get failover ok, dataId={}, group={}, tenant={}, config={}", dataId,
        group, tenant, ContentUtils.truncateContent(content));
    cr.setContent(content);
    configFilterChainManager.doFilter(null, cr);
    content = cr.getContent();
    return content;
}

try {
    content = worker.getServerConfig(dataId, group, tenant, timeoutMs);

    cr.setContent(content);
    configFilterChainManager.doFilter(null, cr);
    content = cr.getContent();

    return content;
} catch (NacosException ioe) {
    if (NacosException.NO_RIGHT == ioe.getErrCode()) {
        throw ioe;
    }
    log.warn("NACOS-0003",
        LoggerHelper.getErrorCodeStr("NACOS", "NACOS-0003", "環境問題", "get from server error"));
    log.warn(agent.getName(), "[get-config] get from server error, dataId={}, group={}, tenant={}, msg={}",
        dataId, group, tenant, ioe.toString());
}
複製程式碼

從原始碼上看,配置會先從本地快取檔案讀取,如果沒讀取到,才會去請求Nacos Server的配置,這個快取檔案在哪呢?就在當前使用者的nacos目錄下 生成的快取檔案:nacos/config/fixed-127.0.0.1_8848_nacos/snapshot/DEFAULT_GROUP/nacos-sdk-java-config,配置內容和釋出到Nacos Server的配置內容是一致的。

移除配置boolean removeConfig(String dataId, String group) throws NacosException

支援程式自動釋出Nacos配置,配置不存在時會直接返回成功,移除配置後,本地的快取檔案也會被刪除

底層原始碼:

try {
    result = agent.httpDelete(url, null, params, encode, POST_TIMEOUT);
} catch (IOException ioe) {
    log.warn("[remove] error, " + dataId + ", " + group + ", " + tenant + ", msg: " + ioe.toString());
    return false;
}
複製程式碼

移除配置同釋出配置一樣,如果移除後馬上查詢,有可能還能將剛移除的配置查出來,也存在一定的時延,需要設定等待時間讀取。

新增配置監聽void addListener(String dataId, String group, Listener listener) throws NacosException

支援動態監聽配置的變化,執行示例原始碼,在Nacos控制檯把配置內容修改為sdk-java-config:change from nacos console,此時觀看IDE控制檯,你會看到如下列印資訊:

當前執行緒:com.alibaba.nacos.client.Worker.longPollingfixed-127.0.0.1_8848 ,監聽到配置內容變化:sdk-java-config:change from nacos console
複製程式碼

移除配置監聽void removeListener(String dataId, String group, Listener listener)

移除監聽後,配置的變化不會再監聽

啟動完整示例,執行結果如下,請注意配置監聽執行緒和配置管理執行緒不是同一個執行緒

當前執行緒:main ,服務狀態:UP
新增監聽
新增監聽成功
釋出配置
釋出配置成功
當前執行緒:com.alibaba.nacos.client.Worker.longPollingfixed-127.0.0.1_8848 ,監聽到配置內容變化:nacos-sdk-java-config:init
當前執行緒:main ,釋出配置後獲取配置內容:nacos-sdk-java-config:init
重新發布配置
重新發布配置成功
當前執行緒:main ,重新發布配置後獲取配置內容:sdk-java-config:update
當前執行緒:com.alibaba.nacos.client.Worker.longPollingfixed-127.0.0.1_8848 ,監聽到配置內容變化:sdk-java-config:update
當前執行緒:com.alibaba.nacos.client.Worker.longPollingfixed-127.0.0.1_8848 ,監聽到配置內容變化:sdk-java-config:change from nacos console
移除配置
移除配置成功
當前執行緒:main ,移除配置後獲取配置內容:null
取消監聽
取消監聽成功
複製程式碼

服務管理

建立NamingService,可以通過 NacosFactory.createNamingService()NamingFactory.createNamingService() 來建立,後者是前者的底層實現方式,這兩種方式都包含如下兩個方法:

createNamingService(serverAddr) createNamingService(properties)

建立示例:

// 方式一
String serverAddr = "127.0.0.1:8848";
NamingService namingService = NamingFactory.createNamingService(serverAddr);

// 方式二
NamingService namingService = NamingFactory.createNamingService(properties)
Properties properties = new Properties();
properties.put("serverAddr", serverAddr);
複製程式碼

檢視NamingService類原始碼,它提供瞭如下方法:

獲取 Nacos Server 當前狀態String getServerStatus()

註冊服務例項void registerInstance(多個引數)

方式一:
String serverIp = "127.0.0.1";
int serverPort = 8848;
String serverAddr = serverIp + ":" + serverPort;
String serviceName = "nacos-sdk-java-discovery";
NamingService namingService = NamingFactory.createNamingService(serverAddr);
namingService.registerInstance(serviceName, serverIp, serverPort);

方式二:
Instance instance = new Instance();
instance.setIp(serverIp);//IP
instance.setPort(serverPort);//埠
instance.setServiceName(serviceName);//服務名
instance.setEnabled(true);//true: 上線 false: 下線
instance.setHealthy(healthy);//健康狀態
instance.setWeight(1.0);//權重
instance.addMetadata("nacos-sdk-java-discovery", "true");//後設資料
NamingService namingService = NamingFactory.createNamingService(serverAddr);
namingService.registerInstance(serviceName, instance);
複製程式碼

註冊後,本地會生成快取檔案 1、在Nacos安裝目錄data目錄下:data/naming/data/public/com.alibaba.nacos.naming.domains.meta.public##nacos-sdk-java-discovery 2、當前使用者的nacos目錄下:/nacos/naming/public/failover/nacos-sdk-java-discovery 3、當前使用者的nacos目錄下:/nacos/naming/public/nacos-sdk-java-discovery

即使刪除服務例項,上面三個快取檔案也不會被刪除,Nacos控制檯服務列表中該服務也還存在著(但服務例項數會變成0);刪除Nacos控制檯的該服務,安全目錄data目錄下的快取檔案會被刪除,但當前使用者的nacos目錄下的檔案不會被刪除,這裡面是什麼機制,我暫時還沒整明白,等後面整明白了再來補充。

刪除服務例項void deregisterInstance(多個引數)

獲取所有服務例項List<Instance> getAllInstances(多個引數)

獲取所有健康或不健康的服務例項List<Instance> selectInstances(多個引數)

隨機獲取一個健康例項(根據負載均衡演算法)Instance selectOneHealthyInstance(多個引數)

新增服務例項監聽void subscribe(多個引數)

新增服務例項監聽void unsubscribe(多個引數)

分頁獲取所有服務例項ListView<String> getServicesOfServer(多個引數)

獲取所有監聽的服務例項List<ServiceInfo> getSubscribeServices()

啟動完整示例,執行結果如下,請注意服務例項監聽執行緒和服務例項管理執行緒不是同一個執行緒

當前執行緒:main ,服務狀態:UP
註冊例項
註冊例項成功
新增監聽
新增監聽成功
當前執行緒:main ,註冊例項後獲取所有例項:[{"clusterName":"DEFAULT","enabled":true,"instanceId":"127.0.0.1#8848#DEFAULT#nacos-sdk-java-discovery","ip":"127.0.0.1","metadata":{},"port":8848,"serviceName":"nacos-sdk-java-discovery","valid":true,"weight":1.0}]
當前執行緒:main ,註冊例項後獲取所有健康例項:[{"clusterName":"DEFAULT","enabled":true,"instanceId":"127.0.0.1#8848#DEFAULT#nacos-sdk-java-discovery","ip":"127.0.0.1","metadata":{},"port":8848,"serviceName":"nacos-sdk-java-discovery","valid":true,"weight":1.0}]
當前執行緒:com.alibaba.nacos.naming.client.listener ,監聽到例項名稱:nacos-sdk-java-discovery
當前執行緒:com.alibaba.nacos.naming.client.listener ,監聽到例項內容:[{"clusterName":"DEFAULT","enabled":true,"instanceId":"127.0.0.1#8848#DEFAULT#nacos-sdk-java-discovery","ip":"127.0.0.1","metadata":{},"port":8848,"serviceName":"nacos-sdk-java-discovery","valid":true,"weight":1.0}]
當前執行緒:main ,註冊例項後獲取一個健康例項:{"clusterName":"DEFAULT","enabled":true,"instanceId":"127.0.0.1#8848#DEFAULT#nacos-sdk-java-discovery","ip":"127.0.0.1","metadata":{},"port":8848,"serviceName":"nacos-sdk-java-discovery","valid":true,"weight":1.0}
當前執行緒:com.alibaba.nacos.naming.client.listener ,監聽到例項名稱:nacos-sdk-java-discovery
當前執行緒:com.alibaba.nacos.naming.client.listener ,監聽到例項內容:[{"clusterName":"DEFAULT","enabled":true,"instanceId":"127.0.0.1#8848#DEFAULT#nacos-sdk-java-discovery","ip":"127.0.0.1","metadata":{"change":"true;"},"port":8848,"serviceName":"nacos-sdk-java-discovery","valid":true,"weight":2.0}]
取消監聽
取消監聽成功
刪除例項
刪除例項成功
Exception in thread "main" java.lang.IllegalStateException: no host to srv for serviceInfo: nacos-sdk-java-discovery
	at com.alibaba.nacos.client.naming.core.Balancer$RandomByWeight.selectAll(Balancer.java:45)
	at com.alibaba.nacos.client.naming.core.Balancer$RandomByWeight.selectHost(Balancer.java:53)
	at com.alibaba.nacos.client.naming.NacosNamingService.selectOneHealthyInstance(NacosNamingService.java:270)
	at com.alibaba.nacos.client.naming.NacosNamingService.selectOneHealthyInstance(NacosNamingService.java:263)
	at com.alibaba.nacos.client.naming.NacosNamingService.selectOneHealthyInstance(NacosNamingService.java:253)
	at com.learn.nacos.discovery.NacosDiscovery.main(NacosDiscovery.java:121)
當前執行緒:main ,刪除例項後獲取所有例項:[]
當前執行緒:main ,刪除例項後獲取所有健康例項:[]
複製程式碼

以上就是 Nacos Java SDK 配置管理和服務管理功能的介紹,請參考示例原始碼學習。

示例原始碼

專案:learn-nacos-sdk-java

程式碼已上傳至碼雲Github上,歡迎下載學習

參考資料

推薦閱讀

  • [Nacos系列:歡迎來到Nacos的世界!][4]

  • [Nacos系列:基於Nacos的註冊中心][5]

  • [Nacos系列:基於Nacos的配置中心][6]

相關文章