Sentinel +InfluxDb 监控数据持久化
InfluxDb简单介绍
InfluxDB
是一个开源分布式时序、事件和指标数据库。使用 Go 语言编写,无需外部依赖。
应用:性能监控,应用程序指标,物联网传感器数据和实时分析等的后端存储。
- 强大的类SQL语法
- -内置http支持,使用http读写
- -基于事件:它支持任意的事件数据
- -无结构(无模式):可以是任意数量的列
- -可度量性:你可以实时对大量数据进行计算
- 持续高并发写入、无更新、数据压缩存储、低查询延时
- 支持min, max, sum, count, mean, median 等一系列函数
- 基于时间序列,支持与时间有关的相关函数(如最大,最小,求和等)
详细内容请参考一下链接
https://jasper-zhang1.gitbooks.io/influxdb/content/
进入正题
1 sentinel-dashboard 添加influxBb依赖包以及简单配置
<dependency>
<groupId>org.influxdb</groupId>
<artifactId>influxdb-java</artifactId>
<version>2.16</version>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
spring.influx.url=http://localhost:8086
#spring.influx.user=root
#spring.influx.password=root
InfluxDb安装后默认账号密码都是root,(默认的情况可以不配置账号密码)
2 创建InfluxDbMetricEntity
@Measurement(name="${spring.influx.measurement:resources_metrics}", database = "${spring.influx.db:sentinel_log}")
public class InfluxDbMetricEntity {
// @Column(name="id")
private Long id;
@Column(name="gmtCreate")
private Long gmtCreate;
// @Column(name="gmtModified")
private Date gmtModified;
@Column(name="app", tag = true)
private String app;
/**
* 监控信息的时间戳
*/
@TimeColumn
@Column(name="time")
private Instant time;
@Column(name="resource", tag = true)
private String resource;
@Column(name="passQps")
private Long passQps;
@Column(name="successQps")
private Long successQps;
@Column(name="blockQps")
private Long blockQps;
@Column(name="exceptionQps")
private Long exceptionQps;
/**
* summary rt of all success exit qps.
*/
@Column(name="rt")
private double rt;
/**
* 本次聚合的总条数
*/
@Column(name="count")
private int count;
// @Column(name="resourceCode")
private int resourceCode;
public static InfluxDbMetricEntity copyOf(InfluxDbMetricEntity oldEntity) {
InfluxDbMetricEntity entity = new InfluxDbMetricEntity();
entity.setId(oldEntity.getId());
entity.setGmtCreate(oldEntity.getGmtCreate());
entity.setGmtModified(oldEntity.getGmtModified());
entity.setApp(oldEntity.getApp());
entity.setTime(oldEntity.getTime());
entity.setResource(oldEntity.getResource());
entity.setPassQps(oldEntity.getPassQps());
entity.setBlockQps(oldEntity.getBlockQps());
entity.setSuccessQps(oldEntity.getSuccessQps());
entity.setExceptionQps(oldEntity.getExceptionQps());
entity.setRt(oldEntity.getRt());
entity.setCount(oldEntity.getCount());
entity.setResource(oldEntity.getResource());
return entity;
}
public synchronized void addPassQps(Long passQps) {
this.passQps += passQps;
}
public synchronized void addBlockQps(Long blockQps) {
this.blockQps += blockQps;
}
public synchronized void addExceptionQps(Long exceptionQps) {
this.exceptionQps += exceptionQps;
}
public synchronized void addCount(int count) {
this.count += count;
}
public synchronized void addRtAndSuccessQps(double avgRt, Long successQps) {
this.rt += avgRt * successQps;
this.successQps += successQps;
}
/**
* {@link #rt} = {@code avgRt * successQps}
*
* @param avgRt average rt of {@code successQps}
* @param successQps
*/
public synchronized void setRtAndSuccessQps(double avgRt, Long successQps) {
this.rt = avgRt * successQps;
this.successQps = successQps;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getGmtCreate() {
return gmtCreate;
}
public void setGmtCreate(Long gmtCreate) {
this.gmtCreate = gmtCreate;
}
public Date getGmtModified() {
return gmtModified;
}
public void setGmtModified(Date gmtModified) {
this.gmtModified = gmtModified;
}
public String getApp() {
return app;
}
public void setApp(String app) {
this.app = app;
}
public Instant getTime() {
return time;
}
public void setTime(Instant time) {
this.time = time;
}
public String getResource() {
return resource;
}
public void setResource(String resource) {
this.resource = resource;
this.resourceCode = resource.hashCode();
}
public Long getPassQps() {
return passQps;
}
public void setPassQps(Long passQps) {
this.passQps = passQps;
}
public Long getBlockQps() {
return blockQps;
}
public void setBlockQps(Long blockQps) {
this.blockQps = blockQps;
}
public Long getExceptionQps() {
return exceptionQps;
}
public void setExceptionQps(Long exceptionQps) {
this.exceptionQps = exceptionQps;
}
public double getRt() {
return rt;
}
public void setRt(double rt) {
this.rt = rt;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
public int getResourceCode() {
return resourceCode;
}
public Long getSuccessQps() {
return successQps;
}
public void setSuccessQps(Long successQps) {
this.successQps = successQps;
}
@Override
public String toString() {
return "MetricEntity{" +
"id=" + id +
", gmtCreate=" + gmtCreate +
", gmtModified=" + gmtModified +
", app='" + app + '\'' +
", time=" + time +
", resource='" + resource + '\'' +
", passQps=" + passQps +
", blockQps=" + blockQps +
", successQps=" + successQps +
", exceptionQps=" + exceptionQps +
", rt=" + rt +
", count=" + count +
", resourceCode=" + resourceCode +
'}';
}
/**
* 将metric实体类转为InfluxDb实体写入时序库
* @param oldEntity
* @return
*/
public static InfluxDbMetricEntity convert(MetricEntity oldEntity) {
InfluxDbMetricEntity entity = new InfluxDbMetricEntity();
entity.setId(oldEntity.getId());
entity.setGmtCreate(oldEntity.getGmtCreate().getTime());
entity.setGmtModified(oldEntity.getGmtModified());
entity.setApp(oldEntity.getApp());
entity.setTime(oldEntity.getTimestamp().toInstant());
entity.setResource(oldEntity.getResource());
entity.setPassQps(oldEntity.getPassQps());
entity.setBlockQps(oldEntity.getBlockQps());
entity.setSuccessQps(oldEntity.getSuccessQps());
entity.setExceptionQps(oldEntity.getExceptionQps());
entity.setRt(oldEntity.getRt());
entity.setCount(oldEntity.getCount());
entity.setResource(oldEntity.getResource());
return entity;
}
/**
* 将InfluxDb实体类转为metric实体读取到控制台
* @return
*/
public MetricEntity toMetricEntity() {
MetricEntity entity = new MetricEntity();
entity.setId(this.getId());
entity.setGmtCreate(new Date(this.getGmtCreate()));
entity.setGmtModified(this.getGmtModified());
entity.setApp(this.getApp());
entity.setTimestamp(new Date(this.getTime().toEpochMilli()));
entity.setResource(this.getResource());
entity.setPassQps(this.getPassQps());
entity.setBlockQps(this.getBlockQps());
entity.setSuccessQps(this.getSuccessQps());
entity.setExceptionQps(this.getExceptionQps());
entity.setRt(this.getRt());
entity.setCount(this.getCount());
entity.setResource(this.getResource());
return entity;
}
}
3 创建InfluxDbMetricsRepository 实现MetricsRepository, InitializingBean
/**
* 实现MetricsRepository将InfluxDb定义为存储库
* 实现InitializingBean运用spring的自动装配
* @author tom
*/
@Repository
public class InfluxDbMetricsRepository implements MetricsRepository<MetricEntity>, InitializingBean {
private static final Logger log = LoggerFactory.getLogger(InfluxDbMetricsRepository.class);
private InfluxDB influxDB;
private String database;
private String measurement;
private InfluxDBResultMapper resultMapper = new InfluxDBResultMapper();
public InfluxDbMetricsRepository(InfluxDB influxDB, @Value("${spring.influx.db:sentinel_log}") String database
, @Value("${spring.influx.measurement:resources_metrics}") String measurement) {
this.influxDB = influxDB;
this.database = database;
this.measurement = measurement;
}
@Override
public void save(MetricEntity metric) {
try {
Point.Builder builder = Point.measurement(measurement);
builder.addFieldsFromPOJO(InfluxDbMetricEntity.convert(metric));
influxDB.write(builder.build());
log.debug("metric save => {}", metric);
} catch (Exception e) {
log.error("metric save error", e);
}
}
@Override
public void saveAll(Iterable<MetricEntity> metrics) {
try {
Iterator<MetricEntity> iter = metrics.iterator();
while (iter.hasNext()) {
influxDB.write(Point.measurement(measurement).addFieldsFromPOJO(InfluxDbMetricEntity.convert(iter.next())).build());
}
influxDB.flush();
log.debug("metric saveAll => {}", metrics);
} catch (Exception e) {
log.error("metric saveAll error", e);
}
}
@Override
public List<MetricEntity> queryByAppAndResourceBetween(String app, String resource, long startTime, long endTime) {
//注意string参数必须用单引号标注!!!! 并且双引号不行!不加也不行!
QueryResult result = influxDB.query(new Query(String.format("select * from %s where app='%s' and resource='%s' and gmtCreate >= %s and gmtCreate <= %s",
this.measurement, app, resource, startTime, endTime)));
log.debug("metric queryByAppAndResourceBetween => {}", result);
List<InfluxDbMetricEntity> list = this.resultMapper.toPOJO(result, InfluxDbMetricEntity.class, this.measurement);
return list.stream().map(m -> m.toMetricEntity()).collect(Collectors.toList());
}
@Override
public List<String> listResourcesOfApp(String app) {
long period = System.currentTimeMillis() - 1000 * 60 * 15;
QueryResult result = influxDB.query(new Query(String.format("select * from %s where app = '%s' and gmtCreate > %s"
, this.measurement, app, period)));
log.debug("metric listResourcesOfApp => {}", result);
List<InfluxDbMetricEntity> rs = this.resultMapper.toPOJO(result, InfluxDbMetricEntity.class, this.measurement);
if (CollectionUtils.isEmpty(rs)) {
return Lists.newArrayList();
}
Map<String, InfluxDbMetricEntity> resourceCount = new HashMap<>(32);
for (InfluxDbMetricEntity metricEntity : rs) {
String resource = metricEntity.getResource();
if (resourceCount.containsKey(resource)) {
InfluxDbMetricEntity 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, InfluxDbMetricEntity.copyOf(metricEntity));
}
}
return resourceCount.entrySet()
.stream()
.sorted((o1, o2) -> {
InfluxDbMetricEntity e1 = o1.getValue();
InfluxDbMetricEntity 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());
}
/**
* bean初始化时
* 自动创建数据库以及measourement
*/
@Override
public void afterPropertiesSet() throws Exception {
Assert.hasLength(this.database, "influxDB database invalid!");
Assert.hasLength(this.measurement, "influxDB measurement invalid!");
QueryResult rs = this.influxDB.query(new Query("show databases"));
log.debug("influxDB database check => {}", rs);
if (rs.hasError()) {
throw new InfluxDBException(rs.getError());
}
if (databaseNotExist(rs)) {
rs = this.influxDB.query(new Query("create database " + this.database));
log.info("create influxDB database => {} result => {}", this.database, rs);
}
this.influxDB.setDatabase(this.database);
this.influxDB.enableBatch();
log.info("influxDB setDatabase => {}", this.database);
}
private boolean databaseNotExist(QueryResult rs) {
return !rs.getResults().get(0).getSeries().get(0).getValues()
.stream().flatMap(list -> list.stream()).anyMatch(d -> d.toString().equals(this.database));
}
}
4 在MetricController注入influxDbMetricsRepository
@Autowired
@Qualifier("influxDbMetricsRepository")
private MetricsRepository<MetricEntity> metricStore;
完成以上操作就可以将监控数据持久化到InfluxDb中啦,监控数据,结合grafana或者Chronograf可以将监控数据做成炫酷的图表展示