Nacos系列:Nacos的JavaSDK使⽤
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.Logger
at java.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.fig.utils.LogUtils.<clinit>(LogUtils.java:49)
at com.alibaba.fig.NacosConfigService.<clinit>(NacosConfigService.java:55)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:264)
at com.alibaba.ateConfigService(ConfigFactory.java:40)
at com.alibaba.ateConfigService(ConfigFactory.java:59)
at com.alibaba.nacos.ateConfigService(NacosFactory.java:52)
at com.fig.NacosConfig.main(NacosConfig.java:12)
根据错误提⽰,应该还需要添加log4j相关依赖,官⽹的⽂档并没有对此说明,我在l添加了下⾯这些依赖才不报错
<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>log4j2 console
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
配置管理
创建ConfigService,可以通过ateConfigService()或ateConfigService()来创建,后者是前者的底层实现⽅式,这两种⽅式都包含如下两个⽅法:createConfigService(serverAddr)
createConfigService(properties)
创建⽰例:
// ⽅式⼀
String serverAddr = "127.0.0.1:8848";
ConfigService configService = ateConfigService(serverAddr);
// ⽅式⼆
ConfigService configService = ateConfigService(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",
log.Name(), "[publish-single] exception, dataId={}, group={}, msg={}", dataId, group,
return false;
}
发布配置后,如果马上⽤getConfig()读取配置,有时候会读不到,设置了⾜够的等待时长后才可保证每次正常读取,看了源码才知道Nacos的配置管理(发布、读取、移除)都是通过HTTP接⼝完成的,但发布配置的时延是多少,官⽹似乎没有说明?⼏秒钟的时延在⼀些对实时性要求很⾼的场景会不会存在影响呢?
读取配置:String getConfig(String dataId, String group, long timeoutMs) throws NacosException
timeoutMs指读取配置超时时间,官⽹推荐设置为3000ms
底层源码:
// 优先使⽤本地配置
String content = Name(), dataId, group, tenant);
if (content != null) {
log.Name(), "[get-config] get failover ok, dataId={}, group={}, tenant={}, config={}", dataId,
group, tenant, uncateContent(content));
cr.setContent(content);
configFilterChainManager.doFilter(null, cr);
content = cr.getContent();
return content;
}
try {
content = ServerConfig(dataId, group, tenant, timeoutMs);
cr.setContent(content);
configFilterChainManager.doFilter(null, cr);
content = cr.getContent();
return content;
} catch (NacosException ioe) {
if (NacosException.NO_RIGHT == ErrCode()) {
throw ioe;
}
log.warn("NACOS-0003",
log.Name(), "[get-config] get from server error, dataId={}, group={}, tenant={}, msg={}",
dataId, group, tenant, String());
}
从源码上看,配置会先从本地缓存⽂件读取,如果没读取到,才会去请求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: " + String());
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,可以通过ateNamingService()或ateNamingService()来创建,后者是前者的底层实现⽅式,这两种⽅式都包含如下两个⽅法:
createNamingService(serverAddr)
createNamingService(properties)
创建⽰例:
// ⽅式⼀
String serverAddr = "127.0.0.1:8848";
NamingService namingService = ateNamingService(serverAddr);
// ⽅式⼆
NamingService namingService = ateNamingService(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 = ateNamingService(serverAddr);
⽅式⼆:
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 = ateNamingService(serverAddr);
注册后,本地会⽣成缓存⽂件
1、在Nacos安装⽬录data⽬录下:data/naming/data/public/com.alibaba.nacos.a.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","va 当前线程: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 当前线程: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":"nac 当前线程: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"当前线程: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,"se 取消监听
取消监听成功
删除实例
删除实例成功
Exception in thread "main" java.lang.IllegalStateException: no host to srv for serviceInfo: nacos-sdk-java-discovery
at com.alibaba.nacos.Balancer$RandomByWeight.selectAll(Balancer.java:45)
at com.alibaba.nacos.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上,欢迎下载学习
参考资料
推荐阅读

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。