問題:之前的配置文件都是散在各個項目中的,導致配置文件的管理比較困難,而且配置的值一旦改變,我們就需要重新編譯部署整個項目,非常麻煩!!!
解決方案:
預備知識:理解springboot的啟動過程。
一、項目結構
二、基礎框架:framework
1、pom.xml
1 <!-- archaius --> 2 <dependency> 3 <groupId>com.netflix.archaius</groupId> 4 <artifactId>archaius-core</artifactId> 5 <version>0.6.6</version> 6 </dependency> 7 <!-- 動態配置,archaius底層 --> 8 <dependency> 9 <groupId>commons-configuration</groupId> 10 <artifactId>commons-configuration</artifactId> 11 <version>1.8</version> 12 </dependency> View Code說明:引入archaius及其底層commons-configuration
2、讀取配置信息的數據源:ConsulConfigurationSource
1 package com.microservice.archaius; 2 3 import java.io.StringReader; 4 import java.util.HashMap; 5 import java.util.Map; 6 import java.util.Properties; 7 8 import org.apache.commons.lang3.StringUtils; 9 10 import com.google.common.base.Optional; 11 import com.netflix.config.PollResult; 12 import com.netflix.config.PolledConfigurationSource; 13 import com.orbitz.consul.Consul; 14 import com.orbitz.consul.KeyValueClient; 15 16 /** 17 * 指定archaius讀取配置的源頭 18 */ 19 public class ConsulConfigurationSource implements PolledConfigurationSource { 20 21 private String keyName; 22 23 public ConsulConfigurationSource(String keyName) { 24 this.keyName = keyName; 25 } 26 27 /** 28 * 默認情況下,每隔60s,該方法會執行一次 29 */ 30 @Override 31 public PollResult poll(boolean initial, Object checkPoint) throws Exception { 32 Consul consul = Consul.builder().build(); 33 KeyValueClient kvClient = consul.keyValueClient(); 34 Optional<String> kvOpt = kvClient.getValueAsString(keyName); 35 String kvStr = StringUtils.EMPTY; 36 if (kvOpt.isPresent()) { 37 kvStr = kvOpt.get(); 38 } 39 40 Properties props = new Properties(); 41 props.load(new StringReader(kvStr));//String->Properties 42 43 Map<String, Object> propMap = new HashMap<>(); 44 for (Object key : props.keySet()) { 45 propMap.put((String) key, props.get(key)); 46 } 47 return PollResult.createFull(propMap); 48 } 49 50 } View Code步驟:
注意:
3、設置PropertySource:ConsulPropertySource
1 package com.microservice.archaius; 2 3 import java.util.Iterator; 4 import java.util.Map; 5 6 import org.springframework.core.env.MapPropertySource; 7 8 import com.netflix.config.AbstractPollingScheduler; 9 import com.netflix.config.ConfigurationManager; 10 import com.netflix.config.DynamicConfiguration; 11 import com.netflix.config.FixedDelayPollingScheduler; 12 import com.netflix.config.PolledConfigurationSource; 13 14 /** 15 * 將 consul讀取的配置信息存入netflix config和PropertySource 16 */ 17 public class ConsulPropertySource extends MapPropertySource { 18 19 /** 20 * @param name 屬性源名稱:這裡就是consul KV中的K 21 * @param source 屬性源:這裡就是consul KV中的V 22 */ 23 public ConsulPropertySource(String name, Map<String, Object> source) { 24 super(name, source);//初始化 25 26 /** 27 * 從consul上讀取屬性並存入netflix config 28 */ 29 PolledConfigurationSource configSource = new ConsulConfigurationSource(name);//定義讀取配置的源頭 30 AbstractPollingScheduler scheduler = new FixedDelayPollingScheduler();//設置讀取配置文件的 31 DynamicConfiguration configuration = new DynamicConfiguration(configSource, scheduler); 32 ConfigurationManager.install(configuration); 33 34 /** 35 * 將屬性存入PropertySource 36 */ 37 @SuppressWarnings("rawtypes") 38 Iterator it = configuration.getKeys(); 39 while (it.hasNext()) { 40 String key = (String) it.next(); 41 this.source.put(key, configuration.getProperty(key)); 42 } 43 } 44 45 } View Code步驟:
注意:
4、初始化器:ConsulPropertySourceBootstrapInitializer
1 package com.microservice.archaius; 2 3 import java.util.HashMap; 4 import java.util.Properties; 5 6 import org.springframework.context.ApplicationContextInitializer; 7 import org.springframework.context.ConfigurableApplicationContext; 8 import org.springframework.core.env.ConfigurableEnvironment; 9 import org.springframework.core.env.MutablePropertySources; 10 import org.springframework.core.env.PropertySource; 11 12 import com.microservice.util.BaseContants; 13 import com.microservice.util.PropertyUtil; 14 15 public class ConsulPropertySourceBootstrapInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> { 16 17 @Override 18 public void initialize(ConfigurableApplicationContext applicationContext) { 19 ConfigurableEnvironment env = applicationContext.getEnvironment(); 20 PropertySource<?> source = this.locate(env); 21 MutablePropertySources propertySources = env.getPropertySources(); 22 propertySources.addLast(source); 23 } 24 25 private PropertySource<?> locate(ConfigurableEnvironment env) { 26 env.getPropertySources().remove(BaseContants.DEFAULT_PROPERTIES_SOURCE_NAME);//移除application.properties的PropertySource 27 Properties props = PropertyUtil.loadProps("bootstrap.properties"); 28 String servicename = props.getProperty(BaseContants.SERVICE_NAME_KEY); 29 String servicetag = props.getProperty(BaseContants.SERVICE_TAG_KEY); 30 return new ConsulPropertySource("service/" + servicename + "/" + servicetag + "/config", new HashMap<>()); 31 } 32 } View Code步驟:
5、兩個輔助類:BaseContants + PropertyUtil
1 package com.microservice.util; 2 3 public class BaseContants { 4 public static final String SERVICE_NAME_KEY = "service.name"; 5 public static final String SERVICE_TAG_KEY = "service.tag"; 6 public static final String DEFAULT_PROPERTIES_SOURCE_NAME = "applicationConfigurationProperties"; 7 } View Code 1 package com.microservice.util; 2 3 import java.io.IOException; 4 import java.io.InputStream; 5 import java.util.Properties; 6 7 /** 8 * 文件操作工具類 9 */ 10 public class PropertyUtil { 11 12 /** 13 * 加載屬性文件*.properties 14 * @param fileName 不是屬性全路徑名稱,而是相對於類路徑的名稱 15 */ 16 public static Properties loadProps(String fileName) { 17 Properties props = null; 18 InputStream is = null; 19 20 try { 21 is = Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName);//獲取類路徑下的fileName文件,並且轉化為輸入流 22 if (is != null) { 23 props = new Properties(); 24 props.load(is); //加載屬性文件 25 } 26 } catch (Exception e) { 27 e.printStackTrace(); 28 } finally { 29 if (is != null) { 30 try { 31 is.close(); 32 } catch (IOException e) { 33 e.printStackTrace(); 34 } 35 } 36 } 37 38 return props; 39 } 40 } View Code6、啟動類:MySpringAplication
1 package com.microservice; 2 3 import org.springframework.boot.SpringApplication; 4 import org.springframework.boot.autoconfigure.SpringBootApplication; 5 6 import com.microservice.archaius.ConsulPropertySourceBootstrapInitializer; 7 import com.microservice.consul.ConsulRegisterListener; 8 9 import springfox.documentation.swagger2.annotations.EnableSwagger2; 10 11 /** 12 * 注意:@SpringBootApplication該注解必須在SpringApplication.run()所在的類上 13 */ 14 @SpringBootApplication 15 @EnableSwagger2 16 public class MySpringAplication { 17 18 public void run(String[] args) { 19 SpringApplication sa = new SpringApplication(MySpringAplication.class); 20 sa.addInitializers(new ConsulPropertySourceBootstrapInitializer());//讀取配置文件 21 sa.addListeners(new ConsulRegisterListener());//consul服務注冊 22 sa.run(args); 23 } 24 25 public static void main(String[] args) { 26 } 27 } View Code說明:添加了initializer
三、微服務A:myserviceA
1、啟動參數配置類:bootstrap.properties
1 service.name=myserviceA 2 service.tag=dev 3 service.port=8080 4 health.url=http://localhost:8080/health 5 health.interval=10 View Code2、測試controller
1 package com.microservice.myserviceA.controller; 2 3 import org.springframework.beans.factory.annotation.Autowired; 4 import org.springframework.beans.factory.annotation.Value; 5 import org.springframework.core.env.Environment; 6 import org.springframework.web.bind.annotation.RequestMapping; 7 import org.springframework.web.bind.annotation.RestController; 8 9 import com.netflix.config.DynamicPropertyFactory; 10 import com.netflix.config.DynamicStringProperty; 11 12 @RestController 13 @RequestMapping("/test/archaius") 14 public class TestController { 15 @Autowired 16 private Environment env; 17 @Value("${mysql.driverClassName}") 18 private String zjgBrother; 19 20 @RequestMapping("/xxx") 21 public String test(){ 22 System.out.println("env->"+env.getProperty("xxx")); 23 System.out.println("value->"+zjgBrother); 24 25 DynamicStringProperty dsp = DynamicPropertyFactory.getInstance().getStringProperty("hystrix.command.HystrixCommandKey.execution.isolation.thread.timeoutInMilliseconds", "xxx"); 26 System.out.println("Dynamic->"+dsp.get()); 27 return "hello"; 28 } 29 } View Code說明:測試了從environment獲取、從@value獲取以及dynamicFactory中獲取三種方式,前兩種是從PropertySource中獲取(不能感知consul上配置的變化),最後一種是從PollResult中獲取(可以感知consul上的配置的變化)
四、測試
難點: