前言
根据官方提供的wiki文档,sentinel控制台的实时监控数据,默认提供的存储数据时间为 5 分钟以内的数据。如果我们的需求是需要持久化的,那就需要我们自己定制实现相关的接口。
https://github.com/alibaba/Sentinel/wiki/%E5%AE%9E%E6%97%B6%E7%9B%91%E6%8E%A7
本文只做一个持久化基于MySQL的方式,对于监控数据,可能用MySQL关系数据库存储不太合适,虽然MySQL也可以通过事件或者任务定期清理或者通过代码定时的清理。
数据定期清理、历史归档的需求,用时序数据库比如InfluxDB可能更适合。
其实有些人说,如果我对历史记录不需要查询的需求,我想说,你没有要求,你搞这个是干啥呢?难道就为了个配置注册中心?历史记录,如同日志,方便排查问题。
基于InfluxDB后续补上,我们也学习一波时序数据库。
一:实时监控
1:阅读官方wiki文档之后,我们理了下面如下步骤:
1.1:需自己扩展实现 MetricsRepository 接口;
1.2:需要注册成 Spring Bean ,并在相应位置通过 @Qualifier 注解指定对应的 bean name 即可。
我们先打开源代码看看这个MetricsRepository接口是如何定义的
com.alibaba.csp.sentinel.dashboard.repository.metric包下的MetricsRepository接口
1.3 接口方法
我们仔细阅读,会发现当前接口定义了4个方法,分别用于查询和保存sentinel的metric数据。方法的注释写的很清楚了,这里还是需要简单过一下:
1.3.1 方法简述
save:保存单个metric
saveAll:保存多个metric
queryByAppAndResourceBetween:通过应用名名称、资源名称、开始时间、结束时间查询metric列表
listResourcesOfApp:通过应用名称查询资源列表
了解过以及实践过Spring data jpa的,估计看这接口的方法定义,和Spring data jpa 很像,即某个实体类xxx对应一个xxxRepository,方法的命令也很规范,save、queryBy…。
本文就基于Spring data jpa来实现基于MySQL持久化
2:控制台
2.1 查看控制台
我们先启动我们自定义的sentinel-dashboard的项目,启动之后,我们访问下控制台
我们现在需要跟控制台结合,看看《实时监控》菜单的界面,大概的可以猜到列表页面的查询流程是怎样的。
菜单属于某一个应用,这里应用名称是sentinel-dashboard;
我们先通过应用名称查询应用下所有的资源,图中看到有2个,资源名称分别是/resource/machineResource.json、/flow/rules.json;
// listResourcesOfApp方法
再通过应用名称、资源名称、时间等查询metric列表用于呈现统计图表;
// queryByAppAndResourceBetween方法
2.2 阅读源码的实现类
我们回到代码,我们用Ctrl+H 看看当前接口的实现类
我们看的默认有一个用内存存储的实现类:InMemoryMetricsRepository
2.3 调用关系
我们再看看MetricsRepository的方法调用
在各个方法上,通过Ctrl+Alt+H 查看方法调用关系:
2.3.1 save
2.3.2 saveAll
2.3.3 queryByAppAndResourceBetween
2.3.4 listResourcesOfApp
查看完4个方法的调用关系,可以看到,MetricsRepository接口的方法
保存:save方法被它的实现类InMemoryMetricsRepository的saveAll调用,再往上走被MetricFetcher调用,用于保存metric数据;
查询:queryByAppAndResourceBetween、listResourcesOfApp被MetricController调用,用于查询metric数据;
如上,就是基本梳理了MetricsRepository接口的4个方法和调用流程,现在接下来我们就要使用MySQL数据库持久化,实现一个MetricsRepository接口。
二:创建数据库表
我们先看看源码MetricsRepository,既然是泛型
我们看看实现类
我们进入当前实体类,模仿着MetricEntity类搞一个表
表DDL如下:
CREATE TABLE `sentinel_metric` (
`id` INT NOT NULL AUTO_INCREMENT COMMENT 'id,主键',
`gmt_create` DATETIME COMMENT '创建时间',
`gmt_modified` DATETIME COMMENT '修改时间',
`app` VARCHAR(100) COMMENT '应用名称',
`timestamp` DATETIME COMMENT '统计时间',
`resource` VARCHAR(500) COMMENT '资源名称',
`pass_qps` INT COMMENT '通过qps',
`success_qps` INT COMMENT '成功qps',
`block_qps` INT COMMENT '限流qps',
`exception_qps` INT COMMENT '发送异常的次数',
`rt` DOUBLE COMMENT '所有successQps的rt的和',
`_count` INT COMMENT '本次聚合的总条数',
`resource_code` INT COMMENT '资源的hashCode',
INDEX app_idx(`app`) USING BTREE,
INDEX resource_idx(`resource`) USING BTREE,
INDEX timestamp_idx(`timestamp`) USING BTREE,
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;
提示:细心查看DDL,发现我们给app(应用名称),resource(资源名称),timestamp加上了索引,因为这3个条件在where 条件中使用到,添加索引增加查询速度;再仔细阅读,为什么count加上了_前缀符号,因为count是mysql关键字,记住,字段名不要瞎JB取。
三:编写接口
首先我们引入spring data jpa 依赖,pom文件添加如下依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>${
spring.boot.version}</version>
</dependency>
3.1 新建实体类
我是把实体类新建到与实现类的实体类对应的目录
import org.springframework.data.annotation.Id;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import java.io.Serializable;
import java.util.Date;
/**
* @author Gaci
* @className MetricPO
* @description 我没有加lombok插件,所以我自己提供了getter和setter方法
* @date 2021/5/8 11:03
**/
@Entity