CAT集成spring boot
1.背景
在上面介绍了CAT如何做跨进程追踪的。公司项目采用业务垂直切分的方式开发、前后端也是分离方式。那么实时监控埋点的地方有如下几处:
- 前端和后端交互
- 后端业务逻辑
- 数据库存储
- 远程PRC调用
- 消息队列
2.集成spring boot
在git官网中有一个包是大家贡献的集成代码。里面就有各种的集成。里面就有和spring boot集成的代码。代码是直接使用了cat client包中catfilter这个类。这个类的逻辑比较复杂,分了不同的情况。然而公司系统结构中没有那么复杂。整体流程是:前端 -> 网关 -> 后端。后端spring boot 工程只有一种类型,就是作为server端。所以我并没有采用client 自带的代码。而是采用别的架构师分享的一种简单的方式。代码如下:
/**
* 请求监控拦截
* @author tengx
*/
public class CatServletFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
CatContext catContext = getCatContext(request);
Cat.logRemoteCallServer(catContext);
Transaction t = Cat.newTransaction(CatConstants.TYPE_URL, request.getRequestURL().toString());
try {
catLogEvent(request);
filterChain.doFilter(servletRequest, servletResponse);
t.setStatus(Transaction.SUCCESS);
} catch (Exception ex) {
t.setStatus(ex);
Cat.logError(ex);
throw ex;
} finally {
t.complete();
}
}
/**
* 获取CAT监控上下文环境
* @param request 请求
* @return CAT上下文环境
*/
private CatContext getCatContext(HttpServletRequest request) {
CatContext catContext = new CatContext();
catContext.addProperty(Cat.Context.ROOT, request.getHeader(CatHttpConstants.CAT_HTTP_HEADER_ROOT_MESSAGE_ID));
catContext.addProperty(Cat.Context.PARENT, request.getHeader(CatHttpConstants.CAT_HTTP_HEADER_PARENT_MESSAGE_ID));
catContext.addProperty(Cat.Context.CHILD, request.getHeader(CatHttpConstants.CAT_HTTP_HEADER_CHILD_MESSAGE_ID));
return catContext;
}
/**
* 使用CAT event事件记录信息
* @param request 请求
*/
private void catLogEvent(HttpServletRequest request) {
Cat.logEvent(CatHttpConstants.SERVER_METHOD, request.getMethod());
Cat.logEvent(CatHttpConstants.SERVER_CLIENT, request.getRemoteHost());
Cat.logEvent(CatHttpConstants.SERVER_REFERER, request.getHeader("referer"));
Cat.logEvent(CatHttpConstants.SERVER_AGENT, request.getHeader("user-agent"));
Cat.logEvent(CatHttpConstants.SERVER_TOKEN, request.getHeader("token"));
Cat.logEvent(CatHttpConstants.SERVER_PARAMS, JsonUtils.toJSONString(request.getParameterMap()));
Cat.logEvent(CatHttpConstants.SERVER_DATETIME, DateUtil.now());
}
@Override
public void destroy() {
}
}
类主要的核心思想就是实现拦截器,在拦截器中使用CAT模型进行记录请求信息。对于异常需要进行捕获,不然会影响正常业务。最后需要对Transaction 进行完成操作。逻辑十分简单。相信大家一看就能明白做什么。
3.集成系统
在工程实践中有这样的场景,有很多个独立的服务都需要使用这个Filter,如果每个工程都加这个类会存在以下缺点:
- 重复代码,每个工程都有这个类
- 不利于以后的扩展。如果以后要扩展这个类的内容,那么就需要替换每个工程中的类。增加的维护成本
- 暴露了监控逻辑。监控逻辑并非是业务逻辑。系统工程一般都是由项目组中的研发进行开发的。研发的能力参差不齐,不能保证研发对这个逻辑如何操作。
基于以上的缺点。我在公司采用的方式就是在业务系统之下还就有一个核心组件层。让各个业务系统依赖这个组件层。然后在这个组件层做一些工具类封装、第三方调用的封装等。这样做的好处有一下几点:
- 屏蔽对外依赖。借用业界的一句名言:“任何复杂的问题就能使用一个中间层来处理!”。有这样一个中间层可以将业务逻辑和三方技术使用进行隔离。在做技术调整的时候不会影响业务逻辑。
- 减少重复代码。将多个系统公用的技术下沉到中间层有利于代码的复用。并且维护成本降低。主要维护中间层一处就好。
确定了在中间层写Filter之后,那么就引申出来下一个问题,在中间层中定义的类,如何让业务系统中加载到呢?如何让业务系统中spring 加载到这个Bean呢?
这里就要知道spring boot 的自动加载机制了。通过在MEAT-INF 下配置spring.factories 文件。在文件中配置自动加载的类。这样当工程引用了这个jar包的时候spring boot 就会自动加载指定的类。代码如下:
/**
* 对URL进行拦截的类
* @author tengx
*/
@Configuration
public class CatFilterConfigure {
@Bean("catFilter")
public Filter createFilter(){
return new CatServletFilter();
}
@Bean
public FilterRegistrationBean catFilter(Filter catFilter) {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(catFilter);
registration.addUrlPatterns("/*");
registration.setName("cat-filter");
registration.setOrder(1);
return registration;
}
}
4.总结:
- 使用filter的方式对spring boot 工程的API请求进行监控
- 任何问题都可以使用一个中间层的方式来处理
- 使用spring boot 动态加载机制来解决中间层类加载问题
推荐阅读
-
spring boot踩坑记
-
spring boot从redis取缓存发生java.lang.ClassCastException异常
-
spring-boot-2.0.3不一样系列之源码篇 - run方法(三)之createApplicationContext,绝对有值得你看的地方
-
Spring Boot @Scheduled定时任务代码实例解析
-
Spring Boot认证:整合Jwt
-
Spring Boot 2整合Redis做缓存
-
spring boot加载资源路径配置和classpath问题解决
-
spring boot使用自定义的线程池执行Async任务
-
Spring Boot简介
-
Spring Boot Dubbo 构建分布式服务的方法