Nacos系列:Nacos的Java SDK使用

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上,欢迎下载学习

欢迎工作一到五年的Java工程师朋友们加入Java架构开发: 957734884,群内提供免费的Java架构学习资料(里面有高可用、高并发、高性能及分布式、Jvm性能调优、Spring源码,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多个知识点的架构资料)合理利用自己每一分每一秒的时间来学习提升自己,不要再用"没有时间“来掩饰自己思想上的懒惰!趁年轻,使劲拼,给未来的自己一个交代!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容