目录
一、简介
二、思路
三、spring mvc
四、示例代码
这篇文章总结一下mvc链路跟踪器的实现,这篇文章的内容相对比较简单。
上一篇文章其实已经顺带把mvc链路跟踪器的思路说了
1、初始化上下文,新建ThreadLoacal
2、从http中获取traceId信息
如果有说明是被别的服务调用,将http中的traceId和rpceId保存到上下文
如果没有说明请求来自于客户端,新建链路跟踪信息,保存到上下文
3、处理结束后将上下文销毁
《springCloud微服务系列——链路跟踪第一篇——设计思路以及通用链路跟踪器》
如果controller层为spring mvc,那么可以使用拦截器拦截请求进行链路跟踪,非常简单。
public class MvcTracker implements Tracker {
private static final FastDateFormat ISO_DATETIME_TIME_ZONE_FORMAT_WITH_MILLIS = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss.SSSZZ");
protected static final String BEGIN_TIME_KEY = "beginTime";
protected static final String RPC_TRACE_INFO_VO_KEY = "rpcTraceInfoVO";
protected TraceClient traceClient;
public MvcTracker(TraceClient traceClient) {
this.traceClient = traceClient;
}
@Override
public void preHandle(MvcHolder holder) {
initTraceData(holder.getRequest(), holder.getHandler(), holder.getProfile());
}
@Override
public void postHandle(MvcHolder holder) {
finishTraceData(holder.getRequest(), holder.getResponse(), holder.getException());
}
private void initTraceData (HttpServletRequest request, Object handler, String profile) {
// 开始时间
request.setAttribute(BEGIN_TIME_KEY, System.currentTimeMillis());
// 为当前线程设置上下文
TraceContext.init();
// 链路跟踪信息基础对象
TraceInfo traceInfo = new TraceInfo();
String traceIdFromHeader = request.getHeader(TraceInfo.TRACE_ID_KEY);
Optional.ofNullable(traceIdFromHeader)
.ifPresent(traceId->{
traceInfo.setTraceId(traceId);
});
Optional.ofNullable(
request.getHeader(TraceInfo.RPC_ID_KEY)
).ifPresent(rpcId->{
traceInfo.setRpcId(rpcId);
});
if(traceIdFromHeader != null) {
traceInfo.setRootRpcId(traceInfo.getRpcId());
} else {
traceInfo.setRootRpcId(TraceInfo.RE_ORIGINAL_ROOT_RPC_ID);
}
// 封装链路跟踪数据
if(handler != null && handler instanceof HandlerMethod && traceIdFromHeader == null) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
Gson gson =new Gson();
String serviceName = null;
try {
serviceName = TargetUtils.getTarget(handlerMethod.getBean()).getClass().getSimpleName();
} catch (Exception e) {
serviceName = handlerMethod.getBean().getClass().getSimpleName();
}
if(serviceName == null) {
serviceName = handlerMethod.getBean().getClass().getSimpleName();
}
RpcTraceInfoVO rpcTraceInfoVO = new RpcTraceInfoVO();
rpcTraceInfoVO.setProfile(profile);
rpcTraceInfoVO.setRequestDateTime(ISO_DATETIME_TIME_ZONE_FORMAT_WITH_MILLIS.format(Calendar.getInstance().getTime()));
rpcTraceInfoVO.setTraceId(traceInfo.getTraceId());
rpcTraceInfoVO.setRpcId(traceInfo.getRpcId());
rpcTraceInfoVO.setRpcType(RpcTypeEnum.HTTP.name());
rpcTraceInfoVO.setServiceCategory("spring mvc");
rpcTraceInfoVO.setServiceName(handlerMethod.getBean().getClass().getName().split("\\$\\$")[0]);
rpcTraceInfoVO.setMethodName(handlerMethod.getMethod().getName());
rpcTraceInfoVO.setRequestParam(gson.toJson(request.getParameterMap()));
rpcTraceInfoVO.setServiceHost(HostUtil.getIP()+":"+request.getLocalPort()+request.getServletPath());
rpcTraceInfoVO.setClientHost(HostUtil.getIP(request));
request.setAttribute(RPC_TRACE_INFO_VO_KEY, rpcTraceInfoVO);
}
// 增加层级
//traceInfo.addHierarchy();
// 链路跟踪信息保存到当前线程上下文中
TraceContext.putTraceInfo(traceInfo);
MDC.put(TraceInfo.TRACE_ID_KEY, traceInfo.getTraceId());
MDC.put(TraceInfo.RPC_ID_KEY, traceInfo.getRpcId());
}
private void finishTraceData(HttpServletRequest request, HttpServletResponse response, Exception ex) {
try {
String traceIdFromHeader = request.getHeader(TraceInfo.TRACE_ID_KEY);
if(traceIdFromHeader != null)
return;
RpcTraceInfoVO rpcTraceInfoVO = (RpcTraceInfoVO) request.getAttribute(RPC_TRACE_INFO_VO_KEY);
if(rpcTraceInfoVO != null) {
long beginTime = (Long) request.getAttribute(BEGIN_TIME_KEY);
rpcTraceInfoVO.setRunTime(System.currentTimeMillis() - beginTime);
if(ex == null) {
rpcTraceInfoVO.setResult(RpcTraceInfoVO.RESULT_SUCCESS);
} else {
rpcTraceInfoVO.setResult(RpcTraceInfoVO.RESULT_FAILURE);
rpcTraceInfoVO.setResponseInfo(ex.getMessage());
}
Gson gson = new Gson();
log.debug(gson.toJson(rpcTraceInfoVO));
traceClient.sendTraceInfo(rpcTraceInfoVO);
}
} catch (Exception e) {
log.error(e.getMessage(), e);
} finally {
request.removeAttribute(RPC_TRACE_INFO_VO_KEY);
TraceContext.removeTraceInfo();
MDC.remove(TraceInfo.TRACE_ID_KEY);
MDC.remove(TraceInfo.RPC_ID_KEY);
}
}
}
public class SpringMvcTracker extends MvcTracker implements Tracker {
public SpringMvcTracker (TraceClient traceClient) {
super(traceClient);
this.traceClient = traceClient;
}
}
@Slf4j
public class TraceInteceptor extends HandlerInterceptorAdapter {
private String profile;
private Tracker tracker;
public TraceInteceptor (String profile, Tracker tracker) {
super();
this.profile = profile;
this.tracker = tracker;
}
/**
*
* http请求处理前操作
*
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
log.info("http preHandle");
if(isTraceHandler(handler)) {
MvcHolder mvcHolder = new MvcHolder();
mvcHolder.setProfile(profile);
mvcHolder.setRequest(request);
mvcHolder.setResponse(response);
mvcHolder.setHandler(handler);
tracker.preHandle(mvcHolder);
}
return super.preHandle(request, response, handler);
}
/**
*
* http请求处理完操作
*
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable Exception ex) throws Exception {
log.info("http afterCompletion");
MvcHolder mvcHolder = new MvcHolder();
mvcHolder.setRequest(request);
mvcHolder.setResponse(response);
mvcHolder.setHandler(handler);
mvcHolder.setException(ex);
tracker.postHandle(mvcHolder);
}
/**
*
* Title: isTraceHandler
* Description: 是否被@Trace注释
* @param handler
* @return
*/
public boolean isTraceHandler(Object handler) {
Object annoation = getAnnotation(handler);
if (annoation == null)
return false;
Trace trace = (Trace) annoation;
if(!trace.value().getName().equals(SpringMvcTracker.class.getName()))
return false;
return true;
}
/**
*
* Title: getAnnotation
* Description: 获得@Trace注释
* @param handler
* @return
*/
private Object getAnnotation(Object handler) {
if(handler == null)
return null;
if (!(handler instanceof HandlerMethod))
return null;
Object annoation = null;
HandlerMethod handlerMethod = (HandlerMethod) handler;
annoation = handlerMethod.getMethodAnnotation(Trace.class);
return annoation;
}
}
https://github.com/wulinfeng2/luminary-component