CAT簡介
CAT(Central Application Tracking),是美團點評基於 Java 開發的一套開源的分布式實時監控系統。美團點評基礎架構部希望在基礎存儲、高性能通信、大規模在線訪問、服務治理、實時監控、容器化及集群智能調度等領域提供業界領先的、統一的解決方案,CAT 目前在美團點評的產品定位是應用層的統一監控組件,在中間件(RPC、數據庫、緩存、MQ 等)框架中得到廣泛應用,為各業務線提供系統的性能指標、健康狀況、實時告警等服務。
歡迎關注微信公眾號:萬貓學社,每周一分享Java技術干貨。
准備工作
對於同步請求API,CAT服務端自然是可以看到的。同步請求API的實例可以參考之前的文章《五分鍾后,你將學會在SpringBoot項目中如何集成CAT調用鏈》。但對於異步請求API,因為不在同一線程中,在子線程中無法獲取到父線程消息樹,所以在CAT服務端是無法看到的對應請求。
歡迎關注微信公眾號:萬貓學社,每周一分享Java技術干貨。
首先,寫一個類實現Cat.Context接口,用於存放消息樹的上下文信息:
public class CatContext implements Cat.Context {
private Map<String, String> properties = new HashMap<>();
@Override
public void addProperty(String key, String value) {
properties.put(key, value);
}
@Override
public String getProperty(String key) {
return properties.get(key);
}
@Override
public String toString() {
return "CatContext{"
+ "properties=" + properties + '}';
}
}
我們可以先父線程消息樹的上下文信息保存下來,然后在子線程使用。先寫一個存放上下文信息的地方:
public class ContextWarehouse {
private static ThreadLocal<CatContext> contextThreadLocal = new ThreadLocal();
public static void setContext(final CatContext context) {
contextThreadLocal.set(context);
}
public static CatContext getContext() {
//先從ContextWarehouse中獲取上下文信息
CatContext context = contextThreadLocal.get();
if (context == null) {
context = new CatContext();
Cat.logRemoteCallClient(context);
}
return context;
}
}
實現Callable接口,創建一個自定義的類,實現了在子線程中存放父線程的上下文信息的功能:
public class OneMoreCallable<V> implements Callable<V> {
private CatContext catContext;
private Callable<V> callable;
public DdCallable(final Callable<V> callable) {
this.callable = callable;
this.catContext = new CatContext();
//獲取父線程消息樹的上下文信息
Cat.logRemoteCallClient(this.catContext);
}
@Override
public V call() throws Exception {
//保存父線程消息樹的上下文信息到子線程
ContextWarehouse.setContext(this.catContext);
return callable.call();
}
}
歡迎關注微信公眾號:萬貓學社,每周一分享Java技術干貨。
定義一些常量,在調用API時作為header中的key:
public class CatHttpConstants {
public static final String CAT_HTTP_HEADER_CHILD_MESSAGE_ID = "DD-CAT-CHILD-MESSAGE-ID";
public static final String CAT_HTTP_HEADER_PARENT_MESSAGE_ID = "DD-CAT-PARENT-MESSAGE-ID";
public static final String CAT_HTTP_HEADER_ROOT_MESSAGE_ID = "DD-CAT-ROOT-MESSAGE-ID";
}
埋點時,在調用API的HttpClient工具類中統一增加代碼,以GET方式為例:
public class HttpClientUtil {
public static String doGet(String url) throws IOException {
HttpGet httpGet = new HttpGet(url);
CloseableHttpResponse response = null;
CloseableHttpClient httpClient = HttpClientBuilder.create().build();
String content = null;
Transaction t = Cat.newTransaction(CatConstants.TYPE_CALL, url);
try {
CatContext context = ContextWarehouse.getContext();
httpGet.setHeader(CatHttpConstants.CAT_HTTP_HEADER_ROOT_MESSAGE_ID, context.getProperty(Cat.Context.ROOT));
httpGet.setHeader(CatHttpConstants.CAT_HTTP_HEADER_PARENT_MESSAGE_ID, context.getProperty(Cat.Context.PARENT));
httpGet.setHeader(CatHttpConstants.CAT_HTTP_HEADER_CHILD_MESSAGE_ID, context.getProperty(Cat.Context.CHILD));
response = httpClient.execute(httpGet);
if (response.getStatusLine().getStatusCode() == 200) {
content = EntityUtils.toString(response.getEntity(), "UTF-8");
t.setStatus(Transaction.SUCCESS);
}
} catch (Exception e) {
Cat.logError(e);
t.setStatus(e);
throw e;
} finally {
if (response != null) {
response.close();
}
if (httpClient != null) {
httpClient.close();
}
t.complete();
}
return content;
}
}
歡迎關注微信公眾號:萬貓學社,每周一分享Java技術干貨。
異步請求實例
下面寫一個異步請求的實例,通過多個商品ID異步獲取對應的商品詳細信息:
public class ProductService {
/**
* 聲明一個大小固定為10的線程池
*/
private static ExecutorService executor = Executors.newFixedThreadPool(10);
/**
* 通過商品ID列表異步獲取對應的商品詳細信息
*
* @param productIds 商品ID列表
* @return 對應的商品詳細信息
*/
public List<String> findProductInfo(List<Long> productIds) {
List<Future<String>> futures = new ArrayList<>();
for (Long productId : productIds) {
futures.add(executor.submit(new DdCallable(() -> {
try {
//調用獲取商品詳細信息的API
return HttpClientUtil.doGet("http://api.product/get?id=" + productId);
} catch (Exception e) {
return "";
}
})));
}
List<String> productInfos = new ArrayList<>();
for (Future<String> future : futures) {
try {
//異步獲取對應商品詳細信息
productInfos.add(future.get());
} catch (Exception e) {
productInfos.add("");
}
}
return productInfos;
}
}
這樣寫以后,在CAT服務端的Transaction報表中就可以查看到異步請求了。
歡迎關注微信公眾號:萬貓學社,每周一分享Java技術干貨。