本文只讲原理, 不讲框架.
分布式系统中日志追踪需要考虑的几个点?
需要一个全服务唯一的 id, 即 traceId, 如何保证?
traceId 如何在服务间传递?
traceId 如何在服务内部传递?
traceId 如何在多线程中传递?
我们一一来解答:
全服务唯一的 traceId, 可以使用 uuid 生成, 正常来说不会出现重复的;
关于服务间传递, 对于调用者, 在协议头加上 traceId, 对于被调用者, 通过前置拦截器或者过滤器统一拦截;
关于服务内部传递, 可以使用 ThreadLocal 传递 traceId, 一处放置, 随处可用;
关于多线程传递, 分为两种情况:
子线程, 可以使用 InheritableThreadLocal
线程池, 需要改造线程池对提交的任务进行包装, 把提交者的 traceId 包装到任务中
比如, 上面这个系统, 系统入口在 A 处, A 调用 B 的服务, B 里面又起了一个线程 B1 去访问 D 的服务, B 本身又去访问 C 服务.
我们就可以这么来跟踪日志:
所有服务都需要一个全局的 InheritableThreadLocal 保存服务内部 traceId 的传递;
所有服务都需要一个前置拦截器或者过滤器, 检测如果请求头没有 traceId 就生成一个, 如果有就取出来, 并把 traceId 放到全局的 InheritableThreadLocal 里面;
- @Slf4j
- @webFilter("/**")
- @Component
- public class TraceFilter implements Filter {
- @Override
- public void init(FilterConfig filterConfig) throws ServletException {
- }
- @Override
- public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
- HttpServletRequest request = (HttpServletRequest) servletRequest;
- // 从请求头中获取 traceId
- String traceId = request.getHeader("traceId");
- // 不存在就生成一个
- if (traceId == null || "".equals(traceId)) {
- traceId = UUID.randomUUID().toString();
- }
- // 放入 MDC 中, 本文来源于工从号彤哥读源码
- MDC.put("traceId", traceId);
- chain.doFilter(servletRequest, servletResponse);
- }
- @Override
- public void destroy() {
- }
- }
- public class TraceThreadPoolExecutor extends ThreadPoolExecutor {
- public TraceThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
- super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
- }
- public TraceThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
- super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
- }
- public TraceThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) {
- super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
- }
- public TraceThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
- super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
- }
- @Override
- public void execute(Runnable command) {
- // 提交者的本地变量
- Map<String, String> contextMap = MDC.getCopyOfContextMap();
- super.execute(()->{
- if (contextMap != null) {
- // 如果提交者有本地变量, 任务执行之前放入当前任务所在的线程的本地变量中
- MDC.setContextMap(contextMap);
- }
- try {
- command.run();
- } finally {
- // 任务执行完, 清除本地变量, 以防对后续任务有影响
- MDC.clear();
- }
- });
- }
- }
- @Slf4j
- @Component
- public class TraceAsyncConfigurer implements AsyncConfigurer {
- @Override
- public Executor getAsyncExecutor() {
- ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
- executor.setCorePoolSize(8);
- executor.setMaxPoolSize(16);
- executor.setQueueCapacity(100);
- executor.setThreadNamePrefix("async-pool-");
- executor.setTaskDecorator(new MdcTaskDecorator());
- executor.setWaitForTasksToCompleteOnShutdown(true);
- executor.initialize();
- return executor;
- }
- @Override
- public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
- return (throwable, method, params) -> log.error("asyc execute error, method={}, params={}", method.getName(), Arrays.toString(params));
- }
- public static class MdcTaskDecorator implements TaskDecorator {
- @Override
- public Runnable decorate(Runnable runnable) {
- Map<String, String> contextMap = MDC.getCopyOfContextMap();
- return () -> {
- if (contextMap != null) {
- MDC.setContextMap(contextMap);
- }
- try {
- runnable.run();
- } finally {
- MDC.clear();
- }
- };
- }
- }
- }
- @Slf4j
- public class HttpUtils {
- public static String get(String url) throws URISyntaxException {
- RestTemplate restTemplate = new RestTemplate();
- MultiValueMap<String, String> headers = new HttpHeaders();
- headers.add("traceId", MDC.get("traceId"));
- URI uri = new URI(url);
- RequestEntity<?> requestEntity = new RequestEntity<>(headers, HttpMethod.GET, uri);
- ResponseEntity<String> exchange = restTemplate.exchange(requestEntity, String.class);
- if (exchange.getStatusCode().equals(HttpStatus.OK)) {
- log.info("send http request success");
- }
- return exchange.getBody();
- }
- }
- @Slf4j
- @RestController
- public class AController {
- @RequestMapping("a")
- public String a(String name) {
- log.info("Hello," + name);
- try {
- // A 中调用 B
- return HttpUtils.get("http://localhost:8002/b");
- } catch (Exception e) {
- log.error("call b error", e);
- }
- return "fail";
- }
- }
- # 本文来源于工从号彤哥读源码
- logging:
- pattern:
- console: '%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(%5p) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr([%X{traceId}]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n%wEx'
- @Slf4j
- @RestController
- public class BController {
- @Autowired
- private BService bService;
- @RequestMapping("b")
- public String b() {
- log.info("Hello, b receive request from a");
- bService.sendMsgBySpring();
- bService.sendMsgByThreadPool();
- return "ok";
- }
- }
- @Slf4j
- @Service
- public class BService {
- public static final TraceThreadPoolExecutor threadPool = new TraceThreadPoolExecutor(5, 5, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(100));
- @Async
- public void sendMsgBySpring() {
- log.info("send msg by spring success");
- }
- public void sendMsgByThreadPool() {
- threadPool.execute(()->log.info("send msg by thread pool success"));
- }
- }
- logging:
- pattern:
- console: '%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(%5p) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr([%X{traceId}]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n%wEx'
- 2019-12-26 21:36:29.132 INFO 5132 --- [nio-8001-exec-2] [8a59cb96-bbc8-42a9-aa62-df7a52875447] com.alan.trace.a.AController : Hello, andy
- 2019-12-26 21:36:35.380 INFO 5132 --- [nio-8001-exec-2] [8a59cb96-bbc8-42a9-aa62-df7a52875447] com.alan.trace.common.HttpUtils : send http request success
- 2019-12-26 21:36:29.244 INFO 2368 --- [nio-8002-exec-1] [8a59cb96-bbc8-42a9-aa62-df7a52875447] com.alan.trace.b.BController : Hello, b receive request from a
- 2019-12-26 21:36:29.247 INFO 2368 --- [nio-8002-exec-1] [8a59cb96-bbc8-42a9-aa62-df7a52875447] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService
- 2019-12-26 21:36:35.279 INFO 2368 --- [ async-pool-1] [8a59cb96-bbc8-42a9-aa62-df7a52875447] com.alan.trace.b.BService : send msg by spring success
- 2019-12-26 21:36:35.283 INFO 2368 --- [pool-1-thread-1] [8a59cb96-bbc8-42a9-aa62-df7a52875447] com.alan.trace.b.BService : send msg by thread pool success
来源: https://www.cnblogs.com/tong-yuan/p/12128796.html