100字范文,内容丰富有趣,生活中的好帮手!
100字范文 > Sentinel 控制台实时监控持久化到MySQL

Sentinel 控制台实时监控持久化到MySQL

时间:2024-02-10 18:46:55

相关推荐

Sentinel 控制台实时监控持久化到MySQL

Sentinel 控制台是流量控制、熔断降级规则统一配置和管理的入口,它为用户提供了机器自发现、簇点链路自发现、监控、规则配置等功能。在 Sentinel 控制台上,我们可以配置规则并实时查看流量控制效果。

Sentinel 控制台中监控数据聚合后直接存在内存中,未进行持久化,且仅保留最近 5 分钟的监控数据。若需要监控数据持久化的功能,可以自行扩展实现 MetricsRepository 接口 。

根据官方文档,对 Sentinel Dashboard 进行优化,将监控数据持久化到 MySQL 数据库,通过 MyBatis-Plus 对监控数据进行操作。

官方地址:/alibaba/Sentinel/wiki/在生产环境中使用-Sentinel

Sentinel 监控

Sentinel 会记录资源访问的秒级数据(若没有访问则不进行记录)并保存在本地日志中,具体格式请见 秒级监控日志文档。Sentinel 控制台可以通过 Sentinel 客户端预留的 HTTP API 从秒级监控日志中拉取监控数据,并进行聚合。

目前 Sentinel 控制台中监控数据聚合后直接存在内存中,未进行持久化,且仅保留最近 5 分钟的监控数据。若需要监控数据持久化的功能,可以自行扩展实现 MetricsRepository 接口(0.2.0 版本),然后注册成 Spring Bean 并在相应位置通过 @Qualifier 注解指定对应的 bean name 即可。MetricsRepository 接口定义了以下功能:

save 与 saveAll:存储对应的监控数据queryByAppAndResourceBetween:查询某段时间内的某个应用的某个资源的监控数据listResourcesOfApp:查询某个应用下的所有资源

其中默认的监控数据类型为 MetricEntity,包含应用名称、时间戳、资源名称、异常数、请求通过数、请求拒绝数、平均响应时间等信息。对于监控数据的存储,用户需要根据自己的存储精度,来考虑如何存储这些监控数据。为了更好地支撑大规模的集群,生产环境通常需要部署多个控制台实例,通常需要仔细设计下监控分片拉取和写入策略。

同时用户可以自行进行扩展,适配 Grafana 等可视化平台,以便将监控数据更好地进行可视化。

Sentinel Dashboard

MetricsRepository

MetricsRepository接口定义

save:保存 metric 信息saveAll:批量保存 metric 信息queryByAppAndResourceBetween:通过应用名称(app)、资源名称(resource)、timestamp 开始时间 、timestamp 结束时间查询 metric 列表listResourcesOfApp:通过应用名称(app) 查询 Metric 列表

public interface MetricsRepository<T> {/*** Save the metric to the storage repository.*/void save(T metric);/*** Save all metrics to the storage repository.*/void saveAll(Iterable<T> metrics);/*** Get all metrics by appName and resourceName between a period of time.*/List<T> queryByAppAndResourceBetween(String app, String resource, long startTime, long endTime);/*** List resource name of provided application name.*/List<String> listResourcesOfApp(String app);}

目前 MetricsRepository 接口的实现是基于内存级别

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

MetricEntity

MetricEntity实体类

public class MetricEntity {private Long id;private Date gmtCreate;private Date gmtModified;private String app;/*** 监控信息的时间戳*/private Date timestamp;private String resource;private Long passQps;private Long successQps;private Long blockQps;private Long exceptionQps;/*** summary rt of all success exit qps.*/private double rt;/*** 本次聚合的总条数*/private int count;private int resourceCode;... ...}

Sentinel Dashboard 整合 MyBatis Plus

MyBatis Plus 整合步骤

Sentinel Dashboard 整合 MyBatis Plus 将监控数据持久化到 MySQL 步骤 :

1、引入相关依赖

2、修改 application.properties 配置

3、根据 MetricEntity 创建 MySQL 数据库脚本并执行

4、通过 MyBatis Plus Generator 生成 entity、mypper、xml 文件

5、新增 MetricsRepository 的数据库实现 InDatabaseMetricsRepository

6、修改 MetricsRepository 相关注入,改为 InDatabaseMetricsRepository 实现

1、引入相关依赖

<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.3</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.26</version></dependency>

2、修改 application.properties 中配置

修改 application.properties 文件,在 application.properties 文件中配置 MySQL 数据库连接地址和 mybatis-plus 相关配置。

