Créez un échafaudage de développement à partir de zéro pour améliorer la fonction Trace dans le scénario de pool de threads asynchrones
avant-propos
Les deux articles suivants sont la base et le principe de la fonction de trace écrite auparavant.
- Créez un échafaudage de développement à partir de zéro et utilisez MDC pour mettre en œuvre le suivi des liens de journal
- Construire et développer des échafaudages à partir de zéro pour un suivi de liaison léger Trace
Cependant, dans le cas où il existe des threads asynchrones dans l'entreprise, la fonction de trace est un peu défectueuse, consultez la description suivante pour plus de détails.
Exemple de code de question
// 有个业务线程池
ThreadPoolExecutor pool = new EasyAdminThreadPoolExecutor(10,10,"laker");
// 模拟业务代码
public void pageList(){
// 1.本地查询
xxxService.pageList();
// 2.模拟异步远程调用,耗时300ms
pool.submit(() -> {
TraceCodeBlock.trace("remoteService.call", value -> {
TimeUnit.MILLISECONDS.sleep(300);
});
}
// 3.本地插入记录
xxxService.insert();
}
Journal des résultats :
// 1 线程laker-9 丢掉了userId和traceId
20:21:35.534 INFO --- [ laker-9] [|] com.laker.admin.framework.aop.trace.Trace:86 `---
`---[302ms] Others-remoteService.call
// 2 tomcat线程 userId=16还有traceId
20:21:35.547 INFO --- [io-8080-exec-38] [16|497ef9d28d20452f84443c66c6f25354] com.laker.admin.framework.aop.trace.Trace:86 `---
`---[428ms] Controller-ExtLeaveController|pageAll
+---[max]:[414ms] Controller-ExtLeaveController.pageAll
| +---[32ms] Others-leaveService.page
| | +---[3ms] Mapper-com.laker.admin.module.ext.mapper.ExtLeaveMapper.selectPage_mpCount
| | `---[1ms] Mapper-com.laker.admin.module.ext.mapper.ExtLeaveMapper.selectPage
Nous voyons deux problèmes avec les journaux.
userId
Question 1 : L'asynchronisme fait que le thread enfant perd la somme dans le thread parenttraceId
.- Question 2 : L'asynchronie fait que le thread enfant et le thread parent n'utilisent pas un objet Trace, c'est-à-dire que les parties 1 et 2 du journal ci-dessus sont divisées .
améliorer
Résoudre le problème ci-dessus est en fait très simple, il suffit de le gérer de manière asynchrone, de copier manuellement la valeur dans le threadlocal du thread parent vers le thread enfant et de l'effacer après utilisation .
pseudo-code
// 父线程信息
userId/traceId/trace = LakerThreadlocal.get();
pool.submit(() -> {
// 传递到子线程
LakerThreadlocal.set(userId/traceId/trace);
// 业务处理
......
// 清空
LakerThreadlocal.clear();
}
Le pseudo-code ci-dessus est le code de base pour résoudre le problème, puis j'ai encapsulé cette logique dans EasyAdminMDCThreadPoolExecutor.java .
Les utilisateurs n'ont qu'à modifier le pool de threads précédent, l'exemple de code est le suivant.
ThreadPoolExecutor pool = new EasyAdminMDCThreadPoolExecutor(10,10,"laker");
L'effet log à ce moment est le suivant
20:21:35.547 INFO --- [io-8080-exec-38] [16|497ef9d28d20452f84443c66c6f25354] com.laker.admin.framework.aop.trace.Trace:86 `---
`---[428ms] Controller-ExtLeaveController|pageAll
+---[max]:[414ms] Controller-ExtLeaveController.pageAll
| +---[410ms] Others-leaveService.page
| | +---[3ms] Mapper-com.laker.admin.module.ext.mapper.ExtLeaveMapper.selectPage_mpCount
| | `---[1ms] Mapper-com.laker.admin.module.ext.mapper.ExtLeaveMapper.selectPage
| | +---[302ms] Others-remoteService.call // 这里是被修复处
Adresse complète du code : https://gitee.com/lakernote/easy-admin