springboot專案整合大眾點評cat
阿新 • • 發佈:2018-12-23
什麼是CAT
Cat是基於Java開發的實時應用監控平臺,為美團點評提供了全面的實時監控告警服務
• CAT作為服務端專案基礎元件,提供了java, c/c++, node, python, go等多語言客戶端,已經在美團點評的基礎架構中介軟體框架(MVC框架,RPC框架,資料庫框架,快取框架等,訊息佇列,配置系統等)深度整合,為美團點評各業務線提供系統豐富的效能指標、健康狀況、實時告警等。
• CAT很大的優勢是它是一個實時系統,CAT大部分系統是分鐘級統計,但是從資料生成到服務端處理結束是秒級別,秒級定義是48分鐘40秒,基本上看到48分鐘38秒資料,整體報表的統計粒度是分鐘級;第二個優勢,監控資料是全量統計,客戶端預計算;鏈路資料是取樣計算。
Cat的產品價值
• 減少線上問題的發現時間
• 減少問題故障的定位時間
• 輔助應用程式的優化工具
Cat的優勢
• 實時處理:資訊的價值會隨時間銳減,尤其是事故處理過程中。
• 全量資料:最開始的設計目標就是全量採集,全量的好處有很多。
• 高可用:所有應用都倒下了,需要監控還站著,並告訴工程師發生了什麼,做到故障還原和問題定位。
• 故障容忍:CAT 本身故障不應該影響業務正常運轉,CAT 掛了,應用不該受影響,只是監控能力暫時減弱。
• 高吞吐:要想還原真相,需要全方位地監控和度量,必須要有超強的處理吞吐能力。
• 可擴充套件:支援分散式、跨 IDC 部署,橫向擴充套件的監控系統。
CAT支援的監控訊息型別包括:
• Transaction 適合記錄跨越系統邊界的程式訪問行為,比如遠端呼叫,資料庫呼叫,也適合執行時間較長的業務邏輯監控,Transaction用來記錄一段程式碼的執行時間和次數。
• Event 用來記錄一件事發生的次數,比如記錄系統異常,它和transaction相比缺少了時間的統計,開銷比transaction要小。
• Heartbeat 表示程式內定期產生的統計資訊, 如CPU%, MEM%, 連線池狀態, 系統負載等。
• Metric 用於記錄業務指標、指標可能包含對一個指標記錄次數、記錄平均值、記錄總和,業務指標最低統計粒度為1分鐘。
cat客戶端的整合步驟:
1.在需要被監控的專案裡引入cat-client 的meven依賴:
<dependency>
<groupId>com.dianping.cat</groupId>
<artifactId>cat-client</artifactId>
<version>3.0.0</version>
</dependency>
2.引入cat的核心過濾器:
package com.kye.map.ucenter.controller;
import com.dianping.cat.servlet.CatFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Description:cat的的核心過濾器
* Author: [email protected]
* Date: 2018/10/24 15:34
* Copyright (c) 跨越新科技 版權所有
*/
@Configuration
public class CatFilterConfigure {
@Bean
public FilterRegistrationBean catFilter() {
FilterRegistrationBean registration = new FilterRegistrationBean();
CatFilter filter = new CatFilter();
registration.setFilter(filter);
registration.addUrlPatterns("/*");
registration.setName("cat-filter");
registration.setOrder(1);
return registration;
}
}
引入這個以後cat專案就能監控到你訪問的url
3.在需要被監控的專案建立如下結構:
app.name=ucenter 這個必須有,cat服務端必須通過這個找到相應的專案
4.需要在你的專案的根目錄建立如下結構的資料夾:
client.xml內容如下:
<?xml version="1.0" encoding="utf-8"?>
<config mode="client" xmlns:xsi="http://www.w3.org/2001/XMLSchema" xsi:noNamespaceSchemaLocation="config.xsd">
<servers>
<!-- Local mode for development -->
<server ip="10.10.242.9" port="2280" http-port="8080" />
<!-- If under production environment, put actual server address as list. -->
<!--
<server ip="192.168.7.71" port="2280" />
<server ip="192.168.7.72" port="2280" />
-->
</servers>
</config>
cat專案的日誌目錄:
)如果專案啟動出問題,或者cat監控不到自己的專案,可以看看這裡的日誌)
5.整合mybatis攔截器(目前只能攔截到增刪改)
package com.kye.map.ucenter.config;
import com.alibaba.druid.pool.DruidDataSource;
import com.dianping.cat.Cat;
import com.dianping.cat.message.Message;
import com.dianping.cat.message.Transaction;
import org.apache.commons.dbcp.BasicDataSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.type.TypeHandlerRegistry;
import org.mybatis.spring.transaction.SpringManagedTransaction;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.util.ReflectionUtils;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.text.DateFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
/**
* 對MyBatis進行攔截,新增Cat監控
* 目前僅支援RoutingDataSource和Druid組合配置的資料來源
*
* @author Steven
*/
@Intercepts({
@Signature(method = "query", type = Executor.class, args = {
MappedStatement.class, Object.class, RowBounds.class,
ResultHandler.class }),
@Signature(method = "update", type = Executor.class, args = { MappedStatement.class, Object.class })
})
public class CatMybatisPlugin implements Interceptor {
private static Log logger = LogFactory.getLog(CatMybatisPlugin.class);
//快取,提高效能
private static final Map<String, String> sqlURLCache = new ConcurrentHashMap<String, String>(256);
private static final String EMPTY_CONNECTION = "jdbc:mysql://localhost:3306/%s?useUnicode=true";
private Executor target;
@Override
public Object intercept(Invocation invocation) throws Throwable {
MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
//得到類名,方法
String[] strArr = mappedStatement.getId().split("\\.");
String methodName = strArr[strArr.length - 2] + "." + strArr[strArr.length - 1];
Transaction t = Cat.newTransaction("SQL", methodName);
//得到sql語句
Object parameter = null;
if(invocation.getArgs().length > 1){
parameter = invocation.getArgs()[1];
}
BoundSql boundSql = mappedStatement.getBoundSql(parameter);
Configuration configuration = mappedStatement.getConfiguration();
String sql = showSql(configuration, boundSql);
//獲取SQL型別
SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();
Cat.logEvent("SQL.Method", sqlCommandType.name().toLowerCase(), Message.SUCCESS, sql);
String s = this.getSQLDatabase();
Cat.logEvent("SQL.Database", s);
Object returnObj = null;
try {
returnObj = invocation.proceed();
t.setStatus(Transaction.SUCCESS);
} catch (Exception e) {
t.setStatus(e);
Cat.logError(e);
} finally {
t.complete();
}
return returnObj;
}
private javax.sql.DataSource getDataSource() {
org.apache.ibatis.transaction.Transaction transaction = this.target.getTransaction();
if (transaction == null) {
logger.error(String.format("Could not find transaction on target [%s]", this.target));
return null;
}
if (transaction instanceof SpringManagedTransaction) {
String fieldName = "dataSource";
Field field = ReflectionUtils.findField(transaction.getClass(), fieldName, javax.sql.DataSource.class);
if (field == null) {
logger.error(String.format("Could not find field [%s] of type [%s] on target [%s]",
fieldName, javax.sql.DataSource.class, this.target));
return null;
}
ReflectionUtils.makeAccessible(field);
javax.sql.DataSource dataSource = (javax.sql.DataSource) ReflectionUtils.getField(field, transaction);
return dataSource;
}
logger.error(String.format("---the transaction is not SpringManagedTransaction:%s", transaction.getClass().toString()));
return null;
}
private String getSqlURL() {
javax.sql.DataSource dataSource = this.getDataSource();
if (dataSource == null) {
return null;
}
if (dataSource instanceof AbstractRoutingDataSource) {
String methodName = "determineTargetDataSource";
Method method = ReflectionUtils.findMethod(AbstractRoutingDataSource.class, methodName);
if (method == null) {
logger.error(String.format("---Could not find method [%s] on target [%s]",
methodName, dataSource));
return null;
}
ReflectionUtils.makeAccessible(method);
javax.sql.DataSource dataSource1 = (javax.sql.DataSource) ReflectionUtils.invokeMethod(method, dataSource);
if (dataSource1 instanceof DruidDataSource) {
DruidDataSource druidDataSource = (DruidDataSource) dataSource1;
return druidDataSource.getUrl();
} else {
logger.error("---only surpport DruidDataSource:" + dataSource1.getClass().toString());
}
} else if(dataSource instanceof BasicDataSource){
return ((BasicDataSource) dataSource).getUrl();
}
return null;
}
private String getSQLDatabase() {
// String dbName = RouteDataSourceContext.getRouteKey();
String dbName = null; //根據設定的多資料來源修改此處,獲取dbname
if (dbName == null) {
dbName = "DEFAULT";
}
String url = CatMybatisPlugin.sqlURLCache.get(dbName);
if (url != null) {
return url;
}
url = this.getSqlURL();//目前監控只支援mysql ,其餘資料庫需要各自修改監控服務端
if (url == null) {
url = String.format(EMPTY_CONNECTION, dbName);
}
CatMybatisPlugin.sqlURLCache.put(dbName, url);
return url;
}
/**
* 解析sql語句
* @param configuration
* @param boundSql
* @return
*/
public String showSql(Configuration configuration, BoundSql boundSql) {
Object parameterObject = boundSql.getParameterObject();
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
String sql = boundSql.getSql().replaceAll("[\\s]+", " ");
if (parameterMappings.size() > 0 && parameterObject != null) {
TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(getParameterValue(parameterObject)));
} else {
MetaObject metaObject = configuration.newMetaObject(parameterObject);
for (ParameterMapping parameterMapping : parameterMappings) {
String propertyName = parameterMapping.getProperty();
if (metaObject.hasGetter(propertyName)) {
Object obj = metaObject.getValue(propertyName);
sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(getParameterValue(obj)));
} else if (boundSql.hasAdditionalParameter(propertyName)) {
Object obj = boundSql.getAdditionalParameter(propertyName);
sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(getParameterValue(obj)));
}
}
}
}
return sql;
}
/**
* 引數解析
* @param obj
* @return
*/
private String getParameterValue(Object obj) {
String value = null;
if (obj instanceof String) {
value = "'" + obj.toString() + "'";
} else if (obj instanceof Date) {
DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.CHINA);
value = "'" + formatter.format((Date)obj) + "'";
} else {
if (obj != null) {
value = obj.toString();
} else {
value = "";
}
}
return value;
}
@Override
public Object plugin(Object target) {
if (target instanceof Executor) {
this.target = (Executor) target;
return Plugin.wrap(target, this);
}
return target;
}
@Override
public void setProperties(Properties properties) {
}
}
6.將mybatis攔截器注入到sqlSessionFactory
package com.kye.map.ucenter.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import javax.sql.