呼叫鏈監控 CAT 之 URL埋點實踐

程式設計師果果發表於2019-04-16

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 之 URL埋點實踐

上圖是本節例項的埋點圖,首先 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 監控介面可以看到本節例項的服務。

呼叫鏈監控 CAT 之 URL埋點實踐

點開 “logView” 可以看到完整的呼叫鏈資訊。

呼叫鏈監控 CAT 之 URL埋點實踐

點選 “Graph” 檢視圖表形式的呼叫鏈資訊。

呼叫鏈監控 CAT 之 URL埋點實踐

原始碼

github.com/gf-huanchup…

參考

github.com/dianping/ca…

歡迎掃碼或微信搜尋公眾號《程式設計師果果》關注我,關注有驚喜~

呼叫鏈監控 CAT 之 URL埋點實踐

相關文章