博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
分布式日志追踪的最佳实践1
阅读量:1973 次
发布时间:2019-04-27

本文共 5300 字,大约阅读时间需要 17 分钟。

分布式日志追踪

分布式环境中无可避免的需要做微服务之间的调用,这导致追踪到整个的业务流程变得麻烦。分布式日志追踪解决了那些问题:

  1. 分布式的性能优化。通过日志追踪可以看出各个环境,各个服务消耗的时间,为性能优化定位问题。
  2. 追踪业务流程。通过链路追踪码,可以追踪到业务发生的整体流程。
  3. 异常追踪。但服务发生异常时,服务可以通过埋点的方式收集追踪码,从而定位问题。

适用范围

框架除Gateway外全部采用Servlet,本文代码不适用与WebFlux等非阻塞模型。

框架采用了PlumeLog分布式框架,因此在工具类中使用了PlumeLog的工具类。

如何在主流的微服务框架中实现日志追踪 ?

本文以使用FeignRestTemplate进行微服务直接调用的案例作为实例,如下图:

在这里插入图片描述

图中是一个比较常见的服务调用案例,在请求在各个微服务模块中传递时,我们可以通过服务的拦截器对请求添加标识(链路追踪码),标记请求的流转过程。在微服务模块中需要对业务处理流程添加标识,这样标识就标记了本次请求的整个生命周期。

具体的请求顺序对应的操作如下:

  1. 前端向网关发起请求。
  2. 网关对请求添加标记(一般放到请求头中),然后调用微服务1。具体见下方 :Servlet日志追踪
  3. 微服务1在Servlet拦截器中拦截请求,并从请求中获取标记,同时将标记标记当前线程,然后通过Feign调用微服务2,这时触发Feign拦截器,拦截器见标记添加到请求中,发送给下个微服务。具体见下方 :Servlet日志追踪Feign的日志追踪
  4. 微服务2接到请求后,同样接收标记并应用,随后通过RestTemplate调用微服务3,这时触发RestTemplate拦截器,拦截器见标记添加到请求中,发送给下个微服务。具体见下方 :Servlet日志追踪RestTemplate的日志追踪
  5. 微服务3接到请求后,同样接收标记并标记业务流程,处理完成后返回给微服务2
  6. 微服务2接到返回后,继续处理业务,完成后返回给微服务1
  7. 微服务1完成业务后,返回给网关
  8. 网关将请求结果返回给前端

Feign的日志追踪

Feign的日志追踪可以通过Feign的拦截器进行处理,如下:

