Spring 中使用Nacos服务发现
引入依赖
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-spring-context</artifactId>
<version>${latest.version}</version>
</dependency>
本文使用的版本为:1.1.1(与Spring 中使用Nacos配置管理一章引用的依赖一样)
示例
package com.yyoo.nacos.spring.discovery;
import com.alibaba.nacos.api.annotation.NacosProperties;
import com.alibaba.nacos.spring.context.annotation.discovery.EnableNacosDiscovery;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableNacosDiscovery(globalProperties = @NacosProperties(serverAddr = "127.0.0.1:8848"))
public class DiscoveryConfiguration {
}
package com.yyoo.nacos.spring.discovery;
import com.alibaba.nacos.api.annotation.NacosInjected;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.List;
@Controller
@RequestMapping("discovery")
public class DiscoveryController {
@NacosInjected
private NamingService namingService;
@RequestMapping(value = "/register", method = RequestMethod.GET)
@ResponseBody
public void register(@RequestParam String serviceName) throws NacosException {
namingService.registerInstance(serviceName,"111.111.1.1",8888);
}
@RequestMapping(value = "/get", method = RequestMethod.GET)
@ResponseBody
public List<Instance> get(@RequestParam String serviceName) throws NacosException {
return namingService.getAllInstances(serviceName);
}
}
@Test
public void testSpring3() throws NacosException {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.scan("com.yyoo");
context.refresh();
String serviceName = "testDiscovery";
DiscoveryController controller = context.getBean(DiscoveryController.class);
controller.register(serviceName);
System.out.println(controller.get(serviceName));
}
注:此处是为了方便,模拟请求Controller,其实效果是一样的。
目前还未注册服务,所以打印结果为:
[Instance{instanceId=‘111.111.1.1#8888#DEFAULT#DEFAULT_GROUP@@testDiscovery’, ip=‘111.111.1.1’, port=8888, weight=1.0, healthy=true, enabled=true, ephemeral=true, clusterName=‘DEFAULT’, serviceName=‘DEFAULT_GROUP@@testDiscovery’, metadata={}}]
此时可以查看Nacos服务管理界面
@NacosInjected注解详解
@NacosInjected 是一个核心注解,用于在Spring Beans 中注入 ConfigService 或 NamingService 实例,并使这些实例可缓存
package com.yyoo.nacos.spring.discovery;
import com.alibaba.nacos.api.annotation.NacosInjected;
import com.alibaba.nacos.api.annotation.NacosProperties;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.naming.NamingService;
import org.springframework.stereotype.Component;
@Component
public class InjectedBean {
@NacosInjected
private ConfigService configService;
@NacosInjected(properties = @NacosProperties(encode = "UTF-8"))
private ConfigService configService2;
@NacosInjected(properties = @NacosProperties(encode = "GBK"))
private ConfigService configService3;
@NacosInjected
private NamingService namingService;
@NacosInjected(properties = @NacosProperties(encode = "UTF-8"))
private NamingService namingService2;
@NacosInjected(properties = @NacosProperties(encode = "GBK"))
private NamingService namingService3;
public void testInjection() {
System.out.println("configService == configService2:" + (configService == configService2));
System.out.println("configService2 == configService3:" + (configService2 == configService3));
System.out.println("namingService == namingService2:" + (namingService == namingService2));
System.out.println("namingService2 == namingService3:" + (namingService2 == namingService3));
}
}
结果:
configService == configService2:true
configService2 == configService3:false
namingService == namingService2:true
namingService2 == namingService3:false
结论: @NacosProperties 相等,则实例将是相同的,无论属性是来自全局还是自定义的 Nacos 属性。反之,如果@NacosProperties不相等,实例不是相同的,建议在同一个应用内使用相同的配置。
配置监听注解
package com.yyoo.nacos.spring.bean;
import com.alibaba.nacos.api.annotation.NacosInjected;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.annotation.NacosConfigListener;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ConfigBean {
@NacosInjected
private ConfigService configService;
@NacosConfigListener(dataId = "com.yyoo.nacos.sdk.CofingServiceTest",groupId = "Nacos:Test")
public void onMessage(String config) {
System.out.println("listener:"+config);
}
}
@NacosConfigListener(dataId = "com.yyoo.nacos.sdk.CofingServiceTest",groupId = "Nacos:Test")
public void onMessage(String config) {
System.out.println("listener:"+config);
}
以上代码等价于:
configService.addListener("com.yyoo.nacos.sdk.CofingServiceTest", "Nacos:Test", new AbstractListener() {
@Override
public void receiveConfigInfo(String config) {
System.out.println("listener:"+config);
}
});
为监听添加类型转换器
@NacosConfigListener 的类型转换包括内置和自定义实现。 默认情况下,内置类型转换基于 Spring DefaultFormattingConversionService来实现。常见的基本类型都可以自动转换。
比如我们上面示例的DataId:“com.yyoo.nacos.sdk.CofingServiceTest”,groupId:“Nacos:Test”
其是一个Json类型,我们监听示例中使用String类型是可以的。如果我们需要将其在监听中解析成对象呢?这个时候就需要类型转换器了,自定义实现NacosConfigConverter接口即可自己实现类型转换器,比如我们自定义实现上面JSON配置内容的类型转换器
package com.yyoo.nacos.spring.bean;
import com.alibaba.nacos.api.config.convert.NacosConfigConverter;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public class TestNacosConfigConverter implements NacosConfigConverter<TestNacosConfig> {
@Override
public boolean canConvert(Class<TestNacosConfig> targetType) {
return true;
}
@Override
public TestNacosConfig convert(String config) {
ObjectMapper mapper = new ObjectMapper();
TestNacosConfig obj = null;
try {
obj = mapper.readValue(config,TestNacosConfig.class);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return obj;
}
}
编写自定义对象
package com.yyoo.nacos.spring.bean;
public class TestNacosConfig {
private String conf1;
private String conf2;
public String getConf1() {
return conf1;
}
public void setConf1(String conf1) {
this.conf1 = conf1;
}
public String getConf2() {
return conf2;
}
public void setConf2(String conf2) {
this.conf2 = conf2;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("TestNacosConfig{");
sb.append("conf1='").append(conf1).append('\'');
sb.append(", conf2='").append(conf2).append('\'');
sb.append('}');
return sb.toString();
}
}
修改添加监听的程序
@NacosConfigListener(dataId = "com.yyoo.nacos.sdk.CofingServiceTest",groupId = "Nacos:Test",converter = TestNacosConfigConverter.class)
public void onMessage(TestNacosConfig config) {
System.out.println("bean listener:"+config);
}
测试代码:
@Test
public void testSpring5() throws NacosException, InterruptedException {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.scan("com.yyoo");
context.refresh();
Thread.sleep(60000); // 休眠60秒,保证主程序不关闭
}
测试代码主程序没有停止前,在Nacos配置界面修改对应的配置conf2=‘test2’,监听程序打印结果:
bean listener:TestNacosConfig{conf1=‘test1’, conf2=‘test2’}
监听超时时间设置
由于运行自定义的 NacosConfigConverter 可能需要一些时间,因此您可以在 @NacosConfigListener.timeout() 属性中设置最大执行时间,以防止它阻塞其他侦听器。只需在@NacosConfigListener注解上加上timeout属性配置即可,timeout默认为1000L
@NacosConfigListener(dataId = "com.yyoo.nacos.sdk.CofingServiceTest",groupId = "Nacos:Test"
,converter = TestNacosConfigConverter.class,timeout = 1000L)
public void onMessage(TestNacosConfig config) {
System.out.println("bean listener:"+config);
}