sentinel监控数据持久化&本地测试

官方文档  https://sentinelguard.io/zh-cn/

wiki: 在生产环境中使用-Sentinel 

      

推荐方案:持久化到 时序数据库InfluxDB  ; 结合Grafana 可视化平台 将监控数据进行多维度的统计和呈现 (百度)。

需求只保留三天数据,所以持久化到mysql数据库,然后定时删除之前的数据:

         

 

 

  •  解压之后通过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:拒绝的qps',
  `exception_qps` INT COMMENT '发送异常的次数',
  `rt` DOUBLE COMMENT '所有successQps的rt的和,单位ms; 控制台响应时间(平均响应时间)=rt/success_qps',
  `_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";
}
}
  • 启动客户端,访问对应地址,刷新控制台页面:

        

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

   

 

 

 

 

 

 

 

 

 

 

 

 

 



 

posted @ 2023-02-16 17:20  随身笔记~  阅读(267)  评论(0编辑  收藏  举报