GitHub地址:https://github.com/leebingbin/SpringCloud.MovieTicketinghtml
監控微服務的方式有不少,例如使用 Spring Boot Actuator 監控微服務實例,使用 Hystrix 監控 Hystrix Command 等。談到微服務追蹤,就不得不提一下 Peter Deutsch 的文章 The Eight Fallacies of Distributed Computing ( 分佈式計算的八大誤區 ) , 內容大體以下:java
· 網絡可靠git
· 延遲爲零github
· 帶寬無限spring
· 網絡絕對安全docker
· 網絡拓撲不會改變json
· 必須有一名管理員後端
· 傳輸成本爲零安全
· 網絡同質化 (由 Java 之父 Golsing 補充) bash
不難能夠看出,該文章不少點都在描述一個問題 —— 網絡問題。網絡經常很脆弱,同時, 網絡資源也是有限的。
微服務之間經過網絡進行通訊。若是可以追蹤每一個請求,瞭解請求通過那些微服務 (從而瞭解信息是如何在服務之間流動),請求耗費時間、網絡延遲、業務邏輯耗費時間等指標,那麼就能更好地分析系統瓶頸、解決系統問題。所以,微服務追蹤頗有必要。
Spring Cloud Sleuth 爲 Spring Cloud 提供了分佈式追蹤的解決方案,它大量借用了 Google Dapper 、Twitter Zipkin 和 Apache HTrace 的設計。
Sleuth 的術語:
· span(跨度):基本工做單元。span 用一個 64 位的 id 惟一標識。除了ID外,span 還包含其餘數據,例如描述、時間戳事件、鍵值對的註解(標籤),spanID、span父ID等。
· trace(追蹤):一組共享「root span」 的 span 組成的樹狀結構稱爲 trace 。trace 也用一個 64 位的 ID 惟一標識,trace 中的全部 span 都共享該 trace 的ID 。
· annotation(標註): annotation 用來記錄事件的存在,其中,核心 annotation 用來定義請求的開始和結束。
- CS ( Client Sent 客戶端發送 ):客戶端發起一個請求,該 annotation 描述了 span 的開始。
- SR ( Server Received 服務器端接收 ): 服務器端得到請求並準備處理它。若是用 SR 減去 CS 時間戳,就能獲得網絡延遲。
- SS ( Server Sent 服務器端發送 ):該 annotation 代表完成請求處理( 當響應發回客戶端時 )。若是用 SS 減去 SR 時間戳,就能獲得服務器端處理請求所需時間。
- CR( Client Received 客戶端接收):span 結束標識。客戶端成功接收到服務器端的響應。若是 CR 減去 CS 時間戳,就能獲得從客戶端發送請求到服務器響應的所需的時間。
# spring-cloud-starter-sleuth
# spring-cloud-starter-sleuth <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-sleuth --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-sleuth</artifactId> <version>1.3.1.RELEASE</version> </dependency>
# logback
# logback <!-- https://mvnrepository.com/artifact/net.logstash.logback/logstash-logback-encoder --> <dependency> <groupId>net.logstash.logback</groupId> <artifactId>logstash-logback-encoder</artifactId> <version>4.11</version> </dependency>
<?xml version="1.0" encoding="UTF-8"?> <configuration> <include resource="org/springframework/boot/logging/logback/defaults.xml" /> <springProperty scope="context" name="springAppName" source="spring.application.name" /> <!-- Example for logging into the build folder of your project --> <property name="LOG_FILE" value="${BUILD_FOLDER:-build}/${springAppName}" /> <property name="CONSOLE_LOG_PATTERN" value="%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr([${springAppName:-},%X{X-B3-TraceId:-},%X{X-B3-SpanId:-},%X{X-B3-ParentSpanId:-},%X{X-Span-Export:-}]){yellow} %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}" /> <!-- Appender to log to console --> <appender name="console" class="ch.qos.logback.core.ConsoleAppender"> <filter class="ch.qos.logback.classic.filter.ThresholdFilter"> <!-- Minimum logging level to be presented in the console logs --> <level>DEBUG</level> </filter> <encoder> <pattern>${CONSOLE_LOG_PATTERN}</pattern> <charset>utf8</charset> </encoder> </appender> <!-- Appender to log to file --> <appender name="flatfile" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${LOG_FILE}</file> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${LOG_FILE}.%d{yyyy-MM-dd}.gz</fileNamePattern> <maxHistory>7</maxHistory> </rollingPolicy> <encoder> <pattern>${CONSOLE_LOG_PATTERN}</pattern> <charset>utf8</charset> </encoder> </appender> <!-- Appender to log to file in a JSON format --> <appender name="logstash" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${LOG_FILE}.json</file> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${LOG_FILE}.json.%d{yyyy-MM-dd}.gz</fileNamePattern> <maxHistory>7</maxHistory> </rollingPolicy> <encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder"> <providers> <timestamp> <timeZone>UTC</timeZone> </timestamp> <pattern> <pattern> { "severity": "%level", "service": "${springAppName:-}", "trace": "%X{X-B3-TraceId:-}", "span": "%X{X-B3-SpanId:-}", "parent": "%X{X-B3-ParentSpanId:-}", "exportable": "%X{X-Span-Export:-}", "pid": "${PID:-}", "thread": "%thread", "class": "%logger{40}", "rest": "%message" } </pattern> </pattern> </providers> </encoder> </appender> <root level="INFO"> <appender-ref ref="console" /> <appender-ref ref="logstash" /> <!--<appender-ref ref="flatfile"/> --> </root> </configuration>
ELK 的搭建比較簡單,參考官方文檔(或個人博客)便可:
http://www.elastic.co/guide/index.html
Docker 搭建 ELK:
http://elk-docker.readthedocs.io/
使用 logstash.conf 啓動 Logstash 。Logstash是一款輕量級的日誌蒐集處理框架,能夠方便的把分散的、多樣化的日誌蒐集起來,並進行自定義的處理,而後傳輸到指定的位置,好比某個服務器或者文件。
官方參考:https://www.elastic.co/products/logstash
# logstash.conf input { file { codec => json path => "/opt/build/*.json" # 改爲你項目打印的json日誌文件 } } filter { grok { math => { "message" => "%{TIMESTAMP_ISO08601:timestamp}\s+%{LOGLEVEL: severity}\s+\[%{DATA:service},%{DATA:trace},%{DATA:span},%{DATA:exportable}\]\s+%{DATA:pid}---\s+ \[%{DATA:thread}\]\s+%{DATA:class}\s+:\s+%{GREEDYDATA:rest}" } } } output { elasticsearch { hosts => "elasticsearch:9222" # 改爲你的 Elasticsearch 地址 } }
Zipkin 是 Twitter 開源的分佈式跟蹤系統,基於 Dapper 的論文設計而來。它的主要功能是收集系統的時序數據,從而追蹤微服務架構的系統延時等問題。Zipkin 還提供了一個很是友好的界面,來幫助分析追蹤數據。
Zipkin 官方網站:https://zipkin.io/
<dependencies> <dependency> <groupId>io.zipkin.java</groupId> <artifactId>zipkin-autoconfigure-ui</artifactId> </dependency> <dependency> <groupId>io.zipkin.java</groupId> <artifactId>zipkin-server</artifactId> </dependency> </dependencies>
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import zipkin.server.EnableZipkinServer; @SpringBootApplication @EnableZipkinServer public class ZipkinServerApplication { public static void main(String[] args) { SpringApplication.run(ZipkinServerApplication.class, args); } }
上面介紹的使用 HTTP 直接收集跟蹤數據。相比 HTTP 的方式來講,使用消息中間件有如下優勢:
· 微服務與 Zipkin Server 解耦,微服務無須知道 Zipkin Server 的網絡地址。
· 一些場景下,Zipkin Server 與微服務網絡可能不通,使用 HTTP 直接收集的方式沒法工做,此時可藉助消息中間件實現數據收集
首先,確定要安裝 RabbitMQ 做爲消息中間件進行演示,RabbitMQ 的安裝能夠參考個人博客或者官方文檔。
<dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-sleuth-zipkin-stream</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-sleuth</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-stream-binder-rabbit</artifactId> </dependency> <dependency> <groupId>io.zipkin.java</groupId> <artifactId>zipkin-autoconfigure-ui</artifactId> </dependency> </dependencies>
修改啓動類,將註解 @EnableZipkinServer 修改成 @EnableZipkinStreamServer 。而後修改配置文件 application.yml ,添加 rabbitmq 配置。
spring: rabbitmq: host: localhost port: 6666 username: guest password: guest
按照上面的配置,Zipkin Server 是將數據存儲在內存中。這種方式通常不適用於生產環境,由於一旦 Zipkin Server 重啓或發生崩潰,就會致使歷史數據的丟失。
Zipkin Server 支持多種後端存儲,如 MySQL, Elasticsearch, Cassanda 等。在這塊以如何將數據存儲在 Elasticsearch 中,讓其使用 RabbitMQ 收集跟蹤數據並使用 Elasticsearch 做爲後端存儲。
<dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-sleuth-zipkin-stream</artifactId> </dependency> <dependency> <groupId>io.zipkin.java</groupId> <artifactId>zipkin-autoconfigure-ui</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-stream-binder-rabbit</artifactId> </dependency> <dependency> <groupId>io.zipkin.java</groupId> <artifactId>zipkin-autoconfigure-storage-elasticsearch-http</artifactId> <version>1.16.2</version> </dependency> </dependencies>
zipkin: storage: type: elasticsearch elasticsearch: cluster: elasticsearch hosts: http://localhost:9200 index: zipkin index-shards: 5 index-replicas: 1
Elasticsearch 官網:https://www.elastic.co/cn/
參考資料:
· 分佈式計算的八大誤區原文:https://blogs.oracle.com/jag/resource/Fallacies.html
· Zipkin 官方網站:https://zipkin.io/
· 消息隊列-ActiveMQ:http://www.javashuo.com/article/p-swnnoefi-nb.html
· RabbitMQ 官網 : https://www.rabbitmq.com/
· Elasticsearch 官方文檔:https://www.elastic.co/guide/cn/elasticsearch/guide/current/index.html
· Elasticsearch 官網:https://www.elastic.co/cn/
· ELK 官方文檔:https://www.elastic.co/guide/index.html
本文爲博主原創文章,轉載請註明出處!