微服務系統拆分導致系統調用鏈路愈發復雜一個前端請求可能最終需要調用很多次后端服務才能完成,當整個請求變慢或不可用時,我們是無法得知該請求是由某個或某些后端服務引起的,這時就需要解決如何快讀定位服務故障點,以對症下葯。於是就有了分布式系統調用跟蹤的誕生。
針對微服務化應用鏈路追蹤的問題,Google在2010年發表了論文《Dapper, a Large-Scale Distributed Systems Tracing Infrastructure》,這篇文章是業內實現鏈路追蹤的標桿和理論基礎,具有非常大的參考價值。目前鏈路追蹤組件主要有Google的Dapper,Twitter 的Zipkin,以及阿里的Eagleeye (鷹眼)等,它們都是非常優秀的鏈路追蹤開源組件。
Spring Cloud Sleuth是 Spring Cloud為分布式服務鏈路跟蹤提供的解決方案。本文我們將詳細介紹與分析 Spring Cloud Sleuth + Zipkin 的實現原理。
sleuth是spring cloud的分布式跟蹤工具,主要記錄鏈路調用數據,本身只支持內存存儲,在業務量大的場景下,為拉提升系統性能也可通過http傳輸數據,也可換做rabbit或者kafka來傳輸數據。
zipkin是Twitter開源的分布時追蹤系統,可接收數據,存儲數據(內存/cassandra/mysql/es),檢索數據,展示數據,他本神不會直接在分布式的系統服務種trace追蹤數據,可便捷的使用sleuth來收集傳輸數據。
Spring Cloud Sleuth
- 提供鏈路追蹤。通過sleuth可以很清楚的看出一個請求都經過了哪些服務;可以很方便的理清服務間的調用關系。
- 可視化錯誤。對於程序未捕捉的異常,可以結合zipkin分析。
- 分析耗時。通過sleuth可以很方便的看出每個采樣請求的耗時,分析出哪些服務調用比較耗時。當服務調用的耗時隨着請求量的增大而增大時,也可以對服務的擴容提供一定的提醒作用。
- 優化鏈路。對於調用頻繁的服務,可以並行調用或針對業務做一些優化措施等。
Spring Cloud Sleuth可以追蹤以下類型的組件:async,hystrix,messaging,websocket,rxjava,scheduling,web(SpringWebMvc,Spring WebFlux, Servlet),webclient(Spring RestTemplate),feign,zuul。通過spring-cloud-sleuth-core的jar包結構,可以很明顯的看出,sleuth支持鏈路追蹤的組件(web下面包括http、client和feign)。
通訊方式:
- sleuth 默認采用 http 通信方式,將數據傳給 zipkin 作頁面渲染,但是 http 傳輸過程中如果由於不可抗因素導致 http 通信中斷,那么此次通信的數據將會丟失。而使用中間件的話,RabbitMQ 消息隊列可以積壓千萬級別的消息,下次重連之后可以繼續消費。
- 隨着線程增多,並發量提升之后,RabbitMQ 異步發送數據明顯更具有優勢。
- RabbitMQ 支持消息、隊列持久化,可以通過消息狀態落庫、重回隊列、鏡像隊列等技術手段保證其高可用。
zipkin
來自Twitte的分布式日志收集工具,分為上傳端(spring-cloud-starter-zipkin,集成到項目中)與服務端(獨立部署,默認將數據存到內存中)
注意: Zipkin僅對RPC通信過程進行記錄,注意它與業務代碼日志是無關的,如果你希望找到一款LogAppender來分析所有Log4j留下的日志,那么建議還是使用Kakfa+ELK這種傳統的方法來實現。
Zipkin Server主要包括四個模塊:
(1)Collector 接收或收集各應用傳輸的數據
(2)Storage 存儲接受或收集過來的數據,當前支持Memory,MySQL,Cassandra,ElasticSearch等,默認存儲在內存中。
(3)API(Query) 負責查詢Storage中存儲的數據,提供簡單的JSON API獲取數據,主要提供給web UI使用
(4)Web 提供簡單的web界面
兩者之間的關系
即sleuth是zipkin的一個java spring 庫。
zipkin架構
下載zipkin
https://github.com/openzipkin/zipkin/releases
安裝Zipkin
Docker方式
The Docker Zipkin project is able to build docker images, provide scripts and a docker-compose.yml
for launching pre-built images. The quickest start is to run the latest image directly:
docker run -d -p 9411:9411 openzipkin/zipkin
Java jar方式
curl -sSL https://zipkin.io/quickstart.sh | bash -s java -jar zipkin.jar
源碼方式
# get the latest source git clone https://github.com/openzipkin/zipkin cd zipkin # Build the server and also make its dependencies ./mvnw -DskipTests --also-make -pl zipkin-server clean install # Run the server java -jar ./zipkin-server/target/zipkin-server-*exec.jar
啟動后訪問:
依賴
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-sleuth</artifactId> <version>2.0.4.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-sleuth-zipkin</artifactId> <version>2.0.4.RELEASE</version> </dependency>
注意,Finchley一定要和2.0.X的sleuth相配合,如果使用了2.2.X,就會報錯。
zipkin-release-2.21以上版本不受sleuth客戶端影響。
配置
spring.zipkin.base-url=http://localhost:9411/ spring.zipkin.service.name=springboot2-nacos-consumer spring.zipkin.sender.type=web
spring.sleuth.sampler.percentage=1
還可以使用RabbitMQ方式,而web方式是通過http向9411發送數據,隨着線程數的增加也就是並發量的增加,mq 傳輸時延將會大大低於 http。
#sleuth 使用 rabbitmq 來向 zipkin 發送數據 spring.zipkin.sender.type=rabbit spring.rabbitmq.host=localhost spring.rabbitmq.port=5672 spring.rabbitmq.username=guest spring.rabbitmq.password=guest
feign調用
@Autowired private EchoService echoService; @GetMapping(value = "/feignEcho/{str}") public String feignEcho(@PathVariable String str) { return echoService.echo(str); }
測試http://localhost:8090/feignEcho/
發現zipkin進行了數據采集
鏈路過程,首先訪問springboot2-nacos-consumer的feignEcho,然后訪問springboot2-nacos-discovery的echo。
spring.sleuth.sampler.percentage參數配置(如果不配置默認0.1),如果我們調大此值為1,可以看到信息收集就更及時。但是當這樣調整后,我們會發現我們的rest接口調用速度比0.1的情況下慢了很多,即使在0.1的采樣率下,我們多次刷新consumer的接口,會發現對同一個請求兩次耗時信息相差非常大。
具體的鏈路
下載zipkin的json文件
[{"traceId":"8da56c7f067fbe9f", "parentId":"8da56c7f067fbe9f", "id":"e55168fadce7489a", "kind":"CLIENT", "name":"get", "timestamp":1592563870357910, "duration":2815, "localEndpoint":{"serviceName":"springboot2-nacos-consumer","ipv4":"192.168.10.6"}, "tags":{"http.method":"GET","http.path":"/echo/22阿薩德發asdf"}}, {"traceId":"8da56c7f067fbe9f", "id":"8da56c7f067fbe9f", "kind":"SERVER", "name":"get /feignecho/{str}", "timestamp":1592563870352265, "duration":9858, "localEndpoint":{"serviceName":"springboot2-nacos-consumer","ipv4":"192.168.10.6"}, "remoteEndpoint":{"ipv6":"::1","port":63372}, "tags":{"http.method":"GET","http.path":"/feignEcho/22%E9%98%BF%E8%90%A8%E5%BE%B7%E5%8F%91asdf","mvc.controller.class":"NacosConsumerController","mvc.controller.method":"feignEcho"}}, {"traceId":"8da56c7f067fbe9f", "parentId":"8da56c7f067fbe9f", "id":"e55168fadce7489a", "kind":"SERVER", "name":"get /echo/{string}", "timestamp":1592563870359087, "duration":1641, "localEndpoint":{"serviceName":"springboot2-nacos-discovery","ipv4":"192.168.10.6"}, "remoteEndpoint":{"ipv4":"192.168.10.6","port":63374}, "tags":{"http.method":"GET","http.path":"/echo/22%E9%98%BF%E8%90%A8%E5%BE%B7%E5%8F%91asdf","mvc.controller.class":"NacosProviderController","mvc.controller.method":"echo"},"shared":true}]
源碼