sentinel监控数据持久化&本地测试
官方文档 https://sentinelguard.io/zh-cn/
wiki: 在生产环境中使用-Sentinel

推荐方案:持久化到 时序数据库InfluxDB ; 结合Grafana 可视化平台 将监控数据进行多维度的统计和呈现 (百度)。
需求只保留三天数据,所以持久化到mysql数据库,然后定时删除之前的数据:
- 先下载源码进行扩展,选择对应版本的资源:https://github.com/alibaba/Sentinel/releases/tag/1.8.6

- 解压之后通过IDE直接打开:

- 持久化到数据库,需要在项目中添加相关依赖,在配置文件中进行配置(可以自行百度选择交互方式,本文采用JPA与数据库交互);
创建数据表:
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 timestamp_idx(`timestamp`) USING BTREE,
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;
#索引根据需求自行添加
pom:
<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>8.0.25</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.26</version>
</dependency>
application.properties :
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://地址:3306/数据库名
spring.datasource.username=root
spring.datasource.password=123456
spring.jpa.hibernate.ddl-auto=none
spring.jpa.hibernate.use-new-id-generator-mappings=false
spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect
spring.jpa.show-sql=false
- 在 datasource/entity下创建表对应的实体类
@Data
@Entity
@Table(name = "sentinel_metric")
public class MetricDTO implements Serializable {
private static final long serialVersionUID = 20230216L;
/**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;
}
- 在 repository/metric 下创建MetricsRepository接口的实现类:
@Transactional
@Repository("jpaMetricsRepository")
public class JpaMetricsRepository implements MetricsRepository<MetricEntity> {
@PersistenceContext
private EntityManager em;
@Override
public void save(MetricEntity metric) {
if (metric == null || StringUtil.isBlank(metric.getApp())) {
return;
}
MetricDTO metricDTO = new MetricDTO();
BeanUtils.copyProperties(metric, metricDTO);
em.persist(metricDTO);
}
@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) {
//开始时间和结束时间可以在MetricController中修改,默认查询最新一分钟之内的数据
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 MetricDTO");
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<MetricDTO> metricDTOList = query.getResultList();
if (CollectionUtils.isEmpty(metricDTOList)) {
return results;
}
for (MetricDTO metricDTO : metricDTOList) {
MetricEntity metricEntity = new MetricEntity();
BeanUtils.copyProperties(metricDTO, 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 MetricDTO");
hql.append(" WHERE app=:app");
hql.append(" AND timestamp>=:startTime");
//查询半小时之内的数据
long startTime = System.currentTimeMillis() - 1000 * 60 * 60;
Query query = em.createQuery(hql.toString());
query.setParameter("app", app);
query.setParameter("startTime", Date.from(Instant.ofEpochMilli(startTime)));
List<MetricDTO> metricDTOList = query.getResultList();
if (CollectionUtils.isEmpty(metricDTOList)) {
return results;
}
List<MetricEntity> metricEntities = new ArrayList<MetricEntity>();
for (MetricDTO metricDTO : metricDTOList) {
MetricEntity metricEntity = new MetricEntity();
BeanUtils.copyProperties(metricDTO, 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());
}
}
- 指定MetricsRepository依赖注入的类型:

- 运行项目,访问 http://localhost:8080/ 输入用户名和密码 都为 sentinel 登录后界面:

- 至此控制台项目改造完成。
本地搭建客户端项目:创建springboot项目 :
注意:搭建过程中因为sentinel版本为1.8.6,springboot版本过低导致报错,错误日志会提示采用2.6.x和2.7.x版本,最终选择升级springboot 版本为 2.6.14
sentinel控制端版本和sentinel客户端版本要对应,否则会报错。
pom文件:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.14</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>rta-consumer</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>rta-consumer</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<log4j2.version>2.17.1</log4j2.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency> <dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency> <!-- 限流、熔断框架 不连通客户端本地可用 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<version>2021.0.1.0</version>
</dependency> <dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-annotation-aspectj</artifactId>
<version>1.8.6</version>
</dependency> <!-- sentinel客户端与dashboard通信依赖 版本要和控制端一致-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-transport-simple-http</artifactId>
<version>1.8.6</version>
</dependency> </dependencies> <build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build> </project>
配置文件 application.yml:
server:
port: 8090
spring:
application:
name: springboot-sentinel
cloud:
sentinel:
transport:
dashboard: 127.0.0.1:8080 #控制台地址
log4j2.xml:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration monitorinterval="10">
<Appenders>
<!--控制台输出配置-->
<Console name="Console" target="SYSTEM_OUT">
<!--日志输出样式-->
<PatternLayout>
<Pattern>%d[%p] [%t] %c[%M(%L)] - %m%n</Pattern>
</PatternLayout>
</Console>
</Appenders>
<Loggers>
<!--控制器默认的looger-->
<Root level="info">
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration>
代码:
@Controller
@RequestMapping("/test")
@Slf4j
public class TestController {
@Autowired
private TestService testService;
@RequestMapping("query")
@ResponseBody
public String query() {
String query = testService.query();
log.info("查询");
return query;
}
}
public interface TestService {
String query();
}
@Service
public class TestServiceImpl implements TestService {
@Override
@SentinelResource(value = "query")
public String query() {
return "service";
}
}
- 启动客户端,访问对应地址,刷新控制台页面:

数据库表中数据添加成功:

sentinel监控数据持久化&本地测试的更多相关文章
- Sentinel上生产环境只差一步,监控数据持久化
之前介绍了Sentinel相关的文章,小伙伴在生产实践中不知道有没有这个疑问?我们的Sentinel控制台监控的数据只能看最近5分钟的,如图 那么就导致历史数据是查看不了的,那肯定是不行的,在生产环境 ...
- sentinel控制台监控数据持久化【InfluxDB】
根据官方wiki文档,sentinel控制台的实时监控数据,默认仅存储 5 分钟以内的数据.如需持久化,需要定制实现相关接口. https://github.com/alibaba/Sentinel/ ...
- sentinel控制台监控数据持久化【MySQL】
根据官方wiki文档,sentinel控制台的实时监控数据,默认仅存储 5 分钟以内的数据.如需持久化,需要定制实现相关接口. https://github.com/alibaba/Sentinel/ ...
- iOS开发——数据持久化&本地数据的存储(使用NSCoder将对象保存到.plist文件)
本地数据的存储(使用NSCoder将对象保存到.plist文件) 下面通过一个例子将联系人数据保存到沙盒的“documents”目录中.(联系人是一个数组集合,内部为自定义对象). 功能如下: ...
- js数据持久化本地数据存储-JSON.parse和JSON.stringify的区别
JSON.stringify()的作用是将 JavaScript 值转换为 JSON 字符串, 而JSON.parse()可以将JSON字符串转为一个对象. 简单点说,它们的作用是相对的,我用JSON ...
- SpringBoot 2.0 + InfluxDB+ Sentinel 实时监控数据存储
前言 阿里巴巴提供的控制台只是用于演示 Sentinel 的基本能力和工作流程,并没有依赖生产环境中所必需的组件,比如持久化的后端数据库.可靠的配置中心等.目前 Sentinel 采用内存态的方式存储 ...
- React-Native 之 GD (十三)数据持久化(realm) 及 公共Cell
1.数据持久化 数据持久化是移动端的一个重要部分,刚发现 Realm 原来已经支持 React-Native 了 步骤一: 引入 realm $ npm install realm --save 步骤 ...
- OC 数据持久化(数据本地化)- 本地存储
// // ViewController.m // IOS_0113_本地存储 // // Created by ma c on 16/1/13. // Copyright (c) 2016年 博文科 ...
- docker mysql 数据持久化到本地、设置不区别表名大小写-清风柳絮-51CTO博客
原文:docker mysql 数据持久化到本地.设置不区别表名大小写-清风柳絮-51CTO博客 Docker MySQL 把数据存储在本地目录,很简单,只需要映射本地目录到容器即可 1.加上-v参数 ...
- Docker数据持久化及实战(Nginx+Spring Boot项目+MySQL)
Docker数据持久化: Volume: (1)创建mysql数据库的container docker run -d --name mysql01 -e MYSQL_ROOT_PASSWORD= my ...
随机推荐
- concurrent.futures之ThreadPoolExecutor python线程池代码实现方法
from concurrent.futures import ThreadPoolExecutor def main(i): print(i) THREAD_NUM = 20 # 线程数 with T ...
- 聊一聊HTTPS双向认证的简单应用
目录 背景 准备工作 ASP.NET Core nginx 反向代理 IIS 部署 总结 参考资料 背景 在三方接口对接中,偶尔会遇到需要传递证书的情况,这种方式其实是在SSL握手过程中会同时验证客户 ...
- uboot启动过程 3
uboot启动过程1描述到 _start -> reset -> save_boot_params -> save_boot_params_ret -> cpu_init_ ...
- 什么是Http? http和https的区别
一.HTTP HTTP (HyperText Transfer Protocol),即超文本运输协议,是实现网络通信的一种规范 在计算机和网络世界有,存在不同的协议,如广播协议.寻址协议.路由协议等等 ...
- 文盘Rust -- 本地库引发的依赖冲突
作者:京东科技 贾世闻 问题描述 clickhouse 的原生 rust 客户端目前比较好的有两个clickhouse-rs 和 clickhouse.rs .clickhouse-rs 是 tcp ...
- Win下依据端口排查步骤
Win下依据端口排查步骤 目录 Win下依据端口排查步骤 1 排查步骤 2 参考 今天突然发现自己电脑出现7680端口的内网连接,决定看看这个端口是做什么用的 1 排查步骤 确认7680端口连接情况与 ...
- ajax请求头添加参数
在请求ajax时 ,有时后台要求在请求头里边上参数 ,这种情况下一般都是在原生APP项目当中,因为要在登录状态下才,所有要传token和登录id, var token = localStorage.g ...
- ABP微服务系列学习-使用Tye启动微服务
Tye是微软开源的一款开发人员工具, 能够用于简化微服务以及分布式应用程序的开发.测试以及部署过程.Tye 的首要目标是简化微服务的开发,具体方式包括仅用一行命令执行多项服务.在容器中使用依赖项目,以 ...
- Linux操作命令(八)1.sort命令 2.uniq命令 3.join命令
1.sort,排序 常用参数 参数 描述 -n 基于字符串的长度来排序,使用此选项允许根据数字值排序,而不是字母值 -k 指定排序关键字 -b 默认情况下,对整行进行排序,从每行的第一个字符开始.这个 ...
- pdf.js 跨域完美解决!
在网上查看很多方法去解决此类跨域问题,及如何动态加载pdf文件.看来看去 请求的由后台处理加header头的 pdf.js 自带的 获取地址栏param参数值的 都是很麻烦的步骤并且有时不能有效解决 ...