一、背景
在微服务框架下,需要通过traceId来定位到整个链路中每个细节的运行情况,这里不引用外部组件,自己来进行实现。当然还保证在多线程环境下的一致性。
二、需求
1)接口入口侧如果有traceId,则获取;如果没有则生成;
2)定义生成traceId的规则;
3)traceId可以默认打印到log中,不需要自己显示定义;
4)通过feign传输到后端服务中
三、实现
3.1)TraceId生成算法
参考:https://help.aliyun.com/document_detail/151840.html
public static String genTraceId() {
try {
StringBuilder sb = new StringBuilder();
InetAddress addr = InetAddress.getLocalHost();
if (null == addr) {
return TRACE_ID_VALUE_DEFAULT;
}
String addrStr = addr.getHostAddress();
if (null == addrStr) {
return TRACE_ID_VALUE_DEFAULT;
}
String[] addrArr = addrStr.split("\\.");
if (addrArr.length != 4) {
return TRACE_ID_VALUE_DEFAULT;
}
sb.append(StringUtils.leftPad(Integer.toHexString(Integer.valueOf(addrArr[0])) , 2, "0"))
.append(StringUtils.leftPad(Integer.toHexString(Integer.valueOf(addrArr[1])) , 2, "0"))
.append(StringUtils.leftPad(Integer.toHexString(Integer.valueOf(addrArr[2])) , 2, "0"))
.append(StringUtils.leftPad(Integer.toHexString(Integer.valueOf(addrArr[3])) , 2, "0"))
.append(System.currentTimeMillis())
.append(RandomUtil.randomNumbers(4))
.append(ProcessHandle.current().pid());
return sb.toString();
} catch (Exception e) {
return TRACE_ID_VALUE_DEFAULT;
}
}
3.2)服务入口日志拦截
@EnableAspectJAutoProxy
@Component
@Aspect
@Slf4j
public class InterfaceLogAspect {
@Around("execution(* com.springboot.k8s.demo.interfaces.*.*(..))")
public Object interfaceAround(ProceedingJoinPoint joinPoint) {
String className = joinPoint.getTarget().getClass().getSimpleName();
Object[] args = joinPoint.getArgs();
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Parameter[] argNames = signature.getMethod().getParameters();
StringBuilder sb = new StringBuilder(className + "." + joinPoint.getSignature().getName() + " -- ");
//获取RequestAttributes
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
//从获取RequestAttributes中获取HttpServletRequest的信息
HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);
TraceIdUtil.initTraceId(request);
StopWatch clock = new StopWatch();
clock.start();
Object retVal = null;
try {
Map<String, Object> paramMap = new HashMap<>();
for (int i = 0; i < argNames.length; i++) {
paramMap.put(argNames[i].getName(), args[i]);
}
LoggerUtils.auditLogInfo(LogLabelEnum.REQUEST_IN.getLogLabel(), paramMap);
retVal = joinPoint.proceed(joinPoint.getArgs());
} catch (Throwable e) {
//..
} finally {
//..
}
return retVal;
}
}
3.3)FeignClient服务之间传递TraceId:
@Service
public class MyRequestInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate template) {
template.header(TraceIdUtil.TRACE_ID_KEY, TraceIdUtil.getTraceId());
}
}
3.4)logback.xml
<appender name="consoleLog" class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>[%-5level][%date{yyyy-MM-dd HH:mm:ss:SSS}][%logger:%line] --%mdc{client} %msg||traceId=%X{Trace-Id}%n</pattern>
</layout>
</appender>
备注:”%X{Trace-Id}”即将MDC中的以Trace-Id为key的值同步到日志中,也就是TraceIdUtil.TRACE_ID_KEY。
完整的TraceIdUtill为:
public class TraceIdUtil {
public static final String TRACE_ID_KEY = "Trace-Id";
public static final String TRACE_ID_VALUE_DEFAULT = "000000";
public static void setTraceId(String traceId) {
//如果参数为空,则设置默认traceId
//将traceId放到MDC中
MDC.put(TRACE_ID_KEY, traceId);
}
public static String getTraceId() {
String traceId = MDC.get(TRACE_ID_KEY);
return StringUtils.isNotBlank(traceId) ? traceId : TRACE_ID_VALUE_DEFAULT;
}
/**
* generate traceId: https://help.aliyun.com/document_detail/151840.html
*/
public static String genTraceId() {
try {
StringBuilder sb = new StringBuilder();
InetAddress addr = InetAddress.getLocalHost();
if (null == addr) {
return TRACE_ID_VALUE_DEFAULT;
}
String addrStr = addr.getHostAddress();
if (null == addrStr) {
return TRACE_ID_VALUE_DEFAULT;
}
String[] addrArr = addrStr.split("\\.");
if (addrArr.length != 4) {
return TRACE_ID_VALUE_DEFAULT;
}
sb.append(StringUtils.leftPad(Integer.toHexString(Integer.valueOf(addrArr[0])) , 2, "0"))
.append(StringUtils.leftPad(Integer.toHexString(Integer.valueOf(addrArr[1])) , 2, "0"))
.append(StringUtils.leftPad(Integer.toHexString(Integer.valueOf(addrArr[2])) , 2, "0"))
.append(StringUtils.leftPad(Integer.toHexString(Integer.valueOf(addrArr[3])) , 2, "0"))
.append(System.currentTimeMillis())
.append(RandomUtil.randomNumbers(4))
.append(ProcessHandle.current().pid());
return sb.toString();
} catch (Exception e) {
return TRACE_ID_VALUE_DEFAULT;
}
}
public static void initTraceId(HttpServletRequest request) {
String traceId = "";
if (request != null) {
//get traceId from request
traceId = request.getHeader(TraceIdUtil.TRACE_ID_KEY);
}
//generate new traceId
if (StringUtils.isBlank(traceId) || TRACE_ID_VALUE_DEFAULT.equals(traceId)){
traceId = TraceIdUtil.genTraceId();
}
TraceIdUtil.setTraceId(traceId);
}
public static void main(String[] args) {
System.out.println(TraceIdUtil.genTraceId());
}
}
四、多线程TraceId复制
4.1)线程配置
@Configuration
public class ThreadPoolConfig {
public static final String ASYNC_EXECUTOR_NAME = "threadPoolTaskExecutor";
@Bean(name=ASYNC_EXECUTOR_NAME)
public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// for passing in request scope context
executor.setTaskDecorator(new ContextCopyingDecorator());
executor.setCorePoolSize(10);
executor.setMaxPoolSize(20);
executor.setQueueCapacity(100);
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setThreadNamePrefix("SeelAsyncThread-");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
executor.initialize();
return executor;
}
}
4.2)定义TaskDecorator
public class ContextCopyingDecorator implements TaskDecorator {
@Override
public Runnable decorate(Runnable runnable) {
RequestAttributes context = RequestContextHolder.currentRequestAttributes();
Map<String, String> contextMap = MDC.getCopyOfContextMap();
return () -> {
try {
RequestContextHolder.setRequestAttributes(context);
MDC.setContextMap(contextMap);
runnable.run();
} finally {
RequestContextHolder.resetRequestAttributes();
}
};
}
}
Author:忆之独秀
Email:leaguenew@qq.com
转载注明出处:
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/10853.html