一、Cat源代码下载和打包
1、安装环境:
下面是本人的安装环境
仅供参考:
1)操作系统:win10,JDK:1.8,tomcat:8.5+, mysql:5.7,cat:3.0.0,[安装git和maven环境链接](https://blog.csdn.net/tiny_du/article/details/119150481)
2)部署cat前准备: 需要提前安装好jdk1.8的环境,tomcat环境,已经mysql环境,在此不进行多讲,有问题的伙伴可以百度自行解决。
2、在镜像网站下载cat源码和cat需要的依赖包
下载地址
cat源码:下载master分支代码;
cat依赖包:下载mvn-repo分支代码;
3、在本地打cat的war包
-
首先把下载的cat源码工程更名,改为cat;**
image
-
看手册说明,要看cat项目根目录的README.md文件**
README.md说明: 1、根目录下 cat-client 模块以后不再维护,下个大版本更新计划移除。新版Java客户端参考:lib/java;--操作:将cat根目录的pom.xml中cat-client的模块删除(删除<module>cat-client</module>) 2、管理端、服务端、告警服务均使用 cat-home 模块部署即可
4、其他模块:
-
integration:cat和一些第三方工具集成的内容(此部分一部分是由社区贡献,一部分官方贡献)
-
lib:CAT 的客户端,包括 Java、C/C++、Python、Node.js、Go
-
script:CAT 数据库脚本
-
在cat工程执行编译命令
编译命令:mvn clean compile 报错1:Could not find artifact org.unidal.framework:foundation-service:jar:2.5.0[ERROR] Failed to execute goal on project cat-core: Could not resolve dependencies for project com.dianping.cat:cat-core:jar:3.0.0: The following artifacts could not be resolved: org.unidal.framework:foundation-service:jar:2.5.0, org.unidal.framework:web-framework:jar:2.4.0, org.unidal.framework:dal-jdbc:jar:2.4.0: Could not find artifact org.unidal.framework:foundation-service:jar:2.5.0 in alimaven (http://maven.aliyun.com/nexus/content/groups/public) -> [Help 1] 解决:将下载的mvn-repo分支解压,放入到本地的.m2仓库中;本地mvn-repo分支解压,解压出org目录;将org目录拷贝到本地的.m2仓库中; 再执行编译命令: 报错2:Failure to find org.codehaus.plexus:plexus-container-default:jar:3.1.0[ERROR] Failed to execute goal org.unidal.maven.plugins:codegen-maven-plugin:2.5.8:dal-model (generate data model) on project cat-core: Execution generate data model of goal org.unidal.maven.plugins:codegen-maven-plugin:2.5.8:dal-model failed: Plugin org.unidal.maven.plugins:codegen-maven-plugin:2.5.8 or one of its dependencies could not be resolved: Failure to find org.codehaus.plexus:plexus-container-default:jar:3.1.0 in http://maven.aliyun.com/nexus/content/groups/public was cached in the local repository, resolution will not be reattempted until the update interval of alimaven has elapsed or updates are forced -> [Help 1] 解决:在.m2仓库找到org.unidal.maven.plugins:codegen-maven-plugin:2.5.8版本的pom文件codegen-maven-plugin-2.5.8.pom用idea打开,点击父引用的default,跳转到default-2.5.8.pom文件中,将文件中foundation-service.version从3.1.0版本修改为4.0.0版本;
<parent>
<groupId>org.unidal.maven.plugins</groupId>
<artifactId>default</artifactId>
<version>2.5.8</version>
</parent>
<foundation-service.version>4.0.0</foundation-service.version>
再执行编译命令:这里是JDK11版本会碰到的问题
报错3:程序包 sun.misc找不到符号类 BASE64Encoder
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.2:compile (default-compile) on project cat-consumer: Compilation failure: Compilation failure:[ERROR] /D:/Nikey/Project/Repository/gitee/cat/cat/cat-consumer/src/main/java/com/dianping/cat/consumer/util/InvidStringBuilder.java:[21,16] 找不到符号[ERROR] 符号: 类 BASE64Encoder[ERROR] 位置: 程序包 sun.misc
解决:找到InvidStringBuilder.java这个类,将import sun.misc.BASE64Encoder; 替换为import org.apache.commons.codec.binary.Base64;将return语句,替换为return "Base64." + Base64.encodeBase64(key.getBytes());//import sun.misc.BASE64Encoder;import org.apache.commons.codec.binary.Base64;// return "Base64." + new BASE64Encoder().encodeBuffer(key.getBytes()).trim(); return "Base64." + Base64.encodeBase64(key.getBytes());
再执行编译命令:编译即可成功
-
在cat根目录执行install命令
执行install命令: mvn clean install -DskipTests 报错:Could not find artifact org.unidal.framework:test-framework:jar:2.4.0 [ERROR] Failed to execute goal on project cat-core: Could not resolve dependencies for project com.dianping.cat:cat-core:jar:3.0.0: Could not find artifact org.unidal.framework:test-framework:jar:2.4.0 in alimaven(http://maven.aliyun.com/nexus/content/groups/public) -> [Help 1] 解决:修改cat根目录的pom.xml文件中test-framework的版本,改为2.5.0 再执行install命令: install成功后,在下面的路径获取到war包,将cat-home-3.0.0.war修改为cat.war或者可以直接下载war包,可以选择适合自己的包文件进行下载。 http://unidal.org/nexus/service/local/repositories/releases/content/com/dianping/cat/cat-home/3.0.0/

-
查看每个module具体生效的pom信息
在cat根目录执行命令:打开cat.txt,即可看到cat-home定义的version mvn help:effective-pom > cat.txt
二、服务端安装和配置
1、准备环境
tomcat:apache-tomcat-8.5.68
mysql:mysql5.7.22
jdk1.8
美团cat源码打的war包:cat.war
2、安装CAT的数据库
-
连接本地的mysql数据库
-
创建数据库,数据库名cat,数据库编码使用utf8mb4,否则可能造成中文乱码等问题;
CREATE SCHEMAcat
DEFAULT CHARACTER SET utf8mb4 ; -
将cat工程中,script目录中的sql拷贝到cat数据库运行
-
script目录:CatApplication.sql
-
sql拷贝到cat数据库运行,初始化数据表;
由于MySQL的系统参数max_allowed_packet默认配置较小,可能会限制server接受的数据包大小,有时候大的插入和更新会被max_allowed_packet 参数限制掉,导致失败,所以要修改max_allowed_packet的值,修改后需要重启mysql; 在mysql的安装目录修改配置文件(如果没有,自己创建一个):C:\Program Files\MySQL\MySQL Server 5.7\my.ini [mysqld] port=3306 max_allowed_packet=1000M [client] port=3306 重启mysql: net stop mysql net start mysql 使用MySQL Workbench工具连接本地的mysql数据库,执行命令: show VARIABLES like '%max_allowed_packet%';
3、Cat配置文件
在tomcat安装的磁盘目录,创建以下2个目录,并且赋予读写权限;我的tomcat安装在了D盘,所以在D盘创建如下2个目录:
D:\data\appdatas\cat
D:\data\applogs\cat
在D:\data\appdatas\cat目录中,添加client.xml,datasources.xml,server.xml配置文件
client.xml配置文件
<?xml version="1.0" encoding="utf-8"?>
<config mode="client">
<servers>
<!-- 服务端地址, 端口固定-->
<!-- 如果有多个Server, 就继续增加相应的节点配置 -->
<!-- 这是告诉客户端应该去链接哪个服务端,从哪个服务端里获取配置信息 ,相关源码也在 DefaultClientConfigManager中 -->
<server ip="127.0.0.1" port="2280" http-port="8080" />
</servers>
</config>
datasources.xml配置文件
<?xml version="1.0" encoding="utf-8"?>
<data-sources>
<data-source id="cat">
<maximum-pool-size>3</maximum-pool-size>
<connection-timeout>1s</connection-timeout>
<idle-timeout>10m</idle-timeout>
<statement-cache-size>1000</statement-cache-size>
<properties>
<driver>com.mysql.jdbc.Driver</driver>
<url><![CDATA[jdbc:mysql://127.0.0.1:3306/cat]]></url> <!-- 请替换为真实数据库URL及Port -->
<user>root</user> <!-- 请替换为真实数据库用户名 -->
<password>root</password> <!-- 请替换为真实数据库密码 -->
<connectionProperties><![CDATA[useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&socketTimeout=120000]]></connectionProperties>
</properties>
</data-source>
</data-sources>
4、tomcat启动cat项目
将cat.war拷贝到tomcat的webapps目录,再启动tomcat;
运行命令启动tomcat:D:\Tool\apache-tomcat-8.5.68\bin\startup.bat
如果mysql的版本使用了8.0,启动tomcat后,会报错:[ChannelManager] Error when try connecting to /127.0.0.1:2280,解决方法:安装5.7版本的mysql就没问题了;
启动成功后,访问:http://127.0.0.1:8080/cat/r
如果遇到
5、按照实际需要,修改服务端配置和客户端路由
使用admin/admin登录
进入【服务端配置】页面修改IP地址
链接地址:http://127.0.0.1:8080/cat/s/config?op=serverConfigUpdate
进入【客户端路由】页面修改IP地址
链接地址:http://127.0.0.1:8080/cat/s/config?op=routerConfigUpdate
6、浏览器访问cat
浏览器访问服务,发现已经可以正常访问cat的页面。不能正常启动,检查你的服务端文件配置主要是IP和端口配置是否正确,配置正确重新启动你的电脑再试试

7、配置cat
cat的默认账号密码是admin/admin
初次部署后,需要对cat进行相对应的配置,否则打开报表页面,全是500的报错提示,需要配置后才能正常显示。


三、客户端集成案例
编写一个springboot与cat整合的案例
1、在pom.xml中加入cat-client 的meven依赖
<dependency>
<groupId>com.dianping.cat</groupId>
<artifactId>cat-client</artifactId>
<version>3.0.0</version>
</dependency>
***<!-- mybatis接入CAT的依赖 -->***
<dependency>
<groupId>com.dianping.cat</groupId>
<artifactId>cat-client-mybatis</artifactId>
<version>2.0.0</version>
</dependency>
2.引入核心过滤器
将CatFilterConfigure.java 放到任意SpringBoot 能扫描到的package下面
import com.dianping.cat.servlet.CatFilter;i
mport org.springframework.boot.web.servlet.FilterRegistrationBean;i
mport org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/** * Description:cat的的核心过滤器
* Author: lxf
* Date: 2021/10/11
*/
@Configurationpublicclass
publicclass 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;
}
}
3. 配置监控项目名
在需要被监听的项目src/main/resources/META-INF目录下添加配置文件app.properties

文件内容为:
app.name=test (自己定义要和平台定义的CMDB一致即可)
在META-INF下面创建client.xml文件启动项目
<?xml version="1.0" encoding="UTF-8"?><config mode="client"><domain id="test"/></config>
出现下面类似日志说明加载成功。

就可以看到test输出到cat的日志:

2.mybatis集成CAT
- 引入拦截器
将CatMybatisPlugin.java 放到任意SpringBoot 能扫描到的package下面
import com.dianping.cat.Cat;
import com.dianping.cat.message.Message;
import com.dianping.cat.message.Transaction;
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;importorg.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.util.ReflectionUtils;
import javax.sql.DataSource;import java.lang.reflect.Field;
import java.lang.reflect.Method;import java.text.DateFormat;importjava.util.*;
import java.util.concurrent.ConcurrentHashMap;import java.util.regex.Matcher;
/** * 对MyBatis进行拦截,添加Cat监控
* @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})
})publicclassCatMybatisPluginimplements Interceptor {
privatestaticLog logger = LogFactory.getLog(CatMybatisPlugin.class);
//缓存,提高性能privatestaticfinalMap sqlURLCache =newConcurrentHashMap(256);
privatestaticfinalString EMPTY_CONNECTION = "jdbc:mysql://unknown:3306/%s?useUnicode=true";
private Executor target;
// druid 数据源的类名称privatestaticfinalString DruidDataSourceClassName = "com.alibaba.druid.pool.DruidDataSource";
// dbcp 数据源的类名称privatestaticfinalString DBCPBasicDataSourceClassName = "org.apache.commons.dbcp.BasicDataSource";
// dbcp2 数据源的类名称privatestaticfinalString DBCP2BasicDataSourceClassName = "org.apache.commons.dbcp2.BasicDataSource";
// c3p0 数据源的类名称privatestaticfinalString C3P0ComboPooledDataSourceClassName = "com.mchange.v2.c3p0.ComboPooledDataSource";
// HikariCP 数据源的类名称privatestaticfinalString HikariCPDataSourceClassName = "com.zaxxer.hikari.HikariDataSource";
// BoneCP 数据源的类名称privatestaticfinalString BoneCPDataSourceClassName = "com.jolbox.bonecp.BoneCPDataSource";
@Override
publicObject 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) {
Cat.logError(e);
} finally {
t.complete();
}
return returnObj;
}
private 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));
returnnull;
}
if(transactioninstanceof SpringManagedTransaction) {
String fieldName = "dataSource";
Field field = ReflectionUtils.findField(transaction.getClass(), fieldName, DataSource.class);
if(field ==null) {
logger.error(String.format("Could not find field [%s] of type [%s] on target [%s]",
fieldName, DataSource.class,this.target));
return null;
}
ReflectionUtils.makeAccessible(field);
DataSource dataSource = (DataSource) ReflectionUtils.getField(field, transaction);
return dataSource;
}
logger.error(String.format("---the transaction is not SpringManagedTransaction:%s", transaction.getClass().toString()));
return null;
}
/** * 重写 getSqlURL 方法
*
* @author fanlychie (https://github.com/fanlychie)
*/private String getSqlURL() {
// 客户端使用的数据源DataSource dataSource =this.getDataSource();
if(dataSource !=null) {
// 处理常见的数据源switch (dataSource.getClass().getName()) {
// druidcase DruidDataSourceClassName:
returngetDataSourceSqlURL(dataSource, DruidDataSourceClassName, "getUrl");
// dbcpcase DBCPBasicDataSourceClassName:
returngetDataSourceSqlURL(dataSource, DBCPBasicDataSourceClassName, "getUrl");
// dbcp2case DBCP2BasicDataSourceClassName:
returngetDataSourceSqlURL(dataSource, DBCP2BasicDataSourceClassName, "getUrl");
// c3p0case C3P0ComboPooledDataSourceClassName:
returngetDataSourceSqlURL(dataSource, C3P0ComboPooledDataSourceClassName, "getJdbcUrl");
// HikariCPcase HikariCPDataSourceClassName:
returngetDataSourceSqlURL(dataSource, HikariCPDataSourceClassName, "getJdbcUrl");
// BoneCPcase BoneCPDataSourceClassName:
returngetDataSourceSqlURL(dataSource, BoneCPDataSourceClassName, "getJdbcUrl");
}
}
returnnull;
}
/** * 获取数据源的SQL地址
*
* @param dataSource 数据源
* @param runtimeDataSourceClassName 运行时真实的数据源的类名称
* @param sqlURLMethodName 获取SQL地址的方法名称
*
* @author fanlychie (https://github.com/fanlychie)
*/private String getDataSourceSqlURL(DataSource dataSource, String runtimeDataSourceClassName, String sqlURLMethodName) {
Class dataSourceClass =null;
try {
dataSourceClass = Class.forName(runtimeDataSourceClassName);
} catch (ClassNotFoundException e) {}
Method sqlURLMethod = ReflectionUtils.findMethod(dataSourceClass, sqlURLMethodName);
return (String) ReflectionUtils.invokeMethod(sqlURLMethod, dataSource);
}
private String getSQLDatabase() {// String dbName = RouteDataSourceContext.getRouteKey();String dbName =null;//根据设置的多数据源修改此处,获取dbnameif(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 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)));
} elseif (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(objinstanceof String) {
value = "'" + obj.toString() + "'";
} elseif(objinstanceof Date) {
DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.CHINA);
value = "'" + formatter.format(newDate()) + "'";
} else {
if(obj !=null) {
value = obj.toString();
} else {
value = "";
}
}
return value;
}
@Override
public Object plugin(Object target) {
if(targetinstanceof Executor) {
this.target = (Executor) target;
returnPlugin.wrap(target,this);
}
return target;
}
@Override
publicvoid setProperties(Properties properties) {
}
}
2.将mybatis拦截器注入到sqlSessionFactory
在MybatisConfigurer.java中添加代码
//添加CAT插件CatMybatisPlugin catMybatisPlugin =new CatMybatisPlugin();
factory.setPlugins(newInterceptor[]{catMybatisPlugin});
就可以看到test输出到cat的SQL:

注意:环境规范
Windows环境(具体在哪个盘下,取决于你的项目): 新建D:/data/appdatas/cat/client.xml目录和文件新建D:/data/applogs/cat/目录Linux环境: 新建/data/appdatas/cat/client.xml新建/data/applogs/cat/目录注意:目录要赋予读写权限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>
<server ip="172.30.201.159" port="2280" http-port="8080"/>
</servers>
</config>
注:
引用原链接:
cat搭建部署详细步骤(美团开源cat监控)
网友评论