五分钟后,你将学会在SpringBoot项目中如何集成CAT调用链
买买买结算系统

一年一度的双十一购物狂欢节就要到了,又到剁手党们开始表演的时刻了。当我们把种草很久的商品放入购物车以后,点击“结算”按钮时,就来到了买买买必不可少的结算页面了。让我们虚拟一个买买买结算系统,为结算页面提供商品、促销、库存等结算信息,就此系统展开如何在SpringBoot项目中集成CAT调用链。买买买结算系统包含以下4个项目:
- 结算UI:为前端页面提供结算的基础数据。
- 购物车API:获取用户购物车中有哪些商品。
- 商品API:获取商品详细信息
- 商品促销API:获取商品的促销信息
- 商品库存API:获取商品的库存信息
时序图如下:

通过Maven添加依赖
<dependency>
<groupId>com.dianping.cat</groupId>
<artifactId>cat-client</artifactId>
<version>3.0.0</version>
</dependency>
配置client.xml
创建/data/appdatas/cat/目录,并创建client.xml文件:
<?xml version="1.0" encoding="utf-8"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema" xsi:noNamespaceSchemaLocation="config.xsd">
<servers>
<server ip="127.0.0.1" port="2280" http-port="8080" />
</servers>
</config>
注意:把127.0.0.1替换成CAT服务端的IP。
配置项目名称
买买买结算系统中一共有5个项目,每个项目都需要配置各自的项目名称。
首先,在每个项目中创建如下文件:
src/main/resources/META-INF/app.properties
然后,在每个项目里添加如下内容:
- 结算UI:
app.name=buy-buy-buy-checkout - 购物车API:
app.name=buy-buy-buy-cart - 商品API:
app.name=buy-buy-buy-product - 商品促销API:
app.name=buy-buy-buy-promotion - 商品库存API:
app.name=buy-buy-buy-store
注意:项目名称只能包含英文字母 (a-z, A-Z)、数字 (0-9)、下划线 (_) 和中划线 (-)
准备埋点
在埋点之前,需要先写两个公共类,方便之后埋点时调用。
- 第一个类实现Cat.Context接口,用于存放调用链上下文信息:
public class CatContext implements Cat.Context {
private Map<String, String> properties = new HashMap<>();
@Override
public void addProperty(String key, String value) {
properties.put(key, value);
}
@Override
public String getProperty(String key) {
return properties.get(key);
}
@Override
public String toString() {
return "CatContext{"
+ "properties=" + properties + '}';
}
}
- 第二个类中定义一些常量,在调用API时作为header中的key。
public class CatHttpConstants {
public static final String CAT_HTTP_HEADER_CHILD_MESSAGE_ID = "DD-CAT-CHILD-MESSAGE-ID";
public static final String CAT_HTTP_HEADER_PARENT_MESSAGE_ID = "DD-CAT-PARENT-MESSAGE-ID";
public static final String CAT_HTTP_HEADER_ROOT_MESSAGE_ID = "DD-CAT-ROOT-MESSAGE-ID";
}
开始埋点
使用CAT进行分布式调用链监控,需要修改项目中的代码进行埋点:
- 在刚刚接收到请求时进行埋点。
- 在准备调用API时进行埋点。
那么在买买买结算系统中需要做哪些代码修改呢?看一下时序图的变化就明白了:

