SpringCloud進階應用(OpenFeign Ribbon Steam Sleuth+Zipkin
- SpringCloud進階應用(OpenFeign Ribbon Steam Sleuth+Zipkin)
-
- 1 Spring Cloud OpenFeign
-
- 1.1業務分析
- 1.2 OpenFeign應用
- 1.3 資料壓縮
- 1.4 OpenFeign日志配置
- 2 Spring Cloud Ribbon
-
- 2.1 Ribbon使用
- 2.2 Ribbon算法
- 3 Spring Cloud Stream
-
- 3.1 SpringCloud Stream工作流程
- 3.2 SpringCloud Stream實戰
-
- 3.2.1 生産者
- 3.2.2 消費者
- 3.3 消息分組
-
- 3.3.1 叢集消費下的分組
- 3.3.2 資料持久化
- 4 Sleuth+Zipkin鍊路追蹤
-
- 4.1 Sleuth/Zipkin介紹
- 4.2 Zipkin安裝
- 4.3 Sleuth鍊路監控
SpringCloud進階應用(OpenFeign Ribbon Steam Sleuth+Zipkin)
1 Spring Cloud OpenFeign
Feign [feɪn] 譯文 僞裝。Feign是一個輕量級的Http封裝工具對象,大大簡化了Http請求,它的使用方法是定義一個接口,然後在上面添加注解。不需要拼接URL、參數等操作。項目首頁:https://github.com/OpenFeign/feign 。
- 內建Ribbon的負載均衡功能
- 內建了Hystrix的熔斷器功能
- 支援請求壓縮
- 大大簡化了遠端調用的代碼,同時功能還增強啦
- Feign以更加優雅的方式編寫遠端調用代碼,并簡化重複代碼
1.1業務分析
如上圖,我們現在要實作打車使用者打車下單,打車下單的時候需要比對指定司機并更改司機狀态,由之前空閑狀态改成接單狀态。這時候就涉及到
hailtaxi-order
服務調用
hailtaxi-driver
服務了,此時如果使用HttpClient工具,操作起來非常麻煩,我們可以使用
SpringCloud OpenFeign
實作調用。
1.2 OpenFeign應用
使用OpenFeign實作服務之間調用,可以按照如下步驟實作:
1:導入feign依賴
2:編寫Feign用戶端接口-将請求位址寫到該接口上
3:消費者啟動引導類開啟Feign功能注解
4:通路接口測試
1)導入依賴
在
hailtaxi-api
中導入如下依賴:
<!--配置feign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
2)建立Feign用戶端接口
修改
hailtaxi-api
建立
com.itheima.driver.feign.DriverFeign
接口,代碼如下:
@FeignClient(value = "hailtaxi-driver")//value = "hailtaxi-driver"指定服務的名字
public interface DriverFeign {
/****
* 更新司機資訊,該方法和hailtaxi-driver服務中的方法保持一緻
*/
@PutMapping(value = "/driver/status/{id}/{status}")
Driver status(@PathVariable(value = "id")String id, @PathVariable(value = "status")Integer status);
}
參數說明:
Feign會通過動态代理,幫我們生成實作類。
注解@FeignClient聲明Feign的用戶端,注解value指明服務名稱
接口定義的方法,采用SpringMVC的注解。Feign會根據注解幫我們生成URL位址
注解@RequestMapping中的/driver,不要忘記。因為Feign需要拼接可通路位址
3)Controller調用
修改
hailtaix-order
的下單方法,在下單方法中調用
DriverFeign
修改司機狀态,代碼如下:
@Autowired
private DriverFeign driverFeign;
/***
* 下單
*/
@PostMapping
public OrderInfo add(){
//建立汽車資訊
Car car = new Car("1","粵B123","BYD","red",1,1);
//建立司機
Driver driver = new Driver("1", "zhangsan", 5f, car, 2);
//建立訂單
OrderInfo orderInfo = new OrderInfo("1", 350, new Date(), "深圳北站", "羅湖港", driver);
//調用OpenFeign修改司機狀态
driverFeign.status("1",2);
return orderInfo;
}
4)啟用OpenFeign
上面所有業務邏輯寫完了,但OpenFeign還并未生效,我們需要在
hailtaxi-order
中開啟
OpenFeign
,隻需要在
OrderApplication
啟動類上添加
@EnableFeignClients(basePackages = "com.itheima.driver.feign")
即可。
此時執行測試,效果如下:
背景輸出如下:
司機狀态變更:Driver(id=1, name=張司機, star=5.0, car=null, status=2)
1.3 資料壓縮
使用者在網絡請求過程中,如果網絡不佳、傳輸資料過大,會造成體驗差的問題,我們需要将傳輸資料壓縮提升體驗。SpringCloud OpenFeign支援對請求和響應進行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 # 設定觸發壓縮的大小下限
#以上資料類型,壓縮大小下限均為預設值
response:
enabled: true # 開啟響應壓縮
1.4 OpenFeign日志配置
通過loggin.level.xx=debug來設定日志級别。然而這個對Feign用戶端不會産生效果。因為@FeignClient注解修飾的用戶端在被代理時,都會建立一個新的Feign.Logger執行個體。我們需要額外通過配置類的方式指定這個日志的級别才可以。
實作步驟:
1. 在application.yml配置檔案中開啟日志級别配置
2. 編寫配置類,定義日志級别bean。
3. 在接口的@FeignClient中指定配置類
4. 重新開機項目,測試通路
1)普通日志等級配置
在
hailtaxi-order
的配置檔案中設定com.itheima包下的日志級别都為debug:
# com.itheima 包下的日志級别都為Debug
logging:
level:
com.itheima: debug
2)Feign日志等級配置
在
hailtaxi-order
啟動類
OrderApplication
中建立
Logger.Level
,定義日志級别:
/***
* 日志級别
* @return
*/
@Bean
public Logger.Level feignLoggerLevel(){
return Logger.Level.FULL;
}
日志級别說明:
Feign支援4中級别:
NONE:不記錄任何日志,預設值
BASIC:僅記錄請求的方法,URL以及響應狀态碼和執行時間
HEADERS:在BASIC基礎上,額外記錄了請求和響應的頭資訊
FULL:記錄所有請求和響應的明細,包括頭資訊、請求體、中繼資料
重新開機項目,即可看到每次通路的日志
2 Spring Cloud Ribbon
什麼是Ribbon?
Ribbon是Netflix釋出的負載均衡器,有助于控制HTTP用戶端行為。為Ribbon配置服務提供者位址清單後,Ribbon就可基于負載均衡算法,自動幫助服務消費者請求。
Ribbon預設提供的負載均衡算法:輪詢,随機,重試法,權重。當然,我們可用自己定義負載均衡算法
2.1 Ribbon使用
1)業務分析
如上圖,當使用者下單調用
hailtaxi-order
服務的時候,該服務會調用
hailtaxi-driver
,此時如果是搶單過程,查詢壓力也會很大,我們可以為
hailtaxi-driver
做叢集,做叢集隻需要把工程複制多分即可,多個工程如下圖:
2)調用測試
此時我們執行
http://localhost:8001/order?token=zhangsan
調用,可以發現已經實作負載均衡了,
18081
和
18084
服務會輪詢着調用。
2.2 Ribbon算法
我們上面沒做任何相關操作,隻是把服務換成了多個就實作了負載均衡,這是因為OpenFeign預設使用了Ribbon的輪詢算法,如下圖依賴包,引入OpenFeign的時候會傳遞依賴Ribbon包:
我們如果想改變相關算法,可以直接在
application.yml
中配置算法即可。
#修改負載均衡算法,預設是輪詢,配置之後變随機
ribbon:
#輪詢
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule
#随機算法
#NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
#重試算法,該算法先按照輪詢的政策擷取服務,如果擷取服務失敗則在指定的時間内會進行重試,擷取可用的服務
#NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RetryRule
#權重法,會根據平均響應時間計算所有服務的權重,響應時間越快服務權重越大被選中的機率越大。剛啟動時如果同統計資訊不足,則使用輪詢的政策,等統計資訊足夠會切換到自身規則。
#NFLoadBalancerRuleClassName: com.netflix.loadbalancer.ZoneAvoidanceRule
3 Spring Cloud Stream
Spring Cloud Stream
是一個用來為微服務應用建構消息驅動能力的架構。它可以基于
Spring Boot
來建立獨立的、可用于生産的
Spring
應用程式。
Spring Cloud Stream
為一些供應商的消息中間件産品提供了個性化的自動化配置實作,并引入了釋出-訂閱、消費組、分區這三個核心概念。通過使用
Spring Cloud Stream
,可以有效簡化開發人員對消息中間件的使用複雜度,讓系統開發人員可以有更多的精力關注于核心業務邏輯的處理。目前
Spring Cloud Stream
支援 RabbitMQ 、 Kafka 自動化配置。
3.1 SpringCloud Stream工作流程
通過Stream可以很好的屏蔽各個中間件的API差異,它統一了API,生産者通過OUTPUT向消息中間件發送消息,此時并不需要關心消息中間件是Kafka還是RabbitMQ,不需要關注他們的API,隻需要用到Stream的API,這樣可以降低學習成本。消費方通過INPUT消費指定的消息,也不需要關注消息中間件的API,架構圖如上圖:
我們對上圖中的對象進行說明:
Application Core:生産者/消費者
inputs:消費者
outputs:生産者
binder:綁定器,主要和消息中間件之間進行綁定操作
Middleware:消息中間件服務
我們項目中真正用應用到Stream,隻需要按照如上流程圖操作即可。
生産者:
1:使用Source綁定消息輸出通道。
2:通過MessageChannel輸出消息。
3:通過@EnableBinding開啟Binder,将生産者綁定到指定MQ服務。
消費者:
1:通過@EnableBinding綁定到指定MQ。
2:通過Sink綁定輸出資料通道。
3:@StreamListener監聽指定通道資料。
3.2 SpringCloud Stream實戰
如上圖,當使用者行程結束,使用者需進入支付操作,當使用者支付完成時,我們需要更新訂單狀态,此時我們可以讓支付系統将支付狀态發送到MQ中,訂單系統訂閱MQ消息,根據MQ消息修改訂單狀态。我們将使用
SpringCloud Stream
實作該功能。
3.2.1 生産者
1)引入依賴
在
hailtaxi-pay
中引入依賴:
<!--stream-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>
2)配置MQ服務
修改
hailtaxi-pay
的
application.yml
添加如下配置:
#Stream
stream:
binders: # 在此處配置要綁定的rabbitmq的服務資訊;
defaultRabbit: # 表示定義的名稱,用于于binding整合
type: rabbit # 消息元件類型
environment: # 設定rabbitmq的相關的環境配置
spring:
rabbitmq:
host: 192.168.211.145
port: 5672
username: guest
password: guest
bindings: # 服務的整合處理
output: # 這個名字是一個通道的名稱
destination: payExchange # 表示要使用的Exchange名稱定義
content-type: application/json # 設定消息類型,本次為json,文本則設定“text/plain”
binder: defaultRabbit # 設定要綁定的消息服務的具體設定
完整配置如下:
server:
port: 18083
spring:
application:
name: hailtaxi-pay
cloud:
#Consul配置
consul:
host: localhost
port: 8500
discovery:
#注冊到Consul中的服務名字
service-name: ${spring.application.name}
#Stream
stream:
binders: # 在此處配置要綁定的rabbitmq的服務資訊;
defaultRabbit: # 表示定義的名稱,用于于binding整合
type: rabbit # 消息元件類型
environment: # 設定rabbitmq的相關的環境配置
spring:
rabbitmq:
host: 192.168.211.145
port: 5672
username: guest
password: guest
bindings: # 服務的整合處理
output: # 這個名字是一個通道的名稱
destination: payExchange # 表示要使用的Exchange名稱定義
content-type: application/json # 設定消息類型,本次為json,文本則設定“text/plain”
binder: defaultRabbit # 設定要綁定的消息服務的具體設定
3)消息輸出管道綁定
建立
com.itheima.pay.mq.MessageSender
代碼如下:
@EnableBinding(Source.class)
public class MessageSender {
@Resource
private MessageChannel output;//消息發送管道
/***
* 發送消息
* @param message
* @return
*/
public Boolean send(Object message) {
//消息發送
boolean bo = output.send(MessageBuilder.withPayload(message).build());
System.out.println("*******send message: "+message);
return bo;
}
}
參數說明:
Source.class:綁定一個輸出消息通道Channel。
MessageChannel:消息發送對象,預設是DirectWithAttributesChannel,發消息在AbstractMessageChannel中完成。
MessageBuilder.withPayload:建構消息。
4)消息發送
在
com.itheima.pay.controller.TaxiPayController
中建立支付方法用于發送消息,代碼如下:
@Autowired
private MessageSender messageSender;
/***
* 支付
* @return
*/
@GetMapping(value = "/wxpay/{id}")
public TaxiPay pay(@PathVariable(value = "id")String id){
//支付操作
TaxiPay taxiPay = new TaxiPay(id,310,3);
//發送消息
messageSender.send(taxiPay);
return taxiPay;
}
3.2.2 消費者
1)修改配置
修改
hailtaxi-order
的核心配置檔案
application.yml
,在檔案中配置要監聽的MQ資訊:
#Stream
stream:
binders: # 在此處配置要綁定的rabbitmq的服務資訊;
defaultRabbit: # 表示定義的名稱,用于于binding整合
type: rabbit # 消息元件類型
environment: # 設定rabbitmq的相關的環境配置
spring:
rabbitmq:
host: 192.168.211.145
port: 5672
username: guest
password: guest
bindings: # 服務的整合處理
input: # 這個名字是一個通道的名稱
destination: payExchange # 表示要使用的Exchange名稱定義
content-type: application/json # 設定消息類型,本次為json,文本則設定“text/plain”
binder: defaultRabbit # 設定要綁定的消息服務的具體設定
2)消息監聽
在
hailtaxi-order
中建立消息監聽對象
com.itheima.order.mq.MessageReceiver
,代碼如下:
@EnableBinding(Sink.class)
public class MessageReceiver {
@Value("${server.port}")
private String port;
/****
* 消息監聽
* @param message
*/
@StreamListener(Sink.INPUT)
public void receive(String message) {
System.out.println("消息監聽(增加使用者積分、修改訂單狀态)-->" + message+"-->port:"+port);
}
}
參數說明:
Sink.class:綁定消費者管道資訊。
@StreamListener(Sink.INPUT):監聽消息配置,指定了消息為application中的input。
我們請求
http://localhost:18083/pay/wxpay/1
測試效果如下:
hailtaxi-order
:
消息監聽(增加使用者積分、修改訂單狀态)-->{"outTradeNo":"1","money":310,"status":3}-->port:18082
3.3 消息分組
消息分組有2個好處,分别是叢集合理消費、資料持久化。
3.3.1 叢集消費下的分組
1)分組的意義
分組在項目中是有非常重大的意義,通常應用于消息并發高、消息堆積的場景,這些場景服務消費方通常會做叢集操作,一旦做叢集操作,我們又需要項目中的消費者合理消費,比如使用者打車支付完成後,我們需要增加使用者積分同時修改訂單狀态,如果叢集環境中有2台伺服器都執行該消費操作,此時使用者積分會增加兩次,就會造成非幂等問題。
此時叢集中相同服務應該屬于同一個組,同一個組中隻允許有一個足節點消費某一個資訊,這樣就可以避免費幂等問題的出現。
2)分組實戰
新增一個
hailtaxi-order
消費者節點:
此時運作起來,
18082
和
18182
節點會同時消費所有資料。
修改
hailtaxi-order
的核心配置檔案
application.yml
,添加分組:
此時再次測試,可以發現消費者不會重複消費資料。
3.3.2 資料持久化
我們把分組去掉,停掉
hailtaxi-order
服務,然後請求
http://localhost:18083/pay/wxpay/1
發送資料,發送完資料後,再啟動
hailtaxi-order
服務,此時發現沒有資料可以消費,這是因為資料沒有持久化,是一種廣播模式,如果需要資料持久化,得給每個消費節點添加group組即可。
4 Sleuth+Zipkin鍊路追蹤
在微服務系統中,一個來自使用者的請求,請求先達到前端A(如前端界面)然後通過遠端調用,到達系統中間件B,C(負載均衡,網關等),最後達到後端服務D,E,後端經過一系列的業務邏輯計算最後将資料傳回給使用者,對于這樣一個請求,經曆了這麼多個服務,怎麼樣将它的請求過程的資料記錄下來呢?這就需要用到服務鍊路追蹤。
4.1 Sleuth/Zipkin介紹
Zipkin:
是一個開放源代碼分布式的跟蹤系統,它可以幫助收集服務的時間資料,以解決微服務架構中的延遲問題,包括資料的收集、存儲、查找和展現。每個服務向zipkin報告計時資料,zipkin會根據調用關系通過Zipkin UI生成依賴關系圖,展示多少跟蹤請求經過了哪些服務,該系統讓開發者可通過一個web前端輕松地收集和分析資料,可非常友善的監測系統中存在的瓶頸。
它可以幫助收集服務的時間資料,以解決微服務架構中的延遲問題,包括資料的收集、存儲、查找和展現
每個服務向zipkin報告計時資料,zipkin會根據調用關系通過Zipkin UI生成依賴關系圖,展示多少跟蹤請求經過了哪些服務,該系統讓開發者可通過一個web前端輕松地收集和分析資料,可非常友善的監測系統中存在的瓶頸
Spring Cloud Sleuth:
為服務之間的調用提供鍊路追蹤,通過使用Sleuth可以讓我們快速定位某個服務的問題。分布式服務追蹤系統包括:資料收集、資料存儲、資料展示。通過Sleuth産生的調用鍊監控資訊,讓我們可以得知微服務之間的調用鍊路,但是監控資訊隻輸出到控制台不太友善檢視。
Sleuth和Zipkin結合,将資訊發送到Zipkin,利用Zipkin的存儲來存儲資訊,利用Zipkin UI來展示資訊。
SpringCloudSleuth有4個特點
特點 | 說明 |
---|---|
提供鍊路追蹤 | 通過sleuth可以很清楚的看出一個請求經過了哪些服務, 可以友善的理清服務局的調用關系 |
性能分析 | 通過sleuth可以很友善的看出每個采集請求的耗時, 分析出哪些服務調用比較耗時,當服務調用的耗時 随着請求量的增大而增大時,也可以對服務的擴容提 供一定的提醒作用 |
資料分析 優化鍊路 | 對于頻繁地調用一個服務,或者并行地調用等, 可以針對業務做一些優化措施 |
可視化 | 對于程式未捕獲的異常,可以在zipkpin界面上看到 |
4.2 Zipkin安裝
關于zipkin在上面我們已經介紹了,我們接下來講解zipkin的安裝。
1)下載下傳
下載下傳位址:http://dl.bintray.com/openzipkin/maven/io/zipkin/java/zipkin-server/
下載下傳後的檔案
zipkin-server-2.12.9-exec.jar
2)運作
運作
zipkin-server-2.12.9-exec.jar
java -jar zipkin-server-2.12.9-exec.jar
回車即可運作,并通路
http://localhost:9411/zipkin/
http://localhost:9411/zipkin/
效果如下:
4.3 Sleuth鍊路監控
1)引入依賴
引入
zipkin
,它自身已經依賴了
sleuth
,在
hailtaxi-order
中引入依賴包如下:
<!--zipkin-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>
依賴關系如下圖:
2)配置服務位址
修改
hailtaxi-order
的配置檔案
applicatin.yml
添加如下配置:
spring:
zipkin:
#zipkin服務位址
base-url: http://localhost:9411
sleuth:
sampler:
probability: 1 #采樣值,0~1之間,1表示全部資訊都手機,值越大,效率越低
我們執行一次下單調用
http://localhost:18082/order
,再看zipkin控制台:
我們剛才調用的鍊路如下圖:
調用鍊路如下分析:
1:調用了hailtaxi-order的POST->order()方法,該方法耗時333毫秒。
2:該方法調用了hailtaxi-driver的put /driver/status/{id}/{status}方法,耗時11毫秒。
分布式服務追蹤系統包括:資料收集、資料存儲、資料展示
通過Sleuth産生的調用鍊監控資訊,讓我們可以得知微服務之間的調用鍊路,但是監控資訊隻輸出到控制台不太友善檢視
Sleuth和Zipkin結合,将資訊發送到Zipkin,利用Zipkin的存儲來存儲資訊,利用Zipkin UI來展示資訊