spring cloud sleuth
- 用于生成关联ID(与具体的请求有关)并装备到HTTP调用上,并将生成数据提供给OpenZipkin的钩子;
- 关联ID:随机生产、唯一的数字或字符串,在事务启用时分配给事务,当事务流经多个服务时,关联ID会从一个服务传递到另一个服务;
- spring cloud sleuth的每个日志条目:应用程序名称(默认为spring.application.name),关联ID,跨度ID(表示整个事务中某一部分的唯一ID,参与事务的每个服务都有自己的跨度ID),是否将数据发送到Zipkin;
- 依赖包:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-sleuth</artifactId> </dependency>
-
使用Zuul将关联ID添加到HTTP响应:
-
将spring cloud sleuth依赖导入Zuul项目中;
-
通过添加zuul后置过滤器,将关联ID添加到HTTP响应中;
package com.thoughtmechanix.zuulsvr.filters; import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.context.RequestContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.sleuth.Tracer; import org.springframework.stereotype.Component; @Component public class ResponseFilter extends ZuulFilter{ private static final int FILTER_ORDER=1; private static final boolean SHOULD_FILTER=true; private static final Logger logger = LoggerFactory.getLogger(ResponseFilter.class); @Autowired Tracer tracer; @Override public String filterType() { return "post"; } @Override public int filterOrder() { return FILTER_ORDER; } @Override public boolean shouldFilter() { return SHOULD_FILTER; } @Override public Object run() { RequestContext ctx = RequestContext.getCurrentContext(); ctx.getResponse().addHeader("tmx-correlation-id", tracer.getCurrentSpan().traceIdString()); return null; } }
-
导入依赖包后,自动装配的Tracker类可用于访问正在执行的当前spring cloud sleuth跟踪信息,然后将其添加到zuul发出的HTTP响应中。
-
zuul网关不会盲目的转发HTTP调用,它接收传入的HTTP请求并终止这个调用,然后构建一个信息的到目标服务的调用,同是会去掉一些敏感信息,如cookie、token等。
-
-
使用OpenZipkin进行分布式跟踪:
-
Zipkin是一个分布式跟踪平台,可用于跟踪跨多个服务调用的事务,Zipkin提供了可视化UI用于查看事务占用的时间量,以及每个微服务所用的时间。
-
建立spring cloud sleuth和Zipkin涉及的四个操作:
-
将spring cloud sleuth和Zipkin依赖添加到捕获跟踪数据的服务中;
-
在每个服务中配置spring属性以指向收集跟踪数据的Zipkin服务器;
-
安装配置Zipkin服务器;
-
定义每个客户端所使用的采样策略,以便向Zipkin服务器发送数据。
-
-
在将跟踪数据发送到Zipkin服务器的每个服务中添加spring cloud sleuth和Zipkin依赖:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-sleuth</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-sleuth-zipkin</artifactId> </dependency>
-
配置服务以指向zipkin(也可以通过rabbitMq和kafka将数据发送到Zipkin服务器),并定义每个服务向Zipkin服务器发送信息的频率:
spring: zipkin: baseUrl: localhost:9411 #Zipkin服务地址 sleuth: sampler: percentage: 0.5 #值在0-1之间,默认为0.1
-
安装和配置Zipkin服务器:
-
导入依赖包;
<dependency> <groupId>io.zipkin.java</groupId> <artifactId>zipkin-autoconfigure-ui</artifactId> </dependency> <dependency> <groupId>io.zipkin.java</groupId> <artifactId>zipkin-server</artifactId> </dependency>
-
开启注解,如果需要通过消息代理(rabbitMq或kafka)来接收数据,则使用sping cloud团队提供的@EnableZipkinStreamServer,此时跟踪并向Zipkin服务器发送消息的服务需要将消息发送到消息代理,同时zipkin服务器也需要配置监听的消息代理,否则使用@EnableZipkinServer注解;
-
Zipkin服务器可以将接收到的数据存储到内存、mysql、或者Elasticsearch等;
-
spring cloud sleuth和Zipkin允许添加自定义跨度,以便跟踪与第三方调用相关的执行时间;
package com.thoughtmechanix.licenses.clients; import com.thoughtmechanix.licenses.model.Organization; import com.thoughtmechanix.licenses.repository.OrganizationRedisRepository; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.sleuth.Span; import org.springframework.cloud.sleuth.Tracer; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; import org.springframework.web.client.RestTemplate; @Component public class OrganizationRestTemplateClient { @Autowired RedisTemplate<String, Organization> redisTemplate; @Autowired private RestTemplate restTemplate; @Autowired Tracer tracer; @Autowired OrganizationRedisRepository orgRedisRepo; private Organization checkRedisCache(String organizationId) { Span newSpan = tracer.createSpan("readLicensingDataFromRedis"); try { return (Organization) redisTemplate.opsForHash().get("organization", organizationId); } catch (Exception ex) { return null; } finally { //使用finally块关闭跨度 //可以将标签信息添加到跨度中,此处提供了将要被捕获的服务的名称,表示我们要捕获访问redis的执行时间 newSpan.tag("peer.service", "redis"); //记录一个时间,告诉spring cloud sleuth它应该完成的时间 newSpan.logEvent(org.springframework.cloud.sleuth.Span.CLIENT_RECV); //关闭跟踪,否则在日志中会得到错误消息,指示跨度已经被打开却尚未关闭 tracer.close(newSpan); } } private void cacheOrganizationObject(Organization org) { try { orgRedisRepo.saveOrganization(org); } catch (Exception ex) { } } public Organization getOrganization(String organizationId) { Organization org = checkRedisCache(organizationId); if (org != null) { return org; } ResponseEntity<Organization> restExchange = restTemplate.exchange( "http://zuulservice/api/organization/v1/organizations/{organizationId}", HttpMethod.GET, null, Organization.class, organizationId); /*Save the record from cache*/ org = restExchange.getBody(); if (org != null) { cacheOrganizationObject(org); } return org; } }
-
查看可视化界面,地址:http://ip:port
-
-