URL监控埋点作用一个http请求来了之后,会自动打点,能够记录每个url的访问情况,并将以此请求后续的调用链路串起来,可以在cat上查看logview可以在cat Transaction及Event 页面上都看到URL和URL.Forward(如果有Forward请求的话)两类数据;Transaction数据中URL点进去的数据就是被访问的具体URL(去掉参数的前缀部分)请将catFilter存放filter的第一个,这样可以保证最大可能性监控所有的请求实践工程说明工程名端口作用cat-ui8082调用入口服务cat-business-consumer8083业务消费服务cat-order-service8084订单服务cat-storage-service8085库存服务

上图是本节实例的埋点图,首先 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

@Componentpublic 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-huanchupk/SpringCloudLearning/tree/master/chapter15

参考

https://github.com/dianping/cat/wiki

欢迎关注我的公众号《程序员果果》,关注有惊喜~~