為什么要學習Spring Cloud
在項目開發中隨著業務越來越多,導致功能之間耦合性高、開發效率低、系統運行緩慢難以維護、不穩定。微服務
架構可以解決這些問題,而Spring Cloud是微服務架構最流行的實現.
1.微服務
微服務架構是使用一套小服務來開發單個應用的方式或途徑,每個服務基于單一業務能力構建,運行在自己的進程中,并使用輕量級機制通信,通常是HTTP API,并能夠通過自動化部署機制來獨立部署。這些服務可以使用不同的編程語言實現,以及不同數據存儲技術,并保持最低限度的集中式管理。 微服務結構圖 :
API Gateway網關是一個服務器,是系統的唯一入口。網關提供RESTful/HTTP的方式訪問服務。而服務端通過服務注冊中心進行服務注冊和管理。
微服務的特點:
?
單一職責:微服務中每一個服務都對應唯一的業務能力,做到單一職責
面向服務:面向服務是說每個服務都要對外暴露服務接口API。并不關心服務的技術實現,做到與平臺和語言無關,也不限定用什么技術實現,只要提供REST的接口即可。
自治:自治是說服務間互相獨立,互不干擾
團隊獨立:每個服務都是一個獨立的開發團隊。
技術獨立:因為是面向服務,提供REST接口,使用什么技術沒有別人干涉
前后端分離:采用前后端分離開發,提供統一REST接口,后端不用再為PC、移動段開發不同接口
數據庫分離:每個服務都使用自己的數據源
2.遠程調用方式
常見的遠程調用方式有以下幾種:
RPC:Remote Procedure Call遠程過程調用,類似的還有RMI。自定義數據格式,基于原生TCP通信,速度快,效率高。早期的Web Service,現在熱門的Dubbo,都是RPC的典型。
HTTP:HTTP其實是一種網絡傳輸協議,基于TCP,規定了數據傳輸的格式。現在客戶端瀏覽器與服務端通信基本都是采用HTTP協議。也可以用來進行遠程服務調用。缺點是消息封裝臃腫。現在熱門的REST風格,就可以通過HTTP協議來實現。
2.1.認識RPC
RPC,即 Remote Procedure Call(遠程過程調用),是一個計算機通信協議。 該協議允許運行于一臺計算機的序調用另一臺計算機的子程序,而程序員無需額外地為這個交互作用編程。說得通俗一點就是:A計算機提供一個服務,B計算機可以像調用本地服務那樣調用A計算機的服務。
RPC調用流程圖:
2.2.認識HTTP
HTTP其實是一種網絡傳輸協議,基于TCP,工作在應用層,規定了數據傳輸的格式。現在客戶端瀏覽器與服務端通信基本都是采用HTTP協議,也可以用來進行遠程服務調用。缺點是消息封裝臃腫,優勢是對服務的提供和調用方沒有任何技術限定,自由靈活,更符合微服務理念。現在熱門的REST風格,就可以通過HTTP協議來實現。
2.3.如何選擇?
如果你們公司全部采用Java技術棧,那么使用Dubbo作為微服務架構是一個不錯的選擇。
如果公司的技術棧多樣化,而且你更青睞Spring家族,那么Spring Cloud搭建微服務是不二之選。會選擇Spring Cloud套件,因此會使用HTTP方式來實現服務間調用。
3.Spring Cloud簡介
3.1.簡介
Spring Cloud是Spring旗下的項目之一,官網地址:點我進入
Spring最擅長的就是集成,把世界上最好的框架拿過來,集成到自己的項目中。
Spring Cloud也是一樣,它將現在非常流行的一些技術整合到一起,實現了諸如:配置管理,服務發現,智能路由,負載均衡,熔斷器,控制總線,集群狀態等等功能。
其主要涉及的組件包括:
Net?ix
Eureka:注冊中心
Zuul:服務網關
Ribbon:負載均衡
Feign:服務調用
Hystrix:熔斷器
以上只是其中一部分,架構圖:
?
3.2.版本
Spring Cloud的版本命名比較特殊,因為它不是一個組件,而是許多組件的集合,它的命名是以A到Z為首字母的一些單詞組成(其實是倫敦地鐵站的名字):
Spring Clound 和Spring Boot版本對應關系
4.微服務場景模擬
4.1.? 創建父工程
微服務中需要同時創建多個項目,為了方便課堂演示,先創建一個父工程,后續的工程都以這個工程為父,使用Maven的聚合和繼承。統一管理子工程的版本和配置
pom.xml文件:
<?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>com.lxs</groupId><artifactId>lxs-springcloud</artifactId><packaging>pom</packaging><version>1.0-SNAPSHOT</version><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.1.5.RELEASE</version><relativePath/></parent><properties><java.version>1.8</java.version><spring-cloud.version>Greenwich.SR1</spring-cloud.version><mapper.starter.version>2.1.5</mapper.starter.version><mysql.version>5.1.46</mysql.version></properties><dependencyManagement><dependencies><!-- springCloud --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>${spring-cloud.version}</version><type>pom</type><scope>import</scope></dependency><!-- 通用Mapper啟動器 --><dependency><groupId>tk.mybatis</groupId><artifactId>mapper-spring-boot-starter</artifactId><version>${mapper.starter.version}</version></dependency><!-- mysql驅動 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>${mysql.version}</version></dependency></dependencies></dependencyManagement><dependencies><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build>
</project>
注意:spring clound和spring boot 的版本對應 greenwich版本clound對應spring boot 2.1.x
注意:注意聚合父工程 <packaging>pom</packaging>
這里已經對大部分要用到的依賴的版本進行了 管理,方便后續使用
4.2.服務提供者
我們新建一個項目,對外提供查詢用戶的服務。
4.2.1.創建Module
選中父工程-springclound,創建子工程:
pom.xml文件
<?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"><parent><artifactId>lxs-springcloud</artifactId><groupId>com.lxs</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>user-service</artifactId><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- 通用Mapper啟動器 --><dependency><groupId>tk.mybatis</groupId><artifactId>mapper-spring-boot-starter</artifactId></dependency><!-- mysql驅動 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency></dependencies>
</project>
項目結構如下
4.2.2.編寫配置文件
創建 user-service\src\main\resources\application.yml 屬性文件,這里我們采用了yaml語法,而不是
properties:
server:port: 9091
spring:datasource:driver-class-name: com.mysql.jdbc.Driverurl: jdbc:mysql://localhost:3306/springcloundusername: rootpassword: root
mybatis:type-aliases-package: com.zx.bean
啟動類
@SpringBootApplication@MapperScan("com.zx.mapper")public class UserApplication {public static void main(String[] args) {SpringApplication.run(UserApplication.class, args);}}
實體類
service:
@Servicepublic class UserService {@Autowiredprivate UserMapper userMapper;public User queryById(Long id) {return this.userMapper.selectByPrimaryKey(id);}}
controller
@RestController@RequestMapping("/user")public class UserController {@Autowiredprivate UserService userService;@GetMapping("/{id}")public User queryById(@PathVariable Long id){return userService.queryById(id);}}
對外提供REST風格web 服務,根據id查詢用戶
完成上述代碼后的項目結構
4.2.4.? 啟動并測試
啟動項目,訪問http://localhost:9091/user/7
4.3.服務調用者
4.3.1.創建工程
與上面類似,這里不再贅述,需要注意的是,我們調用 user-service 的功能,因此不需要Mybatis相關依賴了
拷貝之前的user-service 模塊,更改響應的坐標
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"><parent><artifactId>lxs-springcloud</artifactId><groupId>com.lxs</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>consumer-demo</artifactId><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency></dependencies>
</project>
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.0http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>lxs-springcloud</artifactId><groupId>com.lxs</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>consumer-demo</artifactId><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency></dependencies></project>
項目結構如下:
4.3.2.編寫代碼
啟動器:
@SpringBootApplicationpublic class ConsumerApplication {public static void main(String[] args) {SpringApplication.run(ConsumerApplication.class, args);}? ?@Beanpublic RestTemplate restTemplate(){return new RestTemplate();}}
?
Spring提供了一個RestTemplate模板工具類,對基于http的客戶端進行了封裝,并且實現了對象與json的序列化和反序列化,非常方便。RestTemplate并沒有限定http的客戶端類型,而是進行了抽象,目前常用的3種都有支持:
httpClient
Okhttp
JDK原生的URLConnection(默認的)
實體類
@Datapublic class User {private Long id;// 用戶名private String userName;// 密碼private String password;// 姓名private String name;// 年齡private Integer age;// 性別,1男性,2女性private Integer sex;// 出生日期private Date birthday;// 創建時間private Date created;// 更新時間private Date updated;// 備注private String note;}
controller
@RestController@RequestMapping("/consumer")public class ConsumerController {@Autowiredprivate RestTemplate restTemplate;@GetMapping("/{id}")public User queryById(@PathVariable Long id){String url = "http://localhost:9091/user/" + id;return restTemplate.getForObject(url, User.class);}}
4.3.3.啟動測試:
因為我們沒有配置端口,那么默認就是8080,我們訪問:http://localhost:8080/consume/7
一個簡單的遠程服務調用案例就實現了。
4.4.思考問題
簡單回顧一下,剛才我們寫了什么:
user-service:對外提供了查詢用戶的接口
consumer-demo:通過RestTemplate訪問 http://locahost:9091/user/{id} 接口,查詢用戶數據
存在什么問題?
在consumer中,我們把url地址硬編碼到了代碼中,不方便后期維護
consumer需要記憶user-service的地址,如果出現變更,可能得不到通知,地址將失效
consumer不清楚user-service的狀態,服務宕機也不知道
user-service只有1臺服務,不具備高可用性
即便user-service形成集群,consumer還需自己實現負載均衡
?
以上的問題,我們都將在SpringCloud中得到答案。
6.Eureka注冊中心
6.1.Eureka簡介
問題分析
在剛才的案例中,user-service對外提供服務,需要對外暴露自己的地址。而consumer(調用者)需要記錄服務提供者的地址。將來地址出現變更,還需要及時更新。這在服務較少的時候并不覺得有什么,但是在現在日益復雜的互聯網環境,一個項目肯定會拆分出十幾,甚至數十個微服務。此時如果還人為管理地址,不僅開發困難,將來測試、發布上線都會非常麻煩,
這就好比是網約車出現以前,人們出門叫車只能叫出租車。一些私家車想做出租卻沒有資格,被稱為黑車。而很多
人想要約車,但是無奈出租車太少,不方便。私家車很多卻不敢攔,而且滿大街的車,誰知道哪個才是愿意載人
的。一個想要,一個愿意給,就是缺少引子,缺乏管理啊。
? 此時滴滴這樣的網約車平臺出現了,所有想載客的私家車全部到滴滴注冊,記錄你的車型(服務類型),身份信息(聯系方式)。這樣提供服務的私家車,在滴滴那里都能找到,一目了然。此時要叫車的人,只需要打開APP,輸入你的目的地,選擇車? ? ? ? ? ? ? 型(服務類型),滴滴自動安排一個符合需求的車到你面前,為你服務,完美!
Eureka做什么?
Eureka就好比是滴滴,負責管理、記錄服務提供者的信息。服務調用者無需自己尋找服務,而是把自己的需求告訴Eureka,然后Eureka會把符合你需求的服務告訴你。
同時,服務提供方與Eureka之間通過 “心跳” 機制進行監控,當某個服務提供方出現問題,Eureka自然會把它從服務列表中剔除。
這就實現了服務的自動注冊、發現、狀態監控。
6.2.原理圖
基本架構:
Eureka:就是服務注冊中心(可以是一個集群),對外暴露自己的地址
提供者:啟動后向Eureka注冊自己信息(地址,提供什么服務)
消費者:向Eureka訂閱服務,Eureka會將對應服務的所有提供者地址列表發送給消費者,并且定期更新
心跳(續約):提供者定期通過http方式向Eureka刷新自己的狀態
工作原理圖解析
6.3.入門案例
6.3.1.編寫EurekaServer
Eureka是服務注冊中心,只做服務注冊;自身并不提供服務也不消費服務。可以搭建Web工程使用Eureka,可以使用Spring Boot方式搭建。
1. pom.xml
<?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.0http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>lxs-springcloud</artifactId><groupId>com.lxs</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>eureka-server</artifactId><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-server</artifactId></dependency></dependencies></project>
2. 編寫啟動類:
//聲明當前應用時Eureka服務@EnableEurekaServer@SpringBootApplicationpublic class EurekaServerApplication {public static void main(String[] args) {SpringApplication.run(EurekaServerApplication.class, args);}}
3. 編寫配置:
server:port: 10086spring:application:name: eureka-servereureka:client:service-url:# eureka 服務地址,如果是集群的話;需要指定其它集群eureka地址defaultZone: http://127.0.0.1:10086/eureka# 不注冊自己register-with-eureka: false# 不拉取服務fetch-registry: false
4. 啟動服務,并訪問:http://127.0.0.1:10086/
6.3.2.? 服務注冊
在服務提供工程user-service上添加Eureka客戶端依賴;自動將服務注冊到EurekaServer服務地址列表。
1. 添加依賴
<!-- Eureka客戶端 --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency>
2. 在啟動類上開啟Eureka客戶端功能
?
通過添加 @EnableDiscoveryClient 來開啟Eureka客戶端功能
@SpringBootApplication@MapperScan("com.zx.mapper")@EnableDiscoveryClient //開啟Eureka客戶端發現功能public class UserApplication {public static void main(String[] args) {SpringApplication.run(UserApplication.class, args);}}
3. 編寫配置
server:port: 9091spring:datasource:driver-class-name: com.mysql.jdbc.Driverurl: jdbc:mysql://localhost:3306/springcloudusername: rootpassword: rootapplication:name: user-servicemybatis:type-aliases-package: com.zx.beaneureka:client:service-url:defaultZone: http://127.0.0.1:10086/eureka
注意:
這里我們添加了spring.application.name屬性來指定應用名稱,將來會作為應用的id使用。
不用指定register-with-eureka和fetch-registry,因為默認是true
重啟項目,訪問Eureka監控頁面查看
localhost:10086/
我們發現user-service服務已經注冊成功了
6.3.3.? 服務發現
在服務消費工程consumer-demo上添加Eureka客戶端依賴;可以使用工具類DiscoveryClient根據服務名稱獲取對
應的服務地址列表。
1)添加依賴:
?
<!-- Eureka客戶端 -->
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency>
2)在啟動類添加開啟Eureka客戶端發現的注解
@SpringBootApplication@EnableDiscoveryClient // 開啟Eureka客戶端public class UserConsumerDemoApplication {@Beanpublic RestTemplate restTemplate() {return new RestTemplate(new Okhttp3ClienthttpRequestFactory());}public static void main(String[] args) {SpringApplication.run(UserConsumerDemoApplication.class, args);}}
3)修改配置:
spring:application:name: consumer-demoeureka:client:service-url:defaultZone: http://127.0.0.1:10086/eureka
4)修改代碼,用DiscoveryClient類的方法,根據服務名稱,獲取服務實例:
@RestController@RequestMapping("/consumer")public class ConsumerController {@Autowiredprivate RestTemplate restTemplate;@Autowiredprivate DiscoveryClient discoveryClient;@GetMapping("/{id}")public User queryById(@PathVariable("id") Long id) {List<ServiceInstance> serviceInstances = discoveryClient.getInstances("user-service");ServiceInstance serviceInstance = serviceInstances.get(0);url = "http://" + serviceInstance.getHost() + ":" + serviceInstance.getPort() + "/user/"+ id;return restTemplate.getForObject(url, User.class);}}
5)Debug跟蹤運行:
生成的URL:
訪問結果:
6.4.Eureka詳解
接下來我們詳細講解Eureka的原理及配置。
6.4.1.基礎架構
Eureka架構中的三個核心角色:
服務注冊中心
Eureka的服務端應用,提供服務注冊和發現功能,就是剛剛我們建立的eureka-server
服務提供者
提供服務的應用,可以是Spring Boot應用,也可以是其它任意技術實現,只要對外提供的是REST風格服務即可。本例中就是我們實現的user-service
服務消費者
消費應用從注冊中心獲取服務列表,從而得知每個服務方的信息,知道去哪里調用服務方。本例中就是我們實現的consumer-demo
6.4.2.高可用的Eureka Server
Eureka Server即服務的?注冊中心,在剛才的案例中,我們只有一個EurekaServer,事實上EurekaServer?也可以是一個集群,形成高可用的Eureka中心 。
Eureka Server是一個web應用,可以啟動多個實例(配置不同端口)保證Eureka Server的高可用
服務同步
多個Eureka Server之間也會互相注冊為服務,當服務提供者注冊到Eureka Server集群中的某個節點時,該節點會把服務的信息同步給集群中的每個節點,從而實現數據同步。因此,無論客戶端訪問到Eureka Server集群中的任意一個節點,都可以獲取到完整的服務列表信息。而作為客戶端,需要把信息注冊到每個Eureka中
?
如果有三個Eureka,則每一個EurekaServer都需要注冊到其它幾個Eureka服務中。
例如:有三個分別為10086、10087、10088,則:
10086要注冊到10087和10088上
10087要注冊到10086和10088上
10088要注冊到10086和10087上
動手搭建高可用的EurekaServer
我們假設要搭建兩條EurekaServer的集群,端口分別為:10086和10087
1)我們修改原來的EurekaServer配置:
里面的${XX}? : 含義如果沒有port,那么使用默認的10086端口,如果有,就是指定的端口
server:port: ${port:10086}spring:application:name: eureka-servereureka:client:#eureka的服務地址,如果是集群,需要指定其他集群eureka地址service-url:defaultZone: ${defaultZone:http://127.0.0.1:10086/eureka}
所謂的高可用注冊中心,其實就是把EurekaServer自己也作為一個服務進行注冊,這樣多個EurekaServer之間就能互相發現對方,從而形成集群。
因此我們做了以下修改:
刪除了register-with-eureka=false和fetch-registry=false兩個配置。因為默認值是true,這樣就會吧自己注冊到注冊中心了。
把service-url的值改成了另外一臺EurekaServer的地址,而不是自己
?
2)另外一臺在啟動的時候可以指定端口port和defaultZone配置:
修改原來的啟動配置組件;
在如下界面中的 VM options 中設置 -DdefaultZone=http:127.0.0.1:10087/eureka? 復制一份并修改;在如下界面中的 VM options 中 設置
-Dport=10087 -DdefaultZone=http:127.0.0.1:10086/eureka
-Dport=10086?-DdefaultZone=http:127.0.0.1:10087/eureka
3)啟動測試;同時啟動兩臺eureka server
4)客戶端注冊服務到集群
因為EurekaServer不止一個,因此注冊服務的時候,service-url參數需要變化:
eureka:client:service-url:
# EurekaServer地址,多個地址以','隔開defaultZone: http://127.0.0.1:10086/eureka,http://127.0.0.1:10087/eureka
為了方便后面內容的修改,在測試完上述配置后可以再次改回單個eureka server的方式。
6.4.3.Eureka客戶端和服務端配置
這個小節我們進行一系列的配置:
Eureka客戶端工程
user-service 服務提供
服務地址使用ip方式
續約
consumer-demo 服務消費
獲取服務地址的頻率
?
Eureka服務端工程 eureka-server
失效剔除
自我保護
服務提供者要向EurekaServer注冊服務,并且完成服務續約等工作。
服務注冊
?
服務提供者在啟動時,會檢測配置屬性中的: eureka.client.register-with-erueka=true 參數是否為true,事實上默認就是true。如果值確實為true,則會向EurekaServer發起一個Rest請求,并攜帶自己的元數據信息,
EurekaServer會把這些信息保存到一個雙層Map結構中 。
第一層Map的Key就是服務id,一般是配置中的 spring.application.name 屬性,? ? 例如:user-service
第二層Map的key是服務的實例id。一般host+ serviceId + port,? ? ? ? 例如: localhost:user-service:8081
值則是服務的實例對象,也就是說一個服務,這樣可以同時啟動多個不同實例,形成集群。默認注冊時使用的是主機名或者localhost,如果想用ip進行注冊,可以在 user-service 中添加配置如下:
?
eureka:instance:ip-address: 127.0.0.1 # ip地址prefer-ip-address: true # 更傾向于使用ip,而不是host名
修改完后先后重啟 user-service 和 consumer-demo ;在調用服務的時候就已經變成ip地址;
服務續約
在注冊服務完成以后,服務提供者會維持一個心跳(定時向EurekaServer發起Rest請求),告訴
EurekaServer:“我還活著”。這個我們稱為服務的續約(renew);
有兩個重要參數可以修改服務續約的行為;可以在 user-service 中添加如下配置項:
eureka:instance:lease-expiration-duration-in-seconds: 90lease-renewal-interval-in-seconds: 30
lease-renewal-interval-in-seconds:服務續約(renew)的間隔,默認為30秒
lease-expiration-duration-in-seconds:服務失效時間,默認值90秒
也就是說,默認情況下每個30秒服務會向注冊中心發送一次心跳,證明自己還活著。如果超過90秒沒有發送心
跳,EurekaServer就會認為該服務宕機,會從服務列表中移除,這兩個值在生產環境不要修改,默認即可。
獲取服務列表
當服務消費者啟動時,會檢測 eureka.client.fetch-registry=true 參數的值,如果為true,則會從Eureka Server服務的列表拉取只讀備份,然后緩存在本地。并且 每隔30秒 會重新拉取并更新數據。可以在 consumer-demo 項目中通過下面的參數來修改:
eureka:client:registry-fetch-interval-seconds: 30
生產環境中,我們不需要修改這個值。但是為了開發環境下,能夠快速得到服務的最新狀態,我們可以將其設置小一點。
?
6.4.5.失效剔除和自我保護
如下的配置都是在Eureka Server服務端進行:
服務下線
當服務進行正常關閉操作時,它會觸發一個服務下線的REST請求給Eureka Server,告訴服務注冊中心:“我要下線
了”。服務中心接受到請求之后,將該服務置為下線狀態
失效剔除
有時我們的服務可能由于內存溢出或網絡故障等原因使得服務不能正常的工作,而服務注冊中心并未收到“服務下
線”的請求。相對于服務提供者的“服務續約”操作,服務注冊中心在啟動時會創建一個定時任務,默認每隔一段時間
(默認為60秒)將當前清單中超時(默認為90秒)沒有續約的服務剔除,這個操作被稱為失效剔除。 可以通過
eureka.server.eviction-interval-timer-in-ms 參數對其進行修改,單位是毫秒。
自我保護
我們關停一個服務,就會在Eureka面板看到一條警告:
這是觸發了Eureka的自我保護機制。當一個服務未按時進行心跳續約時,Eureka會統計最近15分鐘心跳失敗的服務實例的比例是否超過了85%,
當EurekaServer節點在短時間內丟失過多客戶端(可能發生了網絡分區故障)。在
生產環境下,因為網絡延遲等原因,心跳失敗實例的比例很有可能超標,但是此時就把服務剔除列表并不妥當
,因為服務可能沒有宕機。Eureka就會把當前實例的注冊信息保護起來,不予剔除。生產環境下這很有效,保證了大多數服務依然可用。
但是這給我們的開發帶來了麻煩, 因此開發階段我們都會關閉自我保護模式:
eureka:server:enable-self-preservation: false # 關閉自我保護模式(缺省為打開)eviction-interval-timer-in-ms: 1000 # 掃描失效服務的間隔時間(缺省為60*1000ms)
7.負載均衡Ribbon
在剛才的案例中,我們啟動了一個user-service,然后通過DiscoveryClient來獲取服務實例信息,然后獲取ip和端口來訪問。
但是實際環境中,我們往往會開啟很多個user-service的集群。此時我們獲取的服務列表中就會有多個,到底該訪問哪一個呢?
一般這種情況下我們就需要編寫負載均衡算法,在多個實例列表中進行選擇。不過Eureka中已經幫我們集成了負載均衡組件:Ribbon,簡單修改代碼即可使用。
什么是Ribbon:
接下來,我們就來使用Ribbon實現負載均衡。
7.1.啟動兩個服務實例
首先我們啟動兩個user-service實例,一個9091,一個9092。
在user-service中配置如下端口:
server:port: ${port:9091}
?
Eureka監控面板:
7.2.開啟負載均衡
因為Eureka中已經集成了Ribbon,所以我們無需引入新的依賴。直接修改代碼:
在RestTemplate的配置方法上添加 @LoadBalanced 注解:
@Bean@LoadBalancedpublic RestTemplate restTemplate() {return new RestTemplate();}
修改調用方式,不再手動獲取ip和端口,而是直接通過服務名稱調用:訪問頁面,查看結果:
@GetMapping("/{id}")public User queryById(@PathVariable("id") Long id) {String url = "http://user-service/user/" + id;return restTemplate.getForObject(url, User.class);}
?
訪問頁面,查看結果;并可以在9091和9092的控制臺查看執行情況:
7.3.源碼跟蹤
為什么只輸入了service名稱就可以訪問了呢?之前還要獲取ip和端口。
顯然是有組件根據service名稱,獲取到了服務實例的ip和端口。因為 consumer-demo 使用的是RestTemplate,
spring使用LoadBalancerInterceptor攔截器 ,這個類會在對RestTemplate的請求進行攔截,然后從Eureka根據服務id獲取服務列表,隨后利用負載均衡算法得到真實的服務地址信息,替換服務id。
我們進行源碼跟蹤:
繼續跟入execute方法:發現獲取了9092端口的服務
再跟下一次,發現獲取的是9092:
7.4.負載均衡策略
Ribbon默認的負載均衡策略是簡單的輪詢,我們可以測試一下:
編寫測試類,在剛才的源碼中我們看到攔截中是使用RibbonLoadBalanceClient來進行負載均衡的,其中有一個
choose方法,是這樣介紹的:
現在這個就是負載均衡獲取實例的方法。
我們對注入這個類的對象,然后對其測試:
@RunWith(SpringRunner.class)@SpringBootTest(classes = UserConsumerDemoApplication.class)public class LoadBalanceTest {@AutowiredRibbonLoadBalancerClient client;@Testpublic void test(){for (int i = 0; i < 100; i++) {ServiceInstance instance = this.client.choose("user-service");System.out.println(instance.getHost() + ":" + instance.getPort());}}}
結果:
符合了我們的預期推測,確實是輪詢方式。
SpringBoot也幫我們提供了修改負載均衡規則的配置入口:
user-service:ribbon:NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
格式是:
{服務名稱}:
? ? ? ? ? ??ribbon:
? ? ? ? ? ? ? ? ? ? ?NFLoadBalancerRuleClassName?:XXXXX,
再次測試,發現結果變成了隨機:
8.Hystrix(豪豬)
8.1.簡介
主頁:https://github.com/Net?ix/Hystrix/
Hystix是Net?ix開源的一個延遲和容錯庫,用于隔離訪問遠程服務,防止出現級聯失敗。
8.2.雪崩問題
微服務中,服務間調用關系錯綜復雜,一個請求,可能需要調用多個微服務接口才能實現,會形成非常復雜的調用
鏈路:
如圖,一次業務請求,需要調用A、P、H、I四個服務,這四個服務又可能調用其它服務。 如果此時,某個服務出現異常:
例如: 微服務I 發生異常,請求阻塞,用戶請求就不會得到響應,則tomcat的這個線程不會釋放,于是越來越多的
用戶請求到來,越來越多的線程會阻塞:
服務器支持的線程和并發數有限,請求一直阻塞,會導致服務器資源耗盡,從而導致所有其它服務都不可用,形成
雪崩效應。
這就好比,一個汽車生產線,生產不同的汽車,需要使用不同的零件,如果某個零件因為種種原因無法使用,那么
就會造成整臺車無法裝配,陷入等待零件的狀態,直到零件到位,才能繼續組裝。 此時如果有很多個車型都需要這
個零件,那么整個工廠都將陷入等待的狀態,導致所有生產都陷入癱瘓。一個零件的波及范圍不斷擴大
Hystrix解決雪崩問題的手段,主要包括:
線程隔離
服務降級
8.3. 線程隔離&服務降級
8.3.1原理
線程隔離示意圖
解讀:
Hystrix為每個依賴服務調用分配一個小的線程池,如果線程池已滿調用將被立即拒絕,默認不采用排隊,加速失敗判定時間。
用戶的請求將不再直接訪問服務,而是通過線程池中的空閑線程來訪問服務,如果線程池已滿,或者請求超時,則會進行降級處理,
什么是服務降級?
服務降級:可以優先保證核心服務。
用戶的請求故障時,不會被阻塞,更不會無休止的等待或者看到系統崩潰,至少可以看到一個執行結果(例如返回友好的提示信息) 。
觸發Hystrix服務降級的情況:
線程池已滿
請求超時
8.3.2.動手實踐
服務降級:及時返回服務調用失敗的結果,讓線程不因為等待服務而阻塞
?
1)? 引入依賴
在 consumer-demo 消費端系統的pom.xml文件添加如下依賴:
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-hystrix</artifactId></dependency>
2)? 開啟熔斷
在啟動類 ConsumerApplication 上添加注解:@EnableCircuitBreaker
@SpringBootApplication@EnableDiscoveryClient@EnableCircuitBreakerpublic class ConsumerApplication {// ...}
可以看到,我們類上的注解越來越多,在微服務中,經常會引入上面的三個注解,于是Spring就提供了一個組合注解:@SpringCloudApplication
?
因此,我們可以使用這個組合注解來代替之前的3個注解 :
@SpringCloudApplicationpublic class ConsumerApplication {// ...}
3)? 編寫降級邏輯
當目標服務的調用出現故障,我們希望快速失敗,給用戶一個友好提示。因此需要提前編寫好失敗時的降級處理邏輯,要使用HystrixCommand來完成。
改造 consumer-demo\src\main\java\zx\com\consumer\controller\ConsumerController.java 處理器 類,如下:
@RestController@RequestMapping("/consumer")@Slf4jpublic class ConsumerController {@Autowiredprivate RestTemplate restTemplate;@Autowiredprivate DiscoveryClient discoveryClient;@GetMapping("{id}")@HystrixCommand(fallbackMethod = "queryByIdFallback")public String queryById(@PathVariable Long id) {String url = "http://user-service/user/" + id;return restTemplate.getForObject(url, String.class);}public String queryByIdFallback(Long id) {log.error("查詢用戶信息失敗。id:{}", id);return "對不起,網絡太擁擠了!";}}
要注意;因為熔斷的降級邏輯方法必須跟正常邏輯方法保證:相同的參數列表和返回值聲明。失敗邏輯中返回User對象沒有太大意義,一般會返回友好提示。所以把queryById的方法改造為返回String,反正也是Json數據。這樣失敗邏輯中返回一個錯誤說明,會比較方便。
說明:
@HystrixCommand(fallbackMethod = "queryByIdFallBack"):用來聲明一個降級邏輯的方法
測試:
當 user-service 正常提供服務時,訪問與以前一致。但是當將 user-service 停機時,會發現頁面返回了降級處理信息:
4) 默認的fallback
剛才把fallback寫在了某個業務方法上,如果這樣的方法很多,那豈不是要寫很多。所以可以把Fallback配置加在
類上,實現默認fallback; 再次改造 consumer-
demo\src\main\java\com\lxs\consumer\controller\ConsumerController.java
@RestController@RequestMapping("/consumer")@Slf4j@DefaultProperties(defaultFallback = "defaultFallback")public class ConsumerController {@Autowiredprivate RestTemplate restTemplate;@Autowiredprivate DiscoveryClient discoveryClient;@GetMapping("{id}")//@HystrixCommand(fallbackMethod = "queryByIdFallback")@HystrixCommandpublic String queryById(@PathVariable Long id) {String url = "http://user-service/user/" + id;return restTemplate.getForObject(url, String.class);}public String queryByIdFallback(Long id) {log.error("查詢用戶信息失敗。id:{}", id);return "對不起,網絡太擁擠了!";}public String defaultFallback() {return "默認提示:對不起,網絡太擁擠了!";}}
@DefaultProperties(defaultFallback = "defaultFallBack"):在類上指明統一的失敗降級方法;該類中所有方法返回類型要與處理失敗的方法的返回類型一致。
5)? 超時設置
在之前的案例中,請求在超過1秒后都會返回錯誤信息,這是因為Hystrix的默認超時時長為1,我們可以通過配置
修改這個值;修改 consumer-demo\src\main\resources\application.yml 添加如下配置:
hystrix:command:default:execution:isolation:thread:timeoutInMilliseconds: 2000
這個配置會作用于全局所有方法。為了方便復制到yml配置文件中,可以復制
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=2000? 到yml文件中會自動格式化后再進行修改。
為了觸發超時,可以在 user-service\src\main\java\com\lxs\user\service\UserService.java 的方法中 休眠2秒;
@RequestMapping("/{id}")public User queryById(@PathVariable("id") Long id) {try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}return userService.queryById(id);}
測試:
可以發現,請求的時長已經到了2s+,證明配置生效了。如果把修改時間修改到2秒以下,又可以正常訪問。
8.4? 服務熔斷
8.4.1熔斷原理
在服務熔斷中,使用的熔斷器,也叫斷路器,其英文單詞為:Circuit Breaker 熔斷機制與家里使用的電路熔斷原理類似;
當如果電路發生短路的時候能立刻熔斷電路,避免發生災難。在分布式系統中應用服務熔斷后;服務調用方可以自己進行判斷哪些服務反應慢或存在大量超時,可以針對這些服務進行主動熔斷,防止整個系統被拖垮。Hystrix的服務熔斷機制,可以實現彈性容錯;當服務請求情況好轉之后,可以自動重連。通過斷路的方式,將后續請求直接拒絕,一段時間(默認5秒)之后允許部分請求通過,如果調用成功則回到斷路器關閉狀態,否則繼續打開,拒絕請求的服務。
Hystrix的熔斷狀態機模型:
狀態機有3個狀態:
Closed:關閉狀態(斷路器關閉),所有請求都正常訪問。
Open:打開狀態(斷路器打開),所有請求都會被降級。Hystrix會對請求情況計數,當一定時間內失敗請求百分比達到閾值,則觸發熔斷,斷路器會完全打開。默認失敗比例的閾值是50%,請求次數最少不低于20次。
Half Open:半開狀態,不是永久的,斷路器打開后會進入休眠時間(默認是5S)。隨后斷路器會自動進入半開狀態。此時會釋放部分請求通過,若這些請求都是健康的,則會關閉斷路器,否則繼續保持打開,再次進行休眠計時
分析圖:
8.4.2動手實踐
為了能夠精確控制請求的成功或失敗,在 consumer-demo 的處理器業務方法中加入一段邏輯;
修改 consumer-demo\src\main\java\com\lxs\consumer\controller\ConsumerController.java
@GetMapping("{id}")@HystrixCommandpublic String queryById(@PathVariable Long id) {if (id == 1) {throw new RuntimeException("太忙了");}String url = "http://user-service/user/" + id;String user = restTemplate.getForObject(url, String.class);return user;}
這樣如果參數是id為1,一定失敗,其它情況都成功。(不要忘了清空user-service中的休眠邏輯)
我們準備兩個請求窗口:
一個請求:http://localhost:8080/consumer/1,注定失敗
?
一個請求:http://localhost:8080/consumer/2,肯定成功
當我們瘋狂訪問id為1的請求時(超過20次),就會觸發熔斷。斷路器會打開,一切請求都會被降級處理。
此時你訪問id為2的請求,會發現返回的也是失敗,而且失敗時間很短,只有20毫秒左右;因進入半開狀態之后2是可以的。
不過,默認的熔斷觸發要求較高,休眠時間窗較短,為了測試方便,我們可以通過配置修改熔斷策略:
hystrix:
? command:
? ? default:
? ? ? execution:
? ? ? ? isolation:
? ? ? ? ? thread:
? ? ? ? ? ? timeoutInMilliseconds: 2000 #服務降級超時時間
? ? ? circuitBreaker:
? ? ? ? errorThresholdPercentage: 50 # 觸發熔斷錯誤比例閾值,默認值50%
? ? ? ? sleepWindowInMilliseconds: 10000 # 熔斷后休眠時長,默認值5秒
? ? ? ? requestVolumeThreshold: 10 # 熔斷觸發最小請求次數,默認值是20
?
9.Feign
在前面的學習中,我們使用了Ribbon的負載均衡功能,大大簡化了遠程調用時的代碼:
如果就學到這里,你可能以后需要編寫類似的大量重復代碼,格式基本相同,無非參數不一樣。有沒有更優雅的方式,來對這些代碼再次優化呢?這就是我們接下來要學的Feign的功能了。
2.1.簡介
為什么叫偽裝?
Feign可以把Rest的請求進行隱藏,偽裝成類似SpringMVC的Controller一樣。你不用再自己拼接url,拼接參數等操作,一切都交給Feign去做。
項目主頁:?https://github.com/OpenFeign/feign
2.2.快速入門
2.2.1.導入依賴
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2.2.2.Feign的客戶端
@FeignClient("user-service")public interface UserClient {//http://user-service/user/123@GetMapping("/user/{id}")User queryById(@PathVariable("id") Long id);}
首先這是一個接口,Feign會通過動態代理,幫我們生成實現類。這點跟Mybatis的mapper很像
@FeignClient ,聲明這是一個Feign客戶端,同時通過 value 屬性指定服務名稱接口中的定義方法,完全采用SpringMVC的注解,Feign會根據注解幫我們生成URL,并訪問獲取結果
@GetMapping中的/user,請不要忘記;因為Feign需要拼接可訪問的地址編寫新的控制器類 ConsumerFeignController ,使用UserClient訪問:
@RestController@RequestMapping("/cf")public class ConsumerFeignController {@Autowiredprivate UserClient userClient;@GetMapping("/{id}")public User queryById(@PathVariable Long id){return userClient.queryById(id);}}
2.2.3.開啟Feign功能在 ConsumerApplication 啟動類上,添加注解,開啟Feign功能
@SpringCloudApplication@EnableFeignClients //開啟feign功能public class ConsumerApplication {public static void main(String[] args) {SpringApplication.run(ConsumerApplication.class, args);}@Bean@LoadBalancedpublic RestTemplate restTemplate() {return new RestTemplate();}}
Feign中已經自動集成了Ribbon負載均衡,因此不需要自己定義RestTemplate進行負載均衡的配置。
2.2.4.啟動測試:
訪問接口:http://localhost:8080/cf/2
正常獲取到了結果。
2.3.負載均衡
Feign中本身已經集成了Ribbon依賴和自動配置:
因此不需要額外引入依賴,也不需要再注冊 RestTemplate 對象。
Fegin內置的ribbon默認設置了請求超時時長,默認是1000,我們可以通過手動配置來修改這個超時時長:
ribbon:ReadTimeout: 2000 # 讀取超時時長ConnectTimeout: 1000 # 建立鏈接的超時時長
或者為某一個具體service指定
user-service? ?ribbon:ReadTimeout: 2000 # 讀取超時時長ConnectTimeout: 1000 # 建立鏈接的超時時長
在user-service中增加睡眠時間2s測試? ?因為ribbon內部有重試機制,一旦超時,會自動重新發起請求。如果不希望重試,可以添加配置:
修改 consumer-demo\src\main\resources\application.yml 添加如下配置 :
ribbon:ConnectTimeout: 1000 # 連接超時時長ReadTimeout: 2000 # 數據通信超時時長MaxAutoRetries: 0 # 當前服務器的重試次數MaxAutoRetriesNextServer: 0 # 重試多少次服務OkToRetryOnAllOperations: false # 是否對所有的請求方式都重試
2.4.Hystrix支持
Feign默認也有對Hystrix的集成:
只不過,默認情況下是關閉的。需要通過下面的參數來開啟:
?0)修改 consumer-demo\src\main\resources\application.yml 添加如下配置:
feign:hystrix:enabled: true # 開啟Feign的熔斷功能
但是,Feign中的Fallback配置不像Ribbon中那樣簡單了。
1)首先,要定義一個類,實現剛才編寫的UserFeignClient,作為fallback的處理類
package com.lxs.consumer.client.fallback;import com.lxs.consumer.client.UserClient;import com.lxs.consumer.pojo.User;import org.springframework.stereotype.Component;@Componentpublic class UserClientFallback implements UserClient {@Overridepublic User queryById(Long id) {User user = new User();user.setId(id);user.setName("用戶異常");return user;}}
2)然后在UserClient中,指定剛才編寫的實現類
@FeignClient(value = "user-service", fallback = UserFeignClientFallback.class)public interface UserFeignClient {@GetMapping("/user/{id}")User queryUserById(@PathVariable("id") Long id);}
3)重啟測試:
重啟啟動 consumer-demo 并關閉 user-service 服務,然后在頁面訪問:http://localhost:8080/cf/7
2.5.請求壓縮(了解)
Spring Cloud Feign 支持對請求和響應進行GZIP壓縮,以減少通信過程中的性能損耗。通過下面的參數即可開啟請
求與響應的壓縮功能:
feign:compression:request:enabled: true # 開啟請求壓縮response:enabled: true # 開啟響應壓縮
同時,我們也可以對請求的數據類型,以及觸發壓縮的大小下限進行設置:
feign:compression:request:enabled: true # 開啟請求壓縮mime-types: text/html,application/xml,application/json # 設置壓縮的數據類型min-request-size: 2048 # 設置觸發壓縮的大小下限
注:上面的數據類型、壓縮大小下限均為默認值。
2.6.日志級別(了解)
前面講過,通過 logging.level.lxs.xx=debug 來設置日志級別。然而這個對Fegin客戶端而言不會產生效果。因為
@FeignClient 注解修改的客戶端在被代理時,都會創建一個新的Fegin.Logger實例。我們需要額外指定這個日志的
級別才可以。
1)在 consumer-demo 的配置文件中設置com.zx包下的日志級別都為 debug?
修改 consumer-demo\src\main\resources\application.yml 添加如下配置:
logging:level:com.zx: debug
2)編寫配置類,定義日志級別
package com.lxs.consumer.config;import feign.Logger;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;@Configurationpublic class FeignConfig {@BeanLogger.Level feignLoggerLevel(){//記錄所有請求和響應的明細,包括頭信息、請求體、元數據return Logger.Level.FULL;}}
這里指定的Level級別是FULL,Feign支持4種級別:
NONE:不記錄任何日志信息,這是默認值。
BASIC:僅記錄請求的方法,URL以及響應狀態碼和執行時間
HEADERS:在BASIC的基礎上,額外記錄了請求和響應的頭信息
FULL:記錄所有請求和響應的明細,包括頭信息、請求體、元數據。
3)在 consumer-demo 的 UserClient 接口類上的@FeignClient注解中指定配置類:
別忘了去掉user-service的休眠時間
@FeignClient(value = "user-service", fallback = UserClientFallback.class, configuration =FeignConfig.class)public interface UserClient {@GetMapping("/user/{id}")User queryById(@PathVariable("id") Long id);}
4)重啟項目,即可看到每次訪問的日志:
10. Spring Cloud Gateway網關
10.1.? 簡介
?Spring Cloud Gateway是Spring官網基于Spring 5.0、 Spring Boot 2.0、Project Reactor等技術開發的網關服務。
?Spring Cloud Gateway基于Filter鏈提供網關基本功能:安全、監控/埋點、限流等。
?Spring Cloud Gateway為微服務架構提供簡單、有效且統一的API路由管理方式。
?Spring Cloud Gateway是替代Net?ix Zuul的一套解決方案。
?Spring Cloud Gateway組件的核心是一系列的過濾器,通過這些過濾器可以將客戶端發送的請求轉發(路由)到對應的微服? ?務。 Spring Cloud Gateway是加在整個微服務最前沿的防火墻和代理器,隱藏微服務結點IP端口信息,從而加強安全保護。? ?Spring Cloud Gateway本身也是一個微服務,需要注冊到Eureka服務注冊中心。
?網關的核心功能是:過濾和路由
10.2 Gateway加入后的架構
不管是來自于客戶端(PC或移動端)的請求,還是服務內部調用。一切對服務的請求都可經過網關,然后再由網關來實現 鑒權、動態路由等等操作。Gateway就是我們服務的統一入口。
?
10.3.? 核心概念
route路由
路由信息的組成:由一個ID、一個目的URL、一組斷言工廠、一組Filter組成。如果路由斷言為真,說明請求URL和配置路由匹配。
Predicate斷言
Spring Cloud Gateway中的斷言函數輸入類型是Spring 5.0框架中的ServerWebExchange。Spring Cloud Gateway的斷言函數允許開發者去定義匹配來自于http Request中的任何信息比如請求頭和參數。
?過濾器Filter
一個標準的Spring WebFilter。 Spring Cloud Gateway中的Filter分為兩種類型的Filter,分別是Gateway Filter和Global Filter。過濾器Filter將會對請求和響應進行修改處理
10.4.? 快速入門
需求:通過網關系統lxs-gateway將包含有 /user 的請求 路由到? http://127.0.0.1:9091/user/id
10.4.1.? 新建工程
打開 父類-springcloud\zx-gateway\pom.xml 文件修改為如下:
<?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"><parent><artifactId>lxs-springcloud</artifactId><groupId>com.zx</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>zx-gateway</artifactId><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency></dependencies></project>
10.4.2.? 編寫啟動類
在zx-gateway中創建 com.zx.gateway.GatewayApplication 啟動類
@SpringBootApplication@EnableDiscoveryClientpublic class GatewayApplication {public static void main(String[] args) {SpringApplication.run(GatewayApplication.class, args);}}
10.4.2.? 編寫配置
創建 zx-gateway\src\main\resources\application.yml 文件,內容如下:
server:port: 10010spring:application:name: api-gatewayeureka:client:service-url:defaultZone: http://127.0.0.1:10086/eurekainstance:prefer-ip-address: true
需要用網關來代理 user-service 服務,先看一下控制面板中的服務狀態 :
ip為:127.0.0.1
端口為:9091
修改 zx-gateway\src\main\resources\application.yml 文件為:
server:?port: 10010
spring:application:name: api-gatewaycloud:gateway:routes:# 路由id,可以隨意寫- id: user-service-route# 代理的服務地址uri: http://127.0.0.1:9091# 路由斷言,可以配置映射路徑predicates:- Path=/user/**eureka:client:service-url:defaultZone: http://127.0.0.1:10086/eurekainstance:prefer-ip-address: true
將符合 Path? uri 參數指定的地址 本例中,我們將路徑中包含有 /user/** 開頭的請求,代理到http://127.0.0.1:9091
10.4.5.? 啟動測試
訪問的路徑中需要加上配置規則的映射路徑,我們訪問:http://localhost:10010/user/7
http://localhost:10010/user/7? ? ---->? ?http://localhost:9091/user/7
10.5.? 面向服務的路由
在剛才的路由規則中,把路徑對應的服務地址寫死了!如果同一服務有多個實例的話,這樣做顯然不合理。 應該根據服務的名稱,去Eureka注冊中心查找 服務對應的所有實例列表,然后進行動態路由!
10.5.1.? 修改映射配置,通過服務名稱獲取
修改 zx-gateway\src\main\resources\application.yml 文件如下:
server:port: 10010spring:application:name: api-gatewaycloud:gateway:routes:# 路由id,可以隨意寫- id: user-service-route# 代理的服務地址uri: lb://user-service# 路由斷言,可以配置映射路徑predicates:- Path=/user/**eureka:client:service-url:defaultZone: http://127.0.0.1:10086/eurekainstance:prefer-ip-address: true
路由配置中uri所用的協議為 lb?時(以uri: lb://user-service為例),gateway將使用 LoadBalancerClient把user-service通過eureka解析為實際的主機和端口,并進行ribbon負載均衡。
10.5.2.? 啟動測試
zx-gateway 進行代理時,會利用Ribbon進行負載均衡訪問:?http://localhost:10010/user/8? ?日志中可以看到使用了負載均衡器:
10.6.? 路由前綴
客戶端的請求地址與微服務的服務地址如果不一致的時候,可以通過配置路徑過濾器實現路徑前綴的添加和
去除。
提供服務的地址:http://127.0.0.1:9091/user/8
添加前綴:對請求地址添加前綴路徑之后再作為代理的服務地址;
http://127.0.0.1:10010/8 --> http://127.0.0.1:9091/user/8? ? ? ? ? ? ? 添加前綴路徑/user
去除前綴: 將請求地址中路徑去除一些前綴路徑之后再作為代理的服務地址;
http://127.0.0.1:10010/api/user/8 --> http://127.0.0.1:9091/user/8?? ? 去除前綴路徑/api
10.6.1.? 添加前綴
在gateway中可以通過配置路由的過濾器Pre?xPath,實現映射路徑中地址的添加;
修改 zx-gateway\src\main\resources\application.yml 文件:
server:port: 10010spring:application:name: api-gatewaycloud:gateway:routes:# 路由id,可以隨意寫- id: user-service-route# 代理的服務地址uri: lb://user-service# 路由斷言,可以配置映射路徑predicates:- Path=/**filters:# 添加請求路徑的前綴- PrefixPath=/usereureka:client:service-url:defaultZone: http://127.0.0.1:10086/eurekainstance:prefer-ip-address: true
通過 Pre?xPath=/xxx 來指定了路由要添加的前綴。 也就是:
Pre?xPath=/user
? http://localhost:10010/8 -->http://localhost:9091/user/8Pre?xPath=/user/abc
http://localhost:10010/8 -->http://localhost:9091/user/abc/8
?
?
10.6.2.? 去除前綴
在gateway中可以通過配置路由的過濾器StripPre?x,實現映射路徑中地址的去除;
修改 zx-gateway\src\main\resources\application.yml 文件:
server:port: 10010spring:application:name: api-gatewaycloud:gateway:routes:# 路由id,可以隨意寫- id: user-service-route# 代理的服務地址uri: lb://user-service# 路由斷言,可以配置映射路徑predicates:- Path=/api/user/**filters:# 表示過濾1個路徑,2表示兩個路徑,以此類推- StripPrefix=1eureka:client:service-url:defaultZone: http://127.0.0.1:10086/eurekainstance:prefer-ip-address: true
通過 StripPre?x=1 來指定了路由要去掉的前綴個數。如:路徑 /api/user/1 將會被代理到 /user/1 。 也就是:
StripPre?x=1
http://localhost:10010/api/user/8 --》http://localhost:9091/user/8StripPre?x=2
http://localhost:10010/api/user/8 --》http://localhost:9091/8
?
?
10.7.? 過濾器
10.7.1.? 簡介
Gateway作為網關的其中一個重要功能,就是實現請求的鑒權。而這個動作往往是通過網關提供的過濾器來實現的。前面的 路由前綴 章節中的功能也是使用過濾器實現的。
Gateway自帶過濾器有幾十個,常見自帶過濾器有:
配置全局默認過濾器
這些自帶的過濾器可以和使用 路由前綴 章節中的用法類似,也可以將這些過濾器配置成不只是針對某個路由;而是可以對所有路由生效,也就是配置默認過濾器:
server:port: 10010spring:application:name: api-gatewaycloud:gateway:routes:# 路由id,可以隨意寫- id: user-service-route# 代理的服務地址uri: lb://user-service# 路由斷言,可以配置映射路徑predicates:- Path=/api/user/**filters:# 表示過濾1個路徑,2表示兩個路徑,以此類推- StripPrefix=1# 默認過濾器,對所有路由都生效default-filters:- AddResponseHeader=X-Response-Foo, Bar- AddResponseHeader=abc-myname,zxeureka:client:service-url:defaultZone: http://127.0.0.1:10086/eurekainstance:prefer-ip-address: true
上述配置后,再訪問 http://localhost:10010/api/user/7 的話;那么可以從其響應中查看到如下信息:
過濾器類型:Gateway實現方式上,有兩種過濾器;
? ?局部過濾器:通過 spring.cloud.gateway.routes.?lters 配置在具體路由下,只作用在當前路由上;如果配置spring.cloud.gateway.default-?lters 上會對所有路由生效也算是全局的過濾器;但是這些過濾器 的實現上都是要實現GatewayFilterFactory接口。
? ?全局過濾器:不需要在配置文件中配置,作用在所有的路由上;實現 GlobalFilter 接口即可。
10.7.2.? 執行生命周期
Spring Cloud Gateway 的 Filter 的生命周期也類似Spring MVC的攔截器有兩個:“pre” 和 “post”。“pre”和 “post”
分別會在請求被執行前調用和被執行后調用
這里的 pre 和 post 可以通過過濾器的 GatewayFilterChain 執行?lter方法前后來實現
10.7.3.? 使用場景
常見的應用場景如下:
? 請求鑒權:一般 GatewayFilterChain 執行?lter方法前,如果發現沒有訪問權限,直接就返回空。
? 異常處理:一般 GatewayFilterChain 執行?lter方法后,記錄異常并返回。
? 服務調用時長統計: GatewayFilterChain 執行?lter方法前后根據時間統計。
10.8.? 自定義過濾器
10.8.1.? 自定義局部過濾器
需求:
在過濾器(MyParamGatewayFilterFactory)中將http://localhost:10010/api/user/8?name=zx? name的
值獲取到并輸出到控制臺;并且參數名是可變的,也就是不一定每次都是name;需要可以通過配置過濾器的時候
做到配置參數名。
在application.yml中對某個路由配置過濾器,該過濾器可以在控制臺輸出配置文件中指定名稱的請求參數的 值。
1)編寫過濾器
package com.zx.gateway.filter;import org.springframework.cloud.gateway.filter.GatewayFilter;import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;import org.springframework.http.server.reactive.ServerhttpRequest;import org.springframework.stereotype.Component;import java.util.Arrays;import java.util.List;@Componentpublic class MyParamGatewayFilterFactory extendsAbstractGatewayFilterFactory<MyParamGatewayFilterFactory.Config> {static final String PARAM_NAME = "param";public MyParamGatewayFilterFactory() {super(Config.class);}public List<String> shortcutFieldOrder() {return Arrays.asList(PARAM_NAME);}@Overridepublic GatewayFilter apply(Config config) {return (exchange, chain) -> {// http://localhost:10010/api/user/8?name=lxs ? config.param ==> name//獲取請求參數中param對應的參數名? 的參數值ServerhttpRequest request = exchange.getRequest();if(request.getQueryParams().containsKey(config.param)){request.getQueryParams().get(config.param).forEach(value -> System.out.printf("------------局部過濾器--------%s = %s------", config.param, value));}return chain.filter(exchange);};}public static class Config{//對應在配置過濾器的時候指定的參數名private String param;public String getParam() {return param;}public void setParam(String param) {this.param = param;}}}
2)修改配置文件
server:port: 10010spring:application:name: api-gatewaycloud:gateway:routes:# 路由id,可以隨意寫- id: user-service-route# 代理的服務地址uri: lb://user-service# 路由斷言,可以配置映射路徑predicates:- Path=/api/user/**filters:# 表示過濾1個路徑,2表示兩個路徑,以此類推- StripPrefix=1# 自定義過濾器- MyParam=name# 默認過濾器,對所有路由都生效default-filters:- AddResponseHeader=X-Response-Foo, Bar- AddResponseHeader=abc-myname,zxeureka:client:service-url:defaultZone: http://127.0.0.1:10086/eurekainstance:prefer-ip-address: true
注意:自定義過濾器的命名應該為:XXXGatewayFilterFactory
http://localhost:10010/api/user/7?name=zx;但是若訪問
http://localhost:10010/api/user/7?name2=xiaoming?則是不會輸出的
10.8.2.? 自定義全局過濾器
需求:編寫全局過濾器,在過濾器中檢查請求中是否攜帶token請求頭。如果token請求頭存在則放行;如果token
為空或者不存在則設置返回的狀態碼為:未授權也不再執行下去。
在zx-gateway工程編寫全局過濾器類MyGlobalFilter
@Componentpublic class MyGlobalFilter implements GlobalFilter, Ordered {@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {System.out.println("--------------全局過濾器MyGlobalFilter------------------");String token = exchange.getRequest().getHeaders().getFirst("token");if(StringUtils.isBlank(token)){//設置響應狀態碼為未授權exchange.getResponse().setStatusCode(httpStatus.UNAUTHORIZED);return exchange.getResponse().setComplete();}return chain.filter(exchange);}@Overridepublic int getOrder() {//值越小越先執行return 1;}}
訪問 http://localhost:10010/api/user/7
訪問 http://localhost:10010/api/user/7?token=abc
10.9.? 負載均衡和熔斷(了解)
Gateway中默認就已經集成了Ribbon負載均衡和Hystrix熔斷機制。但是所有的超時策略都是走的默認值,比如熔
斷超時時間只有1S,很容易就觸發了。因此建議手動進行配置:
hystrix:command:default:execution:isolation:thread:timeoutInMilliseconds: 6000 #服務降級超時時間,默認1Sribbon:ConnectTimeout: 1000 # 連接超時時長ReadTimeout: 2000 # 數據通信超時時長MaxAutoRetries: 0 # 當前服務器的重試次數MaxAutoRetriesNextServer: 0 # 重試多少次服務
10.10. Gateway跨域配置
?
一般網關都是所有微服務的統一入口,必然在被調用的時候會出現跨域問題。
跨域:在js請求訪問中,如果訪問的地址與當前服務器的域名、ip或者端口號不一致則稱為跨域請求。若不解決則
不能獲取到對應地址的返回結果。
如:從在http://localhost:9090 js訪問 http://localhost:9000?的數據,因為端口不同,所以也是跨域請求。
在訪問Spring Cloud Gateway網關服務器的時候,出現跨域問題的話;可以在網關服務器中通過配置解決,允許哪
些服務是可以跨域請求的;具體配置如下:
server:port: 10010spring:application:name: api-gatewaycloud:gateway:routes:# 路由id,可以隨意寫- id: user-service-route# 代理的服務地址uri: lb://user-service# 路由斷言,可以配置映射路徑predicates:- Path=/api/user/**filters:# 表示過濾1個路徑,2表示兩個路徑,以此類推- StripPrefix=1# 自定義過濾器- MyParam=name# 默認過濾器,對所有路由都生效default-filters:- AddResponseHeader=X-Response-Foo, Bar- AddResponseHeader=abc-myname,zxglobalcors:corsConfigurations:'[/**]':#allowedOrigins: * # 這種寫法或者下面的都可以,*表示全部allowedOrigins:- "http://docs.spring.io"allowedMethods:- GETeureka:client:service-url:defaultZone: http://127.0.0.1:10086/eurekainstance:prefer-ip-address: truehystrix:command:default:execution:isolation:thread:timeoutInMilliseconds: 6000 #服務降級超時時間,默認1Sribbon:ConnectTimeout: 1000 # 連接超時時長ReadTimeout: 2000 # 數據通信超時時長MaxAutoRetries: 0 # 當前服務器的重試次數MaxAutoRetriesNextServer: 0 # 重試多少次服務
說明
上述配置表示:可以允許來自 http://docs.spring.io 的get請求方式獲取服務數據。
'[/**]' 表示對所有訪問到網關服務器的請求地址
https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.1.1.RELEASE/multi/multi__cors_con?guration.html
10.11. Gateway的高可用(了解)
啟動多個Gateway服務,自動注冊到Eureka,形成集群。如果是服務內部訪問,訪問Gateway,自動負載均衡,沒
問題。但是,Gateway更多是外部訪問,PC端、移動端等。它們無法通過Eureka進行負載均衡,那么該怎么辦? 此時,可以使用其它的服務網關,來對Gateway進行代理。比如:Nginx
10.12. Gateway與Feign的區別
? ? ?Gateway 作為整個應用的流量入口,接收所有的請求,如PC、移動端等,并且將不同的請求轉- 發至不同的
? 處理微服務模塊,其作用可視為nginx;大部分情況下用作權限鑒定、服務端流量控制
? ? ?Feign 則是將當前微服務的部分服務接口暴露出來,并且主要用于各個微服務之間的服務調用
11.Spring Cloud Con?g分布式配置中心
11.1.? 簡介
在分布式系統中,由于服務數量非常多,配置文件分散在不同的微服務項目中,管理不方便。為了方便配置文件集中管理,需要分布式配置中心組件。在Spring Cloud中,提供了Spring Cloud Con?g,它支持配置文件放在配置服務的本地,也支持放在遠程Git倉庫(GitHub、碼云)。
使用Spring Cloud Con?g配置中心后的架構如下圖
配置中心本質上也是一個微服務,同樣需要注冊到Eureka服務注冊中心!
11.2. Git配置管理
碼云訪問地址:https://gitee.com/
11.2.2.? 創建遠程倉庫
首先要使用碼云上的私有遠程git倉庫需要先注冊帳號;請先自行訪問網站并注冊帳號,然后使用帳號登錄碼云控制
臺并創建公開倉庫。(私有也可以,只不過需要在yml中配置賬號密碼)
?
11.2.3.? 創建配置文件
在新建的倉庫中創建需要被統一配置管理的配置文件。
{application}-{pro?le}.yml 或 {application}-{pro?le}.properties
? ? application為應用名稱
? ? pro?le用于區分開發環境,測試環境、生產環境等
如user-dev.yml,表示用戶微服務開發環境下使用的配置文件。
這里將user-service工程的配置文件application.yml文件的內容復制作為user-dev.yml文件的內容,具體配置如
下:
創建完user-dev.yml配置文件之后,gitee中的倉庫如下:
11.3.? 搭建配置中心微服務
11.3.1.創建項目
創建配置中心微服務工程:
添加依賴,修改 con?g-server\pom.xml 如下:
<?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"><parent><artifactId>lxs-springcloud</artifactId><groupId>com.lxs</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>config-server</artifactId><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-config-server</artifactId></dependency></dependencies></project>
11.3.2.? 啟動類
創建配置中心工程 con?g-server 的啟動類;
con?g-server\src\main\java\com\zx\con?g\Con?gServerApplication.java 如下:
package com.zx.config;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.config.server.EnableConfigServer;@SpringBootApplication@EnableConfigServer //開啟配置服務public class ConfigServerApplication {public static void main(String[] args) {SpringApplication.run(ConfigServerApplication.class, args);}}
11.3.3.? 配置文件
server:port: 12000spring:application:name: config-servercloud:config:server:git:uri: https://gitee.com/root121toor/my-config.giteureka:client:service-url:defaultZone: http://127.0.0.1:10086/eureka
注意上述的 spring.cloud.con?g.server.git.uri 則是在碼云創建的倉庫地址;可修改為你自己創建的倉庫地址
11.3.4.? 啟動測試
啟動eureka注冊中心和配置中心;然后訪問http://localhost:12000/user-dev.yml ,查看能否輸出在碼云存儲管理的user-dev.yml文件。并且可以在gitee上修改user-dev.yml然后刷新上述測試地址也能及時到最新數據。
11.4.? 獲取配置中心配置
前面已經完成了配置中心微服務的搭建,下面我們就需要改造一下用戶微服務 user-service ,配置文件信息不再由微服務項目提供,而是從配置中心獲取。如下對 user-service 工程進行改造。
11.4.1.? 添加依賴
?
在 user-service 工程中的pom.xml文件中添加如下依賴:
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-config</artifactId></dependency>
11.4.2.? 修改配置
1. 刪除 user-service 工程的 user-service\src\main\resources\application.yml 文件(因為該文件從配置中心獲取)
2. 創建 user-service 工程 user-service\src\main\resources\bootstrap.yml 配置文件
spring:cloud:config:# 要與倉庫中的配置文件的application保持一致name: user# 要與倉庫中的配置文件的profile保持一致profile: dev# 要與倉庫中的配置文件所屬的版本(分支)一樣label: masterdiscovery:# 使用配置中心enabled: true# 配置中心服務名service-id: config-servereureka:client:service-url:defaultZone: http://127.0.0.1:10086/eureka
user-service? 工程修改后結構:
bootstrap.yml文件也是Spring Boot的默認配置文件,而且其加載的時間相比于application.yml更早。
application.yml和bootstrap.yml雖然都是Spring Boot的默認配置文件,但是定位卻不相同。bootstrap.yml可以理解成系統級別的一些參數配置,這些參數一般是不會變動的。application.yml 可以用來定義應用級別的參數,如果搭配 spring cloud con?g 使用,application.yml 里面定義的文件可以實現動態替換。
總結就是,bootstrap.yml文件相當于項目啟動時的引導文件,內容相對固定。application.yml文件是微服務的一些常規配置參數,變化比較頻繁。
11.4.3.? 啟動測試
啟動注冊中心 eureka-server 、配置中心 con?g-server 、用戶服務 user-service ,如果啟動沒有報錯其實已經 使用上配置中心內容,可以到注冊中心查看,也可以檢驗 user-service 的服務。
12.Spring Cloud Bus服務總線
12.1.? 問題
前面已經完成了將微服務中的配置文件集中存儲在遠程Git倉庫,并且通過配置中心微服務從Git倉庫拉取配置文件, 當用戶微服務啟動時會連接配置中心獲取配置信息從而啟動用戶微服務。 如果我們更新Git倉庫中的配置文件,那用戶微服務不能及時接收到新的配置信息并更新
?
12.1.1. 修改遠程Git配置
修改在碼云上的user-dev.yml文件,添加一個屬性test.name 。
12.1.2. 修改UserController
修改 user-service 工程中的處理器類;
user-service\src\main\java\com\zx\user\controller\UserController.java 如下:
@RestControllerpublic class UserController {@Autowiredprivate UserService userService;@Value("${test.name}")private String name;@GetMapping("/user/{id}")public User queryById(@PathVariable Long id){System.out.println("配置文件中的test.name為:" + name);return userService.queryById(id);}@RequestMapping("/user/all")public String all() {return "return all user list..........";}}
12.1.3.? 測試
依次啟動注冊中心 eureka-server 、配置中心 con?g-server 、用戶服務 user-service ;然后修改Git倉庫中的配置信息,訪問用戶微服務,查看輸出內容。
結論:通過查看用戶微服務控制臺的輸出結果可以發現,我們對于Git倉庫中配置文件的修改并沒有及時更新到用戶微服務,只有重啟用戶微服務才能生效。
如果想在不重啟微服務的情況下更新配置該如何實現呢? 可以使用Spring Cloud Bus來實現配置的自動更新。
需要注意的是Spring Cloud Bus底層是基于RabbitMQ實現的,默認使用本地的消息隊列服務,所以需要提前啟動本地RabbitMQ服務(安裝RabbitMQ以后才有)
12.2. Spring Cloud Bus簡介
Spring Cloud Bus是用輕量的消息代理將分布式的節點連接起來,可以用于廣播配置文件的更改或者服務的監控管理。也就是消息總線可以為微服務做監控,也可以實現應用程序之間相互通信。 Spring Cloud Bus可選的消息代理有RabbitMQ和Kafka。
使用了Bus之后 :
準備工作
win 安裝資料中\otp_win64_23.0.exe和rabbitmq-server-3.8.5.exe
mac 使用
1
brew install rabbitmq
2
進入到 /usr/local/Cellar/rabbitmq/3.6.6,執行
sbin/rabbitmq-server
3
地址欄輸入: http://localhost:15672/ 賬號密碼都是 guest12.3.? 改造配置中心
在 con?g-server 項目的pom.xml文件中加入Spring Cloud Bus相關依賴
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-bus</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-stream-binder-rabbit</artifactId></dependency>
在 con?g-server 項目修改application.yml文件如下:
server:port: 12000spring:application:name: config-servercloud:config:server:git:uri: https://gitee.com/lxsong77/lxs-config.git# 配置rabbitmq信息;如果是都與默認值一致則不需要配置rabbitmq:host: localhostport: 5672username: guestpassword: guesteureka:client:service-url:defaultZone: http://127.0.0.1:10086/eurekamanagement:endpoints:web:exposure:# 暴露觸發消息總線的地址include: bus-refresh
12.4.? 改造用戶服務
1. 在用戶微服務 user-service 項目的pom.xml中加入Spring Cloud Bus相關依賴
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-bus</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-stream-binder-rabbit</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency>
2. 修改 user-service 項目的bootstrap.yml如下
spring:cloud:config:# 要與倉庫中的配置文件的application保持一致name: user# 要與倉庫中的配置文件的profile保持一致profile: dev# 要與倉庫中的配置文件所屬的版本(分支)一樣label: masterdiscovery:# 使用配置中心enabled: true# 配置中心服務名service-id: config-server# 配置rabbitmq信息;如果是都與默認值一致則不需要配置rabbitmq:host: localhostport: 5672username: guestpassword: guesteureka:client:service-url:defaultZone: http://127.0.0.1:10086/eureka
3. 改造用戶微服務 user-service 項目的UserController
12.5.? 測試
前面已經完成了配置中心微服務和用戶微服務的改造,下面來測試一下,當我們修改了Git倉庫中的配置文件,用戶微服務是否能夠在不重啟的情況下自動更新配置信息。
測試步驟:
第零步: 開啟rabbit? ? cd usr/local/Cellar/rabbitmq/3.8.8/sbin/? ? ? ? ? 然后執行? ? ?./rabbitmq-server restart
第一步:依次啟動注冊中心 eureka-server? con?g-server 、用戶服務 user-service
第二步:訪問用戶微服務http://localhost:9091/user/7;查看IDEA控制臺輸出結果
第三步:修改Git倉庫中配置文件 user-dev.yml 的 test.name 內容
第四步:POST方式請求訪問地址http://127.0.0.1:12000/actuator/bus-refresh
第五步:再次訪問用戶微服務http://localhost:9091/user/7;查看IDEA控制臺輸出結果
請求http://127.0.0.1:12000/actuator/bus-refresh目的:?服務接收到請求后會向消息隊列中發送消息,各個微服務會監聽消息隊列。當微服務接收到隊列中的消息后,會重新從配置中心獲取最新的配置信息
?
---------------------
作者:alibaba_張無忌
來源:CSDN
原文:https://blog.csdn.net/weixin_45434902/article/details/110673405
版權聲明:本文為作者原創文章,轉載請附上博文鏈接!
內容解析By:CSDN,CNBLOG博客文章一鍵轉載插件