# mysqlspring.datasource.driver-class-name=com.mysql.cj.jdbc.Driverspring.datasource.url=jdbc:mysql://127.0.0.1:3306/spring-boot?characterEncoding=utf-8spring.datasource.username=rootspring.datasource.password=root# mybatis plusmybatis-plus.global-config.banner=falsemybatis-plus.configuration.map-underscore-to-camel-case=truemapper-locations: classpath:mapper/*Mapper.xml

3、创建 MySQL 数据库脚本

MetricEntity 实体类中,已经提供了监控信息的相关数据,根据属性,创建对应的 MySQL 脚本

CREATE TABLE `metric` (`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',`app` varchar(255) DEFAULT NULL COMMENT '应用名称',`resource` varchar(255) DEFAULT NULL COMMENT '资源名称',`timestamp` datetime DEFAULT NULL COMMENT '监控信息时间戳',`gmt_create` datetime DEFAULT NULL COMMENT '创建时间',`gmt_modified` datetime 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` decimal(10,2) DEFAULT NULL COMMENT '资源的平均响应时间',`count` int(10) DEFAULT NULL COMMENT '本次聚合的总条数',`resource_code` int(10) DEFAULT NULL COMMENT '资源hashcode',PRIMARY KEY (`id`),KEY `idx_app_timestamp` (`app`,`timestamp`) USING BTREE) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Sentinel监控信息表';

4、生成实体类等文件

通过 MyBatis Plus Generator 生成 entity、mypper、xml 文件

MyBatis Plus Generator 工具下载地址:

GitHub spring-boot-mybatis-plus-generator

实体类 Metric.java

