Zipkin是什麼
Zipkin分佈式跟蹤系統;它能夠幫助收集時間數據,解決在microservice架構下的延遲問題;它管理這些數據的收集和查找;Zipkin的設計是基於谷歌的Google Dapper論文。
每一個應用程序向Zipkin報告定時數據,Zipkin UI呈現了一個依賴圖表來展現多少跟蹤請求通過了每一個應用程序;若是想解決延遲問題,能夠過濾或者排序全部的跟蹤請求,而且能夠查看每一個跟蹤請求佔總跟蹤時間的百分比。html
爲何使用Zipkin
隨着業務愈來愈複雜,系統也隨之進行各類拆分,特別是隨着微服務架構和容器技術的興起,看似簡單的一個應用,後臺可能有幾十個甚至幾百個服務在支撐;一個前端的請求可能須要屢次的服務調用最後才能完成;當請求變慢或者不可用時,咱們沒法得知是哪一個後臺服務引發的,這時就須要解決如何快速定位服務故障點,Zipkin分佈式跟蹤系統就能很好的解決這樣的問題。前端
Zipkin下載和啓動
官方提供了三種方式來啓動,這裏使用第二種方式來啓動;java
wget -O zipkin.jar 'https://search.maven.org/remote_content?g=io.zipkin.java&a=zipkin-server&v=LATEST&c=exec' java -jar zipkin.jar
首先下載zipkin.jar,而後直接使用-jar命令運行,要求jdk8以上版本;git
[root@localhost ~]# java -jar zipkin.jar ******** ** ** * * ** ** ** ** ** ** ** ** ******** **** **** **** **** ****** **** *** **************************************************************************** ******* **** *** **** **** ** ** ***** ** ***** ** ** ** ** ** ** ** ** * *** ** **** ** ** ** ***** **** ** ** *** ****** ** ** ** ** ** ** ** :: Powered by Spring Boot :: (v1.5.8.RELEASE) ...... on || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.HealthMvcEndpoint.invoke(javax.servlet.http.HttpServletRequest,java.security.Principal) 2017-12-06 22:09:17.498 INFO 7555 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup 2017-12-06 22:09:17.505 INFO 7555 --- [ main] o.s.c.support.DefaultLifecycleProcessor : Starting beans in phase 0 2017-12-06 22:09:17.789 INFO 7555 --- [ main] b.c.e.u.UndertowEmbeddedServletContainer : Undertow started on port(s) 9411 (http) 2017-12-06 22:09:17.794 INFO 7555 --- [ main] zipkin.server.ZipkinServer : Started ZipkinServer in 16.867 seconds (JVM running for 19.199)
基於Undertow WEB服務器,提供對外端口:9411,能夠打開瀏覽器訪問http://ip:9411github
詳細參考:https://zipkin.io/pages/quick...web
Zipkin架構
跟蹤器(Tracer)位於你的應用程序中,並記錄發生的操做的時間和元數據,提供了相應的類庫,對用戶的使用來講是透明的,收集的跟蹤數據稱爲Span;
將數據發送到Zipkin的儀器化應用程序中的組件稱爲Reporter,Reporter經過幾種傳輸方式之一將追蹤數據發送到Zipkin收集器(collector),
而後將跟蹤數據進行存儲(storage),由API查詢存儲以向UI提供數據。
架構圖以下:spring
1.Trace
Zipkin使用Trace結構表示對一次請求的跟蹤,一次請求可能由後臺的若干服務負責處理,每一個服務的處理是一個Span,Span之間有依賴關係,Trace就是樹結構的Span集合;apache
2.Span
每一個服務的處理跟蹤是一個Span,能夠理解爲一個基本的工做單元,包含了一些描述信息:id,parentId,name,timestamp,duration,annotations等,例如:json
{ "traceId": "bd7a977555f6b982", "name": "get-traces", "id": "ebf33e1a81dc6f71", "parentId": "bd7a977555f6b982", "timestamp": 1458702548478000, "duration": 354374, "annotations": [ { "endpoint": { "serviceName": "zipkin-query", "ipv4": "192.168.1.2", "port": 9411 }, "timestamp": 1458702548786000, "value": "cs" } ], "binaryAnnotations": [ { "key": "lc", "value": "JDBCSpanStore", "endpoint": { "serviceName": "zipkin-query", "ipv4": "192.168.1.2", "port": 9411 } } ] }
traceId:標記一次請求的跟蹤,相關的Spans都有相同的traceId;
id:span id;
name:span的名稱,通常是接口方法的名稱;
parentId:可選的id,當前Span的父Span id,經過parentId來保證Span之間的依賴關係,若是沒有parentId,表示當前Span爲根Span;
timestamp:Span建立時的時間戳,使用的單位是微秒(而不是毫秒),全部時間戳都有錯誤,包括主機之間的時鐘誤差以及時間服務從新設置時鐘的可能性,
出於這個緣由,Span應儘量記錄其duration;
duration:持續時間使用的單位是微秒(而不是毫秒);
annotations:註釋用於及時記錄事件;有一組核心註釋用於定義RPC請求的開始和結束;瀏覽器
cs:Client Send,客戶端發起請求; sr:Server Receive,服務器接受請求,開始處理; ss:Server Send,服務器完成處理,給客戶端應答; cr:Client Receive,客戶端接受應答從服務器;
binaryAnnotations:二進制註釋,旨在提供有關RPC的額外信息。
3.Transport
收集的Spans必須從被追蹤的服務運輸到Zipkin collector,有三個主要的傳輸方式:HTTP, Kafka和Scribe;
4.Components
有4個組件組成Zipkin:collector,storage,search,web UI
collector:一旦跟蹤數據到達Zipkin collector守護進程,它將被驗證,存儲和索引,以供Zipkin收集器查找;
storage:Zipkin最初數據存儲在Cassandra上,由於Cassandra是可擴展的,具備靈活的模式,並在Twitter中大量使用;可是這個組件可插入,除了Cassandra以外,還支持ElasticSearch和MySQL;
search:一旦數據被存儲和索引,咱們須要一種方法來提取它。查詢守護進程提供了一個簡單的JSON API來查找和檢索跟蹤,主要給Web UI使用;
web UI:建立了一個GUI,爲查看痕跡提供了一個很好的界面;Web UI提供了一種基於服務,時間和註釋查看跟蹤的方法。
實戰
使用Zipkin和Brave實現http服務調用的跟蹤,Brave 是用來裝備Java程序的類庫,提供了面向標準Servlet、Spring MVC、Http Client、JAX RS、Jersey、Resteasy 和 MySQL 等接口的裝備能力,能夠經過編寫簡單的配置和代碼,讓基於這些框架構建的應用能夠向 Zipkin 報告數據。同時 Brave 也提供了很是簡單且標準化的接口,在以上封裝沒法知足要求的時候能夠方便擴展與定製。
提供四個工程,分別對應四個服務分別是:zipkin1,zipkin2,zipkin3,zipkin4;zipkin1經過httpclient調用zipkin2,而後zipkin2經過httpclient調用zipkin3和zipkin4,造成一個調用鏈;四個服務都是基於spring-boot來實現,對應的端口分別是8081,8082,8083,8084;
1.公共maven依賴庫
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>io.zipkin.brave</groupId> <artifactId>brave-core</artifactId> <version>3.9.0</version> </dependency> <dependency> <groupId>io.zipkin.brave</groupId> <artifactId>brave-spancollector-http</artifactId> <version>3.9.0</version> </dependency> <dependency> <groupId>io.zipkin.brave</groupId> <artifactId>brave-web-servlet-filter</artifactId> <version>3.9.0</version> </dependency> <dependency> <groupId>io.zipkin.brave</groupId> <artifactId>brave-apache-http-interceptors</artifactId> <version>3.9.0</version> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> </dependency> </dependencies>
2.核心類ZipkinBean提供須要使用的Bean
import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import com.github.kristofa.brave.Brave; import com.github.kristofa.brave.Brave.Builder; import com.github.kristofa.brave.EmptySpanCollectorMetricsHandler; import com.github.kristofa.brave.Sampler; import com.github.kristofa.brave.SpanCollector; import com.github.kristofa.brave.http.DefaultSpanNameProvider; import com.github.kristofa.brave.http.HttpSpanCollector; import com.github.kristofa.brave.http.HttpSpanCollector.Config; import com.github.kristofa.brave.httpclient.BraveHttpRequestInterceptor; import com.github.kristofa.brave.httpclient.BraveHttpResponseInterceptor; import com.github.kristofa.brave.servlet.BraveServletFilter; @Configuration public class ZipkinBean { /** * 配置收集器 * * @return */ @Bean public SpanCollector spanCollector() { Config config = HttpSpanCollector.Config.builder().compressionEnabled(false).connectTimeout(5000) .flushInterval(1).readTimeout(6000).build(); return HttpSpanCollector.create("http://192.168.237.128:9411", config, new EmptySpanCollectorMetricsHandler()); } /** * Brave各工具類的封裝 * * @param spanCollector * @return */ @Bean public Brave brave(SpanCollector spanCollector) { Builder builder = new Builder("service1");// 指定serviceName builder.spanCollector(spanCollector); builder.traceSampler(Sampler.create(1));// 採集率 return builder.build(); } /** * 攔截器,須要serverRequestInterceptor,serverResponseInterceptor 分別完成sr和ss操做 * * @param brave * @return */ @Bean public BraveServletFilter braveServletFilter(Brave brave) { return new BraveServletFilter(brave.serverRequestInterceptor(), brave.serverResponseInterceptor(), new DefaultSpanNameProvider()); } /** * httpClient客戶端,須要clientRequestInterceptor,clientResponseInterceptor分別完成cs和cr操做 * * @param brave * @return */ @Bean public CloseableHttpClient httpClient(Brave brave) { CloseableHttpClient httpclient = HttpClients.custom() .addInterceptorFirst(new BraveHttpRequestInterceptor(brave.clientRequestInterceptor(), new DefaultSpanNameProvider())) .addInterceptorFirst(new BraveHttpResponseInterceptor(brave.clientResponseInterceptor())).build(); return httpclient; } }
3.核心類ZipkinController對外接口
@RestController public class ZipkinController { @Autowired private CloseableHttpClient httpClient; @GetMapping("/service1") public String service() throws Exception { Thread.sleep(100); HttpGet get = new HttpGet("http://localhost:8082/service2"); CloseableHttpResponse response = httpClient.execute(get); return EntityUtils.toString(response.getEntity(), "utf-8"); } }
分別啓動四個服務,而後瀏覽器訪問:http://localhost:8081/service1,正常調用結果返回:
service3 service4
能夠觀察zipkin web ui,查看服務的調用鏈: