• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

微服务TraceId设计(SpringCloud OpenFeign)

武飞扬头像
忆之独秀
帮助1

一、背景

     在微服务框架下,需要通过traceId来定位到整个链路中每个细节的运行情况,这里不引用外部组件,自己来进行实现。当然还保证在多线程环境下的一致性。

二、需求

1)接口入口侧如果有traceId,则获取;如果没有则生成;

2)定义生成traceId的规则;

3)traceId可以默认打印到log中,不需要自己显示定义;

4)通过feign传输到后端服务中

三、实现

3.1)TraceId生成算法

    参考:https://help.aliyun.com/document_detail/151840.html 

  1.  
    public static String genTraceId() {
  2.  
     
  3.  
    try {
  4.  
    StringBuilder sb = new StringBuilder();
  5.  
    InetAddress addr = InetAddress.getLocalHost();
  6.  
    if (null == addr) {
  7.  
    return TRACE_ID_VALUE_DEFAULT;
  8.  
    }
  9.  
    String addrStr = addr.getHostAddress();
  10.  
    if (null == addrStr) {
  11.  
    return TRACE_ID_VALUE_DEFAULT;
  12.  
    }
  13.  
    String[] addrArr = addrStr.split("\\.");
  14.  
    if (addrArr.length != 4) {
  15.  
    return TRACE_ID_VALUE_DEFAULT;
  16.  
    }
  17.  
     
  18.  
    sb.append(StringUtils.leftPad(Integer.toHexString(Integer.valueOf(addrArr[0])) , 2, "0"))
  19.  
    .append(StringUtils.leftPad(Integer.toHexString(Integer.valueOf(addrArr[1])) , 2, "0"))
  20.  
    .append(StringUtils.leftPad(Integer.toHexString(Integer.valueOf(addrArr[2])) , 2, "0"))
  21.  
    .append(StringUtils.leftPad(Integer.toHexString(Integer.valueOf(addrArr[3])) , 2, "0"))
  22.  
    .append(System.currentTimeMillis())
  23.  
    .append(RandomUtil.randomNumbers(4))
  24.  
    .append(ProcessHandle.current().pid());
  25.  
     
  26.  
    return sb.toString();
  27.  
    } catch (Exception e) {
  28.  
    return TRACE_ID_VALUE_DEFAULT;
  29.  
    }
  30.  
    }
学新通

3.2)服务入口日志拦截

  1.  
    @EnableAspectJAutoProxy
  2.  
    @Component
  3.  
    @Aspect
  4.  
    @Slf4j
  5.  
    public class InterfaceLogAspect {
  6.  
     
  7.  
    @Around("execution(* com.springboot.k8s.demo.interfaces.*.*(..))")
  8.  
    public Object interfaceAround(ProceedingJoinPoint joinPoint) {
  9.  
    String className = joinPoint.getTarget().getClass().getSimpleName();
  10.  
    Object[] args = joinPoint.getArgs();
  11.  
    MethodSignature signature = (MethodSignature) joinPoint.getSignature();
  12.  
    Parameter[] argNames = signature.getMethod().getParameters();
  13.  
    StringBuilder sb = new StringBuilder(className "." joinPoint.getSignature().getName() " -- ");
  14.  
     
  15.  
    //获取RequestAttributes
  16.  
    RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
  17.  
    //从获取RequestAttributes中获取HttpServletRequest的信息
  18.  
    HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);
  19.  
    TraceIdUtil.initTraceId(request);
  20.  
     
  21.  
    StopWatch clock = new StopWatch();
  22.  
    clock.start();
  23.  
    Object retVal = null;
  24.  
    try {
  25.  
    Map<String, Object> paramMap = new HashMap<>();
  26.  
    for (int i = 0; i < argNames.length; i ) {
  27.  
    paramMap.put(argNames[i].getName(), args[i]);
  28.  
    }
  29.  
    LoggerUtils.auditLogInfo(LogLabelEnum.REQUEST_IN.getLogLabel(), paramMap);
  30.  
    retVal = joinPoint.proceed(joinPoint.getArgs());
  31.  
    } catch (Throwable e) {
  32.  
    //..
  33.  
    } finally {
  34.  
    //..
  35.  
    }
  36.  
    return retVal;
  37.  
    }
  38.  
    }
学新通

3.3)FeignClient服务之间传递TraceId:

  1.  
    @Service
  2.  
    public class MyRequestInterceptor implements RequestInterceptor {
  3.  
    @Override
  4.  
    public void apply(RequestTemplate template) {
  5.  
    template.header(TraceIdUtil.TRACE_ID_KEY, TraceIdUtil.getTraceId());
  6.  
    }
  7.  
    }

3.4)logback.xml

  1.  
    <appender name="consoleLog" class="ch.qos.logback.core.ConsoleAppender">
  2.  
    <layout class="ch.qos.logback.classic.PatternLayout">
  3.  
    <pattern>[%-5level][%date{yyyy-MM-dd HH:mm:ss:SSS}][%logger:%line] &#45;&#45;%mdc{client} %msg||traceId=%X{Trace-Id}%n</pattern>
  4.  
     
  5.  
    </layout>
  6.  
    </appender>

备注:"%X{Trace-Id}"即将MDC中的以Trace-Id为key的值同步到日志中,也就是TraceIdUtil.TRACE_ID_KEY。