第一个埋点,在刚刚接收到请求时,这里使用Filter实现,代码如下:
public class CatServletFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
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));
Cat.logRemoteCallServer(catContext);
Transaction t = Cat.newTransaction(CatConstants.TYPE_URL, request.getRequestURI());
try {
Cat.logEvent("Service.method", request.getMethod(), Message.SUCCESS, request.getRequestURL().toString());
Cat.logEvent("Service.client", request.getRemoteHost());
filterChain.doFilter(servletRequest, servletResponse);
t.setStatus(Transaction.SUCCESS);
} catch (Exception ex) {
t.setStatus(ex);
Cat.logError(ex);
throw ex;
} finally {
t.complete();
}
}
@Override
public void destroy() {
}
}
Filter已经写好了,接下来还需要把Filter注册到SpringBoot中:
@Configuration
public class CatConfiguration {
@Bean
public FilterRegistrationBean catServletFilter() {
FilterRegistrationBean registration = new FilterRegistrationBean();
CatServletFilter filter = new CatServletFilter();
registration.setFilter(filter);
registration.addUrlPatterns("/*");
registration.setName("cat-servlet-filter");
registration.setOrder(1);
return registration;
}
}
第两个埋点,在调用API的HttpClient工具类中统一增加代码,以GET方式为例:
public static String doGet(String url) throws IOException {
HttpGet httpGet = new HttpGet(url);
CloseableHttpResponse response = null;
CloseableHttpClient httpClient = HttpClientBuilder.create().build();
String content = null;
Transaction t = Cat.newTransaction(CatConstants.TYPE_CALL, url);
try {
Context ctx = new CatContext();
Cat.logRemoteCallClient(ctx);
httpGet.setHeader(CatHttpConstants.CAT_HTTP_HEADER_ROOT_MESSAGE_ID, ctx.getProperty(Cat.Context.ROOT));
httpGet.setHeader(CatHttpConstants.CAT_HTTP_HEADER_PARENT_MESSAGE_ID, ctx.getProperty(Cat.Context.PARENT));
httpGet.setHeader(CatHttpConstants.CAT_HTTP_HEADER_CHILD_MESSAGE_ID, ctx.getProperty(Cat.Context.CHILD));
response = httpClient.execute(httpGet);
if (response.getStatusLine().getStatusCode() == 200) {
content = EntityUtils.toString(response.getEntity(), "UTF-8");
t.setStatus(Transaction.SUCCESS);
}
} catch (Exception e) {
Cat.logError(e);
t.setStatus(e);
throw e;
} finally {
if (response != null) {
response.close();
}
if (httpClient != null) {
httpClient.close();
}
t.complete();
}
return content;
}
结语
以上便是SpringBoot集成CAT调用链的整个实例了,可以灵活应用,更加优雅的集成到实际项目中。
另外,CAT还有很多丰富的功能,可参见官网。
五分钟后,你将学会在SpringBoot项目中如何集成CAT调用链的更多相关文章
- SpringBoot12 QueryDSL01之QueryDSL介绍、springBoot项目中集成QueryDSL
1 QueryDSL介绍 1.1 背景 QueryDSL的诞生解决了HQL查询类型安全方面的缺陷:HQL查询的扩展需要用字符串拼接的方式进行,这往往会导致代码的阅读困难:通过字符串对域类型和属性的不安 ...
- SpringBoot项目中遇到的BUG
1.启动项目的时候报错 1.Error starting ApplicationContext. To display the auto-configuration report re-run you ...
- SpringBoot项目中,表单的验证操作
在创建Springboot项目中,我们使用了表单验证操作,这一操作将极大地简化我们编程的开发 1.接收数据,以及验证 @PostMapping("/save") public Mo ...
- 国际化的实现i18n--错误码国际化以及在springboot项目中使用
国际化 ,英文叫 internationalization 单词太长 ,又被简称为 i18n(取头取尾中间有18个字母); 主要涉及3个类: Locale用来设置定制的语言和国家代码 Resource ...
- Spring-Boot项目中配置redis注解缓存
Spring-Boot项目中配置redis注解缓存 在pom中添加redis缓存支持依赖 <dependency> <groupId>org.springframework.b ...
- 在SpringBoot项目中添加logback的MDC
在SpringBoot项目中添加logback的MDC 先看下MDC是什么 Mapped Diagnostic Context,用于打LOG时跟踪一个“会话“.一个”事务“.举例,有一个web ...
- 自身使用的springboot项目中比较全的pom.xml
在学习的时候常建新的项目,mark下商用的jar <dependency> <groupId>org.mybatis</groupId> <artifactI ...
- springboot 项目中获取默认注入的序列化对象 ObjectMapper
在 springboot 项目中使用 @SpringBootApplication 会自动标记 @EnableAutoConfiguration 在接口中经常需要使用时间类型,Date ,如果想要格式 ...
- springboot项目中js、css静态文件路径访问
springboot静态文件访问的问题,相信大家也有遇到这个问题,如下图项目结构. 项目结构如上所示,静态页面引入js.css如下所示. 大家肯定都是这样写的,但是运行的话就是出不来效果,图片也不显示 ...
随机推荐
- 基于操作系统原理的Linux 的基本操作和常用命令的使用
一.实验目的 1.学会不同Linux用户登录的方法. 2.掌握常用Linux命令的使用方法. 3.了解Linux命令中参数选项的用法和作用. 二.实验内容 1. 文件操作命令 (1) 查看文件与目录 ...
- SpringBootSecurity学习(10)网页版登录之记住我功能
场景 很多登录都有记住我这个功能,在用户登陆一次以后,系统会记住用户一段时间,在这段时间,用户不用反复登陆就可以使用我们的系统.记住用户功能的基本原理如下图: 用户登录的时候,请求发送给过滤器User ...
- Java之字符流读写文件、文件的拷贝
字符流读数据 – 按单个字符读取 创建字符流读文件对象: Reader reader = new FileReader("readme.txt"); 调用方法读取数据: int d ...
- js校验对象是否全部为空
function judgeIsNotBlank(obj) { var bool = true; var arr = Object.keys(obj); ; for(var key in obj){ ...
- freemarker常用属性
1.th:action 定义后台控制器的路径,类似<form>标签的action属性. 示例如下. <form id="login" th:action=&quo ...
- 使用Hexo开源博客系统,轻松搭建你的个人博客(2)- 配置篇
上一章节,我们介绍了Hexo的基础搭建,搭建完大家一定发现,是英文版本的,并且页面有点丑陋.这一章节,就来跟大家介绍Hexo的配置和主题的设置. 站点信息 上一章有跟大家提到过_config.yml这 ...
- 插入排序--JavaScript描述
记录一个插入排序写法 <script> var arr = [123,34,23,6,1,4,23,324,65,122]; for (let i =1, j = i ; i < a ...
- TensorFlow基本计算单元与基本操作
在学习深度学习等知识之前,首先得了解著名的框架TensorFlow里面的一些基础知识,下面首先看一下这个框架的一些基本用法. import tensorflow as tf a = 3 # Pytho ...
- 洛谷:P3950 部落冲突
原题地址:https://www.luogu.org/problemnew/show/P3950 题目简述 给定一棵树,每次给定一个操作,有如下两种: 将某条边染黑 2.询问给定的u,v两点间是否有边 ...
- 对BUG的分析与理解
对BUG的分析与理解 bug的分类 bug,其实就是软件期望的行为与实际行为的差异.从程序的角度来看,在软件整个生命周期中都会有bug的出现.需求分析过程中,需求理解的不足,导致的理解错位 ,遗漏甚至 ...