调用链监控 CAT 之 URL埋点实践

5次阅读

共计 4437 个字符,预计需要花费 12 分钟才能阅读完成。

URL 监控埋点作用

一个 http 请求来了之后,会自动打点,能够记录每个 url 的访问情况,并将以此请求后续的调用链路串起来,可以在 cat 上查看 logview
可以在 cat Transaction 及 Event 页面上都看到 URL 和 URL.Forward(如果有 Forward 请求的话)两类数据;Transaction 数据中 URL 点进去的数据就是被访问的具体 URL(去掉参数的前缀部分)
请将 catFilter 存放 filter 的第一个,这样可以保证最大可能性监控所有的请求

实践
工程说明

工程名
端口
作用

cat-ui
8082
调用入口服务

cat-business-consumer
8083
业务消费服务

cat-order-service
8084
订单服务

cat-storage-service
8085
库存服务

上图是本节实例的埋点图,首先 cat-ui 的入口 和 调用点 加入 cat 埋点,cat-business-consumer 的入口和调用点加入埋点,cat-order-service 和 cat-storage-service 不再调用其他微服务,所以只在入口加入埋点。通过这样的埋点,可以组成一条完整的调用链。
关键代码
调用链上下文通用类
CatContextImpl.java
/**
* Cat.context 接口实现类,用于 context 调用链传递,相关方法 Cat.logRemoteCall() 和 Cat.logRemoteServer()
*/
public class CatContextImpl implements Cat.Context {

private Map<String, String> properties = new HashMap<>(16);

@Override
public void addProperty(String key, String value) {
properties.put(key, value);
}

@Override
public String getProperty(String key) {
return properties.get(key);
}
}
CatHttpConstants
/**
* 添加 header 常量,用于 http 协议传输 rootId、parentId、childId 三个 context 属性
*/
public class CatHttpConstants {

/**
* http header 常量
*/
public static final String CAT_HTTP_HEADER_ROOT_MESSAGE_ID = “X-CAT-ROOT-MESSAGE-ID”;
public static final String CAT_HTTP_HEADER_PARENT_MESSAGE_ID = “X-CAT-ROOT-PARENT-ID”;
public static final String CAT_HTTP_HEADER_CHILD_MESSAGE_ID = “X-CAT-ROOT-CHILD-ID”;

}
CatServletFilter
/**
* http 协议传输,远程调用链目标端接收 context 的 filter,
* 通过 header 接收 rootId、parentId、childId 并放入 CatContextImpl 中,调用 Cat.logRemoteCallServer() 进行调用链关联
* 注: 若不涉及调用链,则直接使用 cat-client.jar 中提供的 filter 即可
* 使用方法(视项目框架而定):
* 1、web 项目:在 web.xml 中引用此 filter
* 2、Springboot 项目,通过注入 bean 的方式注入此 filter
*/
public class CatServletFilter implements Filter {

private String[] urlPatterns = new String[0];

@Override
public void init(FilterConfig filterConfig) throws ServletException {
String patterns = filterConfig.getInitParameter(“CatHttpModuleUrlPatterns”);
if (patterns != null) {
patterns = patterns.trim();
urlPatterns = patterns.split(“,”);
for (int i = 0; i < urlPatterns.length; i++) {
urlPatterns[i] = urlPatterns[i].trim();
}
}
}

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;

String url = request.getRequestURL().toString();
for (String urlPattern : urlPatterns) {
if (url.startsWith(urlPattern)) {
url = urlPattern;
}
}

CatContextImpl catContext = new CatContextImpl();
catContext.addProperty(Cat.Context.ROOT, request.getHeader(CatHttpConstants.CAT_HTTP_HEADER_ROOT_MESSAGE_ID));
catContext.addProperty(Cat.Context.PARENT, request.getHeader(CatHttpConstants.CAT_HTTP_HEADER_PARENT_MESSAGE_ID));
catContext.addProperty(Cat.Context.CHILD, request.getHeader(CatHttpConstants.CAT_HTTP_HEADER_CHILD_MESSAGE_ID));
Cat.logRemoteCallServer(catContext);

Transaction t = Cat.newTransaction(CatConstants.TYPE_URL, url);

try {

Cat.logEvent(“Service.method”, request.getMethod(), Message.SUCCESS, request.getRequestURL().toString());
Cat.logEvent(“Service.client”, request.getRemoteHost());

filterChain.doFilter(servletRequest, servletResponse);

t.setStatus(Transaction.SUCCESS);
} catch (Exception ex) {
t.setStatus(ex);
Cat.logError(ex);
throw ex;
} finally {
t.complete();
}
}

@Override
public void destroy() {

}
}
本节实例中每个工程都会用到调用链上下文通用类。
cat-ui 工程
CatRestInterceptor
@Component
public class CatRestInterceptor implements ClientHttpRequestInterceptor {

@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
Transaction t = Cat.newTransaction(CatConstants.TYPE_REMOTE_CALL, request.getURI().toString());

try {
HttpHeaders headers = request.getHeaders();

// 保存和传递 CAT 调用链上下文
Cat.Context ctx = new CatContextImpl();
Cat.logRemoteCallClient(ctx);
headers.add(CatHttpConstants.CAT_HTTP_HEADER_ROOT_MESSAGE_ID, ctx.getProperty(Cat.Context.ROOT));
headers.add(CatHttpConstants.CAT_HTTP_HEADER_PARENT_MESSAGE_ID, ctx.getProperty(Cat.Context.PARENT));
headers.add(CatHttpConstants.CAT_HTTP_HEADER_CHILD_MESSAGE_ID, ctx.getProperty(Cat.Context.CHILD));

// 保证请求继续被执行
ClientHttpResponse response = execution.execute(request, body);
t.setStatus(Transaction.SUCCESS);
return response;
} catch (Exception e) {
Cat.getProducer().logError(e);
t.setStatus(e);
throw e;
} finally {
t.complete();
}

}
}
CatServletFilter 对 cat-ui 的入口进行了埋点,CatRestInterceptor 实现 ClientHttpRequestInterceptor 接口 可以对 RestTemplate 发起的请求进行拦截,利用这一点对调用点埋点,同时在 Http Header 中存入 调用链的上下文,将调用链传递下去。
cat-business-consumer、cat-order-service、cat-storage-service 中的埋点与 cat-ui 埋点的方式相同。
测试
发起请求
curl http://127.0.0.1:8082/start
cat 监控界面可以看到本节实例的服务。

点开“logView”可以看到完整的调用链信息。

点击“Graph”查看图表形式的调用链信息。

源码
https://github.com/gf-huanchu…
参考
https://github.com/dianping/c…
欢迎扫码或微信搜索公众号《程序员果果》关注我,关注有惊喜~

正文完
 0