完整的TraceIdUtill为:

  1.  
    public class TraceIdUtil {
  2.  
     
  3.  
    public static final String TRACE_ID_KEY = "Trace-Id";
  4.  
     
  5.  
    public static final String TRACE_ID_VALUE_DEFAULT = "000000";
  6.  
     
  7.  
    public static void setTraceId(String traceId) {
  8.  
    //如果参数为空,则设置默认traceId
  9.  
    //将traceId放到MDC中
  10.  
    MDC.put(TRACE_ID_KEY, traceId);
  11.  
    }
  12.  
     
  13.  
    public static String getTraceId() {
  14.  
    String traceId = MDC.get(TRACE_ID_KEY);
  15.  
     
  16.  
    return StringUtils.isNotBlank(traceId) ? traceId : TRACE_ID_VALUE_DEFAULT;
  17.  
    }
  18.  
     
  19.  
    /**
  20.  
    * generate traceId: https://help.aliyun.com/document_detail/151840.html
  21.  
    */
  22.  
    public static String genTraceId() {
  23.  
     
  24.  
    try {
  25.  
    StringBuilder sb = new StringBuilder();
  26.  
    InetAddress addr = InetAddress.getLocalHost();
  27.  
    if (null == addr) {
  28.  
    return TRACE_ID_VALUE_DEFAULT;
  29.  
    }
  30.  
    String addrStr = addr.getHostAddress();
  31.  
    if (null == addrStr) {
  32.  
    return TRACE_ID_VALUE_DEFAULT;
  33.  
    }
  34.  
    String[] addrArr = addrStr.split("\\.");
  35.  
    if (addrArr.length != 4) {
  36.  
    return TRACE_ID_VALUE_DEFAULT;
  37.  
    }
  38.  
     
  39.  
    sb.append(StringUtils.leftPad(Integer.toHexString(Integer.valueOf(addrArr[0])) , 2, "0"))
  40.  
    .append(StringUtils.leftPad(Integer.toHexString(Integer.valueOf(addrArr[1])) , 2, "0"))
  41.  
    .append(StringUtils.leftPad(Integer.toHexString(Integer.valueOf(addrArr[2])) , 2, "0"))
  42.  
    .append(StringUtils.leftPad(Integer.toHexString(Integer.valueOf(addrArr[3])) , 2, "0"))
  43.  
    .append(System.currentTimeMillis())
  44.  
    .append(RandomUtil.randomNumbers(4))
  45.  
    .append(ProcessHandle.current().pid());
  46.  
     
  47.  
    return sb.toString();
  48.  
    } catch (Exception e) {
  49.  
    return TRACE_ID_VALUE_DEFAULT;
  50.  
    }
  51.  
    }
  52.  
     
  53.  
    public static void initTraceId(HttpServletRequest request) {
  54.  
    String traceId = "";
  55.  
    if (request != null) {
  56.  
    //get traceId from request
  57.  
    traceId = request.getHeader(TraceIdUtil.TRACE_ID_KEY);
  58.  
    }
  59.  
    //generate new traceId
  60.  
    if (StringUtils.isBlank(traceId) || TRACE_ID_VALUE_DEFAULT.equals(traceId)){
  61.  
    traceId = TraceIdUtil.genTraceId();
  62.  
    }
  63.  
     
  64.  
    TraceIdUtil.setTraceId(traceId);
  65.  
    }
  66.  
     
  67.  
    public static void main(String[] args) {
  68.  
    System.out.println(TraceIdUtil.genTraceId());
  69.  
    }
  70.  
    }
学新通

四、多线程TraceId复制

4.1)线程配置

  1.  
    @Configuration
  2.  
    public class ThreadPoolConfig {
  3.  
     
  4.  
    public static final String ASYNC_EXECUTOR_NAME = "threadPoolTaskExecutor";
  5.  
     
  6.  
    @Bean(name=ASYNC_EXECUTOR_NAME)
  7.  
    public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
  8.  
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
  9.  
    // for passing in request scope context
  10.  
    executor.setTaskDecorator(new ContextCopyingDecorator());
  11.  
    executor.setCorePoolSize(10);
  12.  
    executor.setMaxPoolSize(20);
  13.  
    executor.setQueueCapacity(100);
  14.  
    executor.setWaitForTasksToCompleteOnShutdown(true);
  15.  
    executor.setThreadNamePrefix("SeelAsyncThread-");
  16.  
    executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
  17.  
    executor.initialize();
  18.  
    return executor;
  19.  
    }
  20.  
    }
学新通

4.2)定义TaskDecorator

  1.  
    public class ContextCopyingDecorator implements TaskDecorator {
  2.  
     
  3.  
    @Override
  4.  
    public Runnable decorate(Runnable runnable) {
  5.  
    RequestAttributes context = RequestContextHolder.currentRequestAttributes();
  6.  
    Map<String, String> contextMap = MDC.getCopyOfContextMap();
  7.  
    return () -> {
  8.  
    try {
  9.  
    RequestContextHolder.setRequestAttributes(context);
  10.  
    MDC.setContextMap(contextMap);
  11.  
    runnable.run();
  12.  
    } finally {
  13.  
    RequestContextHolder.resetRequestAttributes();
  14.  
    }
  15.  
    };
  16.  
    }
  17.  
    }
学新通

Author:忆之独秀

Email:leaguenew@qq.com

转载注明出处:

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgjkheh
系列文章
更多 icon
同类精品
更多 icon
继续加载