根据Sentinel官方文档,Sentinel 控制台中监控数据聚合后直接存在内存中,未进行持久化,且仅保留最近 5 分钟的监控数据。若需要监控数据持久化的功能,需要自行扩展实现 MetricsRepository 接口。
https://github.com/alibaba/Sentinel/wiki/在生产环境中使用-Sentinel-控制台 文档中也给出了指导步骤:
1.自行扩展实现 MetricsRepository 接口;
2.注册成 Spring Bean 并在相应位置通过 @Qualifier 注解指定对应的 bean name 即可。
添加Maven依赖
在sentinel-dashboard的pom.xml中添加以下内容:
<!-- 添加mybatisplus依赖 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.0.5</version>
</dependency>
<!-- 添加mysql依赖,版本自定义 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
在这里我使用mybatis的增强工具 mybatisplus 来操作数据库,由于sentinel是基于springboot 2.0.5.RELEASE版本开发的,所以mybatisplus选用的版本是3.0.5。
集成高版本的mybatisplus会有问题。mybatisplus 3.0.6使用的是springboot 2.1.0.RELEASE
数据库脚本
CREATE TABLE `sentinel_metrics` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`gmt_create` datetime DEFAULT NULL COMMENT '创建时间',
`gmt_modified` datetime DEFAULT NULL COMMENT '修改时间',
`app` varchar(100) DEFAULT NULL COMMENT '应用名称',
`timestamp` datetime DEFAULT NULL COMMENT '监控信息的时间戳',
`resource` varchar(500) DEFAULT NULL COMMENT '资源名',
`pass_qps` bigint(20) DEFAULT NULL COMMENT '通过QPS',
`success_qps` bigint(20) DEFAULT NULL COMMENT '成功QPS',
`block_qps` bigint(20) DEFAULT NULL COMMENT '拒绝QPS',
`exception_qps` bigint(20) DEFAULT NULL COMMENT '异常QPS',
`rt` double DEFAULT NULL COMMENT '所有successQps的rt的和',
`count` int(11) DEFAULT NULL COMMENT '本次聚合的总条数',
`resource_code` int(11) DEFAULT NULL COMMENT '资源的hashCode',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
修改application.properties配置
在application.properties配置文件尾部添加以下配置:
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/sentinel?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8&allowMultiQueries=true
spring.datasource.username=****
spring.datasource.password=****
mybatis-plus.global-config.db-config.id-type=auto
mybatis-plus.global-config.db-config.field-strategy=not_null
mybatis-plus.global-config.db-config.table-underline=true
mybatis-plus.global-config.db-config.db-type=mysql
mybatis-plus.mapper-locations=xml/*Mapper.xml # mybatis mapper文件地址
mybatis-plus.type-aliases-package=com.alibaba.csp.sentinel.dashboard.datasource.entity # 实体类路径
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
数据库实体类
/**
* <p>
*
* </p>
*
* @author tigerkin
* @since 2022-03-08
*/
@TableName("sentinel_metrics")
public class SentinelMetricsEntity extends Model<SentinelMetricsEntity> {
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@TableId(value = "id", type = IdType.INPUT)
private Long id;
/**
* 创建时间
*/
@TableField("gmt_create")
private Date gmtCreate;
/**
* 修改时间
*/
@TableField("gmt_modified")
private Date gmtModified;
/**
* 应用名称
*/
private String app;
/**
* 监控信息的时间戳
*/
private Date timestamp;
/**
* 资源名
*/
private String resource;
/**
* 通过qps
*/
@TableField("pass_qps")
private Long passQps;
/**
* 成功qps
*/
@TableField("success_qps")
private Long successQps;
/**
* 限流qps
*/
@TableField("block_qps")
private Long blockQps;
/**
* 异常qps
*/
@TableField("exception_qps")
private Long exceptionQps;
/**
* 所有successQps的rt的和
*/
private Double rt;
/**
* 本次聚合的总条数
*/
private Integer count;
/**
* 资源的hashCode
*/
@TableField("resource_code")
private Integer resourceCode;
/** getter、setter、toString方法 **/
}
实现MetricsRepository接口
- save 与 saveAll:存储对应的监控数据
- queryByAppAndResourceBetween:查询某段时间内的某个应用的某个资源的监控数据
- listResourcesOfApp:查询某个应用下的所有资源
/**
* @ClassName CutsomMetricsRepository
* @Description
* @Author tigerkin
* @Date 2022/3/8 15:40
*/
@Component
@MapperScan(value = {"com.alibaba.csp.sentinel.dashboard.mapper"})
public class CustomMetricsRepository implements MetricsRepository<MetricEntity>{
private static final Logger LOG = LoggerFactory.getLogger(CustomMetricsRepository.class);
@Autowired
SentinelMetricsMapper sentinelMetricsMapper;
private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
@Override
public void save(MetricEntity entity) {
if (entity == null || StringUtil.isBlank(entity.getApp())) {
return;
}
readWriteLock.writeLock().lock();
try {
LOG.info("========> save -> entity: {}", entity.toString());
SentinelMetricsEntity metrics = SentinelMetricsEntity.setSentinelMetrics(entity);
metrics.insert();
} finally {
readWriteLock.writeLock().unlock();
}
}
@Override
public void saveAll(Iterable<MetricEntity> metrics) {
if (metrics == null) {
return;
}
readWriteLock.writeLock().lock();
try {
LOG.info("========> saveAll -> metrics: {}", metrics.toString());
List<SentinelMetricsEntity> metricsIns = new ArrayList<>();
metrics.forEach(e -> metricsIns.add(SentinelMetricsEntity.setSentinelMetrics(e)));
sentinelMetricsMapper.insertBatch(metricsIns);
} finally {
readWriteLock.writeLock().unlock();
}
}
@Override
public List<MetricEntity> queryByAppAndResourceBetween(String app, String resource, long startTime, long endTime) {
List<MetricEntity> results = new ArrayList<>();
if (StringUtil.isBlank(app)) {
return results;
}
readWriteLock.readLock().lock();
try {
QueryWrapper<SentinelMetricsEntity> qw = new QueryWrapper<>();
qw.eq("app", app);
qw.eq("resource", resource);
qw.ge("timestamp", new Date(startTime));
qw.le("timestamp", new Date(endTime));
qw.orderByAsc("timestamp");
List<SentinelMetricsEntity> sentinelMetrics = sentinelMetricsMapper.selectList(qw);
return sentinelMetrics.stream().map(e -> SentinelMetricsEntity.setMetricEntity(e)).collect(Collectors.toList());
} finally {
readWriteLock.readLock().unlock();
}
}
@Override
public List<String> listResourcesOfApp(String app) {
List<String> results = new ArrayList<>();
if (StringUtil.isBlank(app)) {
return results;
}
readWriteLock.readLock().lock();
try {
results = sentinelMetricsMapper.selectResourceByApp(app);
return results;
} finally {
readWriteLock.readLock().unlock();
}
}
}
指定MetricsRepository依赖注入的类型
把MetricController、MetricFetcher类中的MetricsRepository属性上添加@Qualifier(“customMetricsRepository”)
MetricController:
MetricFetcher:
可以在MetricController中修改统计时长,默认是5分钟,这里我改成了30分钟。
最后启动sentinel-dashboard项目。
到这里Sentinel实时监控数据持久化就实现了,重启sentinel控制台也能看到监控数据。
mybatisplus官方文档:https://baomidou.com/pages/24112f/
sentinel源码:https://github.com/alibaba/Sentinel
sentinel官方文档:
https://github.com/alibaba/Sentinel/wiki/控制台
https://github.com/alibaba/Sentinel/wiki/在生产环境中使用-Sentinel-控制台