CAT使用
背景
到了新厂后,厂里的架构师,也是我所在的平台组的老大,想找个中间件进行对dubbo服务的调用链进行监控,最原始的要求是:
监控调用链的性能,针对性能差的调用能够定位到是调用链的哪个环节比较耗时
然后找了CAT,恰巧前东家一直在用CAT,于是让我去调研、搞个demo出来试试看,是否可行。瞎搞了差不多2个星期,有了点结果,总结下。
目标
1.用CAT定位调用链性能差的环节
但用过CAT的人都知道,CAT对开发来讲最好的一个功能就是快速定位系统问题,所以潜在的一个目标或许可以加上
2.快速定位异常并且通知对应的开发
3.分析接入CAT带来的影响,包括正面和负面 (每个新接入的组件都需要)
CAT简介
github:https://github.com/dianping/cat
官方简介:CAT是基于Java开发的实时应用监控平台,包括实时应用监控,业务监控。
CAT监控系统将每次URL、Service的请求内部执行情况都封装为一个完整的消息树,所以使用CAT能够监控到调用链的每个环节。毫无疑问,最原始的需求(监控调用链)CAT是满足的。
实际使用过程中,发现CAT可以做的具体细节上有很多,包括:异常监控、日志监控、DAO层监控、缓存监控、JVM参数监控、数据库监控。
CAT支持集群搭建。
有哪些厂在用:
大众点评、美团、携程、同程旅游、猎聘网、OPPO、真旅网、陆金所、汽配铺、中科软、洋码头、周末去哪儿……
CAT服务端安装部署
第一步:mvn编译
用git下载cat项目,网速比较慢的话直接下载zip压缩包,进入目录,执行 mvn clean install -DskipTests
第二步:初始化构建配置文件以及创建数据库
执行 mvn cat:install 按照提示输入数据的配置 地址 账号 密码
注意:这一步需要对系统运行盘下的/data/appdatas/cat和/data/applogs/cat有读写权限,windows的话,根据部署的tomcat的位置而定,比如在E盘,则需要对e:/data/appdatas/cat和e:/data/applogs/cat有读写权限。
执行完这一步后,进入/data/appdatas/cat目录看是否有生成 server.xml client.xml datasources.xml三个文件,分别是服务器配置、客户端配置、数据库。
server.xml,是CAT服务端的配置,
IP地址:http端口
client.xml,接入CAT的客户端的系统的文件,主要配置,其中port是网络传输端口,用于消息网络传输,http-port是http请求端口,如果没有写对,客户端的log4J日志是无法传输到CAT的。
datasources.xml,是cat服务端的数据库配置
/data/applogs/cat 下只有日志文件,是服务端的日志配置文件。
第三步:部署
详情参考: http://unidal.org/cat/r/home?op=view&docName=deploy
进入cat-home的target目录下,刚刚编译生成的cat-alpha.war,重命名为cat.war,部署到tomcat的webapps。 访问http://localhost:8080/cat/r 即可。
注意:cat部署在resin服务器访问有问题,亲测访问不了,暂时没找到原因,cat官方也暂时没处理,所以直接用tomcat。
CAT接入web业务系统
详情参考:http://unidal.org/cat/r/home?op=view&docName=integration
配置client.xml
在本地/data/appdatas/cat目录配置client.xml 文件,主要是配置服务器的参数
项目代码相关CAT配置
- Web.xml中新增filter
- maven 依赖添加cat-client,这个来源于cat的代码编译 出来的,目前最新版本是1.4.0
- 在src/main/resource 目录下新增 META-INF新增 app.properties文件,用于配置客户端在服务端的的项目名称,内容如下:app.name=testTransaction3
- log4J日志集成,为了将程序所有异常信息都记录到cat方便看到程序的问题,将系统中的log4j的日志信息记录到CAT中。配置如下:
log4j.rootLogger=info,stdout,cat,FILE
…….
log4j.appender.cat=com.dianping.cat.log4j.CatAppender
再次强调,client.xml的http端口一定要正确,否则log4J日志流转不到CAT
CAT接入dubbo服务
一般情况下,服务器上dubbo服务启动有2种方式:
1.把dubbo服务封装成一个web项目,通过war包部署启动
2.把dubbo服务打成一个jar包,然后通过com.alibaba.dubbo.container.Main启动。
新厂的dubbo服务启动的方式就是采用第二种,稍微有些不同的是,使用了maven-shade-plugin 插件,将开发的dubbo服务 和 所依赖的jar打包成一个jar包,然后通过com.alibaba.dubbo.container.Main启动。
然后,将cat集成的时候睬坑了…… 细节后面再撸一篇文章搞出来
但dubbo服务集成CAT的工作还是跟web项目差不多的,一是配置app.properties,二是配置client.xml文件,三是配置log4J日志,四是maven加上cat-client依赖。
根据情况,采用了 写dubbo filter的方式比较合适。代码如下:
public class CatFilter implements Filter {
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
Transaction transaction = Cat.newTransaction("Service", invocation.getInvoker().getInterface().getSimpleName() + "." + invocation.getMethodName());
Result result = new RpcResult();
try {
result = invoker.invoke(invocation);
transaction.setStatus(Transaction.SUCCESS);
} catch (Throwable e) {
Cat.getProducer().logError(e); // 记录log4j的日志到cat
transaction.setStatus(e);
throw new RpcException(e);
} finally {
transaction.complete();
}
return result;
}
}
对每次dubbo服务调用都做了埋点记录,适用于客户端和服务端。
配置完后,初步能够监控到web系统和dubbo服务的异常和log4J日志,以及RPC调用的调用链,能满足日常的使用,更复杂的内容可以考虑自行扩展。
DAO层的监控埋点
到实际的使用时,如果需要对DAO层进行监控,可以通过实现mybatis的intercepter进行埋点,示例如下:
import com.dianping.cat.Cat;
import com.dianping.cat.message.Transaction;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import java.util.Properties;
/**
* Created by kevin on 16/12/29.
*/
@Intercepts(value =
{@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
})
public class CatSelectInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
String[] ids = StringUtils.split(mappedStatement.getId(), ".");
String proxyMethodId = ids[ids.length - 2] + "." + ids[ids.length - 1];
Transaction transaction = Cat.newTransaction("DAO", proxyMethodId);
try {
Object proceed = invocation.proceed();
transaction.setStatus(Transaction.SUCCESS);
return proceed;
} catch (Exception e) {
transaction.setStatus(e);
Cat.logError(e);
throw e;
} finally {
transaction.complete();
}
}
}
原理无非就是写个mybatis的Interceptor,没什么好说的。
如果需要记录SQL语句,可以自行扩展,不仔细讲。
完