欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

dubbo服务间的日志追踪解决方案

程序员文章站 2022-07-03 15:47:09
...

dubbo服务间日志追踪解决方案

技术背景

springboot 集成dubbo,logback , 主要组件MDC,RpcContext,实现日志追踪。

业务场景

RPC服务间调用,消费端和服务端标记追踪ID记录,前后保持一致,方便查找、监控某个业务的完整的执行流程,比如:客户端发起下单操作,后端要经过扣款服务,减库存服务,商品服务,用户服务,一套流程走下来,可能业务流程非常多,且要经过多个系统,如果日志记录的不是特别详细,可能查找问题特别慢。

步骤:

  1. 将拦截器配置到应用中
    也可以配置多个拦截器类,比如登录校验等
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class SessionConfiguration implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new TraceIdInterceptor()).addPathPatterns("/**");
    }
}

  1. spring拦截器类
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.UUID;

/**
 * 日志追踪
 */
@Component
public class TraceIdInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // generate traceId
        String traceId = UUID.randomUUID().toString().replace("-", "");
        // put traceId
        MDC.put(Constants.TRACE_ID, traceId);
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        // clear traceId
        MDC.remove(Constants.TRACE_ID);
    }
}
  1. web端的过滤器类
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.*;
import org.slf4j.MDC;

@Activate(group = {org.apache.dubbo.common.Constants.PROVIDER, org.apache.dubbo.common.Constants.CONSUMER})
public class DubboTraceIdFilter implements Filter {
    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        RpcContext rpcContext = RpcContext.getContext();

        // before
        if (rpcContext.isProviderSide()) {
            // get traceId from dubbo consumer,and set traceId to MDC
            String traceId = rpcContext.getAttachment(Constants.TRACE_ID);
            MDC.put(Constants.TRACE_ID, traceId);
        }

        if (rpcContext.isConsumerSide()) {
            // get traceId from MDC, and set traceId to rpcContext
            String traceId = MDC.get(Constants.TRACE_ID);
            rpcContext.setAttachment(Constants.TRACE_ID, traceId);
        }

        Result result = invoker.invoke(invocation);

        // after
        if (rpcContext.isProviderSide()) {
            // clear traceId from MDC
            MDC.remove(Constants.TRACE_ID);
        }
        return result;
    }

    @Override
    public Result onResponse(Result result, Invoker<?> invoker, Invocation invocation) {
        return result;
    }
}

  1. 服务端过滤器类
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.*;
import org.slf4j.MDC;

@Activate(group = {org.apache.dubbo.common.Constants.PROVIDER, org.apache.dubbo.common.Constants.CONSUMER})
public class DubboTraceIdFilter implements Filter {
    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        RpcContext rpcContext = RpcContext.getContext();
        // before
        if (rpcContext.isProviderSide()) {
            // get traceId from dubbo consumer,and set traceId to MDC
            String traceId = rpcContext.getAttachment(Constants.TRACE_ID);
            MDC.put(Constants.TRACE_ID, traceId);
        }
        if (rpcContext.isConsumerSide()) {
            // get traceId from MDC, and set traceId to rpcContext
            String traceId = MDC.get(Constants.TRACE_ID);
            rpcContext.setAttachment(Constants.TRACE_ID, traceId);
        }
        Result result = invoker.invoke(invocation);

        // after
        if (rpcContext.isProviderSide()) {
            // clear traceId from MDC
            MDC.remove(Constants.TRACE_ID);
        }
        return result;
    }

    @Override
    public Result onResponse(Result result, Invoker<?> invoker, Invocation invocation) {
        return result;
    }
}

  1. 集成logback.xml文件

将[%X{traceId}]编辑到日志配置文件中即可,traceId和过滤器里设置的key保持一致

    <!-- 标准输出配置 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{YYYY-MM-dd HH:mm:ss.SSS} [%thread][%X{traceId}] %-5level [%file:%line] - %msg%n</pattern>
        </encoder>
    </appender>
  1. 使用demo
@Slf4j
@Service
public class DemoServiceImpl implements DemoService {

	@Override
    public void test() {
        log.info("test==========================");
    }
}

输出示例:
2019-11-01 19:12:36.139 [ServerHandler-xx.x.xxx.xxx-thread-8][4cc1e695e05e42559a4293ad318af000] INFO [ServiceImpl.java:277] - test==========================

另一种实现场景(http )

如果是通过http 调用三方接口,可以将生成的追踪ID,赋值到header中,被调用方接到后,可以记录到日志中。

总结:上面的实现方式还是比较简单的,当然还有些追踪组件,比如zipkin、Spring Cloud Sleuth,可以根据自有项目场景选择使用哪种。