Journaliser le transfert de traceId entre le multithreading Java

avant-propos

Dans l'environnement de production, en raison de l'environnement simultané, l'ordre de sortie du journal est dispersé sur différentes lignes. Grâce à cela, traceIdplusieurs sorties de journal différentes de la même demande peuvent être rapidement localisées, et il est très pratique de suivre les demandes et de localiser les problèmes. Cependant, si le multi-threading est utilisé dans le code, vous constaterez que le thread nouvellement ouvert ne portera pas le thread parent traceId. Par conséquent, en héritant MDCdes informations de contexte du thread parent, le thread nouvellement ouvert est cohérent avec le thread parent traceId.

Descriptif MDC :

MDC (Mapped Diagnostic Context) est une technologie de journalisation couramment utilisée. MDC peut stocker des informations clés dans le contexte du thread et les transmettre à différents composants de la chaîne d'appel en cas de besoin.

Avantages de l'utilisation de MDC pour fournir des journaux :

  1. Suivi pratique des demandes : Grâce à MDC, les informations clés peuvent être enregistrées et transmises tout au long du cycle de vie de la demande, telles que l'ID de la demande, l'ID utilisateur, etc., de sorte qu'il est facile de suivre les demandes et de localiser les problèmes.
  2. Améliorez l'efficacité du débogage : MDC peut stocker les informations contextuelles de chaque composant de la chaîne d'appel, afin que le problème puisse être diagnostiqué plus rapidement et que le temps de dépannage puisse être raccourci lors du débogage.
  3. Prise en charge des systèmes distribués : dans les systèmes distribués, MDC peut transférer des informations clés entre différents nœuds, ce qui permet de localiser rapidement les problèmes lors d'appels entre nœuds.
  4. Améliorer la lisibilité du code : les informations de contexte enregistrées par MDC peuvent être formatées sous une forme facile à lire par la sortie du journal, améliorant ainsi la lisibilité du code.

Code d'implémentation :

/**
 * 继承ThreadPoolTaskExecutor,实现多线程处理任务时传递日志traceId
 */
public class ThreadPoolTaskExecutorMdcUtil extends ThreadPoolTaskExecutor {
    
    

    @Override
    public void execute(Runnable task) {
    
    
        super.execute(wrap(task));
    }

    @Override
    public <T> Future<T> submit(Callable<T> task) {
    
    
        return super.submit(wrap(task));
    }

    @Override
    public Future<?> submit(Runnable task) {
    
    
        return super.submit(wrap(task));
    }

    private <T> Callable<T> wrap(final Callable<T> callable) {
    
    
        // 获取当前线程的MDC上下文信息
        Map<String, String> context = MDC.getCopyOfContextMap();
        return () -> {
    
    
            if (context != null) {
    
    
                // 传递给子线程
                MDC.setContextMap(context);
            }
            try {
    
    
                return callable.call();
            } finally {
    
    
                // 清除MDC上下文信息,避免造成内存泄漏
                MDC.clear();
            }
        };
    }

    private Runnable wrap(final Runnable runnable) {
    
    
        Map<String, String> context = MDC.getCopyOfContextMap();
        return () -> {
    
    
            if (context != null) {
    
    
                MDC.setContextMap(context);
            }
            try {
    
    
                runnable.run();
            } finally {
    
    
                // 清除MDC上下文信息,避免造成内存泄漏
                MDC.clear();
            }
        };
    }
}

Ensuite, utilisez simplement la classe comme vous utiliseriez normalement le pool de threads ThreadPoolTaskExecutorMdcUtil.

Par exemple, injectez un exemple de code de bean de pool de threads :

@Bean("thread-pool-receive")
public ThreadPoolTaskExecutor receiveThreadPoolExecutor() {
    
    
    // new的是自定义的线程池
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutorMdcUtil();
    executor.setCorePoolSize(1);
    executor.setMaxPoolSize(10);
    // 缓存队列
    executor.setQueueCapacity(10000);
    // 允许线程的空闲时间60秒:
    executor.setKeepAliveSeconds(60);
    // 线程池名的前缀:设置好了之后可以方便我们定位处理任务所在的线程池
    executor.setThreadNamePrefix("test-");
    // 拒绝策略为调用者执行
    executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
    executor.initialize();
    return executor;
}

Guess you like

Origin blog.csdn.net/weixin_43811294/article/details/130119245