最近公司想要用配置中心,由於公司用的有傳統的spring項目,有springboot項目,爲了兼容都可以採用配置中心,作了一些嘗試,通過比較仍是傾向於使用nacos,傳統dubbo採用spring方式讀取xml讀取配置文件的方式啓動,其配置數據源,redis,rabbitmq等採用的是xml的配置,xml中取值是個問題,爲了兼容xml能從遠程配置中心更好的取值,作了一系列嘗試。html
比較當前的一些配置中心java
Nacos的部署結構比較簡單,運維成本較低。Apollo部署組件較多,運維成本比Nacos高。Spring Cloud Config生產高可用的成本最高。node
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場景不適合開放的大規模自動化運維APIredis
1、傳統的spring加載xml項目啓動兼容spring
典型啓動方式是:數據庫
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依賴bootstrap
<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.xmlapi
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中springboot
<import resource="spring/applicationContext-nacos.xml" />
nacos配置中心上添加的配置內容須要什麼配置什麼便可。app
採用註解形式
<?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 { }
三、或者在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的加載順序是從上到下來加載的
2、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
3、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寫錯了,修改過來後,直接可用兼容。
4、一個springboot集成springcloud jar無xml配置的demo
直接採用徹底註解的方式加載,寫了一個小demo,採用三中依賴,不用排除上面exclusions中的包,一個獨立的demo,集成redis集羣,運行ok
一、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
http://www.javashuo.com/article/p-huhkkysg-nn.html
@SpringBootApplication @NacosPropertySource(dataId = "example", autoRefreshed = true) public class NacosConfigApplication { public static void main(String[] args) { SpringApplication.run(NacosConfigApplication.class, args); } }