public class Metric implements Serializable {private static final long serialVersionUID = 1L;/*** id*/@TableId(value = "id", type = IdType.AUTO)private Long id;/*** 创建时间*/private Date gmtCreate;/*** 修改时间*/private Date gmtModified;/*** 应用名称*/private String app;/*** 监控信息时间戳*/private Date timestamp;/*** 资源名称*/private String resource;/*** 通过QPS*/private Long passQps;/*** 成功QPS*/private Long successQps;/*** 限流QPS*/private Long blockQps;/*** 异常QPS*/private Long exceptionQps;/*** 资源平均响应时间*/private Double rt;/*** 本次聚合的总条数*/private Integer count;/*** 资源hashcode*/private Integer resourceCode;// setter 、getter}

MetricMapper.java

其中 MetricsRepository 的 saveAll 的实现是通过循环调用 save 方法,如果对数据库进行操作,可以使用批量插入操作,减少对数据库的频繁调用。

metrics.forEach(this::save);

在 MetricMapper.xml 文件中,batchInsert 为批量保存操作接口。

@Mapperpublic interface MetricMapper extends BaseMapper<Metric> {int batchInsert(List<Metric> metricList);}

MetricMapper.xml文件中 batchInsert 是 MetricMapper 接口中 int batchInsert(List<Metric> metricList) 对应的批量保存 SQL 。

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE mapper PUBLIC "-////DTD Mapper 3.0//EN" "/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.alibaba.csp.sentinel.dashboard.datasource.mapper.MetricMapper"><!-- 通用查询映射结果 --><resultMap id="BaseResultMap" type="com.alibaba.csp.sentinel.dashboard.datasource.entity.Metric"><id column="id" property="id" /><result column="gmt_create" property="gmtCreate" /><result column="gmt_modified" property="gmtModified" /><result column="app" property="app" /><result column="timestamp" property="timestamp" /><result column="resource" property="resource" /><result column="pass_qps" property="passQps" /><result column="success_qps" property="successQps" /><result column="block_qps" property="blockQps" /><result column="exception_qps" property="exceptionQps" /><result column="rt" property="rt" /><result column="count" property="count" /><result column="resource_code" property="resourceCode" /></resultMap><insert id="batchInsert">insert into metric(gmt_create, gmt_modified, app, timestamp, resource, pass_qps,block_qps, success_qps, exception_qps, rt, count, resource_code)values<foreach collection="list" separator="," item="item">(#{item.gmtCreate,jdbcType=TIMESTAMP}, #{item.gmtModified,jdbcType=TIMESTAMP}, #{item.app,jdbcType=VARCHAR},#{item.timestamp,jdbcType=TIMESTAMP}, #{item.resource,jdbcType=VARCHAR}, #{item.passQps,jdbcType=BIGINT},#{item.blockQps,jdbcType=BIGINT}, #{item.successQps,jdbcType=BIGINT}, #{item.exceptionQps,jdbcType=BIGINT},#{item.rt,jdbcType=DECIMAL}, #{item.count,jdbcType=INTEGER}, #{item.resourceCode,jdbcType=INTEGER})</foreach></insert></mapper>

5、MetricsRepository 数据库持久化实现

新增一个 MetricsRepository 接口的实现类 InDatabaseMetricsRepository.java ,用来将监控信息保存到数据库,并且通过数据库进行操作。

@Componentpublic class InDatabaseMetricsRepository implements MetricsRepository<MetricEntity> {private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();@Resourceprivate MetricMapper metricMapper;@Overridepublic void save(MetricEntity entity) {if (entity == null || StringUtil.isBlank(entity.getApp())) {return;}readWriteLock.writeLock().lock();try {metricMapper.insert(toPo(entity));} finally {readWriteLock.writeLock().unlock();}}@Overridepublic void saveAll(Iterable<MetricEntity> metrics) {if (metrics == null) {return;}readWriteLock.writeLock().lock();try {List<Metric> metricList = new ArrayList<>();metrics.forEach(metric -> metricList.add(toPo(metric)));metricMapper.batchInsert(metricList);} finally {readWriteLock.writeLock().unlock();}}@Overridepublic 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 {Metric metric = new Metric();metric.setApp(app);metric.setResource(resource);QueryWrapper<Metric> queryWrapper = new QueryWrapper<>(metric);queryWrapper.between("timestamp", new Date(startTime), new Date(endTime));List<Metric> metricList = metricMapper.selectList(queryWrapper);if (CollectionUtils.isEmpty(metricList)){return results;}metricList.forEach(e->results.add(toPo(e)));return results;} finally {readWriteLock.readLock().unlock();}}@Overridepublic List<String> listResourcesOfApp(String app) {List<String> results = new ArrayList<>();if (StringUtil.isBlank(app)) {return results;}final long minTimeMs = System.currentTimeMillis() - 1000 * 60;Map<String, MetricEntity> resourceCount = new ConcurrentHashMap<>(32);readWriteLock.readLock().lock();try {QueryWrapper<Metric> queryWrapper = new QueryWrapper<>();queryWrapper.eq("app", app);queryWrapper.ge("timestamp", new Date(minTimeMs));List<Metric> metricList = metricMapper.selectList(queryWrapper);List<MetricEntity> metricEntityList = new ArrayList<>();metricList.forEach(e -> metricEntityList.add(toPo(e)));if (CollectionUtils.isEmpty(metricEntityList)){return results;}for (MetricEntity newEntity : metricEntityList) {String resource = newEntity.getResource();if (resourceCount.containsKey(resource)) {MetricEntity oldEntity = resourceCount.get(resource);oldEntity.addPassQps(newEntity.getPassQps());oldEntity.addRtAndSuccessQps(newEntity.getRt(), newEntity.getSuccessQps());oldEntity.addBlockQps(newEntity.getBlockQps());oldEntity.addExceptionQps(newEntity.getExceptionQps());oldEntity.addCount(1);} else {resourceCount.put(resource, MetricEntity.copyOf(newEntity));}}// 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(Entry::getKey).collect(Collectors.toList());} finally {readWriteLock.readLock().unlock();}}private Metric toPo(MetricEntity metricEntity){Metric metric = new Metric();BeanUtils.copyProperties(metricEntity,metric,Metric.class);return metric;}private MetricEntity toPo(Metric metric){MetricEntity metricEntity = new MetricEntity();BeanUtils.copyProperties(metric,metricEntity,MetricEntity.class);return metricEntity;}}

6、修改 MetricsRepository 注入

MetricController 和 MetricFetcher 中 使用 MetricsRepository ,修改 MetricsRepository 的注入。

com.alibaba.csp.sentinel.dashboard.controller.MetricController

com.alibaba.csp.sentinel.dashboard.metric.MetricFetcher

官方 Wiki 中

在相应位置通过 @Qualifier 注解指定对应的 bean name 即可

@Qualifier("inDatabaseMetricsRepository")@Autowiredprivate MetricsRepository<MetricEntity> metricStore;

如果不想使用 @Autowired 和 @Qualifier,也可以使用 @Resource 注解。

@Resource(name = "inDatabaseMetricsRepository")private MetricsRepository<MetricEntity> metricStore;

启动 Sentinel Dashboard

使用mvn clean package将 Sentinel Dashboard 打成 jar ,在命令行通过 java -jar 启动 Sentinel Dashboard 控制台

java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 \-Dproject.name=sentinel-dashboard -jar target/sentinel-dashboard.jar

也可以使用 Spring Boot 直接启动

我们打开控制台 http://localhost:8080 ,同时调用已经接入 Sentinel 服务,此时会有相关的监控信息

数据库 metric 表保存了接口请求的监控信息

源码

项目源码:

GitHub Sentinel Dashboard

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。