【sentinel】监控数据db持久化

sentinel github地址

需求:实现sentinel的监控数据持久化


分析原有实现


github下载源码,打开sentinel-dashboard模块。
监控数据查看的controller:com.alibaba.csp.sentinel.dashboard.controller.MetricController
查看源码,原有的存储实现如下:
在这里插入图片描述
com.alibaba.csp.sentinel.dashboard.repository.metric.MetricsRepository是一个接口,原有的具体实现是:

com.alibaba.csp.sentinel.dashboard.repository.metric.repository.InMemoryMetricsRepository

在这里插入图片描述
是基于内存存储的监控数据。实现方法为:

com.alibaba.csp.sentinel.dashboard.repository.metric.repository.InMemoryMetricsRepository#save

在这里插入图片描述


db改造


使用jpa的方式实现存储

添加依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
            <version>${spring.boot.version}</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.49</version>
        </dependency>

表结构

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`),
  KEY `app_idx` (`app`) USING BTREE,
  KEY `resource_idx` (`resource`) USING BTREE,
  KEY `timestamp_idx` (`timestamp`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=200202 DEFAULT CHARSET=utf8mb4;

entity

package com.alibaba.csp.sentinel.dashboard.repository.metric.po;

import java.io.Serializable;
import java.util.Date;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

/**
 * @author fy
 * @date 2022/11/22
 */
@Entity
@Table(name = "sentinel_metrics")
public class SentinelMetricPO implements Serializable {


    /**id,主键*/
    @Id
    @GeneratedValue
    @Column(name = "id")
    private Long id;

    /**创建时间*/
    @Column(name = "gmt_create")
    private Date gmtCreate;

    /**修改时间*/
    @Column(name = "gmt_modified")
    private Date gmtModified;

    /**应用名称*/
    @Column(name = "app")
    private String app;

    /**统计时间*/
    @Column(name = "timestamp")
    private Date timestamp;

    /**资源名称*/
    @Column(name = "resource")
    private String resource;

    /**通过qps*/
    @Column(name = "pass_qps")
    private Long passQps;

    /**成功qps*/
    @Column(name = "success_qps")
    private Long successQps;

    /**限流qps*/
    @Column(name = "block_qps")
    private Long blockQps;

    /**发送异常的次数*/
    @Column(name = "exception_qps")
    private Long exceptionQps;

    /**所有successQps的rt的和*/
    @Column(name = "rt")
    private Double rt;

    /**本次聚合的总条数*/
    @Column(name = "count")
    private Integer count;

    /**资源的hashCode*/
    @Column(name = "resource_code")
    private Integer resourceCode;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public Date getGmtCreate() {
        return gmtCreate;
    }

    public void setGmtCreate(Date gmtCreate) {
        this.gmtCreate = gmtCreate;
    }

    public Date getGmtModified() {
        return gmtModified;
    }

    public void setGmtModified(Date gmtModified) {
        this.gmtModified = gmtModified;
    }

    public String getApp() {
        return app;
    }

    public void setApp(String app) {
        this.app = app;
    }

    public Date getTimestamp() {
        return timestamp;
    }

    public void setTimestamp(Date timestamp) {
        this.timestamp = timestamp;
    }

    public String getResource() {
        return resource;
    }

    public void setResource(String resource) {
        this.resource = resource;
    }

    public Long getPassQps() {
        return passQps;
    }

    public void setPassQps(Long passQps) {
        this.passQps = passQps;
    }

    public Long getSuccessQps() {
        return successQps;
    }

    public void setSuccessQps(Long successQps) {
        this.successQps = successQps;
    }

    public Long getBlockQps() {
        return blockQps;
    }

    public void setBlockQps(Long blockQps) {
        this.blockQps = blockQps;
    }

    public Long getExceptionQps() {
        return exceptionQps;
    }

    public void setExceptionQps(Long exceptionQps) {
        this.exceptionQps = exceptionQps;
    }

    public Double getRt() {
        return rt;
    }

    public void setRt(Double rt) {
        this.rt = rt;
    }

    public Integer getCount() {
        return count;
    }

    public void setCount(Integer count) {
        this.count = count;
    }

    public Integer getResourceCode() {
        return resourceCode;
    }

    public void setResourceCode(Integer resourceCode) {
        this.resourceCode = resourceCode;
    }
}

存储实现类

package com.alibaba.csp.sentinel.dashboard.repository.metric.repository;

import java.time.Instant;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import com.alibaba.csp.sentinel.dashboard.datasource.entity.MetricEntity;
import com.alibaba.csp.sentinel.dashboard.repository.metric.MetricsRepository;
import com.alibaba.csp.sentinel.dashboard.repository.metric.po.SentinelMetricPO;
import com.alibaba.csp.sentinel.util.StringUtil;

import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

/**
 * @author fy
 * @date 2022/11/22
 */
@Transactional
@Repository("sentinelMetricsRepository")
public class SentinelMetricsRepository implements MetricsRepository<MetricEntity> {

    @PersistenceContext
    private EntityManager em;

    @Override
    public void save(MetricEntity metric) {
        if (metric == null || StringUtil.isBlank(metric.getApp())) {
            return;
        }

        SentinelMetricPO metricPO = new SentinelMetricPO();
        BeanUtils.copyProperties(metric, metricPO);
        em.persist(metricPO);
    }

    @Override
    public void saveAll(Iterable<MetricEntity> metrics) {
        if (metrics == null) {
            return;
        }

        metrics.forEach(this::save);
    }

    @Override
    public List<MetricEntity> queryByAppAndResourceBetween(String app, String resource, long startTime, long endTime) {
        List<MetricEntity> results = new ArrayList<MetricEntity>();
        if (StringUtil.isBlank(app)) {
            return results;
        }

        if (StringUtil.isBlank(resource)) {
            return results;
        }

        StringBuilder hql = new StringBuilder();
        hql.append("FROM SentinelMetricPO");
        hql.append(" WHERE app=:app");
        hql.append(" AND resource=:resource");
        hql.append(" AND timestamp>=:startTime");
        hql.append(" AND timestamp<=:endTime");

        Query query = em.createQuery(hql.toString());
        query.setParameter("app", app);
        query.setParameter("resource", resource);
        query.setParameter("startTime", Date.from(Instant.ofEpochMilli(startTime)));
        query.setParameter("endTime", Date.from(Instant.ofEpochMilli(endTime)));

        List<SentinelMetricPO> metricPOs = query.getResultList();
        if (CollectionUtils.isEmpty(metricPOs)) {
            return results;
        }

        for (SentinelMetricPO metricPO : metricPOs) {
            MetricEntity metricEntity = new MetricEntity();
            BeanUtils.copyProperties(metricPO, metricEntity);
            results.add(metricEntity);
        }

        return results;
    }

    @Override
    public List<String> listResourcesOfApp(String app) {
        List<String> results = new ArrayList<>();
        if (StringUtil.isBlank(app)) {
            return results;
        }

        StringBuilder hql = new StringBuilder();
        hql.append("FROM SentinelMetricPO");
        hql.append(" WHERE app=:app");
        hql.append(" AND timestamp>=:startTime");

        // TODO: 2022/11/22 查看多久历史监控数据 30分钟
        long startTime = System.currentTimeMillis() - 1000 * 60 * 30;
        Query query = em.createQuery(hql.toString());
        query.setParameter("app", app);
        query.setParameter("startTime", Date.from(Instant.ofEpochMilli(startTime)));

        List<SentinelMetricPO> metricPOs = query.getResultList();
        if (CollectionUtils.isEmpty(metricPOs)) {
            return results;
        }

        List<MetricEntity> metricEntities = new ArrayList<MetricEntity>();
        for (SentinelMetricPO metricPO : metricPOs) {
            MetricEntity metricEntity = new MetricEntity();
            BeanUtils.copyProperties(metricPO, metricEntity);
            metricEntities.add(metricEntity);
        }

        Map<String, MetricEntity> resourceCount = new HashMap<>(32);

        for (MetricEntity metricEntity : metricEntities) {
            String resource = metricEntity.getResource();
            if (resourceCount.containsKey(resource)) {
                MetricEntity oldEntity = resourceCount.get(resource);
                oldEntity.addPassQps(metricEntity.getPassQps());
                oldEntity.addRtAndSuccessQps(metricEntity.getRt(), metricEntity.getSuccessQps());
                oldEntity.addBlockQps(metricEntity.getBlockQps());
                oldEntity.addExceptionQps(metricEntity.getExceptionQps());
                oldEntity.addCount(1);
            } else {
                resourceCount.put(resource, MetricEntity.copyOf(metricEntity));
            }
        }

        // Order by last minute b_qps DESC.
        return resourceCount.entrySet()
            .stream()
            .sorted((o1, o2) -> {
                MetricEntity e1 = o1.getValue();
                MetricEntity e2 = o2.getValue();
                int t = e2.getBlockQps().compareTo(e1.getBlockQps());
                if (t != 0) {
                    return t;
                }
                return e2.getPassQps().compareTo(e1.getPassQps());
            })
            .map(Map.Entry::getKey)
            .collect(Collectors.toList());
    }
}

controller层替换存储实现

在这里插入图片描述

在这里插入图片描述

最终效果

在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Sentinel是一款开源的流量控制和熔断降级框架,而Nacos是一个动态服务发现、配置管理和服务管理平台。在Sentinel,可以通过实现NacosRepository接口将数据持久化到Nacos。 具体实现步骤如下: 1. 首先,需要引入Sentinel和Nacos的相关依赖包。 2. 创建一个实现NacosRepository接口的类,该类负责将Sentinel规则数据持久化到Nacos。可以参考以下代码示例: ```java import com.alibaba.csp.sentinel.datasource.Converter;import com.alibaba.csp.sentinel.datasource.nacos.NacosDataSource; import com.alibaba.csp.sentinel.datasource.nacos.NacosDataSourceBuilder; import com.alibaba.csp.sentinel.datasource.nacos.NacosDataSourceConfiguration; import com.alibaba.csp.sentinel.datasource.nacos.NacosDataSourceListener; import com.alibaba.csp.sentinel.init.InitFunc; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.TypeReference; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.config.ConfigService; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.naming.NamingFactory; import com.alibaba.nacos.api.naming.NamingService; import com.alibaba.nacos.api.naming.pojo.Instance; import java.util.List; import java.util.Properties; public class NacosRepository implements InitFunc { private static final String GROUP_ID = "SENTINEL_GROUP"; private static final String FLOW_DATA_ID = "FLOW_RULES"; @Override public void init() throws Exception { // Nacos配置 Properties properties = new Properties(); properties.put(PropertyKeyConst.SERVER_ADDR, "localhost:8848"); properties.put(PropertyKeyConst.NAMESPACE, "public"); // 创建Nacos数据源 NacosDataSource<List<FlowRule>> dataSource = NacosDataSourceBuilder .createDataSourceBuilder() .groupId(GROUP_ID) .dataId(FLOW_DATA_ID) .configService(getConfigService(properties)) .objectDeserializer(new Converter<String, List<FlowRule>>() { @Override public List<FlowRule> convert(String source) { return JSON.parseObject(source, new TypeReference<List<FlowRule>>() {}); } }) .build(); // 注册Nacos数据源监听器 dataSource.addListener(new NacosDataSourceListener<List<FlowRule>>() { @Override public void receiveConfigInfo(List<FlowRule> config) { // 处理配置更新事件 // 将配置更新到Sentinel } @Override public void handleException(Throwable throwable) { // 处理异常事件 } }); // 初始化Nacos数据源 dataSource.init(); } private ConfigService getConfigService(Properties properties) throws NacosException { return NamingFactory.createConfigService(properties); } } ``` 在上述代码,我们创建了一个NacosDataSource对象,并通过NacosDataSourceBuilder进行配置。其,groupId和dataId用于指定Nacos的配置分组和数据ID。objectDeserializer用于将Nacos的配置反序列化为Sentinel规则对象。 然后,我们通过addListener方法注册了一个NacosDataSourceListener,用于监听Nacos配置的变化。在receiveConfigInfo方法,可以处理配置更新事件,将更新后的配置更新到Sentinel。 最后,在init方法,我们初始化了NacosDataSource对象。 3. 在Sentinel的配置文件,配置NacosRepository类的初始化: ``` -Dcsp.sentinel.init-block-args=NacosRepository ``` 这样,当Sentinel启动时,会自动初始化NacosRepository类,并将规则数据持久化到Nacos。 以上就是将Sentinel规则数据持久化到Nacos的实现方式。通过实现NacosRepository接口,可以将Sentinel规则数据与Nacos进行动态同步,实现规则持久化和动态更新。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值