当开发者进行微服务架构开发时,通常会根据业务来划分微服务,各业务之间通过REST进行调用。对于一个用户的请求,往往需要多个微服务协同才能完成处理并形成最后结果返回给用户。在这个过程中,用户请求所经过的每一个微服务都会形成一条复杂的、分布式的服务调用链路,链路中的任何一环出现问题或者网络超时,都会导致用户请求的失败。
而当这种失败的情况出现时,追踪问题便变得复杂,正是存在这样的问题,Spring Cloud推出了Sleuth组件。
一、Sleuth简介
Spring Cloud Sleuth为微服务之间调用提供了一套完整的服务链路跟踪解决方案。通过Sleuth可以很清楚地了解到一个用户请求经过了哪些微服务,每个微服务处理花费了多长时间,从而让开发者可以方便地理清各微服务间的调用关系。并且Sleuth默认集成Zipkin,提供了可视化的服务调用图形界面,对于程序未捕获的异常,也可以展示在上面。
在Spring Cloud微服务的架构中,微服务之间是通过HTTP协议(REST)方式进行通信的,所以Spring Cloud Sleuth在实现时也是基于HTTP的,通过在HTTP中的header(头部)添加跟踪所需要的信息,使得在不影响现有业务的基础上完成对服务请求的追踪。Sleuth的实现原理可以总结如下:
·服务追踪:对于同一个用户请求,认为是同一条链路,并赋值一个相同的TraceID,在后续中通过该标识就可以在多个微服务之间找到完整的处理链路。
·服务监控:对于链路上的每一个微服务处理,Sleuth会再生成一个独立的SpanID,同时记录请求到达时间和离开时间等信息,以作为用户请求追踪的依据,从而判断每一个微服务的处理效率。
二、Sleuth术语
下面介绍一下Sleuth所涉及的一些主要术语。Spring Cloud Sleuth在设计的时候参考了Google的Dapper's论文,所以在术语上也对该论文进行了借鉴。
Span是Sleuth中最基本的工作单元。微服务发起一次请求就是一个新Span。Span使用唯一的、长度为64位的ID作为标识。在Span中可以带有其他数据,如描述、时间戳、键值对、起始Span的ID等数据。Span有起始和结束,可以用于跟踪服务处理时间信息。Span一般都是成对出现,因为有始必有终,所以一旦创建了一个Span,就必须在未来某个时间点结束它。
Trace是一次用户请求所涉及的所有Span的集合,采用树形结构进行管理。
Annotation主要用于记录时间信息。它包含cs、sr、ss与cr。cs是指客户端发送(Client Sent),表示一个Span的起始点。sr是服务端接收(Server Received),表示服务端接收到请求并开始处理。如果减去cs的时间戳,则可以计算出网络传输耗时。 ss是指服务端完成请求处理,应答信息被发回客户端(Server Sent)。通过减去sr的时间戳,可以计算出服务端处理请求的耗时。 而cr则表示客户端接收(Client Received),标志着一个Span生命周期的结束,客户端成功地接收到服务端的应答信息。如果减去cs的时间戳,则可以计算出整个请求的响应耗时。
三、示例演示
继续沿用之前的项目代码。集成Sleuth需要依赖于Zipkin Server,当Spring Cloud为F版本的时候,已经不需要我们自己构建Zipkin Server了,只需要下载jar即可,下载地址:
https://dl.bintray.com/openzipkin/maven/io/zipkin/java/zipkin-server/
下载完成jar 包之后,执行java -jar zipkin-server-2.10.1-exec.jar运行即可,启动后默认端口9411。
成功启动后可以在浏览器输入http://localhost:9411进行访问,出现Zipkin Server则表示成功。
然后对我们之前的book-server与user-server进行改造,集成Spring Cloud Sleuth。两个服务的修改是一致的,所以这里我以其中一个book-server为例。
首先在pom文件中引入Sleuth的依赖spring-cloud-starter-zipkin。
使用Sleuth不需要在启动类上手动声明,但需要提供一个Sampler的bean对象,这里为了简单,直接声明在了核心启动类中。
然后接下来去修改配置文件,在spring节点下,配置上Zipkin Server的地址即可。
然后依次启动需要的工程,eureka-server、config-server,book-server、user-server。
工程启动成功后,然后我们访http://localhost:10820/swagger-ui.html,调用/user/find/feign/{id}请求,这个请求当时是测试feign组件提供的。
请求成功调用,然后我们再点开Zipkin Server,点击Find Traces,这时候可以看到已经展示了我们刚刚调用的请求。
正当我欢欣鼓舞的时候,诶,似乎有点点不对劲,依赖分析中内容为空,怎么都刷新不出来,然后再仔细一看,刚刚的请求调用中也只有user-server的信息,而没有book-server的信息。然后我主动去调用了book-server的请求,发现在Zipkin Server中依旧没有加载出book-server请求调用信息,甚至服务列表都没有book-server。
在一番探究后,发现spring-cloud-starter-zipkin与spring-cloud-starter-bus-amqp同时使用存在冲突,因为zipkin与服务间通信默认使用的是 http的方式,而在引入spring-rabbit后,zipkin与服务间通信会变成使用rabbitmq,所以Zipkin Server便再无法正确收集到请求调用信息。
解决办法,可以在配置文件中添加spring.zipkin.sender.type: web,声明使用http通信,或者通信方式改为使用rabbitmq。
这里我添加了spring.zipkin.sender.type,然后再次调用请求,调用请求后点击依赖分析,可以看到服务间依赖关系已经可以展示出来,而且请求中也显示了user-server与book-server具体的请求调用信息。
本意外到此就大功告成圆满结束了,谁知道不是不知道,一试吓一跳,这样配置后直接导致bus在加载config时报错,config服务无法读取配置文件,坑啊,于是继续摸索。终于翻阅到,除了配置spring.zipkin.sender.type外,还需要配置spring.sleuth.web.client.enabled:true,配置之后,报错解决,完美启动。
源码地址: https://github.com/imyanger/springcloud-project/tree/master/p8-sleuth