阅读文本大概需要3分钟。
根据官方wiki文档,Sentinel控制台的实时监控数据,默认仅存储 5 分钟以内的数据。如需持久化,需要定制实现相关接口。
/alibaba/Sentinel/wiki/在生产环境中使用-Sentinel
给出了指导步骤:
自行扩展实现 MetricsRepository 接口;
注册成 Spring Bean 并在相应位置通过 @Qualifier 注解指定对应的 bean name 即可;
0x01:MetricsRepository接口定义
}packagecom.alibaba.csp.sentinel.dashboard.repository.metric;importjava.util.List;publicinterfaceMetricsRepository<T>{voidsave(Tmetric);voidsaveAll(Iterablemetrics);ListqueryByAppAndResourceBetween(Stringapp,Stringresource,longstartTime,longendTime);ListlistResourcesOfApp(Stringapp);
该接口就只定义4个方法,分别用于保存和查询Sentinel的metric数据。注释其实很清楚了,解析如下:
save:保存单个metric
saveAll:保存多个metric
queryByAppAndResourceBetween:通过应用名称、资源名称、开始时间、结束时间查询metric列表
listResourcesOfApp:通过应用名称查询资源列表
目前该接口只有一个基于内存级别的实现类:com.alibaba.csp.sentinel.dashboard.repository.metric.InMemoryMetricsRepository。
另外还有一个实体类com.alibaba.csp.sentinel.dashboard.datasource.entity.MetricEntity,如下图
梳理了相关的类关系就可以实现了。
0x02:根据MetricEntity新建数据库和新建实体类
建表语句如下
PRIMARYKEY(`id`) )ENGINE=INNODBDEFAULTCHARSET=utf8;--创建监控数据表CREATETABLE`t_sentinel_metric`(`id`INTNOTNULLAUTO_INCREMENTCOMMENT'id,主键',`gmt_create`DATETIMECOMMENT'创建时间',`gmt_modified`DATETIMECOMMENT'修改时间',`app`VARCHAR(100)COMMENT'应用名称',`timestamp`DATETIMECOMMENT'统计时间',`resource`VARCHAR(500)COMMENT'资源名称',`pass_qps`INTCOMMENT'通过qps',`success_qps`INTCOMMENT'成功qps',`block_qps`INTCOMMENT'限流qps',`exception_qps`INTCOMMENT'发送异常的次数',`rt`DOUBLECOMMENT'所有successQps的rt的和',`_count`INTCOMMENT'本次聚合的总条数',`resource_code`INTCOMMENT'资源的hashCode',INDEXapp_idx(`app`)USINGBTREE,INDEXresource_idx(`resource`)USINGBTREE,INDEXtimestamp_idx(`timestamp`)USINGBTREE,
实体类如下
*@author2230 * */@Entity@Table(name="t_sentinel_metric")publicclassMetricDtoimplementsSerializable{privatestaticfinallongserialVersionUID=7200023615444172715L;/**id,主键*/@Id@GeneratedValue@Column(name="id")privateLongid;/**创建时间*/@Column(name="gmt_create")privateDategmtCreate;/**修改时间*/@Column(name="gmt_modified")privateDategmtModified;/**应用名称*/@Column(name="app")privateStringapp;/**统计时间*/@Column(name="timestamp")privateDatetimestamp;/**资源名称*/@Column(name="resource")privateStringresource;/**通过qps*/@Column(name="pass_qps")privateLongpassQps;/**成功qps*/@Column(name="success_qps")privateLongsuccessQps;/**限流qps*/@Column(name="block_qps")privateLongblockQps;/**发送异常的次数*/@Column(name="exception_qps")privateLongexceptionQps;/**所有successQps的rt的和*/@Column(name="rt")privateDoublert;/**本次聚合的总条数*/@Column(name="_count")privateIntegercount;/**资源的hashCode*/@Column(name="resource_code")privateIntegerresourceCode;//getset方法省略 }packagecom.alibaba.csp.sentinel.dashboard.datasource.entity;importjavax.persistence.*;importjava.io.Serializable;importjava.util.Date;/**
0x03:pom.xml添加依赖
因为是基于JPA和MySQL数据库实现,所以需要添加JPA依赖和MySQL数据库驱动依赖
<dependency><groupId>org.springframework.bootgroupId><artifactId>spring-boot-starter-data-jpaartifactId><version>${spring.boot.version}version>dependency><dependency><groupId>mysqlgroupId><artifactId>mysql-connector-javaartifactId><version>5.1.47version>dependency>
0x04:实现MetricsRepository接口,把数据持久化到MySQL数据库
注意实现添加@Repository("jpaMetricsRepository")配置
*/yinjihuan/p/10574998.html */cdfive/p/9838577.html */wk52525/article/details/104587239/ */alibaba/spring-cloud-alibaba/wiki/Sentinel * *@author2230 * */@Transactional@Repository("jpaMetricsRepository") publicclassJpaMetricsRepositoryimplementsMetricsRepository<MetricEntity>{@PersistenceContext privateEntityManagerem;@Override publicvoidsave(MetricEntitymetric){if(metric==null||StringUtil.isBlank(metric.getApp())){return; } MetricDtometricDto=newMetricDto(); BeanUtils.copyProperties(metric,metricDto); em.persist(metricDto); }@Override publicvoidsaveAll(Iterablemetrics){if(metrics==null){return; } metrics.forEach(this::save); }@Override publicListqueryByAppAndResourceBetween(Stringapp,Stringresource,longstartTime,longendTime){Listresults=newArrayList();if(StringUtil.isBlank(app)){returnresults; }if(StringUtil.isBlank(resource)){returnresults; } StringBuilderhql=newStringBuilder(); hql.append("FROMMetricDto"); hql.append("WHEREapp=:app"); hql.append("ANDresource=:resource"); hql.append("ANDtimestamp>=:startTime"); hql.append("ANDtimestamp<=:endTime"); Queryquery=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)));ListmetricDtos=query.getResultList();if(CollectionUtils.isEmpty(metricDtos)){returnresults; }for(MetricDtometricDto:metricDtos){ MetricEntitymetricEntity=newMetricEntity(); BeanUtils.copyProperties(metricDto,metricEntity); results.add(metricEntity); }returnresults; }@Override publicList<String>listResourcesOfApp(Stringapp){List<String>results=newArrayList<>();if(StringUtil.isBlank(app)){returnresults; } StringBuilderhql=newStringBuilder(); hql.append("FROMMetricDto"); hql.append("WHEREapp=:app"); hql.append("ANDtimestamp>=:startTime"); longstartTime=System.currentTimeMillis()-1000*60; Queryquery=em.createQuery(hql.toString()); query.setParameter("app",app); query.setParameter("startTime",Date.from(Instant.ofEpochMilli(startTime)));ListmetricDtos=query.getResultList();if(CollectionUtils.isEmpty(metricDtos)){returnresults; }ListmetricEntities=newArrayList();for(MetricDtometricDto:metricDtos){ MetricEntitymetricEntity=newMetricEntity(); BeanUtils.copyProperties(metricDto,metricEntity); metricEntities.add(metricEntity); }Map<String,MetricEntity>resourceCount=newHashMap<>(32);for(MetricEntitymetricEntity:metricEntities){Stringresource=metricEntity.getResource();if(resourceCount.containsKey(resource)){ MetricEntityoldEntity=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)); } }//Orderbylastminuteb_qpsDESC.returnresourceCount.entrySet() .stream() .sorted((o1,o2)->{ MetricEntitye1=o1.getValue(); MetricEntitye2=o2.getValue();intt=e2.getBlockQps().compareTo(e1.getBlockQps());if(t!=0){returnt; }returne2.getPassQps().compareTo(e1.getPassQps()); }) .map(Map.Entry::getKey) .collect(Collectors.toList()); } }packagecom.alibaba.csp.sentinel.dashboard.repository.metric;importjava.time.Instant;importjava.util.ArrayList;importjava.util.Date;importjava.util.HashMap;importjava.util.List;importjava.util.Map;importjava.util.stream.Collectors;importjavax.persistence.EntityManager;importjavax.persistence.PersistenceContext;importjavax.persistence.Query;importorg.springframework.beans.BeanUtils;importorg.springframework.stereotype.Repository;importorg.springframework.transaction.annotation.Transactional;importorg.springframework.util.CollectionUtils;importcom.alibaba.csp.sentinel.dashboard.datasource.entity.MetricDto;importcom.alibaba.csp.sentinel.dashboard.datasource.entity.MetricEntity;importcom.alibaba.csp.sentinel.util.StringUtil;/**
0x05:application.properties配置文件添加数据库配置
spring.datasource.driver-class-name=com.mysql.jdbc.Driverspring.datasource.url=jdbc:mysql://127.0.0.1:3306/gateway_v2?characterEncoding=utf8&useSSL=true spring.datasource.username=root spring.datasource.password=root #springdatajpa 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
主要配置数据库连接信息和JPA的配置项,JPA使用Hibernate实现。
0x06:数据库持久化换成JpaMetricsRepository实现
找到如下两个类
com.alibaba.csp.sentinel.dashboard.controller.MetricControllercom.alibaba.csp.sentinel.dashboard.metric.MetricFetcher
在metricStore属性上添加多一个@Qualifier("jpaMetricsRepository")注解,如下图
0x07:验证
设置sentinel-dashboard工程的启动参数
-Dserver.port=8080-Dcsp.sentinel.dashboard.server=localhost:8080-Dproject.name=sentinel-dashboard
具体可以参考【Sentinel如何进行流量监控 】;可以发现数据已经保存到MySQL数据库。
备注:以上代码改造都是在sentinel-dashboard项目上。
参考:/cdfive/p/9838577.html
☆
往期精彩
☆
01Sentinel如何进行流量监控
02Nacos源码编译
03基于Apache Curator框架的ZooKeeper使用详解
04spring boot项目整合xxl-job
05互联网支付系统整体架构详解
关注我
每天进步一点点
喜欢!在看☟