一、springboot项目集成大众点评cat
一、 什么是CAT
1.cat簡(jiǎn)介
Cat是基于Java開(kāi)發(fā)的實(shí)時(shí)應(yīng)用監(jiān)控平臺(tái),為美團(tuán)點(diǎn)評(píng)提供了全面的實(shí)時(shí)監(jiān)控告警服務(wù)
? CAT作為服務(wù)端項(xiàng)目基礎(chǔ)組件,提供了java, c/c++, node, python, go等多語(yǔ)言客戶(hù)端,已經(jīng)在美團(tuán)點(diǎn)評(píng)的基礎(chǔ)架構(gòu)中間件框架(MVC框架,RPC框架,數(shù)據(jù)庫(kù)框架,緩存框架等,消息隊(duì)列,配置系統(tǒng)等)深度集成,為美團(tuán)點(diǎn)評(píng)各業(yè)務(wù)線(xiàn)提供系統(tǒng)豐富的性能指標(biāo)、健康狀況、實(shí)時(shí)告警等。
? CAT很大的優(yōu)勢(shì)是它是一個(gè)實(shí)時(shí)系統(tǒng),CAT大部分系統(tǒng)是分鐘級(jí)統(tǒng)計(jì),但是從數(shù)據(jù)生成到服務(wù)端處理結(jié)束是秒級(jí)別,秒級(jí)定義是48分鐘40秒,基本上看到48分鐘38秒數(shù)據(jù),整體報(bào)表的統(tǒng)計(jì)粒度是分鐘級(jí);第二個(gè)優(yōu)勢(shì),監(jiān)控?cái)?shù)據(jù)是全量統(tǒng)計(jì),客戶(hù)端預(yù)計(jì)算;鏈路數(shù)據(jù)是采樣計(jì)算。
2.Cat的產(chǎn)品價(jià)值
? 減少線(xiàn)上問(wèn)題的發(fā)現(xiàn)時(shí)間
? 減少問(wèn)題故障的定位時(shí)間
? 輔助應(yīng)用程序的優(yōu)化工具
3.Cat的優(yōu)勢(shì)
? 實(shí)時(shí)處理:信息的價(jià)值會(huì)隨時(shí)間銳減,尤其是事故處理過(guò)程中。
? 全量數(shù)據(jù):最開(kāi)始的設(shè)計(jì)目標(biāo)就是全量采集,全量的好處有很多。
? 高可用:所有應(yīng)用都倒下了,需要監(jiān)控還站著,并告訴工程師發(fā)生了什么,做到故障還原和問(wèn)題定位。
? 故障容忍:CAT 本身故障不應(yīng)該影響業(yè)務(wù)正常運(yùn)轉(zhuǎn),CAT 掛了,應(yīng)用不該受影響,只是監(jiān)控能力暫時(shí)減弱。
? 高吞吐:要想還原真相,需要全方位地監(jiān)控和度量,必須要有超強(qiáng)的處理吞吐能力。
? 可擴(kuò)展:支持分布式、跨 IDC 部署,橫向擴(kuò)展的監(jiān)控系統(tǒng)。
4.CAT支持的監(jiān)控消息類(lèi)型包括
? Transaction 適合記錄跨越系統(tǒng)邊界的程序訪(fǎng)問(wèn)行為,比如遠(yuǎn)程調(diào)用,數(shù)據(jù)庫(kù)調(diào)用,也適合執(zhí)行時(shí)間較長(zhǎng)的業(yè)務(wù)邏輯監(jiān)控,Transaction用來(lái)記錄一段代碼的執(zhí)行時(shí)間和次數(shù)。
? Event 用來(lái)記錄一件事發(fā)生的次數(shù),比如記錄系統(tǒng)異常,它和transaction相比缺少了時(shí)間的統(tǒng)計(jì),開(kāi)銷(xiāo)比transaction要小。
? Heartbeat 表示程序內(nèi)定期產(chǎn)生的統(tǒng)計(jì)信息, 如CPU%, MEM%, 連接池狀態(tài), 系統(tǒng)負(fù)載等。
? Metric 用于記錄業(yè)務(wù)指標(biāo)、指標(biāo)可能包含對(duì)一個(gè)指標(biāo)記錄次數(shù)、記錄平均值、記錄總和,業(yè)務(wù)指標(biāo)最低統(tǒng)計(jì)粒度為1分鐘。
二、cat客戶(hù)端的集成步驟:
1.在需要被監(jiān)控的項(xiàng)目里引入cat-client 的meven依賴(lài):
<dependency><groupId>com.dianping.cat</groupId><artifactId>cat-client</artifactId><version>3.0.0</version></dependency>2.引入cat的核心過(guò)濾器:
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的的核心過(guò)濾器* * Date: 2018/10/24 15:34**/ @Configuration public class CatFilterConfigure {@Beanpublic FilterRegistrationBean catFilter() {FilterRegistrationBean registration = new FilterRegistrationBean();CatFilter filter = new CatFilter();registration.setFilter(filter);registration.addUrlPatterns("/*");registration.setName("cat-filter");registration.setOrder(1);return registration;} }引入這個(gè)以后cat項(xiàng)目就能監(jiān)控到你訪(fǎng)問(wèn)的url
3.在需要被監(jiān)控的項(xiàng)目建立如下結(jié)構(gòu):
app.name=ucenter 這個(gè)必須有,cat服務(wù)端必須通過(guò)這個(gè)找到相應(yīng)的項(xiàng)目
4.需要在你的項(xiàng)目的根目錄建立如下結(jié)構(gòu)的文件夾:
client.xml內(nèi)容如下:
<?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項(xiàng)目的日志目錄:
)如果項(xiàng)目啟動(dòng)出問(wèn)題,或者cat監(jiān)控不到自己的項(xiàng)目,可以看看這里的日志)
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;/** * 對(duì)MyBatis進(jìn)行攔截,添加Cat監(jiān)控 * 目前僅支持RoutingDataSource和Druid組合配置的數(shù)據(jù)源 * * @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;@Overridepublic Object intercept(Invocation invocation) throws Throwable {MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];//得到類(lèi)名,方法String[] strArr = mappedStatement.getId().split("\\.");String methodName = strArr[strArr.length - 2] + "." + strArr[strArr.length - 1];Transaction t = Cat.newTransaction("SQL", methodName);//得到sql語(yǔ)句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類(lèi)型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; //根據(jù)設(shè)置的多數(shù)據(jù)源修改此處,獲取dbnameif (dbName == null) {dbName = "DEFAULT";}String url = CatMybatisPlugin.sqlURLCache.get(dbName);if (url != null) {return url;}url = this.getSqlURL();//目前監(jiān)控只支持mysql ,其余數(shù)據(jù)庫(kù)需要各自修改監(jiān)控服務(wù)端if (url == null) {url = String.format(EMPTY_CONNECTION, dbName);}CatMybatisPlugin.sqlURLCache.put(dbName, url);return url;}/*** 解析sql語(yǔ)句* @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;}/*** 參數(shù)解析* @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;}@Overridepublic Object plugin(Object target) {if (target instanceof Executor) {this.target = (Executor) target;return Plugin.wrap(target, this);}return target;}@Overridepublic 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.DataSource;@Configuration public class MybatisConfig implements EnvironmentAware {private Environment environment;@Beanpublic DataSource getDateSource(){DruidDataSource dataSource = new DruidDataSource();dataSource.setUrl(environment.getProperty("spring.datasource.url"));dataSource.setUsername(environment.getProperty("spring.datasource.username"));dataSource.setPassword(environment.getProperty("spring.datasource.password"));dataSource.setMaxActive(10);dataSource.setDriverClassName(environment.getProperty("spring.datasource.driverClassName"));dataSource.setMaxIdle(5);return dataSource;}@Beanpublic SqlSessionFactory getSqlSession(DataSource dataSource) throws Exception {SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();factoryBean.setDataSource(dataSource);CatMybatisPlugin catMybatisPlugin = new CatMybatisPlugin();factoryBean.setPlugins(new Interceptor[]{catMybatisPlugin});Resource[] resources = new PathMatchingResourcePatternResolver().getResources("classpath*:mappers/**/*.xml");factoryBean.setMapperLocations(resources);SqlSessionFactory sessionFactory = factoryBean.getObject();return sessionFactory;}@Beanpublic MapperScannerConfigurer getMapperScannerConfigurer(SqlSessionFactory sqlSessionFactory){MapperScannerConfigurer configurer = new MapperScannerConfigurer();configurer.setBasePackage("com.kye.map.ucenter.domain.mappers");configurer.setSqlSessionFactory(sqlSessionFactory);return configurer;}@Beanpublic SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {return new SqlSessionTemplate(sqlSessionFactory);} // // @Bean // @Override // public PlatformTransactionManager annotationDrivenTransactionManager() { // return new DataSourceTransactionManager(dataSource); // }@Overridepublic void setEnvironment(Environment environment) {this.environment = environment;} }7.@CatAnnotation注解的使用:
只需要在需要攔截的方法上加上@CatAnnotation 即可 type和value的值可以自定義
@Override@CatAnnotation(type = "ServiceGetById",value = "getById")public Resource getById(String resourceId) {return resourceMapper.get(resourceId);}三、相關(guān)的參考文檔
- CAT項(xiàng)目的開(kāi)源地址: https://github.com/dianping/cat
- CAT官方站點(diǎn):http://unidal.org/cat/r
- 微盟的CAT接入文檔:http://tx.cat.weimob.com/cat/doc.html
- 參考文檔:http://fanlychie.github.io/post/cat-setup.html
四、 系列文章
總結(jié)
以上是生活随笔為你收集整理的一、springboot项目集成大众点评cat的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: windows重建linux引导,Lin
- 下一篇: PS替换图片中的颜色-局部以及整体变色