目录
为了简化后续做类似功能的童鞋操作,故整理一篇针对traceId和自定义属性全链路传递的处理操作,整体调试流程还是耽误了不少时间的。
要做的目标
实现全链路traceId传递,自定义全链路traceUrl标记(traceUrl的规则是比如:比如A-》B-》 C 这样得到的就是 traceUrl=/A/B/C)
思路
1.依赖sleuth传递各种信息 ,使用方式遵循它的规则
2.feign调用处增加处理标记逻辑
3.web服务入口处增加拦截处理标记逻辑
4.异步线程池处理逻辑(sleuth自带功能)
实现
1.项目封装一个基础的jar包,比如trace-jar
A.依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<!-- Import dependency management from Spring Boot -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
B.代码编写
//AOP处理feign逻辑前
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.util.StringUtils;
@Aspect
public class FeignClientTraceAop {
@Around("@within(org.springframework.cloud.openfeign.FeignClient)")
Object around(final ProceedingJoinPoint pjp) throws Throwable {
String traceUrl = Tracer.getBaggage(TraceConstants.TRACE_KEY_TRACE_URL);
if (!StringUtils.hasText(traceUrl)) {
Tracer.createBaggage(TraceConstants.TRACE_KEY_TRACE_URL, TraceConstants.DELIMITER + TraceConstants.getLocalAppName());
}
return pjp.proceed();
}
}
//常量类
import org.springframework.util.StringUtils;
public final class TraceConstants {
public static final String TRACE_KEY_TRACE_ID = "traceId";
public static final String TRACE_KEY_TRACE_URL = "traceUrl";
public static final String DELIMITER = "/";
public static final String APP_NAME_KEY = "appcode";
public static final String APP_NAME_DEFAULT = "MIS_APP_NAME";
private static String appName = null;
public static String getLocalAppName() {
if (appName != null) {
return appName;
}
appName = FrameProperty.get(APP_NAME_KEY);
if (StringUtils.hasText(appName)) {
return appName;
}
appName = APP_NAME_DEFAULT;
return appName;
}
//入口过滤器处理
import org.springframework.util.StringUtils;
import org.slf4j.MDC;
import javax.servlet.*;
import java.io.IOException;
public class TraceExtFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
///拦截一下把自己的 APPName处理掉
String traceUrl = Tracer.getBaggage(TraceConstants.TRACE_KEY_TRACE_URL);
String content;
if (!StringUtils.hasText(traceUrl)) {
content = TraceConstants.DELIMITER + TraceConstants.getLocalAppName();
} else {
content = traceUrl + TraceConstants.DELIMITER + TraceConstants.getLocalAppName();
}
Tracer.createBaggage(TraceConstants.TRACE_KEY_TRACE_URL, content);
MDC.put(TraceConstants.TRACE_KEY_TRACE_URL, content);
filterChain.doFilter(servletRequest, servletResponse);
}
//提供个Tracer静态接口,简化使用,容器不启动,不要直接使用否则逸出
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.cloud.sleuth.Span;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public class Tracer implements ApplicationContextAware {
private static final Logger logger = LoggerFactory.getLogger(com.qhxc.commons.trace.Tracer.class);
private static org.springframework.cloud.sleuth.Tracer sleuthTracer;
public static String getTraceId() {
Span span = sleuthTracer.currentSpan();
return span.context().traceId();
}
public static String getSpanId() {
return sleuthTracer.currentSpan().context().spanId();
}
public static void createBaggage(String key, String value) {
sleuthTracer.createBaggage(key, value);
}
public static String getBaggage(String key) {
return sleuthTracer.getBaggage(key).get();
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
try {
org.springframework.cloud.sleuth.Tracer bean = applicationContext.getBean(org.springframework.cloud.sleuth.Tracer.class);
this.sleuthTracer = bean;
} catch (BeansException e) {
logger.error("初始化trace功能异常,请检查org.springframework.cloud.sleuth.Tracer注入情况", e);
throw e;
}
}
//自动化注入类
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.cloud.sleuth.autoconfig.instrument.web.SleuthWebProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
@Configuration
public class TraceAutoConfiguration {
@Bean
Tracer delegateInnerTracer() {
return new Tracer();
}
@Bean
FeignClientTraceAop feignClientTraceAop() {
return new FeignClientTraceAop();
}
@Bean
public FilterRegistrationBean traceExtFilter() {
FilterRegistrationBean registration = new FilterRegistrationBean();
TraceExtFilter filter = new TraceExtFilter();
registration.setFilter(filter);
registration.addUrlPatterns("/*");
registration.setName("trace-ext-filter");
registration.setOrder(SleuthWebProperties.TRACING_FILTER_ORDER + 1);
return registration;
}
}
//增加配置属性--这个点可以通过代码注入或者写到配置文件加载进去。具体实现可协商自己团队内部实现方式。
// 注释:这两个配置属性就是用来传递和一以及可以写入到MDC 的
spring.sleuth.baggage.correlation-fields=traceUrl
spring.sleuth.baggage-keys=traceUrl
C:使用功能方增加依赖上上一步封装的jar包
//此步骤省略,看团队内部如何共享jar包,比如放到nexus(仓库)上公用,或者其他的使用方式。
理论上上边操作不需要使用方做任何操作,依赖上上边实现的对应功能即可。
模拟测试
假定一个server端提供接口,一个client端调用(feign),然后通过浏览器调用client。
形成链路:前端---》 client----> server
1.server端实现
提供一个简单接口(内部假定有线程池异步处理)
(注意点:线程池对象必须托管到spring容器内部才可传递)
2.client端实现
提供一个api接口 /test/feign,然后调用server端的feign接口
3.效果请求
前端请求url:http://localhost:7777/test/fegin
客户端日志显示:
server端日志显示:
INFO :2021-07-16 11:22:27.473[192.168.1.10][http-nio-8060-exec-1][DirectJDKLog.java:173]o.a.c.c.C.[Tomcat].[localhost].[/] traceid[] traceUrl[] -Initializing Spring DispatcherServlet 'dispatcherServlet'
INFO :2021-07-16 11:22:27.473[192.168.1.10][http-nio-8060-exec-1][FrameworkServlet.java:525]o.s.web.servlet.DispatcherServlet traceid[] traceUrl[] -Initializing Servlet 'dispatcherServlet'
INFO :2021-07-16 11:22:27.477[192.168.1.10][http-nio-8060-exec-1][FrameworkServlet.java:547]o.s.web.servlet.DispatcherServlet traceid[] traceUrl[] -Completed initialization in 4 ms
INFO :2021-07-16 11:22:28.842[192.168.1.10][http-nio-8060-exec-2][MessageController.java:36]c.q.c.x.n.c.MessageController traceid[79c00c369592d43a] traceUrl[/test-feign] -span-traceId:79c00c369592d43a
INFO :2021-07-16 11:22:28.842[192.168.1.10][http-nio-8060-exec-2][MessageController.java:37]c.q.c.x.n.c.MessageController traceid[79c00c369592d43a] traceUrl[/test-feign] -span-spanId:10dc2d03bc0bea8f
INFO :2021-07-16 11:22:28.845[192.168.1.10][http-nio-8060-exec-2][MessageController.java:66]c.q.c.x.n.c.MessageController traceid[79c00c369592d43a] traceUrl[/test-feign] -traceId:null
INFO :2021-07-16 11:22:28.845[192.168.1.10][simpleTaskExecutor-1][MessageController.java:40]c.q.c.x.n.c.MessageController traceid[79c00c369592d43a] traceUrl[/test-feign/notification-message] -thread1:simpleTaskExecutor-1
INFO :2021-07-16 11:22:28.845[192.168.1.10][simpleTaskExecutor-2][MessageController.java:53]c.q.c.x.n.c.MessageController traceid[79c00c369592d43a] traceUrl[/test-feign/notification-message] -thread2:simpleTaskExecutor-2
INFO :2021-07-16 11:22:28.845[192.168.1.10][http-nio-8060-exec-2][MessageController.java:67]c.q.c.x.n.c.MessageController traceid[79c00c369592d43a] traceUrl[/test-feign] -traceUrl:/test-feign/notification-message
INFO :2021-07-16 11:22:28.845[192.168.1.10][simpleTaskExecutor-1][MessageController.java:41]c.q.c.x.n.c.MessageController traceid[79c00c369592d43a] traceUrl[/test-feign/notification-message] -span1-traceId:79c00c369592d43a
INFO :2021-07-16 11:22:28.846[192.168.1.10][simpleTaskExecutor-2][MessageController.java:54]c.q.c.x.n.c.MessageController traceid[79c00c369592d43a] traceUrl[/test-feign/notification-message] -span2-traceId:79c00c369592d43a
INFO :2021-07-16 11:22:28.846[192.168.1.10][simpleTaskExecutor-1][MessageController.java:42]c.q.c.x.n.c.MessageController traceid[79c00c369592d43a] traceUrl[/test-feign/notification-message] -span1-spanId:066d1273f201f91c
INFO :2021-07-16 11:22:28.846[192.168.1.10][simpleTaskExecutor-2][MessageController.java:55]c.q.c.x.n.c.MessageController traceid[79c00c369592d43a] traceUrl[/test-feign/notification-message] -span2-spanId:2aded07e33b5cfa9
INFO :2021-07-16 11:22:28.846[192.168.1.10][simpleTaskExecutor-1][MessageController.java:45]c.q.c.x.n.c.MessageController traceid[79c00c369592d43a] traceUrl[/test-feign/notification-message] -111traceId:null
INFO :2021-07-16 11:22:28.846[192.168.1.10][simpleTaskExecutor-2][MessageController.java:58]c.q.c.x.n.c.MessageController traceid[79c00c369592d43a] traceUrl[/test-feign/notification-message] -2222traceId:null
INFO :2021-07-16 11:22:28.846[192.168.1.10][simpleTaskExecutor-1][MessageController.java:46]c.q.c.x.n.c.MessageController traceid[79c00c369592d43a] traceUrl[/test-feign/notification-message] -111traceUrl:/test-feign/notification-message
INFO :2021-07-16 11:22:28.846[192.168.1.10][simpleTaskExecutor-2][MessageController.java:59]c.q.c.x.n.c.MessageController traceid[79c00c369592d43a] traceUrl[/test-feign/notification-message] -222traceUrl:/test-feign/notification-message
INFO :2021-07-16 11:22:28.862[192.168.1.10][http-nio-8060-exec-2][AuthUtil.java:31]c.q.cloud.xch.notice.utils.AuthUtil traceid[79c00c369592d43a] traceUrl[/test-feign] -验证签名调用方签名:CrBAUzw+sRNfOeggxo5mwEhUR60=
INFO :2021-07-16 11:22:28.904[192.168.1.10][http-nio-8060-exec-2][TokenUtil.java:48]c.q.cloud.xch.notice.utils.TokenUtil traceid[79c00c369592d43a] traceUrl[/test-feign] -生成签名result=CrBAUzw+sRNfOeggxo5mwEhUR60=
INFO :2021-07-16 11:22:28.905[192.168.1.10][http-nio-8060-exec-2][AuthUtil.java:33]c.q.cloud.xch.notice.utils.AuthUtil traceid[79c00c369592d43a] traceUrl[/test-feign] -验证签名新生成签名:CrBAUzw+sRNfOeggxo5mwEhUR60=
INFO :2021-07-16 11:22:28.938[192.168.1.10][http-nio-8060-exec-2][AbstractAopLog.java:35]c.q.c.x.n.log.aoplog.ServiceAopLog traceid[79c00c369592d43a] traceUrl[/test-feign] -M:MessageServiceImpl.acceptMessage P:{"funCode":"SMS_MAIL_001","funSecret":"TZ51QXHVCuilmLw8","params":[{"key":"email","value":"laowang@xxxx.com"},{"key":"mobile","value":"18500198888"}],"userId":123,"areaCode":"ZH","uniqueId":"ABC-1626253170864","authToken":"CrBAUzw+sRNfOeggxo5mwEhUR60=","timeStamp":1626405748769} R: {"code":0,"msg":"ok","data":{"messageId":"43"}}
INFO :2021-07-16 11:22:28.939[192.168.1.10][http-nio-8060-exec-2][AbstractAopLog.java:35]c.q.c.x.n.l.aoplog.ControllerAopLog traceid[79c00c369592d43a] traceUrl[/test-feign] -M:MessageController.sendEventMessage P:{"funCode":"SMS_MAIL_001","funSecret":"TZ51QXHVCuilmLw8","params":[{"key":"email","value":"laowang@xxxx.com"},{"key":"mobile","value":"18500198888"}],"userId":123,"areaCode":"ZH","uniqueId":"ABC-1626253170864","authToken":"CrBAUzw+sRNfOeggxo5mwEhUR60=","timeStamp":1626405748769} R: {"code":0,"msg":"ok","data":{"messageId":"43"}}
以上就实现了traceUrl的串联服务调用功能,以上功能仅为了测试,若应用到企业级服务还需要封装处理。一般可以实现,类似标记传递后,做根据标记流量负载分流控制,服务治理环境标记,限制版本,监控注册客户端等,还有日志类功能记录等等。
注意点:logback日志配置注意点
参考网上的:%X{traceId}::%X{X-B3-TraceId:-} %X{traceUrl}
X-B3-TraceId 在这个版本视乎是读取不到的,各种测试都没有;
断点接口:ch.qos.logback.classic.pattern.MDCConverter#convert 看不到这个变量只有traceId和spanId
参考: