springboot整合cat

一、 什么是CAT

1.cat简介

Cat是基于Java开发的实时应用监控平台,为美团点评提供了全面的实时监控告警服务
• CAT作为服务端项目基础组件,提供了java, c/c++, node, python, go等多语言客户端,已经在美团点评的基础架构中间件框架(MVC框架,RPC框架,数据库框架,缓存框架等,消息队列,配置系统等)深度集成,为美团点评各业务线提供系统丰富的性能指标、健康状况、实时告警等。
• CAT很大的优势是它是一个实时系统,CAT大部分系统是分钟级统计,但是从数据生成到服务端处理结束是秒级别,秒级定义是48分钟40秒,基本上看到48分钟38秒数据,整体报表的统计粒度是分钟级;第二个优势,监控数据是全量统计,客户端预计算;链路数据是采样计算。

2.Cat的产品价值

• 减少线上问题的发现时间
• 减少问题故障的定位时间
• 辅助应用程序的优化工具

3.Cat的优势

• 实时处理:信息的价值会随时间锐减,尤其是事故处理过程中。
• 全量数据:最开始的设计目标就是全量采集,全量的好处有很多。
• 高可用:所有应用都倒下了,需要监控还站着,并告诉工程师发生了什么,做到故障还原和问题定位。
• 故障容忍:CAT 本身故障不应该影响业务正常运转,CAT 挂了,应用不该受影响,只是监控能力暂时减弱。
• 高吞吐:要想还原真相,需要全方位地监控和度量,必须要有超强的处理吞吐能力。
• 可扩展:支持分布式、跨 IDC 部署,横向扩展的监控系统。

4.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.1.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的的核心过滤器
* 
* Date: 2018/10/24 15:34
*
*/
@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.DataSource;

@Configuration
public class MybatisConfig implements EnvironmentAware {

    private Environment environment;

    @Bean
    public 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;
    }



    @Bean
    public 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;
    }

    @Bean
    public MapperScannerConfigurer getMapperScannerConfigurer(SqlSessionFactory sqlSessionFactory){
        MapperScannerConfigurer configurer = new MapperScannerConfigurer();
        configurer.setBasePackage("com.kye.map.ucenter.domain.mappers");
        configurer.setSqlSessionFactory(sqlSessionFactory);
        return configurer;
    }


    @Bean
    public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
//
//    @Bean
//    @Override
//    public PlatformTransactionManager annotationDrivenTransactionManager() {
//        return new DataSourceTransactionManager(dataSource);
//    }

    @Override
    public 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);
    }

三、相关的参考文档

四、 系列文章

  1. springboot项目集成大众点评cat
  2. cat服务端配置
  3. springboot集成大众点评ca之邮件告警

转载:https://juejin.cn/post/7022222491502247943
作者:TOPEN
来源:稀土掘金

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值