javamelody监控数据持久化的目录 sentinel监控数据持久化_List

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

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

         

javamelody监控数据持久化的目录 sentinel监控数据持久化_spring_02

 

 

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

             

javamelody监控数据持久化的目录 sentinel监控数据持久化_spring_03

 

 

 

  • 持久化到数据库,需要在项目中添加相关依赖,在配置文件中进行配置(可以自行百度选择交互方式,本文采用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依赖注入的类型:

javamelody监控数据持久化的目录 sentinel监控数据持久化_数据_04

       

javamelody监控数据持久化的目录 sentinel监控数据持久化_spring_05

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

      

javamelody监控数据持久化的目录 sentinel监控数据持久化_List_06

  •  至此控制台项目改造完成。

本地搭建客户端项目:创建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";
    }
}
  • 启动客户端,访问对应地址,刷新控制台页面:

        

javamelody监控数据持久化的目录 sentinel监控数据持久化_spring_07

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

   

javamelody监控数据持久化的目录 sentinel监控数据持久化_spring_08