在微服務(wù)架構(gòu)中,眾多的微服務(wù)之間互相調(diào)用蜂怎,如何清晰地記錄服務(wù)的調(diào)用鏈路是一個(gè)需要解決的問(wèn)題囊嘉。同時(shí),由于各種原因识椰,跨進(jìn)程的服務(wù)調(diào)用失敗時(shí)猴抹,運(yùn)維人員希望能夠通過(guò)查看日志和查看服務(wù)之間的調(diào)用關(guān)系來(lái)定位問(wèn)題整陌,而
Spring cloud sleuth
組件正是為了解決微服務(wù)跟蹤的組件划纽。
sleuth的原理介紹可以參考這篇文章: 服務(wù)鏈路追蹤(Spring Cloud Sleuth)
本文主要講解sleuth的兩方面用法
- sleuth+elk 結(jié)合脆侮,聚合微服務(wù)日志
- sleuth+ zipkin結(jié)合,顯示文件調(diào)用鏈路
本文代碼參考hello+world+helloworldh+helloworldfeign+track這5個(gè)項(xiàng)目
sleuth+elk聚合日志
sleuth配置
- 在微服務(wù)項(xiàng)目引入下列依賴(lài)
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
- 配置文件
application.yml
增加
logging:
level:
root: INFO
org.springframework.web.servlet.DispatcherServlet: DEBUG
org.springframework.cloud.sleuth: DEBUG
- 依次啟動(dòng)
hello項(xiàng)目
和helloworld項(xiàng)目
勇劣,在瀏覽器輸入http://localhost:8020/message
后兩個(gè)項(xiàng)目的控制臺(tái)輸出如下日志
其中a7c81616d25c1a88
是TraceId靖避,后面跟著的是SpanId,依次調(diào)用有一個(gè)全局的TraceId比默,將調(diào)用鏈路串起來(lái)幻捏。
查看日志文件并不是一個(gè)很好的方法,當(dāng)微服務(wù)越來(lái)越多日志文件也會(huì)越來(lái)越多退敦,通過(guò)ELK可以將日志聚合粘咖,并進(jìn)行可視化展示和全文檢索蚣抗。
sleuth配合elk使用
ELK是一款日志分析系統(tǒng)侈百,它是Logstash
+ElasticSearch
+Kibana
的技術(shù)組合,它可以通過(guò)logstash收集各個(gè)微服務(wù)日志翰铡,并通過(guò)Kibana進(jìn)行可視化展示钝域,而且還可以對(duì)大量日志信息通過(guò)ElasticSearch進(jìn)行全文檢索。
操作步驟:
- 安裝ELK锭魔,我使用了docker安裝了ELK例证,具體安裝步驟可參考這篇文章:
Docker ElK安裝部署使用教程
區(qū)別是在啟動(dòng)logstash時(shí),指定了日志來(lái)源路徑
/opt/logstash/bin/logstash -e
'input { file { codec => json path => "/opt/build/*.json" } }
output { elasticsearch { hosts => ["localhost"] } }'
- 項(xiàng)目添加依賴(lài)
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>4.9</version>
</dependency>
- 在src/java/resources目錄下新建logback-spring.xml,配置如下內(nèi)容
<?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"/>
<property name="LOG_FILE"
value="${BUILD_FOLDER:-build}/${springAppName}"/>
<property name="CONSOLE_LOG_PATTERN"
value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p})
%clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan}
%clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
<!-- 控制臺(tái)輸出 -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>DEBUG</level>
</filter>
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
</encoder>
</appender>
<!-- 按照每天生成日志文件 -->
<appender name="filelog" 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>
<!--日志文件保留天數(shù)-->
<MaxHistory>7</MaxHistory>
</rollingPolicy>
<encoder>
<!--格式化輸出:%d表示日期迷捧,%thread表示線(xiàn)程名织咧,%-5level:級(jí)別從左顯示5個(gè)字符寬度%msg:日志消息胀葱,%n是換行符-->
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
</encoder>
<!--日志文件最大的大小-->
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<MaxFileSize>10MB</MaxFileSize>
</triggeringPolicy>
</appender>
<!-- 使用json格式保存日志文件 -->
<appender name="jsonlog" 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>
<!--日志文件保留天數(shù)-->
<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>
<!--日志文件最大的大小-->
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<MaxFileSize>10MB</MaxFileSize>
</triggeringPolicy>
</appender>
<root level="INFO">
<appender-ref ref="console"/>
<appender-ref ref="jsonlog"/>
<appender-ref ref="filelog"/>
</root>
</configuration>
因?yàn)樯厦娴娜罩疚募玫絪pring.application.name,所以需要項(xiàng)目名稱(chēng)的配置挪到bootstrap.yml笙蒙。
- 測(cè)試結(jié)果抵屿,在瀏覽器輸入
http://localhost:8020/message
發(fā)起幾次調(diào)用后,打開(kāi)http://localhost:5601后
看到上述的Kibana頁(yè)面捅位,說(shuō)明可以正常使用ELK查詢(xún)轧葛,分析跟蹤日志。
sleuth 結(jié)合zipkin
通過(guò)查看日志分析微服務(wù)的調(diào)用鏈路并不是一個(gè)很直觀(guān)的方案艇搀,結(jié)合zipkin可以很直觀(guān)地顯示微服務(wù)之間的調(diào)用關(guān)系尿扯。
通過(guò)zipkin可以將調(diào)用鏈路可視化顯示。
下面講解配置步驟:
服務(wù)端zipkin-server配置
- 新建項(xiàng)目
track
焰雕,并引入依賴(lài)
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-server</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-autoconfigure-ui</artifactId>
</dependency>
- 啟動(dòng)類(lèi)添加
@EnableDiscoveryClient
和@EnableZipkinServer
注解 - 配置文件
application.yml
spring:
application:
name: sleuth-server
server:
port: 9411
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
instance:
instanceId: ${spring.application.name}:${spring.cloud.client.ipAddress}:${server.port}
以上服務(wù)端就搭建好了
客戶(hù)端整合zipkin步驟
- 客戶(hù)端添加依賴(lài)
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
- 配置文件添加
spring:
zipkin:
base-url: http://127.0.0.1:9411
sleuth:
sampler:
percentage: 1.0
指定了zipkin server的地址衷笋,下面制定需采樣的百分比,默認(rèn)為0.1淀散,即10%右莱,這里配置1,是記錄全部的sleuth信息档插,是為了收集到更多的數(shù)據(jù)(僅供測(cè)試用)慢蜓。在分布式系統(tǒng)中,過(guò)于頻繁的采樣會(huì)影響系統(tǒng)性能郭膛,所以這里配置需要采用一個(gè)合適的值晨抡。
zipkin改進(jìn)
在這里對(duì)zipkin進(jìn)行改進(jìn),主要包含兩方面
- 通過(guò)消息中間件收集sleuth數(shù)據(jù)
- 持久化sleuth數(shù)據(jù)
1则剃、通過(guò)消息中間件收集sleuth數(shù)據(jù)
通過(guò)消息中間件可以將zipkin server和微服務(wù)解耦耘柱,微服務(wù)無(wú)需知道zipkin server地址,只需將sleuth數(shù)據(jù)傳入消息中間件棍现。同時(shí)调煎,也可以解決zipkin server與微服務(wù)網(wǎng)絡(luò)不通情況。
改造服務(wù)端
- 修改zipkin server(trace項(xiàng)目)配置
<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-server</artifactId>
</dependency>-->
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-autoconfigure-ui</artifactId>
</dependency>
- 配置文件application.yml增加
rabbitmq:
host: localhost
port: 5673
username: guest
password: guest
這是sleuth數(shù)據(jù)來(lái)源
- 啟動(dòng)類(lèi)
@EnableZipkinServer
改為@EnableZipkinStreamServe
以上服務(wù)端改造完畢己肮,下面改造客戶(hù)端(以helloworld-feign項(xiàng)目為例
)
改造客戶(hù)端
以helloworldfeign
項(xiàng)目為例
- 修改依賴(lài)
<!-- 使用消息中間件后士袄,不再直接和zipkin server-->
<!-- <dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-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>
- 配置文件增加
spring:
rabbitmq:
host: localhost
port: 5673
username: guest
password: guest
并刪除下面指向zipkin server的配置
spring:
zipkin:
base-url: http://127.0.0.1:9411
這樣就完成了客戶(hù)端的配置,依次啟動(dòng)track
和helloworldfeign
項(xiàng)目谎僻,通過(guò)http://localhost:8030/message
調(diào)用其他服務(wù)后娄柳,在zipkin server 成功獲取了helloworldfeign
的sleuth數(shù)據(jù)。
在以上的過(guò)程中艘绍,只要重啟zipkin server,發(fā)現(xiàn)之前的數(shù)據(jù)丟失赤拒。這是因?yàn)閦ipkin server獲取的數(shù)據(jù)是放在內(nèi)存的,我們可以獲取的服務(wù)追蹤數(shù)據(jù)放入到ElasticSearch
2、持久化sleuth數(shù)據(jù)
- 修改依賴(lài)
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-zipkin-stream</artifactId>
</dependency>
<!-- 持久化到ElasticSearch-->
<!--<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>-->
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-autoconfigure-storage-elasticsearch-http</artifactId>
<version>1.29.2</version>
</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>
- 配置文件增加
zipkin:
storage:
type: elasticsearch
elasticsearch:
cluster: elasticsearch
hosts: http://localhost:9200
index: zipkin
index-shards: 5
index-replicas: 1
再次啟動(dòng)track項(xiàng)目后挎挖,可以將數(shù)據(jù)持久化到elasticsearch这敬。