nacos作为配置中心兼容xml配置文件
最近公司想要用配置中心,因为公司用的有传统的spring项目,有springboot项目,为了兼容都能够采用配置中心,做了一些尝试,经过比较还是倾向于使用nacos,传统dubbo采用spring方式读取xml读取配置文件的方式启动,其配置数据源,redis,rabbitmq等采用的是xml的配置,xml中取值是个问题,为了兼容xml能从远程配置中心更好的取值,做了一系列尝试。
比较当前的一些配置中心
Nacos的部署结构比较简单,运维成本较低。Apollo部署组件较多,运维成本比Nacos高。Spring Cloud Config生产高可用的成本最高。
Apollo支持Spring Boot和Spring Cloud项目,但是实现方式不同于标准,无法做无缝迁移,从Spring Cloud迁移到Apollo,存在代码改造和兼容性成本。
Nacos通过Spring Cloud for Alibaba支持Spring Boot和Spring Cloud生态,符合Spring生态中的标准实现方式,可以无缝从Spring Cloud Conig迁移到Nacos。
Apollo和Nacos相对于Spring Cloud Config的生态支持更广,在配置管理流程上做的更好。
Apollo相对于Nacos在配置管理做的更加全面,但使用起来也要麻烦一些。
Nacos使用起来相对比较简洁,在对性能要求比较高的大规模场景更适合。此外,Nacos除了提供配置中心的功能,还提供了动态服务发现、服务共享与管理的功能,降低了服务化改造过程中的难度。Nacos目前项目上的人力投入、社区的活跃度等也比较高
整体上来看,Nacos的读写性能最高,Apollo次之,Spring Cloud Config的依赖Git场景不适合开放的大规模自动化运维API
一、传统的spring加载xml项目启动兼容
典型启动方式是:
1 public static void main(String[] args) throws IOException { 2 ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( 3 new String[] {"applicationContext.xml"}); 4 context.start(); 5 System.out.println("------------"); 6 System.in.read(); // 为保证服务一直开着, 利用输入流的阻塞来模拟. 7 }
这种项目不适合通过注解进行,所以只能采用配置,网上参考的有些坑,主要是jar包冲突的问题,这里放上我修改的jar依赖
<dependency> <groupId>com.alibaba.nacos</groupId> <artifactId>nacos-spring-context</artifactId> <version>0.3.0</version> <exclusions> <exclusion> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> </exclusion> </exclusions> </dependency>
application-nacos.xml
1 <?xml version="1.0" encoding="UTF-8" ?> 2 <beans xmlns="http://www.springframework.org/schema/beans" xmlns:nacos="http://nacos.io/schema/nacos" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://nacos.io/schema/nacos http://nacos.io/schema/nacos.xsd 5 http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> 6 7 8 <!--nacos配置,这里是使用配置文件的方式,这只是其中的一种方式--> 9 <!--开启注解--> 10 <nacos:annotation-driven></nacos:annotation-driven> 11 <!--指定nacos配置地址--> 12 <nacos:global-properties server-addr="localhost:8848"/> 13 <!--指定dataId,group-id, 是否是自动刷新--> 14 <nacos:property-source data-id="dubbo-config" group-id="DEFAULT_GROUP" auto-refreshed="true"/> 15 </beans>
同时需要将该xml导入到基本的application.xml中
<import resource="spring/applicationContext-nacos.xml" />
nacos配置中心上添加的配置内容需要什么配置什么即可。
采用注解形式
<?xml version="1.0" encoding="UTF-8" ?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:nacos="http://nacos.io/schema/nacos" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://nacos.io/schema/nacos http://nacos.io/schema/nacos.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!--开启注解--> <nacos:annotation-driven></nacos:annotation-driven> </beans>
@Configuration @EnableNacosConfig(globalProperties = @NacosProperties(serverAddr = "127.0.0.1:8848")) @NacosPropertySource(dataId = "dubbo-config", autoRefreshed = true) public class nacosConfig { }
3、或者在application.xml里面整合好nacos的xml内容(命名空间,开启注解)采用注解
xmlns:nacos="http://nacos.io/schema/nacos"
xsi:schemaLocation="http://nacos.io/schema/nacos http://nacos.io/schema/nacos.xsd
同时需要注意
<nacos:annotation-driven></nacos:annotation-driven>放置的前后位置,不合适会报错,多更换几个位置试试就可以了,xml的加载顺序是从上到下来加载的
二、springboot项目兼容xml,这里有三种方式,可以采用上面的方式,也可以采用官方文档的注解方式,但是发现采用上面的方式配置动态刷新没有成功,而采用注解方式,需要注意的是注入值应该采用@NacosValue(value ="${xxxx}",autoRefreshed = true)方能能够实现自动刷新,而采用@Value("${xxxx}")不能实现自动刷新
第一种,采用同上面的方式
第二种,采用注解方式,jar包采用传统spring整合的jar包,依赖同上,
package com.topband.beings.config; import com.alibaba.nacos.api.annotation.NacosProperties; import com.alibaba.nacos.spring.context.annotation.config.EnableNacosConfig; import com.alibaba.nacos.spring.context.annotation.config.NacosPropertySource; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.ImportResource; @Configuration //@ImportResource({ "classpath:config/applicationContext-nacos.xml" })注:这个是采用xml配置需要添加的,因为不是采用显示加载application.xml启动 @EnableNacosConfig(globalProperties = @NacosProperties(serverAddr = "127.0.0.1:8848")) @NacosPropertySource(dataId = "being-springboot-config", autoRefreshed = true) public class NacosConfig { }
然后在controller注入值时采用是注入值应该采用@NacosValue(value ="${xxxx}",autoRefreshed = true)即可实现实时刷新,并且访问数据库什么的都是正常的
第三种,采用如下jar包,然后完全按照官方文档来即可
<dependency> <groupId>com.alibaba.boot</groupId> <artifactId>nacos-config-spring-boot-starter</artifactId> <version>0.2.1</version> <exclusions> <exclusion> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> </exclusion> </exclusions> </dependency>
application.properties
nacos.config.server-addr=127.0.0.1:8848
启动类上加注解
@SpringBootApplication @NacosPropertySource(dataId = "xxxx你nacos上的dataId", autoRefreshed = true) public class NacosConfigApplication { public static void main(String[] args) { SpringApplication.run(NacosConfigApplication.class, args); } }
同样的,采用@NacosValue(value ="${xxxx}",autoRefreshed = true)方能能够实现自动刷新,而采用@Value("${xxxx}")不能实现自动刷新,并且@NacosValue(value ="${xxxx}")也不能实时刷新,autoRefreshed默认为false
三、springboot直接采用springcloud的jar配置
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> <version>0.2.1.RELEASE</version> </dependency>
采用上述jar包去兼容xml的取值时,报不能加载属性值错误。没有成功,后来发现是application-name写错了,修改过来后,直接可用兼容。
四、一个springboot集成springcloud jar无xml配置的demo
直接采用完全注解的方式加载,写了一个小demo,采用三中依赖,不用排除上面exclusions中的包,一个独立的demo,集成redis集群,运行ok
1、bootstrap.properties
server.port=8002
spring.application.name=xxx-test
spring.profiles.active=local
spring.cloud.nacos.config.file-extension=properties
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
#spring.cloud.nacos.config.namespace=d6775f80-ed7a-409a-8dbc-49b2cddee4d1
在 Nacos Spring Cloud 中,dataId
的完整格式如下:
${prefix}-${spring.profile.active}.${file-extension}
prefix
默认为spring.application.name
的值,也可以通过配置项spring.cloud.nacos.config.prefix
来配置。spring.profile.active
即为当前环境对应的 profile,详情可以参考 Spring Boot文档。 注意:当spring.profile.active
为空时,对应的连接符-
也将不存在,dataId 的拼接格式变成${prefix}.${file-extension}
file-exetension
为配置内容的数据格式,可以通过配置项spring.cloud.nacos.config.file-extension
来配置。目前只支持properties
和yaml
类型。
通过 Spring Cloud 原生注解 @RefreshScope
实现配置自动更新:
@RestController @RefreshScope public class TestController { private Logger logger = LoggerFactory.getLogger(this.getClass()); @Autowired RedisTemplate<String,String> template; @Value("${id}") private String id; @GetMapping("/getId") public String getId(){ logger.info("看一看id的变化: {}",id); template.opsForValue().set("hello","world"); String result = template.opsForValue().get("hello"); return "redis result: "+result+"\tid: "+id; } }
Redis属性及配置
1 package com.xx.test.config; 2 3 4 import org.springframework.beans.factory.annotation.Value; 5 import org.springframework.cloud.context.config.annotation.RefreshScope; 6 import org.springframework.stereotype.Component; 7 8 @Component 9 @RefreshScope 10 public class RedisProperties { 11 @Value("${redis.expireSeconds}") 12 private int expireSeconds; 13 @Value("${redis.clusterNodes}") 14 private String clusterNodes; 15 @Value("${redis.commandTimeout}") 16 private int commandTimeout; 17 @Value("${redis.maxTotal}") 18 private int maxTotal; 19 @Value("${redis.maxTotal}") 20 private int maxIdle; 21 @Value("${redis.maxWaitMillis}") 22 private int maxWaitMillis; 23 @Value("${redis.testOnBorrow}") 24 private boolean testOnBorrow; 25 @Value("${redis.maxRedirects}") 26 private int maxRedirects; 27 28 public int getExpireSeconds() { 29 return expireSeconds; 30 } 31 32 public void setExpireSeconds(int expireSeconds) { 33 this.expireSeconds = expireSeconds; 34 } 35 36 public String getClusterNodes() { 37 return clusterNodes; 38 } 39 40 public void setClusterNodes(String clusterNodes) { 41 this.clusterNodes = clusterNodes; 42 } 43 44 public int getCommandTimeout() { 45 return commandTimeout; 46 } 47 48 public void setCommandTimeout(int commandTimeout) { 49 this.commandTimeout = commandTimeout; 50 } 51 52 public int getMaxTotal() { 53 return maxTotal; 54 } 55 56 public void setMaxTotal(int maxTotal) { 57 this.maxTotal = maxTotal; 58 } 59 60 public int getMaxIdle() { 61 return maxIdle; 62 } 63 64 public void setMaxIdle(int maxIdle) { 65 this.maxIdle = maxIdle; 66 } 67 68 public int getMaxWaitMillis() { 69 return maxWaitMillis; 70 } 71 72 public void setMaxWaitMillis(int maxWaitMillis) { 73 this.maxWaitMillis = maxWaitMillis; 74 } 75 76 public boolean isTestOnBorrow() { 77 return testOnBorrow; 78 } 79 80 public void setTestOnBorrow(boolean testOnBorrow) { 81 this.testOnBorrow = testOnBorrow; 82 } 83 84 public int getMaxRedirects() { 85 return maxRedirects; 86 } 87 88 public void setMaxRedirects(int maxRedirects) { 89 this.maxRedirects = maxRedirects; 90 } 91 }
config
1 package com.xxx.test.config; 2 3 import com.xxx.test.common.redis.JRedisClient; 4 import org.springframework.beans.factory.annotation.Autowired; 5 import org.springframework.context.annotation.Bean; 6 import org.springframework.context.annotation.Configuration; 7 import org.springframework.data.redis.connection.RedisClusterConfiguration; 8 import org.springframework.data.redis.connection.RedisNode; 9 import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; 10 import org.springframework.data.redis.core.RedisTemplate; 11 import org.springframework.data.redis.serializer.StringRedisSerializer; 12 import redis.clients.jedis.JedisPoolConfig; 13 14 import java.util.ArrayList; 15 import java.util.List; 16 17 @Configuration 18 public class RedisConfig { 19 20 @Autowired 21 private RedisProperties redisProperties; 22 23 @Bean 24 public RedisClusterConfiguration redisClusterConfiguration(){ 25 RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration(); 26 redisClusterConfiguration.setMaxRedirects(redisProperties.getMaxRedirects()); 27 28 List<RedisNode> nodeList = new ArrayList<>(); 29 30 String[] cNodes = redisProperties.getClusterNodes().split(","); 31 //分割出集群节点 32 for(String node : cNodes) { 33 String[] hp = node.split(":"); 34 System.out.println("addr: "+hp[0]+"\t ip:"+Integer.parseInt(hp[1])); 35 nodeList.add(new RedisNode(hp[0], Integer.parseInt(hp[1]))); 36 } 37 redisClusterConfiguration.setClusterNodes(nodeList); 38 return redisClusterConfiguration; 39 } 40 41 42 @Bean 43 public JedisPoolConfig jedisPoolConfig(){ 44 JedisPoolConfig jedisPoolConfig = new JedisPoolConfig(); 45 jedisPoolConfig.setMaxIdle(redisProperties.getMaxIdle()); 46 jedisPoolConfig.setTestOnBorrow(redisProperties.isTestOnBorrow()); 47 jedisPoolConfig.setMaxTotal(redisProperties.getMaxTotal()); 48 jedisPoolConfig.setMaxWaitMillis(redisProperties.getMaxWaitMillis()); 49 return jedisPoolConfig; 50 } 51 @Bean 52 public JedisConnectionFactory getConnectionFactory(){ 53 return new JedisConnectionFactory(redisClusterConfiguration(),jedisPoolConfig()); 54 } 55 @Bean 56 public StringRedisSerializer stringRedisSerializer(){ 57 return new StringRedisSerializer(); 58 } 59 @Bean 60 public RedisTemplate redisTemplate(){ 61 RedisTemplate redisTemplate = new RedisTemplate(); 62 redisTemplate.setConnectionFactory(getConnectionFactory()); 63 redisTemplate.setKeySerializer(stringRedisSerializer()); 64 redisTemplate.setHashKeySerializer(stringRedisSerializer()); 65 return redisTemplate; 66 } 67 @Bean 68 public JRedisClient jRedisClient(){ 69 JRedisClient jRedisClient = new JRedisClient(); 70 jRedisClient.setRedisTemplate(redisTemplate()); 71 return jRedisClient; 72 } 73 74 @Bean 75 public RedisTemplate redisTemplate2(){ 76 RedisTemplate redisTemplate = new RedisTemplate(); 77 redisTemplate.setConnectionFactory(getConnectionFactory()); 78 redisTemplate.setKeySerializer(stringRedisSerializer()); 79 redisTemplate.setHashKeySerializer(stringRedisSerializer()); 80 redisTemplate.setValueSerializer(stringRedisSerializer()); 81 return redisTemplate; 82 } 83 @Bean 84 public JRedisClient jRedisClient2(){ 85 JRedisClient jRedisClient = new JRedisClient(); 86 jRedisClient.setRedisTemplate(redisTemplate2()); 87 return jRedisClient; 88 } 89 90 }
nacos上配置按照约定的dataid进行配置即可。
参考:官方文档https://nacos.io/zh-cn/docs/quick-start.html
https://blog.csdn.net/qq_25484147/article/details/86358209
@SpringBootApplication @NacosPropertySource(dataId = "example", autoRefreshed = true) public class NacosConfigApplication { public static void main(String[] args) { SpringApplication.run(NacosConfigApplication.class, args); } }