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
本文为博主原创文章,转载请注明出处!