springcloud 框架的集合
1.單體應用架構存在的問題
一個歸檔包(例如war)包含所有功能的應用程序,通常稱爲單體應用。
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-gR1olHCy-1584790934338)(img\圖片 1.png)]
- 複雜性高:定時炸彈
- 技術債務:no broken don’t fix
- 可靠性差:單個bug,導致整個系統癱瘓
- 阻礙技術創新
2.架構的演變
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-xtuOuQwW-1584790934339)(img\圖片 2.png)]
3.什麼是微服務
微服務架構就是將單一程序開發成一系列微小服務,每個微服務運行在自己的進程中,並使用輕量級的機制通信,通常是HTTP RESTFUL API。這些服務圍繞業務能力來劃分,並通過自動化部署機制來獨立部署。這些服務集中管理,可以使用不同的編程語言,不同數據庫,以保證最低限度的集中式管理。
- 每個服務可獨立運行在自己的進程裏
- 一系列獨立運行的微服務共同構建起整個系統
- 每個服務爲獨立的業務開發,一個微服務只關注某個特定的功能,例如訂單管理,用戶管理
- 微服務之間通過一些輕量的通信機制進行通信,例如Restful API進行調用
- 可以使用不同的語言與數據存儲
官網鏈接:https://www.martinfowler.com/articles/microservices.html
4.微服務的解決方案
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-ffksNDbg-1584790934339)(img\圖片 3.png)]
5.什麼是springcloud
Spring Cloud是一個含概多個子項目的開發工具集,集合了衆多的開源框架,他利用了Spring Boot開發的便利性實現了很多功能,如服務註冊,服務註冊發現,負載均衡等.Spring Cloud在整合過程中主要是針對Netflix(耐非)開源組件的封裝.
NetFlix 是美國的一個在線視頻網站,微服務業的翹楚,他是公認的大規模生產級微服務的傑出實踐者,NetFlix的開源組件已經在他大規模分佈式微服務環境中經過多年的生產實戰驗證,因此spring cloud中很多組件都是基於NetFlix組件的封裝
Spring Cloud的出現真正的簡化了分佈式架構的開發
6.springcloud的特點
-
服務註冊和發現
-
路由
-
service - to - service調用
-
負載均衡
-
斷路器
7.Spring Cloud 的服務架構圖
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-acD48ClV-1584790934340)(img\圖片 4.png)]
8.Eureka組件
Eureka是Netfilx開源的服務發現組件,本身是一個基於rest的服務,它包含client和server兩部分。
Spirng Cloud將它集成在子項目Spirng Cloud Netfilx中,從而實現服務的註冊和發現
1.eureka中的server和client的介紹及特點
- Eureka Server:提供服務發現的能力,各個微服務啓動時,會向Eureka Server註冊自己的信息例如(IP,端口號,服務名稱等),Eureka會存儲這些信息
- Eureka Client:是一個java的客戶端用來簡化Eureka Server的交互
- 微服務啓動後會週期性的(默認30秒)向Eureka Server發送心跳,如果Eureka在規定的時間沒有收到心跳,則會註銷該實例(默認90秒)
- Eureka Client會緩存服務註冊表中的信息。這種方式有一定的優勢首先可以降低Eureka Server的壓力,其次當所有的Eureka Server宕機服務調用方依然可以完成調用
2.服務註冊與服務發現
- 服務註冊:當微服務client啓動時,向Eureka Server發起註冊,並上報該節點的相關信息
- 服務發現:client從Eureka Server獲取註冊信息,然後發起調用
3.Eureka Server開發
1.引入springcloud的相關依賴
<!--倉庫的位置-->
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<!--引入版本號-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.RC1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<!--eureka server-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
2.入口類的開發
//@EnableDiscoveryClient //不僅支持Eureka作爲註冊中心還支持zookeeper
@EnableEurekaServer //只支持Eureka
@SpringBootApplication
public class EurekaServer {
public static void main(String[] args) {
SpringApplication.run(EurekaServer.class,args);
}
}
3.配置文件
#設置eureka server 的交互地址,之後的服務獲取和服務註冊都需要依賴於這個地址
eureka.client.service-url.defaultZone=http://localhost:8761/eureka
#表示是否將自己註冊到eureka上
eureka.client.register-with-eureka=false
#表示是否從eureka server上獲取註冊信息
eureka.client.fetch-registry=false
#應用服務名 微服務服務名
spring.application.name=eureka-server
#端口號
server.port=8761
4.Eureka Client的開發
1.jar包和Server相同
將spring-cloud-starter-eureka-server改爲spring-cloud-starter-eureka
2.入口類
@EnableDiscoveryClient
@SpringBootApplication
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
3.配置文件
#註冊中心的地址 聲明
eureka.client.service-url.defaultZone=http://localhost:8761/eureka
#微服務實例名
spring.application.name=eureka-client1
#端口號
server.port=8762
5.Eureka client之間的相互調用
1.導入相關的jar
和eureka server 一致
2.java配置RestTemplate
@Configuration
public class RestConf {
@Bean
@LoadBalanced
public RestTemplate getRestTemplate(){
RestTemplate restTemplate = new RestTemplate();
return restTemplate;
}
}
3.調用
@RestController
@RequestMapping("test")
public class TestController {
@Autowired
private RestTemplate restTemplate;
@RequestMapping("test")
public String test(String name){
//調用其他微服務實例 http請求 httpClint
String forObject = restTemplate.getForObject("http://localhost:8762/product/product?name=" + name, String.class);
return forObject;
}
}
6.client的高可用
保證端口號不一致(測試環境)
保證實例名一致
1.配置如下
- Client3
eureka.client.service-url.defaultZone=http://peer:8761/eureka
spring.application.name=eureka-producter
server.port=8763
- Client4
eureka.client.service-url.defaultZone=http://peer:8761/eureka
spring.application.name=eureka-producter
server.port=8764
2.導入ribbon的jar包
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-ribbon -->
<!-- ribbon 組件主要做負載均衡 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
5.關閉自我保護機制
3.修改請求
//調用其他微服務實例 http請求 httpClint
String forObject = restTemplate.getForObject("http://EUREKA-CLIENT2/product/product?name=" + name, String.class);
7.Eureka的自我保護機制
Eureka進入自我保護機制的最直接體現,是Eureka首頁輸出警告如圖:
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-C2rL4Gdy-1584790934341)(img\自我保護機制.png)]
默認情況下,如果Eureka Server在一定時間內沒有接受到服務實例的心跳,Eureka將會註銷該實例(默認90秒).但是當網絡分區發生故障時,微服務和Eureka Server 無法正常通信.以上行爲可能變得特別危險了-因爲微服務本身是健康的,此時不能註銷該服務實例.
Eureka通過自我保護機制來解決這個問題,當Eureka Server在短時間丟失過多的服務實例(可能發生了網絡分區的故障,那麼這個節點進入自我保護模式,一旦進入此模式,Eureka Server將會保護服務註冊表中的信息,不再刪除服務註冊表中的數據(也就是不再註銷任何的服務實例),當網絡故障恢復後,Eureka會自動退出自我保護模式。
綜上,自我保護模式是一種應對網絡故障的安全保護措施,它的架構哲學是寧可同時保留所有的微服務,也不盲目註銷任何健康的微服務,使用自我保護模式可以讓Eureka,更加健壯,穩定。
Eureka在運行期間會統計心跳失敗的比例,在15分鐘內是否低於85%,如果出現了低於的情況,Eureka Server會將當前的實例註冊信息保護起來,同時提示一個警告,一旦進入保護模式,Eureka Server將會嘗試保護其服務註冊表中的信息,不再刪除服務註冊表中的數據。也就是不會註銷任何微服務。
自我保護模式被激活的條件是:在 1 分鐘後,Renews (last min) < Renews threshold
。
這兩個參數的意思:
Renews threshold
:Eureka Server 期望每分鐘收到客戶端實例續約的總數。Renews (last min)
:Eureka Server 最後 1 分鐘收到客戶端實例續約的總數。
具體的值,我們可以在 Eureka Server 界面可以看到:
在springcloud中可以在eureka-server中關閉自我保護機制
#關閉自我保護機制 默認開啓
eureka.server.enable-self-preservation=false
如果想及時剔除eureka的服務除了關閉自我保護機制外,可以調低eureka的心跳值
eureka-server服務端
配置文件中我們添加如下配置
#關閉保護機制,以確保註冊中心將不可用的實例正確剔除
eureka.server.enable-self-preservation=false
#(代表是5秒,單位是毫秒,清理失效服務的間隔 )
eureka.server.eviction-interval-timer-in-ms=5000
客戶端
配置文件中我們添加如下配置
# 心跳檢測檢測與續約時間
# 測試時將值設置設置小些,保證服務關閉後註冊中心能及時踢出服務
# 配置說明
# lease-renewal-interval-in-seconds 每間隔10s,向服務端發送一次心跳,證明自己依然”存活“
# lease-expiration-duration-in-seconds 告訴服務端,如果我20s之內沒有給你發心跳,就代表我“死”了,將我踢出掉。
eureka.instance.lease-renewal-interval-in-seconds=10
eureka.instance.lease-expiration-duration-in-seconds=20
8.Eureka Server的高可用
單節點的Eureka Server 不適合線上的生產環境,Eureka Client會定時連接Eureka Server,獲取服務註冊表中的信息並緩存在本地,微服務消費遠程API總是使用本地緩存的數據,因此一般來說既是Eureka Server發生宕機,也不會影響到服務的調用,但是如果Eureka Server宕機時某些微服務也出現了不可用的情況,Eurek Client中的緩存若不被更新,可能會影響到服務的調用,甚至影響到整個系統的高可用性,因此在生產環境會部署一個高可用的Eureka Server集羣。
Eureka可以通過運行多個實例並互相註冊實現高可用部署,Eureka Server實例會彼此同步信息。
server.port=8761
eureka.client.service-url.defaultZone=http://peer2:8762/eureka
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
eureka.instance.hostname=peer1
server.port=8762
eureka.client.service-url.defaultZone=http://peer1:8761/eureka
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
eureka.instance.hostname=peer2
注意:在client註冊eureka server時 需要填寫所有eureka的地址
eureka.client.service-url.defaultZone=http://peer:8761/eureka,http://peer1:8765/eureka
9.Eureka的健康監測
人會生病,就像人一樣服務有時候也會出現異常情況,我們也需要知道某個服務的健康狀況。我們可以通過添加如下依賴,開啓某個服務的健康檢查。
<!--健康檢測-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
我們來訪問一下這個接口http://localhost:8761/health,看到了一個很簡短的健康報告:
{"description":"Spring Cloud Eureka Discovery Client","status":"UP"}
類似的還有
- info 顯示任意的應用信息
- metrics 展示當前應用的指標信息 true
- mappings 顯示所有@RequestMapping路徑的整理列表
- trace 顯示trace信息(默認爲最新的一些HTTP請求)
- health 展示應用的健康信息
- beans 顯示一個應用中所有Spring Beans的完整列表
這其中有一些是敏感信息,出於安全考慮,一般用戶無法訪問,如果內網情況下可以
#開啓健康檢查
eureka.client.healthcheck.enabled=true
#關掉認證(公網下的生產環境不建議,內網部署可以)
management.security.enabled=false
9.http VS rpc
RPC(Remote Procedure Call)—遠程過程調用協議,它是一種通過網絡從遠程計算機程序上請求服務,而不需要了解底層網絡技術的協議。RPC協議假定某些傳輸協議的存在,如TCP或UDP,爲通信程序之間攜帶信息數據。
在OSI網絡通信模型中,RPC跨越了傳輸層和應用層。RPC使得開發包括網絡分佈式多程序在內的應用程序更加容易。
應用間通信方式主要是HTTP和RPC,在微服務架構中兩大配方的主角分別是:
- Dubbo RPC框架
基於dubbo開發的應用還是要依賴周邊的平臺生態, 相比其它的RPC框架, dubbo在服務治理與服務集成上可謂是非常完善, 不僅提供了服務註冊,發現還提供了負載均衡,集羣容錯等基礎能力同時,還提供了面向開發測試節點的Mock和泛化調用等機制。 在spring cloud 出現之前dubbo在國內應用十分廣泛,但dubbo定位始終是一個RPC框架。
- SpringCloud 微服務框架(HTTP通信 輕量型協議,通用層協議)
Spring Cloud 的目標是微服務架構下的一棧式解決方案,自dubbo復活後dubbo官方表示要積極適配到spring cloud的生態方式,比如作爲springcloud的二進制通信方案來發揮dubbo的性能優勢,或者通過dubbo的模塊化以及對http的支持適配到Spring Cloud,但是到目前爲止dubbo與spring cloud 還是不怎麼兼容,spring cloud 微服務架構下微服務之間使用http的RestFul方式進行通信,Http RestFul 本身輕量易用適用性強,可以很容易跨語言,跨平臺,或者與已有的系統集成。10.使用Ribbon完成負載均衡
10.使用Ribbon完成負載均衡
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-Fal4Dfp1-1584790934342)(img\負載均衡策略.jpg)]
1.Ribbon簡介
ribbin是Netflix發佈的負載均衡器,有助於控制http和tcp客戶端的行爲,爲ribbon配置服務提供者列表後,ribbon就可以基於某種負載均衡算法,自動的幫助服務消費者去請求。ribbon提供了很多的負載均衡算法例如
Ribbon 的負載均衡策略
- RoundRobinRule 輪訓策略 按順序循環選擇 Server
- RandomRule 隨機策略 隨機選擇 Server
- AvailabilityFilteringRule 可用過濾策略 會先過濾由於多次訪問故障而處於斷路器跳閘狀態的服務,還有併發的連接數量超過閾值的服務,然後對剩餘的服務列表按照輪詢策略進行訪問
- WeightedResponseTimeRule 響應時間加權策略 根據平均響應的時間計算所有服務的權重,響應時間越快服務權重越大被選中的概率越高,剛啓動時如果統計信息不足,則使用RoundRobinRule策略,等統計信息足夠會切換到
- RetryRule 重試策略 先按照RoundRobinRule的策略獲取服務,如果獲取失敗則在制定時間內進行重試,獲取可用的服務。
- BestAviableRule 最低併發策略 會先過濾掉由於多次訪問故障而處於斷路器跳閘狀態的服務,然後選擇一個併發量最小的服務
默認爲輪詢策略
在springCloud中,當ribbon和Eureka配和使用時ribbon可以自動獲取服務註冊列表,並基於負載均衡算法,請求其中的一個服務提供實例
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-PunqU037-1584790934343)(img\圖片5.png)]
2.爲服務消費者整合ribbon
1.ribbon和springcloud的啓動器
**注意:**如果已經引入了spring-cloud-starter-eureka-server該依賴已經包含了spring-cloud-starter-ribbon,所以不需要再次引入
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-ribbon -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
2.ribbon整合RestTemplate
@Bean
@LoadBalanced
RestTemplate getRestTemplate(){
return new RestTemplate();
}
3.使用
@Autowired
RestTemplate restTemplate;
@RequestMapping("/hi")
public String hi(String name) {
String restTemplateForObject = restTemplate.getForObject("http://HI-SERVICE/test/test?name=" + name, String.class);
return restTemplateForObject;
}
4.驗證負載均衡
@Resource
LoadBalancerClient loadBalancerClient;
@RequestMapping("/test")
public void test() {
ServiceInstance choose = loadBalancerClient.choose("EUREKA-CLIENT2");
// ===http://SKY-20181016DKS:8763 計算機名+端口號
System.out.println("==="+choose.getUri());
// ===SKY-20181016DKS 計算機名
System.out.println("==="+choose.getHost());
// ===8763 端口號
System.out.println("==="+choose.getPort());
// ===EUREKA-CLIENT2 服務實例名
System.out.println("==="+choose.getServiceId());
}
3.自定義ribbon配置 修改負載均衡策略
#調整負載均衡策略 把輪循修改爲隨機
EUREKA-CLIENT2.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule
11.使用feign實現聲明式的調用
使用RestTemplate+ribbon已經可以完成對端的調用,爲什麼還要使用feign?
@RequestMapping("/test")
public String test(String name) {
String restTemplateForObject = restTemplate.getForObject("http://EUREKA-CLIENT2/product/product?name=" + name, String.class);
return restTemplateForObject;
}
上述代碼採用url拼接參數的形式發送請求,如果要發送多個請求參數那麼代碼就會變得很低效並且難以維護。例如
http://localhost:8762/test/test?name=zhangcn&password=111111&age=18
如果使用字符串拼接的方式,那麼代碼可以編排爲:
@RequestMapping("/test")
public String test(String name, String password, Integer age) {
HashMap<String, Object> map = new HashMap<>();
map.put("name",name);
map.put("password",password);
map.put("age",age);
//調用其他的微服務實例 http請求 httpClient
String restTemplateForObject = restTemplate.getForObject("http://EUREKA-CLIENT2/product/product?name={name}&password={password}&age={age}", String.class,map);
return restTemplateForObject;
}
在這裏url僅僅包含三個參數,如果url爲10個參數那麼代碼會變的更加難以維護。
1.feign簡介
Feign是一個聲明式的僞Http客戶端,它使得寫Http客戶端變得更簡單。使用Feign,只需要創建一個接口並註解。它具有可插拔的註解特性(可以使用springmvc的註解),可使用Feign 註解和JAX-RS註解。Feign支持可插拔的編碼器和解碼器。Feign默認集成了Ribbon,並和Eureka結合,默認實現了負載均衡的效果並且springcloud爲feign添加了springmvc註解的支持。
2.爲服務消費者整合feign
1.引入feign的相關依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
2.入口類開啓feign支持
/*開啓feign的支持*/
@EnableFeignClients
@EnableEurekaClient
@SpringBootApplication
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
3.創建feign接口配置
//name=所調用的微服務實例名
@FeignClient(name = "EUREKA-CLIENT2")
public interface TestFeign {
//生聲明式 僞客戶端
@RequestMapping("/feign/test")
public String testFeign(@RequestParam("name") String name);
//多個參數 一定要使用 @RequestParam
@RequestMapping("/feign/tests")
public String testFeigns(@RequestParam("name")String name,@RequestParam("password")String password,@RequestParam("age")Integer age);
//對象類型參數 提交方式要是POST
@RequestMapping(value = "/feign/testUser",method = RequestMethod.POST)
public String testFeignUser(User user);
}
4.Feign服務提供方
@RestController
@RequestMapping("feign")
public class FeignController {
//單個參數
@RequestMapping("test")
public String test(String name){
return "hello Client2 8763:"+name;
}
//多個參數
@RequestMapping("tests")
public String tests(String name,String password,Integer age){
return "hello Client2 8763: "+ name+"-"+password+"-"+age;
}
//接收對象類型的參數 要加@RequestBody
@RequestMapping("testUser")
public String testUser(@RequestBody User user){
user.setAge(-1);
return "hello Client2 8763: "+user;
}
}
5.測試
@RestController
@RequestMapping("testFeign")
public class TestFeignController {
@Resource
TestFeign testFeign;
//單個參數
@RequestMapping("testFeign")
public String testFeign(String name){
String s = testFeign.testFeign(name);
System.out.println("===feign: "+s);
return "hello Feign 8765: "+s;
}
//多個參數
@RequestMapping("testFeigns")
public String testFeigns(String name,String password,Integer age){
String s = testFeign.testFeigns(name,password,age);
System.out.println("===feign: "+s);
return "hello Feign 8765: "+s;
}
//接收對象
@RequestMapping("feignTestUser")
public String feignTestUser(UserDto userDto){
System.out.println(userDto.getSex());
System.out.println(userDto.getStatus());
User user = new User(userDto.getName(), userDto.getPassword(), userDto.getAge());
String feigns = testFeign.feignsUser(user);
return "client-feign 8765== "+feigns;
}
}
3.feign日誌
很多的場景下,需要了解feign處理請求的具體細節,如何滿足這種需求呢?
feign對日誌的處理非常靈活可爲每個feign客戶端指定日誌記錄策略,每個客戶端都會創建一個logger默認情況下logger的名稱是feign的全限定名需要注意的是,feign日誌的打印只會DEBUG級別做出響應。
我們可以爲feign客戶端配置各自的logger.lever對象,告訴feign記錄那些日誌logger.lever有以下的幾種值
- NONE 不記錄任何日誌
- BASIC 僅僅記錄請求方法,url,響應狀態代碼及執行時間
- HEAdERS 記錄Basic級別的基礎上,記錄請求和響應的header
- FULL 記錄請求和響應的header,body和元數據
1.java配置核心日誌類
@Configuration
public class feignConf {
@Bean
public Logger.Level feignConfiguration() {
return Logger.Level.FULL;
}
}
2.配置feign客戶端的日誌級別
#日誌 ERROR、WARN、INFO、DEBUG 高-低
logging.level.com.baizhi.feign.TestFeign=debug
4.feign構造多參數請求
1.錯誤方式
feign的注意事項:
//1.多參數傳輸的時候 必須要在feign接口上進行參數的綁定
public String testFeign(@RequestParam("name") String name, @RequestParam("password") String password, @RequestParam("age") Integer age);
//2.以對象格式爲參數進行數據傳輸時 必須設置feign的請求形式爲post
//3.在服務方接收對象參數時需在形參上加入@RequestBody的註解
public interface feignPost {
@RequestMapping(value = "/test/test1", method = RequestMethod.GET)
public User sayHi(User user);
}
錯誤日誌信息:
feign.FeignException: status 405 reading feignPost#sayHi(User); content:
{"timestamp":1546852876890,"status":405,"error":"Method Not Allowed","exception":"org.springframework.web.HttpRequestMethodNotSupportedException","message":"Request method 'POST' not supported","path":"/test/test1"}
由異常信息可知,儘管指定了get的方法,feign依然會使用post發送請求(對象傳輸時)。正確的方式如下:
2.get
@FeignClient(name = "EUREKA-CLIENT2")
public interface TestFeign {
//對象類型參數 提交方式要是POST
@RequestMapping(value = "/feign/testUser",method = RequestMethod.GET)
public String testFeignUser(User user);
}
3.post
feign配置
@FeignClient(name = "EUREKA-CLIENT2")
public interface TestFeign {
//對象類型參數 提交方式要是POST
@RequestMapping(value = "/feign/testUser",method = RequestMethod.POST)
public String testFeignUser(User user);
}
服務提供方
@RestController
@RequestMapping("feign")
public class FeignController {
@RequestMapping("/testUser")
public User testUser(@RequestBody User user) {
return user;
}
}
12.使用Hystrix實現微服務的容錯處理
1.實現容錯的手段
如果服務提供者響應的速度特別慢,那麼消費者對提供者的請求就會強制等待,直到提供者響應或者超時。在高負載的情況下,如果不做任何處理,此類問題可能會導致服務消費者的資源耗盡甚至整個系統的崩潰。例如曾經發生的一個案例,某個電子商務網站在某個星期五發生過載,過多的並法請求,導致用戶支付請求延遲很久沒有響應,在等待很長時間後最終失敗,支付失敗又導致用戶重新刷新頁面再次嘗試支付,進一步增加了服務器的負載,最終整個系統崩潰了。
1.1雪崩效應
我們常把基礎服務故障,導致級聯服務故障的現象,稱爲雪崩效應。雪崩效應描述的是提供方不可用,導致消費方不可用並將不可用逐漸放大的過程。
1.2.如何容錯
想要避免雪崩效應,必須有強大的容錯機制,該容錯機制需要實現以下兩點
-
爲網絡請求設置超時
-
使用斷路器模式
正常情況下斷路器關閉可正常請求服務,當一定時間內請求達到了一定的閾(yu)值(錯誤率達到百分之50或者100次/分鐘),斷路器就會打開此時不會再去請求依賴服務,斷路器打開一段時間之後,會自動進入半開的狀態。此時斷路器允許一個請求請求服務實例,如果該服務可以調用成功,則關閉斷路器,否則繼續保持打開狀態。
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-SRemwLHy-1584790934344)(img\hystrix.png)]
斷路器開啓或者關閉的條件:
1、 當滿足一定的閥值的時候(默認10秒內超過20個請求次數)
2、 當失敗率達到一定的時候(默認10秒內超過50%的請求失敗)
3、 到達以上閥值,斷路器將會開啓
4、 當開啓的時候,所有請求都不會進行轉發
5、 一段時間之後(默認是5秒),這個時候斷路器是半開狀態,會讓其中一個請求進行轉發。如果成功,斷路器會關閉,若失敗,繼續開啓。重複4和5。
2.使用hystrix實現容錯
hystrix是Netfilx開源的延遲和容錯庫,用於隔離訪問遠程系統,服務或者第三方庫,防止級聯失敗,從而提升系統的可用性和容錯性
3.通用方式整合Hystrix
1.爲項目添加依賴
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-hystrix -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
2.在消費者的入口類上添加註解
@EnableHystrix
@EnableEurekaClient
@SpringBootApplication
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
3.讓當前方法具備容錯的能力
@RestController
@RequestMapping("test")
public class TestController {
@Autowired
private RestTemplate restTemplate;
@Resource
LoadBalancerClient loadBalancerClient;
@RequestMapping("test")
@HystrixCommand(fallbackMethod = "testFallback") //指定容錯方法 方法名參數表一致
public String test(String name,String password,Integer age){
HashMap<String, Object> map = new HashMap<>();
map.put("name",name);
map.put("password",password);
map.put("age",age);
//調用其他微服務實例 http請求 httpClint
String forObject = restTemplate.getForObject("http://EUREKA-CLIENT2/product/product?name={name}&password={password}&age={age}", String.class, map);
return forObject;
}
//容錯方法 與原方法參數表一致
public String testFallback(String name,String password,Integer age){
return "method invoke fail "+name;
}
}
4.測試
1.啓動eureka服務
2.啓動生產者
3.啓動消費者
4.訪問消費者
結果
{"name":"xiaohei","password":null,"age":null}
5.關閉生產者
6.訪問消費者
結果
{"name":"xiaohei","password":null,"age":-1}
我們知道,當請求失敗,被拒絕超時或者短路器打開時都會進入到回退方法,但是進入回退方法並不是意味着斷路器被打開,那怎樣纔算斷路器打開呢?
5.實驗 進行健康監測實時查看斷路器是否打開
1.啓動eureka服務
2.啓動生產者
3.啓動消費者
4.訪問消費者
結果
{"name":"xiaohei","password":null,"age":null}
5.訪問 http://ip:port/health
{
status:"up",
hystrix:{
status:"up"
}
}
6.關閉生產者
7.訪問消費者
結果
{"name":"xiaohei","password":null,"age":-1}
8.訪問 http://ip:port/health
{
status:"up",
hystrix:{
status:"up"
}
}
通過這裏我們可以發現,儘管執行了fallback方法,但此時hystrix狀態依然是up,這是因爲咱們的失敗率還沒有達到閥值(默認5秒20次失敗),這裏再次強調,執行回退的邏輯並不代表斷路器已經打開,請求超時,失敗,被拒絕以及斷路器打開都會執行回退邏輯
9.5秒請求20次
{
status:"up",
hystrix:{
status:"CIRCUIT_OPEN"(斷路器打開的標識)
}
}
4.feign整合Hystrix
很顯然@HystrixCommand是添加到方法上的,那麼對於feign來說一定不適用,如何讓Hystrix整合feign
1.feign方法上添加相關注解
@FeignClient(name = "EUREKA-CLIENT2",fallback = FeignClientFallBack.class)
public interface TestFeign {
//對象類型參數 提交方式要是POST
@RequestMapping(value = "/feign/testUser",method = RequestMethod.POST)
public String testFeignUser(User user);
}
2.實現Feign接口
@Component
public class FeignClientFallBack implements TestFeign{
@Override
public String testFeignUser(User user) {
return "method invoke fail ";
}
}
3.添加feign的配置文件
#爲feign開啓斷路器
feign.hystrix.enabled=true
14.統一配置中心
爲什麼需要統一配置中心?
統一配置中心顧名思義,就是將配置統一管理,配置統一管理的好處是在日後大規模集羣部署服務應用時相同的服務配置一致,日後再修改配置只需要統一修改全部同步,不需要一個一個服務手動維護
統一配置中心的架構圖:
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-5hizY6E5-1584790934345)(img\圖片11.png)]
1.配置中心服務器的開發
1.添加依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
2.添加註解支持
@EnableEurekaClient
@EnableConfigServer
@SpringBootApplication
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class, args);
}
}
3.在遠程的倉庫創建配置文件(yml,properties,yaml)
4.配置相關的配置文件
#註冊到註冊中心
eureka.client.service-url.defaultZone=http://localhost:8761/eureka
#默認端口號 8888
server.port=8888
# 實例名
spring.application.name=config
#配置git的遠程倉庫 https 暫時不支持ssh
spring.cloud.config.server.git.uri=https://gitee.com/my_os_do_you_know/springcloud-config.git
5.啓動配置中心服務
http://localhost:8888/order-a.yml
http://localhost:8888/order-a.yaml
http://localhost:8888/order-a.properties
http://localhost:8888/order-a.json
以上四種訪問方式都可以
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-MF1juSTb-1584790934346)(img\圖片 13.png)]
6.服務的配置的訪問規則
{name}-{profiles}.yml
{name}/{profiles:.[^-].}
{name}-{profiles}.json
{label}/{name}-{profiles}.yml
{name}/{profiles}/{label:.*}
{label}/{name}-{profiles}.properties
{label}/{name}-{profiles}.json
{name}/{profile}/{label}/**
{name}/{profile}/{label}/**
說明:
label: 分支名稱 默認是master分支
name:文件的服務的名稱(自定義的名稱)
profiles:不同環境
2.創建配置中心客戶端
1.導入相關依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-client</artifactId>
</dependency>
2.添加配置
#開啓配置中心
spring.cloud.config.enabled=true
#找到配置中心實例
spring.cloud.config.discovery.service-id=CONFIG
#指定名字
spring.cloud.config.name=product
#指定環境
spring.cloud.config.profile=1
#指定分支
spring.cloud.config.label=master
#指定配置中心的uri
spring.cloud.config.uri=http://localhost:9999
3.注意將配置文件名修改爲 bootstrap.yml
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-OvSTnDB8-1584790934347)(img\15.png)]
15.路由網關(zuul)
在微服務架構中,需要多個基礎的服務治理組件,包括服務註冊與發現、服務消費、負載均衡、斷路器、智能
路由、配置管理等,由這個基礎組件相互協作,共同組建了一個簡單的微服務系統。一個簡單的微服務系統如下
圖
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-0Wc7fU6I-1584790934347)(img\16.png)]
注意:A服務和B服務是可以相互調用的,並且配置服務也是註冊到服務註冊中心的。
總結:在Spring Cloud微服務系統中,一種常見的負載均衡方式是,客戶端的請求先先經過負載均衡(
Ngnix),再到達服務網關(zuul集羣),然後再到具體的服務。服務統一註冊到高可用的服務註冊中心集羣,服務的所有的配置文件由配置服務管理,配置服務的配置文件倉庫,方便開發人員隨時改配置。
1.Zuul 簡介
Zuul的主要功能是路由轉發和過濾器。路由功能是微服務的一部分,比如/api/user轉發到到user服
務,/api/shop轉發到到shop服務。zuul默認和Ribbon結合實現了負載均衡的功能
1.搭建Zull
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>
2.編寫Zuul的入口類
@EnableZuulProxy
@EnableEurekaClient
@SpringBootApplication
public class ServiceZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceZuulApplication.class, args);
}
}
3.編寫application.yml
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8080/eureka/
server:
port: 8769
spring:
application:
name: service-zuul
zuul:
routes:
api-a:
path: /api-a/**
serviceId: service-ribbon
api-b:
path: /api-b/**
serviceId: service-feign
#關閉安全認證
management.security.enabled=false
#設置斷路時間
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=5000