美文网首页
使用requestId在分布式系统追踪请求

使用requestId在分布式系统追踪请求

作者: RobertCrazying | 来源:发表于2018-01-06 18:50 被阅读3874次

    背景

    现在大多数企业开发的系统都是分布式系统了,随着系统的复杂如何有效地追踪定位线上问题也变得更加困难。我们可以使用requestId(traceId)来解决这一问题。

    实现思路

    1. 使用过滤器或切面在每个HTTP请求进到Controller前生成一个唯一标识请求的requestId,可以使用UUID,放在ThreadLocal里,方便上下文调用。
        public static final String REQUEST_ID_KEY = "requestId";
        public static ThreadLocal<String> requestIdThreadLocal = new ThreadLocal<String>();
     
        private static final Logger logger = LoggerFactory.getLogger(RequestIdUtil.class);
     
        public static String getRequestId(HttpServletRequest request) {
            String requestId = null;
            String parameterRequestId = request.getParameter(REQUEST_ID_KEY);
            String headerRequestId = request.getHeader(REQUEST_ID_KEY);
     
            if (parameterRequestId == null &amp;&amp; headerRequestId == null) {
                logger.info("request parameter 和header 都没有requestId入参");
                requestId = UUID.randomUUID().toString();
            } else {
                requestId = parameterRequestId != null ? parameterRequestId : headerRequestId;
            }
     
            requestIdThreadLocal.set(requestId);
     
            return requestId;
        }
    
    1. requestId结合日志打印,这里使用slf4j标准里的MDC实现,例子使用logback打印日志。
      代码使用:
            MDC.put("requestId", requestId);
    
    

    logback配置示例:

    <configuration> 
         <appender name="logfile" class="ch.qos.logback.core.rolling.RollingFileAppender">
             <Encoding>UTF-8</Encoding>
             <File>${log_base}/java-base-web.log</File>
             <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                 <FileNamePattern>${log_base}/java-base-web-%d{yyyy-MM-dd}-%i.log</FileNamePattern>
                 <MaxHistory>10</MaxHistory>
                 <TimeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                    <MaxFileSize>200MB</MaxFileSize>
                 </TimeBasedFileNamingAndTriggeringPolicy> 
             </rollingPolicy>
             <layout class="ch.qos.logback.classic.PatternLayout">
                <pattern>%d^|^%X{requestId}^|^%-5level^|^%logger{36}%M^|^%msg%n</pattern>
             </layout>
         </appender>
         <root level="info"> 
            <appender-ref ref="logfile" /> 
         </root> 
    </configuration>
    

    主要是在pattern那里指定,使用MDC里面的变量用的是%X{requestId},requestId为放进MDC里面的key,

    达到的效果就是打印出来的日志自动带上了requestId。效果如下:

    [a0b3589f-fe87-4d3a-9151-8925afd0a6c6]  at com.test.demo.TestMain2.test2(TestMain2.java:26)  
    
    

    相当于切日志,这样我们线上就可以使用requestId在ELK上搜索属于这个请求的日志了。

    1. 使用HTTP Client或者OKHTTP等在后台请求其他系统的服务的时候把放在上下文的requestId放在请求参数或者头里,推荐放在header,上面的切面一开始会判断requestId是否在请求里面,有的话就继续沿用调用方的requestId,这样子在被调用的系统也能追踪到属于同一个调用链的日志了。
        String requestId = RequestIdUtil.requestIdThreadLocal.get();
        headerMap.put(RequestIdUtil.REQUEST_ID_KEY, requestId);
        Map<String, String> paramMap = new HashMap<String, String>();
        String resultString = JsonHttpClientUtil.post(testHttpClientUrl, headerMap, paramMap, "UTF-8");
        logger.info(resultString);
    
    1. requestId可以放到一个封装响应类里面,这样子我们就可以在控制台里面看到响应参数里面的requestId,方便在ELK里面查找日志了。也可以结合异常体系使用,抛的异常打印的日志自然也带上了requestId,同时可以使用钉钉机器人来通知开发区定位问题,同时带上requestId和异常堆栈信息就行了。这样就能方便的知道异常发生的上下文日志了。

    总结

    上面总结了一些工作中使用起来比较便利的定位问题的方法体系。希望可以给读者带来一些启示。

    相关文章

      网友评论

          本文标题:使用requestId在分布式系统追踪请求

          本文链接:https://www.haomeiwen.com/subject/vxaynxtx.html