import feign.RequestInterceptor;import feign.RequestTemplate;public class FeignTraceRequestInterceptor implements RequestInterceptor {
@Override public void apply(RequestTemplate requestTemplate) {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder .getRequestAttributes(); RequestContextHolder.setRequestAttributes(attributes, true); if(ObjectUtil.isNull(attributes)) {
return; } HttpServletRequest request = attributes.getRequest(); String traceId = request.getHeader(TraceUtil.TRACE_NAME); traceId = StringUtils.isBlank(traceId) ? TraceUtil.get() : traceId; traceId = StringUtils.isBlank(traceId) ? TraceUtil.getTraceId() : traceId; // 如果追踪码不是空白就传递 requestTemplate.header(TraceUtil.TRACE_NAME,traceId); }}

当feign调用时,拦截器会拦截到Feign的请求 :

  1. 首先我们从请求中获取追踪码。有可能追踪码来源于上一个服务的请求。
  2. 如果追踪码为空,就中线程中(业务流程中)获取当前追踪码。
  3. 如果追踪码还是空,就新创建一个追踪码。
  4. 最后将追踪码放到请求头中,传递给下个服务。

应用Feign拦截器

首先,声明Feign配置:

public class FeignHeaderConfig{
@Bean public RequestInterceptor requestInterceptor(){
return new FeignBasicAuthRequestInterceptor(); } @Bean public FeignTraceRequestInterceptor FeignTraceRequestInterceptor(){
return new FeignTraceRequestInterceptor(); }}

声明配置器时需要将写的拦截器注入到配置

然后应用配置:

@FeignClient(value = "feign-test-service",configuration = FeignHeaderConfig.class)

RestTemplate的日志追踪

RestTemplate的日志追踪同样使用拦截器处理:

import org.springframework.http.client.ClientHttpRequestExecution;import org.springframework.http.client.ClientHttpRequestInterceptor;import org.springframework.http.client.ClientHttpResponse;public class RestTraceInterceptor implements ClientHttpRequestInterceptor {
@Override public ClientHttpResponse intercept(HttpRequest httpRequest, byte[] bytes, ClientHttpRequestExecution clientHttpRequestExecution) throws IOException {
// 从头里获取追踪码 String traceId = TraceUtil.getTraceId(httpRequest); // 如果追踪码是个空,就从 线程里去追踪码 traceId = StringUtils.isBlank(traceId) ? TraceUtil.get() : traceId; // 如果线程追踪码还是空,就创建新的追踪码 traceId = StringUtils.isBlank(traceId) ? TraceUtil.getTraceId() : traceId; // 请求头传递参数 httpRequest.getHeaders().add(TraceUtil.TRACE_NAME, traceId); // 保证请求继续被执行 return clientHttpRequestExecution.execute(httpRequest, bytes); }}

当RestTemplate调用时,拦截器会拦截到请求 几乎和Feign一致:

  1. 首先我们从请求中获取追踪码。有可能追踪码来源于上一个服务的请求。
  2. 如果追踪码为空,就中线程中(业务流程中)获取当前追踪码。
  3. 如果追踪码还是空,就新创建一个追踪码。
  4. 最后将追踪码放到请求头中,传递给下个服务。

应用RestTemplate拦截器

在创建RestTemplate时,声明使用拦截器即可:

@Configurationpublic class MyLoadBalanceConfig {
@Bean public RestTraceInterceptor restTraceInterceptor(){
return new RestTraceInterceptor(); } @Bean @LoadBalanced public RestTemplate getRestTemplate(){
RestTemplate restTemplate = new RestTemplate(); restTemplate.setInterceptors(Collections.singletonList(restTraceInterceptor())); return restTemplate; }}

如果在分布式环境中必须要加上 @LoadBalanced 注解,否则服务使用服务名进行调用。

Servlet中日志追踪

同样,在servlet中以旧使用Servlet拦截器处理:

import com.kgo.trace.utils.TraceUtil;import org.apache.commons.lang3.StringUtils;import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class TraceInterceptor extends HandlerInterceptorAdapter {
@Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 从请求中获取追踪码 String traceId = TraceUtil.getTraceId(request); // 如果追踪码是个空,就从 线程里去追踪码 traceId = StringUtils.isBlank(traceId) ? TraceUtil.get() : traceId; // 如果线程追踪码还是空,就创建新的追踪码 traceId = StringUtils.isBlank(traceId) ? TraceUtil.getTraceId() : traceId; // 放到线程中 TraceUtil.set(traceId); return true; }}

处理逻辑与RestTemplate和Feign的处理逻辑几乎一致,不再赘述。

应用 Servlet拦截器

这个比较简单,不做赘述:

@Configurationpublic class ContractMvcConfig implements WebMvcConfigurer {
@Override public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(getTraceInterceptor()).addPathPatterns("/**"); } @Bean public TraceInterceptor getTraceInterceptor() {
return new TraceInterceptor(); }}

如果在分布式环境中必须要加上 @LoadBalanced 注解,否则服务使用服务名进行调用。


日志工具类源码请见下篇分解

转载地址:http://hafpf.baihongyu.com/

你可能感兴趣的文章
libuv实现ping包发送和接收
查看>>
基础架构系列篇-系统centos7安装docker+COMPOSE
查看>>
基础架构系列篇-NGINX部署VUE
查看>>
基础架构系列篇-系统centos7安装kafka
查看>>
基础架构系列篇-系统centos7中docker安装分布式文件存储服务minio
查看>>
软件质量的8个特性
查看>>
应届渣渣前端的艰难求职之路
查看>>
2021年不可错过的17种JS优化技巧(一)
查看>>
在 Vue 中用 Axios 异步请求API
查看>>
MySQL进阶查询(SELECT 语句高级用法)
查看>>
Mysql 之主从复制
查看>>
【NLP学习笔记】中文分词(Word Segmentation,WS)
查看>>
【超越白皮书7】你需要知道关于ETH2.0的几个事实
查看>>
对于时间复杂度的通俗理解
查看>>
如何输入多组数据并输出每组数据的和?
查看>>
行阶梯型矩阵
查看>>
MATLAB指定路径保存图片方法
查看>>
JAVA学习笔记6 - 数组
查看>>
JAVA学习笔记10 - 继承
查看>>
Android 开发学习笔记 00 - Getting Started
查看>>