Sentinel控制台实时监控数据持久化【Mysql】

根据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-控制台

猜你喜欢

转载自blog.csdn.net/weixin_42270645/article/details/123416086