CAT中實現非同步請求的呼叫鏈檢視
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技術乾貨