• 分布式 trace id 实现


    原创转载请注明出处:https://www.cnblogs.com/agilestyle/p/14794027.html

    Background

    在分布式环境下,请求要在多个服务之间进行调用,服务集群每天接收上百万个请求,一旦某个用户请求发生异常,如何快速定位查询到这个请求。

    一般的做法是:给同一个请求打上一个相同的标记。这样,只要拿到这个标记就可以查询到这个请求链路上所有的入参、出参、以及耗时,通常把这个标记叫做 requestId。我们可以采用Logback MDC、Servlet Filter 和 Spring AOP 的方式,完成项目通用分布式 Trace 的实现。

    Project Directory

    Maven Dependency

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>org.fool</groupId>
        <artifactId>springboot</artifactId>
        <version>1.0-SNAPSHOT</version>
    
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.3.10.RELEASE</version>
            <relativePath/>
        </parent>
    
        <properties>
            <maven.compiler.source>8</maven.compiler.source>
            <maven.compiler.target>8</maven.compiler.target>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-aop</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.apache.commons</groupId>
                <artifactId>commons-lang3</artifactId>
            </dependency>
        </dependencies>
    
        <build>
            <finalName>${artifactId}</finalName>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    
    </project>

    application.properties

    server.port=8080
    
    spring.http.encoding.force=true
    spring.http.encoding.charset=UTF-8
    spring.http.encoding.enabled=true
    
    logging.level.org.fool.springboot=debug

    logback.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <configuration debug="false" scan="true" scanPeriod="30 seconds">
        <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
            <encoder>
                <charset>utf-8</charset>
                <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%X{requestId}] [%thread] %logger{5} - %m%n</pattern>
            </encoder>
        </appender>
    
        <root level="INFO">
            <appender-ref ref="CONSOLE"/>
        </root>
    </configuration>

    Note: %X{requestId}

    MonitorAspect.java

    package org.fool.springboot.aspect;
    
    import lombok.extern.slf4j.Slf4j;
    import org.apache.commons.lang3.exception.ExceptionUtils;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Pointcut;
    import org.aspectj.lang.reflect.MethodSignature;
    import org.fool.springboot.util.JsonUtils;
    import org.springframework.stereotype.Component;
    
    @Component
    @Aspect
    @Slf4j
    public class MonitorAspect {
    
        @Pointcut("execution(public * org.fool.springboot.controller..*.*(..))")
        public void controllerLog() {
        }
    
        @Pointcut("execution(public * org.fool.springboot.service..*.*(..))")
        public void serviceLog() {
        }
    
    
        @Around("controllerLog() || serviceLog()")
        public Object handle(ProceedingJoinPoint pjp) throws Throwable {
            String declaringTypeName = pjp.getSignature().getDeclaringTypeName();
            String methodName = pjp.getSignature().getName();
            String inputParamNames = JsonUtils.objectToJson(((MethodSignature) pjp.getSignature()).getParameterNames());
            String inputParamValues = JsonUtils.objectToJson(pjp.getArgs());
    
            long startTime = System.currentTimeMillis();
            log.info("[{}][{}] input param names: {}", declaringTypeName, methodName, inputParamNames);
            log.info("[{}][{}] input param values: {}", declaringTypeName, methodName, inputParamValues);
            log.info("[{}][{}] invoke start time: {}", declaringTypeName, methodName, startTime);
    
            try {
                Object result = pjp.proceed();
                log.info("[{}][{}] invoke result: {}", declaringTypeName, methodName, JsonUtils.objectToJson(result));
                return result;
            } catch (Throwable t) {
                log.error("[{}][{}] invoke error: {}", declaringTypeName, methodName, ExceptionUtils.getStackTrace(t));
                throw t;
            } finally {
                long endTime = System.currentTimeMillis();
                log.info("[{}][{}] invoke end time: {}, cost: {} ms", declaringTypeName, methodName, endTime, endTime - startTime);
            }
        }
    }

    Note: 打印输出入参、出参、耗时 

    ApiFilter.java

    package org.fool.springboot.filter;
    
    import org.slf4j.MDC;
    import org.springframework.core.Ordered;
    import org.springframework.stereotype.Component;
    import org.springframework.web.filter.OncePerRequestFilter;
    
    import javax.servlet.FilterChain;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.util.UUID;
    
    @Component
    public class ApiFilter extends OncePerRequestFilter implements Ordered {
    
        public static final String REQUEST_ID_MDC_KEY = "requestId";
    
        @Override
        public int getOrder() {
            return Ordered.HIGHEST_PRECEDENCE;
        }
    
        @Override
        protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
            MDC.put(REQUEST_ID_MDC_KEY, UUID.randomUUID().toString());
            try {
                filterChain.doFilter(httpServletRequest, httpServletResponse);
            } finally {
                MDC.remove(REQUEST_ID_MDC_KEY);
            }
        }
    }

    JsonUtils.java

    package org.fool.springboot.util;
    
    import com.fasterxml.jackson.core.JsonProcessingException;
    import com.fasterxml.jackson.databind.JavaType;
    import com.fasterxml.jackson.databind.ObjectMapper;
    
    import java.util.List;
    import java.util.Map;
    
    public class JsonUtils {
        private static final ObjectMapper MAPPER = new ObjectMapper();
    
        public static String objectToJson(Object data) {
            try {
                String string = MAPPER.writeValueAsString(data);
                return string;
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
            return null;
        }
    
        public static <T> T jsonToPojo(String jsonData, Class<T> beanType) {
            try {
                T t = MAPPER.readValue(jsonData, beanType);
                return t;
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            return null;
        }
    
        public static <T> List<T> jsonToList(String jsonData, Class<T> beanType) {
            JavaType javaType = MAPPER.getTypeFactory().constructParametricType(List.class, beanType);
    
            try {
                List<T> list = MAPPER.readValue(jsonData, javaType);
                return list;
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            return null;
        }
    
        public static <K, V> Map<K, V> jsonToMap(String jsonData, Class<K> keyType, Class<V> valueType) {
            JavaType javaType = MAPPER.getTypeFactory().constructMapType(Map.class, keyType, valueType);
    
            try {
                Map<K, V> map = MAPPER.readValue(jsonData, javaType);
                return map;
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            return null;
        }
    }

    TestService.java

    package org.fool.springboot.service;
    
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.stereotype.Service;
    
    @Service
    @Slf4j
    public class TestService {
        public void test(String username, String password) {
            log.info("mock business service");
        }
    }

    TestController.java

    package org.fool.springboot.controller;
    
    import lombok.extern.slf4j.Slf4j;
    import org.fool.springboot.service.TestService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    @Slf4j
    public class TestController {
        @Autowired
        private TestService testService;
    
        @GetMapping(path = "/test")
        public String test() {
            testService.test("hello", "world");
            return "SUCCESS";
        }
    }

    Application.java

    package org.fool.springboot;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    @SpringBootApplication
    public class Application {
        public static void main(String[] args) {
            SpringApplication.run(Application.class, args);
        }
    }

    Run 

    curl http://localhost:8080/test


    欢迎点赞关注和收藏

    强者自救 圣者渡人
  • 相关阅读:
    html5的键盘事件
    阻止滑屏
    JS 复制到黏贴板上
    最新拖动原理
    方法——<37>
    验证——正则<37>
    《高级程序设计》 9 客户端检测
    《高级程序设计》8 BOM
    《高级程序设计》7 函数表达式
    《高级程序设计》6 面向对象的程序设计
  • 原文地址:https://www.cnblogs.com/agilestyle/p/14794027.html
Copyright © 2020-2023  润新知