開發顛覆者SpringBoot實戰---------SpringCloud學習
SpringCloud 為開發人員提供了快速構建分散式系統的一些工具,包括配置管理、服務發現、斷路器、路由、微代理、事件匯流排、全域性鎖、決策競選、分散式會話等等。SpringCloud 提供了兩個基礎庫:Spring Cloud Context和Spring Cloud Commons。Context用於引導上下文、加密、重新整理範圍和環境端點,提供實用程式和特殊服務;Commons提供通用的(如Spring Cloud Netflix和Spring Cloud Consul)抽象和常用類。官方推薦SpringCloud使用bootstrap.yml(.properties)來作為它的配置檔案。它有以下幾個模組:
- Eureka:服務的註冊和發現
- Feign:服務的消費
- Ribbon:負載均衡
- Hystrix:斷路器
- Hystrix Dashboard:斷路器監控
- Zuul:路由閘道器
- Config:配置中心
本文的springboot版本是2.1.0.RELEASE,springcloud的版本是Greenwich.M1。特別說明一點,早期的版本會有許多坑,網上的許多教程也是老版本的教程,教大家解決這些坑,之前學習springcloud時使用的就是早期版本,慘不忍睹,這次使用最新的版本,發現基本沒有出現過問題,專案搭建起來也很容易,建議使用springboot的最新版本。
一、業務場景介紹
假設有一個簡易的電商網站,分為使用者模組、訂單模組、庫存模組,流程如下:
- 使用者進行相應的操作後(如付款),需要給使用者增加購物積分(使用者模組)
- 需要建立訂單(訂單模組)
- 需要減少庫存(庫存模組)
也就是說,使用者模組收到資訊反饋後,自身進行相應的操作後需要通知訂單和庫存兩個模組也進行相應的操作。
二、服務註冊中心Eureka
需要考慮的問題是,使用者模組想要呼叫訂單和庫存模組怎麼來呼叫,分散式的業務時,壓根不會知道訂單和庫存模組在哪臺機器上,所以就需要一個微服務框架中的註冊中心,專門負責服務的註冊和發現。
- Eureka Server:就是註冊中心,所有服務都會在註冊中心註冊,上面保留了各個服務的機器和埠號
- Eureka Client:就是各個服務,使用者、訂單、庫存模組分別將自己註冊到註冊中心上
下面是程式碼:
新建springboot專案,pom檔案:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>springboot</groupId>
<artifactId>springcloud_eureka</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>springcloud_eureka</name>
<description>註冊中心</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.M1</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</project>
新建bootstrap.properties檔案:
server.port=8760
#Eureka Server的hostname為localhost
eureka.instance.hostname=localhost
#宣告本服務不需要在Eureka Server上註冊,本身就是一個Eureka Server
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
在啟動類中新增@EnableEurekaServer,代表開啟EurekaServer的支援,表明自己是一個服務。這樣我們就有了一個服務的註冊中心,分散式的其他服務都需要在服務中心進行註冊。
二、服務消費(客戶端)Feign
現在有了註冊中心,也知道各個服務在哪臺機器哪個埠,就需要一個可以替我們向其他服務傳送請求的一個工具,而不是我們自己編寫程式碼去傳送http請求了,也就是Feign。
Feign的基本原理是動態代理,當對某個介面使用@FeignClient時,Feign就會建立一個代理物件,根據介面上的@RequestMapping等註解,來動態的構造出你的請求,然後發起請求,拿到相應結果。
下面是程式碼:
新建兩個專案,創建出訂單和庫存兩個系統,引入相同依賴:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.47</version>
</dependency>
新建bootstrap.properties檔案,訂單專案spring.application.name使用service-client-order,庫存專案spring.application.name使用service-client-stock
#本服務(客戶端)的名稱,不能使用下劃線,只能使用中劃線
spring.application.name=service-client-order/service-client-stock
#指向EurekaServer服務的地址
eureka.client.service-url.defaultZone=http://localhost:8760/eureka
啟動項中新增@EnableEurekaClient,表明自己是一個客戶端。
訂單專案新增:
/**
* @Description:訂單實體類
* @Author:wb
*/
@Data
@NoArgsConstructor
public class Order {
private String userId;
private Integer count;
private String descrpition;
}
/**
* @Description:訂單服務
* @Author:wb
*/
@RestController
@RequestMapping("orderService")
public class OrderService {
@Value("${server.port}")
private String port;
@RequestMapping(value = "unifiedOrder", method = RequestMethod.POST)
public Order unifiedOrder(@RequestBody Order order){
order.setDescrpition("success!this is orderService,my port is " + port);
return order;
}
}
庫存專案新增:
/**
* @Description:庫存服務
* @Author:wb
*/
@RestController
@RequestMapping("stockService")
public class StockService {
@Value("${server.port}")
private String port;
@RequestMapping(value = "reduceStock", method = RequestMethod.GET)
public String reduceStock(@RequestParam Integer count){
return "this is stockService,my port is " + port + ",count is " + count;
}
}
再新建一個專案,創建出使用者系統,引入依賴:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.47</version>
</dependency>
新建bootstrap.properties檔案:
#本服務(客戶端)的名稱,不能使用下劃線,只能使用中劃線
spring.application.name=service-client-user
#指向EurekaServer服務的地址
eureka.client.service-url.defaultZone=http://localhost:8760/eureka
建立FeignClient客戶端和呼叫介面:
/**
* @Description:Feign客戶端
* @Author:wb
*/
@Component
@FeignClient(name = "service-client-stock")
public interface OrderAndStockFeignClient {
@RequestMapping(value = "stockService/reduceStock", method = RequestMethod.GET)
String invokeStock(@RequestParam(value = "count") Integer count);
}
/**
* @Description:使用者服務
* @Author:wb
*/
@RestController
@RequestMapping("userService")
public class UserService {
@Autowired
private OrderAndStockFeignClient orderAndStockFeignClient;
@RequestMapping(value = "pay", method = RequestMethod.GET)
public String unifiedOrder(Integer count){
String invokeStock = orderAndStockFeignClient.invokeStock(count);
return invokeStock;
}
}
這樣服務的客戶端建立好了,(訂單專案暫時沒有用到,後面會用)我們可以在註冊中心檢視到已經有服務在註冊中心註冊了,直接訪問http://localhost:8760,可以看到:
介面訪問即可看到http://localhost:8769/userService/pay?count=20
三、負載均衡Ribbon
下面問題又來了,如果庫存服務部署到5臺機器上,如果按照上面的方法,使用者模組呼叫的永遠只是一臺機器上的介面,換句話說,如果使用負載均衡的話,Feign怎麼知道該請求哪臺機器呢?所以這時候就需要使用Ribbon作為負載均衡,它的預設配置使用Round Ribbon的輪巡演算法,就是所有相同application的機器依次查詢,不會出現隨機現象。Ribbon的原理是:
- 首先Ribbon會從 Eureka Client裡獲取到對應的服務登錄檔,也就知道了所有的服務都部署在了哪些機器上,在監聽哪些埠號
- 然後Ribbon就可以使用預設的Round Robin演算法,從中選擇一臺機器
- Feign就會針對這臺機器,構造併發起請求
下面是程式碼:
訂單和庫存專案各開啟兩個,可以在idea中進行設定,取消勾選Single instance only,修改埠號後開啟多個例項,用於測試使用:
更新使用者專案程式碼:
/**
* @Description:Feign客戶端
* @Author:wb
*/
@Component
@FeignClient(name = "server-ribbon", fallbackFactory = OrderFeignClientFallBackFactory.class)
//@FeignClient(name = "server-ribbon", fallback = OrderFeignClientFallBack.class)
public interface OrderAndStockFeignClient {
@RequestMapping(value = "ribbonOrderService/unifiedOrder", method = RequestMethod.POST)
Order invokeOrder(@RequestBody Order order);
@RequestMapping(value = "ribbonStockService/reduceStock", method = RequestMethod.GET)
String invokeStock(@RequestParam(value = "count") Integer count);
}
/**
* @Description:使用者服務
* @Author:wb
*/
@RestController
@RequestMapping("userService")
public class UserService {
@Autowired
private OrderAndStockFeignClient orderAndStockFeignClient;
@RequestMapping(value = "pay", method = RequestMethod.GET)
public String unifiedOrder(String userId, Integer count){
Order order = new Order();
order.setUserId(userId);
order.setCount(count);
Order invokeOrder = orderAndStockFeignClient.invokeOrder(order);
String invokeStock = orderAndStockFeignClient.invokeStock(count);
return invokeOrder + "-------------" + invokeStock;
}
}
新建Ribbon專案,引入依賴:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.0</version>
<scope>provided</scope>
</dependency>
新建bootstrap.properties檔案:
#本服務(客戶端)的名稱,不能使用下劃線,只能使用中劃線
spring.application.name=server-ribbon
#指向EurekaServer服務的地址
eureka.client.service-url.defaultZone=http://localhost:8760/eureka
建立負載均衡客戶端:
/**
* @Description:啟動類:負載均衡客戶端
* @Author:wb
*/
@SpringBootApplication
@EnableEurekaClient//ribbon負載均衡客戶端
public class SpringcloudRibbonApplication {
public static void main(String[] args) {
SpringApplication.run(SpringcloudRibbonApplication.class, args);
}
@LoadBalanced//表明這個restRemplate開啟負載均衡的功能,注入ioc容器中
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
/**
* @Description:庫存負載均衡伺服器
* @Author:wb
*/
@RestController
@RequestMapping("ribbonStockService")
public class RibbonStockService {
@Autowired
private RestTemplate restTemplate;
@RequestMapping(value = "reduceStock", method = RequestMethod.GET)
public String reduceStock(@RequestParam Integer count){
//指定代理的客戶端服務名稱和方法
return restTemplate.getForObject("http://service-client-stock/stockService/reduceStock?count=" + count, String.class );
}
}
/**
* @Description:訂單負載均衡伺服器
* @Author:wb
*/
@RestController
@RequestMapping("ribbonOrderService")
public class RibbonOrderService {
@Autowired
private RestTemplate restTemplate;
@RequestMapping(value = "unifiedOrder", method = RequestMethod.POST)
public Order reduceStock(@RequestBody Order order){
//指定代理的客戶端服務名稱和方法
return restTemplate.postForObject("http://service-client-order/orderService/unifiedOrder", order, Order.class );
}
}
再次介面訪問http://localhost:8769/userService/pay?count=20&userId=test,即可看到埠號 的變化,實現了負載均衡。
四、斷路器
在微服務架構中,根據業務來拆分成一個個的服務,服務與服務之間可以相互呼叫(RPC),在Spring Cloud可以用RestTemplate+Ribbon和Feign來呼叫。為了保證其高可用,單個服務通常會叢集部署。由於網路原因或者自身的原因,服務並不能保證100%可用,如果單個服務出現問題,呼叫這個服務就會出現執行緒阻塞,此時若有大量的請求湧入,Servlet容器的執行緒資源會被消耗完畢,導致服務癱瘓。服務與服務之間的依賴性,故障會傳播,會對整個微服務系統造成災難性的嚴重後果,這就是服務故障的“雪崩”效應。
所以提出斷路器的概念,較底層的服務如果出現故障,會導致連鎖故障。當對特定的服務的呼叫的不可用達到一個閥值(Hystric 是5秒20次) 斷路器將會被開啟。斷路開啟後,可用避免連鎖故障,fallback方法可以直接返回一個固定值。
1、Ribbon使用斷路器
對server-ribbon專案進行改造,pom檔案新增新引入:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
啟動類新增@EnableHystrix,修改方法:
/**
* @Description:庫存負載均衡伺服器
* @Author:wb
*/
@RestController
@RequestMapping("ribbonStockService")
public class RibbonStockService {
@Autowired
private RestTemplate restTemplate;
@RequestMapping(value = "reduceStock", method = RequestMethod.GET)
@HystrixCommand(fallbackMethod = "errorMethod")
public String reduceStock(@RequestParam Integer count){
//指定代理的客戶端服務名稱和方法
return restTemplate.getForObject("http://service-client-stock/stockService/reduceStock?count=" + count, String.class );
}
public String errorMethod(Integer count){
return "this is error,your paramter is " + count;
}
}
正常啟動專案後,可以正常訪問;關掉代理的兩個服務,再次訪問會出現錯誤提示,而不是導致服務出現異常。
2、Feign使用斷路器
對使用者專案進行改造,application.properties檔案中新增
#設定連線riibon時的超時連線時間
service-ribbon.ribbon.ConnectTimeout=1000
service-ribbon.ribbon.ReadTimeout=5000
#開啟feign客戶端斷路器功能
feign.hystrix.enabled=true
更新程式碼:
/**
* @Description:Feign客戶端
* @Author:wb
*/
@Component
@FeignClient(name = "server-ribbon", fallbackFactory = OrderFeignClientFallBackFactory.class)
//@FeignClient(name = "server-ribbon", fallback = OrderFeignClientFallBack.class)
public interface OrderAndStockFeignClient {
@RequestMapping(value = "ribbonOrderService/unifiedOrder", method = RequestMethod.POST)
Order invokeOrder(@RequestBody Order order);
@RequestMapping(value = "ribbonStockService/reduceStock", method = RequestMethod.GET)
String invokeStock(@RequestParam(value = "count") Integer count);
}
/**
* @Description:兩種方式實現斷路器功能
* @Author:wb
*/
@Component
class OrderAndStockFeignClientFallBack implements OrderAndStockFeignClient {
@Override
public Order invokeOrder(Order order) {
order.setDescrpition("this is error");
return order;
}
@Override
public String invokeStock(Integer count) {
return "this is error,paramter is " + count;
}
}
@Component
class OrderFeignClientFallBackFactory implements FallbackFactory<OrderAndStockFeignClient>{
@Override
public OrderAndStockFeignClient create(Throwable throwable) {
return new OrderAndStockFeignClient() {
@Override
public Order invokeOrder(Order order) {
order.setDescrpition("this is error,cause is " + throwable.getMessage());
return order;
}
@Override
public String invokeStock(Integer count) {
return "this is error,paramter is " + count + ",cause is " + throwable.getMessage();
}
};
}
}
Feign使用斷路器,官網文件中給出兩種方式,一種只需要實現Feign客戶端介面,fallback 指定實現的這個類;另一種繼承FallbackFactory,重寫方法獲得的引數cause可以獲得發生熔斷的原因,fallbackFactory指定繼承的這個類。
3、Hystrix Dashboard斷路器監控(儀表盤)
這部分是比較早寫的,與本文不匹配,可以忽略,感覺使用的也不多。
Hystrix Dashboard主要目的是監控斷路器的情況,具體使用沒有研究,現在基於service-ribbon來改造。
pom檔案新增:
<!--引入監控-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--引入儀表盤-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
啟動類中新增@EnableHystrixDashboard和@EnableCircuitBreaker,增加下面程式碼:
@Bean
public ServletRegistrationBean getServlet(){
HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
registrationBean.setLoadOnStartup(1);
registrationBean.addUrlMappings("/hystrix.stream");
registrationBean.setName("HystrixMetricsStreamServlet");
registrationBean.setName("HystrixMetricsStreamServlet");
return registrationBean;
}
訪問http://localhost:8764/hystrix,在引數中輸入http://localhost:8764/hystrix.stream,title中隨意輸入,訪問後可以看到儀表盤自動根據http://localhost:8764/ribbon的訪問,而時刻跟蹤變化,以及發生故障的情況,儀表盤的變化。
五、路由閘道器Zuul
在Spring Cloud微服務系統中,一種常見的負載均衡方式是,客戶端的請求首先經過負載均衡(zuul、Ngnix),再到達服務閘道器(zuul叢集),然後再到具體的服。服務統一註冊到高可用的服務註冊中心叢集,服務的所有的配置檔案由配置服務管理,配置服務的配置檔案放在git倉庫,方便開發人員隨時改配置。
路由閘道器的作用就是處理請求,前端呼叫後臺幾百個服務和介面,不需要記住所有服務的名稱和地址,只需要根據請求的特徵將請求轉發給各個服務。路由功能是微服務的一部分,比如/api/user轉發到到user服務,/api/shop轉發到到shop服務。zuul預設和Ribbon結合實現了負載均衡的功能。
新建Zuul專案,引入依賴:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
新建bootstrap.properties檔案:
#本服務(客戶端)的名稱,不能使用下劃線,只能使用中劃線
spring.application.name=server-zuul
#指向EurekaServer服務的地址
eureka.client.service-url.defaultZone=http://localhost:8760/eureka
zuul.routes.zuul1.path=/stockZuul/**
zuul.routes.zuul1.service-id=SERVER-RIBBON
zuul.routes.zuul2.path=/orderZuul/**
zuul.routes.zuul2.service-id=SERVER-RIBBON
zuul.routes.zuul3.path=/userZuul/**
zuul.routes.zuul3.service-id=SERVICE-CLIENT-USER
注意,zuul.routes後面的一個引數自己命名的(zuul1),但是path和service-id要統一;path是自己設定的訪問路徑,service-id是對應註冊中服務的名稱,所以用大寫。
在啟動類中新增@EnableZuulProxy和@EnableEurekaClient,開啟路由功能,訪問http://localhost:8760/userZuul/userService/pay?userId=test&count=20,可以看到轉發後訪問的不同結果。
還可以簡單的開啟過濾功能:
/**
* 路由過濾
* @author think
*/
@Component
public class Myfilter extends ZuulFilter {
//filterType代表過濾器型別,pre路由之前,routing路由之時,post路由之後,error發生錯誤呼叫
@Override
public String filterType() {
return "pre";
}
//過濾的順序
@Override
public int filterOrder() {
return 0;
}
//是否過濾,裡面可以寫邏輯判斷
@Override
public boolean shouldFilter() {
return true;//永遠過濾
}
//過濾的具體邏輯,可以查詢sql等
@Override
public Object run() throws ZuulException {
//zuul自帶的Request容器,可以獲得request
RequestContext context = RequestContext.getCurrentContext();
HttpServletRequest request = context.getRequest();
String token = request.getParameter("token");
if (token == null) {
context.setSendZuulResponse(false);//路由不通過
try {
context.getResponse().getWriter().write("sorry,token is null");
}catch (Exception e){
}
}
return null;//通過與不通過都不需要返回值
}
}
六、配置中心
在分散式系統中,由於服務數量巨多,為了方便服務配置檔案統一管理,實時更新,所以需要分散式配置中心元件。在Spring Cloud中,有分散式配置中心元件spring cloud config ,它支援配置服務放在配置服務的記憶體中(即本地),也支援放在遠端Git倉庫中。在spring cloud config 元件中,分兩個角色,一是config server,二是config client。
1、Config Server配置中心
新建Config Server專案,引入依賴:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
新建bootstrap.properties檔案:
#本服務(客戶端)的名稱,不能使用下劃線,只能使用中劃線
spring.application.name=server-config
#指向EurekaServer服務的地址
eureka.client.service-url.defaultZone=http://localhost:8760/eureka
#git地址
spring.cloud.config.server.git.uri=https://gitee.com/zajiayouzai/springboot.git
#倉庫路徑
spring.cloud.config.server.git.search-paths=/config
#倉庫分支
spring.cloud.config.label=master
#git使用者名稱和密碼,git為公開倉庫,一般不需要密碼
#spring.cloud.config.server.git.username=
#spring.cloud.config.server.git.password=
啟動類新增@EnableConfigServer,開啟配置伺服器功能。
關於配置檔案的說明,git上面配置檔案的命名規則一般是{applicationName}-{profile}.yml/properties,我的配置檔名稱為server-config-dev.properties,
使用配置中心服務直接訪問http://localhost:8762/server-config/dev即可看到檔案的內容,路徑規則同樣是http://id:port/{applicationName}/{profile}。這種直接訪問的方式,不會去區分是否是application.properties配置檔案中的search-paths的respo路徑,他會掃描整個git倉庫,符合命名規則的配置檔案都會被掃描出來。
2、Config Client配置檔案讀取
更新庫存專案:
/**
* @Description:庫存服務
* @Author:wb
*/
@RestController
@RequestMapping("stockService")
public class StockService {
@Value("${server.port}")
private String port;
@Value("${version}")
private String version;
@RequestMapping(value = "reduceStock", method = RequestMethod.GET)
public String reduceStock(@RequestParam Integer count){
return "this is stockService,my port is " + port + ",my version is " + version + ",count is " + count;
}
}
更新bootstrap.properties檔案:
#本服務(客戶端)的名稱,不能使用下劃線,只能使用中劃線
spring.application.name=service-client-stock
#指向EurekaServer服務的地址
eureka.client.service-url.defaultZone=http://localhost:8760/eureka
#指明遠端倉庫的分支
spring.cloud.config.label=master
#指明配置檔案的環境
spring.cloud.config.profile=dev
#指向配置中心
#spring.cloud.config.uri=http://localhost:8888/
#是否從配置中心讀取檔案
spring.cloud.config.discovery.enabled=true
#配置中心的服務名稱
spring.cloud.config.discovery.service-id=server-config
下面是我總結的以前版本的坑,但是新版本其實只要注意第一點就可以了:
- git上面的配置檔名稱,一定與客戶端的applicationName和profile相對應上
- 配置中心和讀取配置檔案的客戶端pom檔案是不一樣的,配置中心一定是spring-cloud-config-server,客戶端一定是spring-cloud-starter-config,各需要一個即可,不要多不要少,多了少了都會報錯。
- 以上兩點最為關鍵,還有一個就是之前提到的版本問題。其他方面都是不重要的,根據網上的教程,埠號只能是固定的8888,在啟動類中新增@RefreshScope註解,這些都不是解決問題的關鍵。
- bootstrap.properties這個名稱還是要固定,雖然在本例中使用application.properties同樣可用,但是後面講到的高可用的配置中心時候還是要使用bootstrap.properties。
- 另外說一個配置中心設定倉庫路徑的問題:spring.cloud.config.server.git.search-paths=/respo,這段配置指定的路徑名稱在我們實際操作中沒有起到什麼作用,但是它是必須存在的。如果你的配置檔案沒有放在git倉庫的根目錄下,那麼你就必須有這段配置,告訴程式需要掃描的路徑;否則沒有這段配置的話,配置中心只會讀取根目錄下的檔案,而不去掃描其他資料夾下面的配置檔案。防止入坑,可以把這段配置設定為:/**,這樣不管你配置檔案放在什麼地方,都不會報錯了。
3、讀取本地配置檔案
讀取本地配置檔案比較簡單,改造service-config-server,在resources下新建一個資料夾config,新增新檔案service-config-client-test.properties,這裡檔案的命名規則與git上面的命名規則一致,修改application.properties:
server.port=8888
spring.application.name=service-config-server
#使用本地配置檔案
spring.profiles.active = native
#同樣可以指定硬碟的檔案位置,注意這裡只能填寫的是路徑,不能具體到檔案
spring.cloud.config.server.native.searchLocations=classpath:/config/
修改service-config-client的bootstrap.properties:
server.port=8768
spring.application.name=service-config-client
spring.cloud.config.profile=test
spring.cloud.config.uri= http://localhost:8888/
這樣就可以成功訪問到專案中或者本地硬碟上面的配置檔案。
最後所有專案: