I. Configuration and use

1. The pom

<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-sleuth</artifactId> < version > 2.0.1. RELEASE < / version > < / dependency >Copy the code

2. Logback. XML configuration

XML expression of the business system is modified. TraceId is recorded in: X-b3-tracEID. SpanId is not added because it is rarely paid attention to.

<! <property name="PATTERN" value="%-5level [%d{HH:mm:ss.SSS}] [traceId:%yellow(%X{x-B3-tracEID})] [%thread] %logger{36} - %msg%n"/>Copy the code

3. BaseResult writing

import lombok.Data; /** * @author baiyan * @time 2020/11/13 13:17 */ @Data public class BaseResult { /** * httpCode */ private Integer code;  /** * service code */ private String errorCode; /** * private String message; /** * Link ID */ private String traceId; public BaseResult() { } public BaseResult(Integer code, String message) { this.code = code; this.message = message; } public BaseResult(Integer code, String errorCode, String message) { this.code = code; this.errorCode = errorCode; this.message = message; } protected static final Integer CODE_SUCCESS = 200; protected static final Integer CODE_SYSTEM_ERROR = 500; protected static final Integer CODE_CLIENT_ERROR = 400; Protected static final String MESSAGE_SUCCESS = "Request succeeded "; }Copy the code

4. Return body attribute assignment

/ track link * * * * * @ author baiyan * @ date 2020/12/03 * / @ ControllerAdvice public class AddTraceIdResponseBodyAdvice implements ResponseBodyAdvice<BaseResult> { @Autowired private Tracer tracer; @Override public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<? >> converterType) { return BaseResult.class.isAssignableFrom(returnType.getParameterType()); } @Override public BaseResult beforeBodyWrite(BaseResult body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<? >> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) { body.setTraceId(tracer.currentSpan().context().traceIdString()); return body; }}Copy the code

5.SpringContextUtil

/** * SpringContext Utility class ** @author baiyan * @date 2020/11/13 */ @Component public class SpringContextUtil implements Private static ApplicationContextAware {/** * spring ApplicationContext */ private static ApplicationContext; /** * Implement the callback method for the ApplicationContextAware interface. Set context * @param applicationContext */ @Override public void setApplicationContext(applicationContext) applicationContext) { SpringContextUtil.applicationContext = applicationContext; } /** * @return ApplicationContext */ public static ApplicationContext getApplicationContext() { return applicationContext; } /** * Obtain an Object * @param name * @return Object * @throws BeansException */ public static Object getBean(String Name) throws  BeansException { return applicationContext.getBean(name); } @param clazz * @return Object * @throws BeansException */ public static <T> T getBean(Class<T> clazz) throws BeansException { return applicationContext.getBean(clazz); }}Copy the code

6.TraceUtil

public class TraceUtil {

    public static String getTraceId(){
        Tracer tracer=(Tracer)SpringContextUtil.getBean("tracer");
        if(tracer!=null){
            Span span=tracer.currentSpan();
            TraceContext traceContext=span.context();
            return traceContext.traceIdString();
        }
        return null;
    }
}
Copy the code

7. Use the controller layer

Front end example:A log example is as follows:When the configuration is complete, any logs printed by the console will carry the thread’s log (passed internally via ThreadLocal), including calls to feign (passed between feign).

2. Multithreaded pass traceId

Problem 1.

TraceId can be passed within a single thread, and multiple threads can pass parameter issues. The current CurrentTraceContext Zipkin class provides a way to handle threads and thread pools by implementing Runnable and re-implementing the run method, which solves the problem of thread pools. Of course, it provides not only the method of creating threads, but also the method of thread pools and Callable. This method cannot retrieve the parent thread traceId from the thread pool.

2. Solutions

The thread pool to solve After many tests, using LazyTraceThreadPoolTaskExecutor traceId can be realized. The thread pools that should be used for traceId wrapping in the Base package are shown below

interface

/** * Static blocking thread pool * @author baiyan * @since 2020/3/23 */ public interface BlockThreadPoolService {/** * add task to thread pool * @param Task * @return task (must implement Runnable) */ Future<? > addTask(Runnable task); /** * Execute a batch of tasks asynchronously until the task is completed * @param task */ void runTasksUntilEnd(List<Runnable> task); /** * Add loop running tasks to thread pool * @param task (must implement Runnable interface) * @param interval */ void loopTask(Runnable task, long interval); /** * Add loop tasks to thread pool * @param task (must implement Runnable) * @param interval (in milliseconds) * @param delay (in milliseconds) * @param delay (in milliseconds) Indicates that the task starts to be scheduled after delay ms */ void loopTask(Runnable task, long interval, long delay); /** * stop the thread pool */ public void stop(); }Copy the code

The implementation class

/ * * * thread ChiJi * @ author baiyan * @ since 2020/3/24 * / @ Slf4j public class BlockThreadPoolServiceBase implements BlockThreadPoolService {/** # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # / / @setset_name () private int maximumPoolSize = 150; /** private int keepAliveTime = 60; /** private int keepAliveTime = 60; /** private int queueSize = 100; /** private int queueSize = 100; /** is initialized */ @setterprivate Boolean inited = false; /** private ScheduledExecutorService ScheduledExecutorService; / * * trace tracking thread pool * / private LazyTraceThreadPoolTaskExecutor LazyTraceThreadPoolTaskExecutor; Public void init() {if(inited) {return; } ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor(); threadPoolTaskExecutor.setCorePoolSize(corePoolSize); threadPoolTaskExecutor.setMaxPoolSize(maximumPoolSize); threadPoolTaskExecutor.setQueueCapacity(queueSize); threadPoolTaskExecutor.setKeepAliveSeconds(keepAliveTime); threadPoolTaskExecutor.setThreadNamePrefix("nssa-Thread-"); threadPoolTaskExecutor.setAllowCoreThreadTimeOut(true); threadPoolTaskExecutor.initialize(); this.lazyTraceThreadPoolTaskExecutor = new LazyTraceThreadPoolTaskExecutor(SpringContextUtil.getApplicationContext(),threadPoolTaskExecutor); inited = true; } /** * Add task * @param task * @return */ @override public Future<? > addTask(Runnable task) { if(! inited) { init(); } return this.lazyTraceThreadPoolTaskExecutor.submit(TtlRunnable.get(task)); } @Override public void stop() { lazyTraceThreadPoolTaskExecutor.shutdown(); if(scheduledExecutorService ! = null) { scheduledExecutorService.shutdownNow(); } } @Override public synchronized void loopTask(Runnable task, long interval) { loopTask(task, interval, 0); } @Override public void loopTask(Runnable task, long interval, long delay) { if(scheduledExecutorService == null) { ThreadFactory threadFactory = new ScheduledThreadFactory("schedule-pool-%d-%s"); scheduledExecutorService = Executors.newScheduledThreadPool(1, threadFactory); } int minInterval=100; If (interval < minInterval) {throw new IllegalArgumentException(" not allowed to schedule circular tasks less than 100ms "); } scheduledExecutorService.scheduleAtFixedRate(TtlRunnable.get(task), delay, interval, TimeUnit.MILLISECONDS); } @Override public void runTasksUntilEnd(List<Runnable> tasks) { List<Future<? >> futures = new ArrayList<Future<? > > (); for(Runnable task : tasks) { futures.add(addTask(task)); } for(Future<? > f : futures) { try { f.get(); } catch (Exception e) { log.warn("", e); }}} / * * * for singleton thread pool example * @ return * / protected LazyTraceThreadPoolTaskExecutor getExecutorService () {return lazyTraceThreadPoolTaskExecutor; } static class ScheduledThreadFactory implements ThreadFactory {private static final AtomicInteger poolNumber = new AtomicInteger(1); private final ThreadGroup group; private final AtomicInteger threadNumber = new AtomicInteger(1); private final String namePrefix; ScheduledThreadFactory(String namePrefix) { SecurityManager s = System.getSecurityManager(); group = (s ! = null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup(); this.namePrefix = String.format(namePrefix, poolNumber.getAndIncrement(), "%d"); } String getThreadName() { return String.format(namePrefix, threadNumber.getAndIncrement()); } @Override public Thread newThread(Runnable r) { Thread t = new Thread(group, r, getThreadName(), 0); if (! t.isDaemon()){ t.setDaemon(true); } if (t.getPriority() ! = Thread.NORM_PRIORITY){ t.setPriority(Thread.NORM_PRIORITY); } return t; }}}Copy the code

The native JDK provides InheritableThreadLocal, which solves the problem of passing variables between a parent thread and a parent thread, but the parent thread loses data when passed to the thread pool. To solve this problem, ali’s open source component TTL is used to solve parent-child thread variable passing when tasks are submitted to the thread pool

Interested can have a look at the corresponding source code, design is very clever, attached link: github.com/alibaba/tra…

3. Service components Components

  1. Multithreading is used because it takes time to create and destroy the context of a thread because there is a thread pool. It is recommended that tasks with short execution time or frequent creation of threads, such as message sending, be handled in the thread pool.
  2. It is strongly recommended that the application not have too many thread pools. A maximum of two thread pools are recommended. Thread pools can be divided or not divided according to service conditions. For a large number of timing situations, it is recommended to use MQ to peak MQ and reduce the CPU load of the application running.
  3. Recommendations are not relevant to the current main thread, but may block code segments of the main thread from being given to the thread pool for processing, such as: MQ message sending.

Create a singleton thread pool using the base package thread pool instance

/** * public class MessageThreadPool {private volatile static. */ public class MessageThreadPool {private volatile static BlockThreadPoolService blockThreadPoolService; public static BlockThreadPoolService getThreadPool() { if(blockThreadPoolService == null){ synchronized (MessageThreadPool.class){ if(blockThreadPoolService == null) { blockThreadPoolService = new BlockThreadPoolServiceBase(); } } } return blockThreadPoolService ; }}Copy the code

Single task call

MessageThreadPool. GetThreadPool (.) addTask (() - > System. Out. Println (" test "));Copy the code

Multitasking call

Runnable r1=()->log.info("hello1:");
Runnable r2=()->log.info("hello1:");
Runnable r3=()->log.info("hello1:");
Runnable r4=()->log.info("hello1:");
MessageThreadPool.getThreadPool().runTasksUntilEnd(
        Lists.newArrayList(r1,r2,r3,r4)
);
Copy the code

Pass traceId in RabbitMQ

To send a message: To send a message, add the traceId of the current thread to the header. After receiving the message, use the @header parameter to remove the corresponding traceId, and then call the SLF4J tool class, mdC. put(” X-b3-tracEID “,traceId), to trace the corresponding link information.

This is just one way to solve this problem, but there is a problem with the step of receiving the message, and if there is a thread pool, it can’t be delivered, so it needs to be explored.