springboot项目集成大众点评cat

什么是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>
1
2
3
4
5
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: fuyongnan@ky-tech.com.cn
 * 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;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
引入这个以后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>
1
2
3
4
5
6
7
8
9
10
11
12
13
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) {
   }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239

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;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74


7.@CatAnnotation注解的使用:
只需要在需要拦截的方法上加上@CatAnnotation 即可 type和value的值可以自定义

  @Override
    @CatAnnotation(type = "ServiceGetById",value = "getById")
    public Resource getById(String resourceId) {
        return resourceMapper.get(resourceId);
    }
    ~~~


————————————————
版权声明:本文为CSDN博主「YourAaron」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weter_drop/article/details/83349651

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值