最近在做压测引擎相关的开发,需要将聚合数据发送到InfluxDB保存以便实时分析和控制QPS。

下面介绍对InfluxDB的使用。

什么是InfluxDB

InfluxDB是一款用Go语言编写的开源分布式时序、事件和指标数据库,无需外部依赖。该数据库现在主要用于存储涉及大量的时间戳数据,如DevOps监控数据,APP metrics, loT传感器数据和实时分析数据。

InfluxDB特征:

  • 无结构(无模式):可以是任意数量的列(tags)。
  • 可以设置metric的保存时间。
  • 支持与时间有关的相关函数(如min、max、sum、count、mean、median等),方便统计。
  • 支持存储策略:可以用于数据的删改(influxDB没有提供数据的删除与修改方法)。
  • 支持连续查询:是数据库中自动定时启动的一组语句,和存储策略搭配可以降低InfluxDB的系统占用量。
  • 原生的HTTP支持,内置HTTP API。
  • 支持类似SQL语法。
  • 支持设置数据在集群中的副本数。
  • 支持定期采样数据,写入另外的measurement,方便分粒度存储数据。
  • 自带web管理界面,方便使用(登入方式:http://< InfluxDB-IP >:8083)。
  • 支持Grafana画图展示。

PS:有了InfluxDB+Grafana后,你就可以写一些简单的程序了,可以只负责写后端逻辑部分,数据都可以存入InfluxDB,然后通过Grafana展示出来。

Mac安装InfluxDB

# 安装
brew install influxdb
# 启动
influxd -config /usr/local/etc/influxdb.conf
# 查看influxdb运行配置
influxd config
# 启动客户端
influx -precision rfc3339

InfluxDB开启UDP配置

vim /usr/local/etc/influxdb.conf

开启udp配置,其他为默认值

[[udp]]
enabled = true

udp配置含义:

[[udp]] – udp配置

    enabled:是否启用该模块,默认值:false。

    bind-address:绑定地址,默认值:”:8089″。

    database:数据库名称,默认值:”udp”。

    retention-policy:存储策略,无默认值。

    batch-size:默认值:5000。

    batch-pending:默认值:10。

    read-buffer:udp读取buffer的大小,0表示使用操作系统提供的值,如果超过操作系统的默认配置则会出错。 该配置的默认值:0。

    batch-timeout:超时时间,默认值:”1s”。

    precision:时间精度,无默认值。

Java发送UDP数据报

我们知道InfluxDB是支持Http的,为什么我们还要采用UDP方式发送数据呢?

基于下列原因:

  1. TCP数据传输慢,UDP数据传输快。
  2. 网络带宽需求较小,而实时性要求高。
  3. InfluxDB和服务器在同机房,发生数据丢包的可能性较小,即使真的发生丢包,对整个请求流量的收集影响也较小。

我们采用了worker线程调用addMetric方法将数据存储到缓存 map 中,send线程池来进行每个指定时间发送数据到Influxdb。

代码如下(也可参考JmeterUdpMetricsSender类):

@Slf4j
public class InfluxDBClient implements Runnable {
private String measurement = "example"; private final Object lock = new Object(); private InetAddress hostAddress; private int udpPort; private volatile Map<String, List<Response>> metrics = new HashMap<>(); private long time; private String transaction; public InfluxDBClient(String influxdbUrl, String transaction) {
this.transaction = transaction;
try {
log.debug("Setting up with url:{}", influxdbUrl);
String[] urlComponents = influxdbUrl.split(":");
if (urlComponents.length == 2) {
hostAddress = InetAddress.getByName(urlComponents[0]);
udpPort = Integer.parseInt(urlComponents[1]);
} else {
throw new IllegalArgumentException("InfluxDBClient url '" + influxdbUrl + "' is wrong. The format shoule be <host/ip>:<port>");
}
} catch (Exception e) {
throw new IllegalArgumentException("InfluxDBClient url '" + influxdbUrl + "' is wrong. The format shoule be <host/ip>:<port>", e);
}
} public void addMetric(Response response) {
synchronized (lock) {
if (metrics.containsKey(response.getLabel())) {
metrics.get(response.getLabel()).add(response);
} else {
metrics.put(response.getLabel(), new ArrayList<>(Collections.singletonList(response)));
}
}
} @Override
public void run() {
sendMetrics();
} private void sendMetrics() {
Map<String, List<Response>> tempMetrics;
//复制数据到tempMetrics,清空原来metrics并初始化上次的大小
synchronized (lock) {
if (isEmpty(metrics)) {
return;
}
time = System.currentTimeMillis();
tempMetrics = metrics;
metrics = new HashMap<>();
for (Map.Entry<String, List<Response>> entry : tempMetrics.entrySet()) {
metrics.put(entry.getKey(), new ArrayList<>(entry.getValue().size()));
}
}
final Map<String, List<Response>> copyMetrics = tempMetrics;
final List<MetricTuple> aggregateMetrics = aggregate(copyMetrics);
StringBuilder sb = new StringBuilder(aggregateMetrics.size() * 200);
//发送tempMetrics,生成一行数据,然后换行
for (MetricTuple metric : aggregateMetrics) {
sb.append(metric.getMeasurement()).append(metric.getTag()).append(" ")
.append(metric.getField()).append(" ").append(metric.getTimestamp() + "000000").append("\n");
}
//udp发送数据到Influxdb
try (DatagramSocket ds = new DatagramSocket()) {
byte[] buf = sb.toString().getBytes();
DatagramPacket dp = new DatagramPacket(buf, buf.length, this.hostAddress, this.udpPort);
ds.send(dp);
log.debug("send {} to influxdb", sb.toString());
} catch (SocketException e) {
log.error("Cannot open udp port!", e);
} catch (IOException e) {
log.error("Error in transferring udp package", e);
}
} /**
* 得到聚合数据
*
* @param metrics
* @return
*/
private List<MetricTuple> aggregate(Map<String, List<Response>> metrics) { } public boolean isEmpty(Map<String, List<Response>> map) {
for (Map.Entry<String, List<Response>> entry : map.entrySet()) {
if (!entry.getValue().isEmpty()) {
return false;
}
}
return true;
}
}

参考文档

  1. InfluxDB中文文档
  2. 玩转时序数据库InfluxDB

Java使用UDP发送数据到InfluxDB的更多相关文章

  1. PL/SQL 调用JAVA使用UDP发送数据

    步骤如下 1.直接在SQL命令中写入JAVA代码(用SYS帐号执行,不然权限等太麻烦) create or replace and resolve java source named udp as i ...

  2. 通过 UDP 发送数据的简单范例

    package j2se.core.net.udp; import java.io.IOException;import java.net.DatagramPacket;import java.net ...

  3. java实现udp发送端和接收端

    发送端: package demo02; import java.io.IOException; import java.net.DatagramPacket; import java.net.Dat ...

  4. 使用 log4js UDP 发送数据到 logstash

    本文地址 http://www.cnblogs.com/jasonxuli/p/6532723.html 因为 nodejs 一般会部署在多台机器,并且每台机器会起多个进程,因此查看日志时往往要人工区 ...

  5. TCP和UDP发送数据包的大小问题

    用UDP协议发送时,用sendto函数最大能发送数据的长度为:65535-20-8=65507字节,其中20字节为IP包头长度,8字节为UDP包头长度.用sendto函数发送数据时,如果指的的数据长度 ...

  6. CocoaAsyncSocket UDP发送数据超过包大小限制(Message too long)

    最近在做iOS上,基于UDP传输音视频时遇到的一个问题,这边纪录一下: 由于考虑实时性比较高,所以采用了 CocoaAsyncSocket 的UDP框架来实现,将视频切割成一帧帧的图片发给服务端,不过 ...

  7. android 使用UDP发送数据 DatagramSocket 创建对象为null

    DatagramSocket socket=null; try { socket = new DatagramSocket();  //这里创建对象为空 } catch (SocketExceptio ...

  8. Android(java)学习笔记80:UDP协议发送数据

    UDP协议发送数据:我们总是先运行接收端,再运行发送端发送端: 1 package cn.itcast_02; import java.io.IOException; import java.net. ...

  9. Android(java)学习笔记20:UDP协议发送数据

    1. UDP协议发送数据:我们总是先运行接收端,再运行发送端发送端: package cn.itcast_02; import java.io.IOException; import java.net ...

随机推荐

  1. java下Mysql基本操作

    https://www.cnblogs.com/centor/p/6142775.html

  2. iOS- 什么是GitHub?关于它的自我介绍「初识 GitHub」

    1 前言 我一直认为 GitHub 是程序员必备技能,程序员应该没有不知道 GitHub 的才对,我当初接触 GitHub 也大概工作了一年多才开始学习使用,我读者里很多是初学者,而且还有很多是在校大 ...

  3. 封装react组件——三级联动

    思路: 数据设计:省份为一维数组,一级市为二维数组,二级市/区/县为三维数组.这样设计的好处在于根据数组索引实现数据的关联. UI组件: MUI的DropDownMenu组件或Select Field ...

  4. teamcity执行jmeter脚本使用Executable with parameters方式不能正确运行解决思路

    如下图是选择command Line:Executable with parameters设置启动jmeter.bat  命令如下 command Executable: D:\apache-jmet ...

  5. 笔记:delphi 与 Query

    以下不保存证正确 Query用SQL语言执行过的,没有必要Cancel.Post,因为其会对数据库直接操作:执行Update.Insert.Delete请用SQL语句: 用Table使用对当前记录直接 ...

  6. mybatis的mapper参数传递

    简单参数传递 简单参数传递是指: 传递单个基本类型参数,数字类型.String 传递多个基本类型参数 parameterType 属性可以省略: 传递单个基本类型参数  SQL语句中参数的引用名称并不 ...

  7. VLD 无法打印堆栈调用情况

    调试时遇到了一个比较郁闷的问题:同样一个MFC工程,复制之后无任何附加操作,VLD便无法正常打印内存泄漏处的堆栈调用了 百度了一下,重要找到了答案:“VLD不支持中文” 复制工程时windows自动在 ...

  8. UVA10047_The Monocycle

    这题....有点奇葩,但是不难. 在矩形方阵里,某人可以往前走或者左拐右拐.都需要消耗一个单位时间. 问某人从一个点走向另一个点的最短时间,并且走过的路程是5的倍数. 由于n,m都小,直接f[n][m ...

  9. DAY4-Flask项目

    项目出现的问题: 问题处在import requests.requests库已经安装了啊; 找了半天也不知道具体错误在哪里,根据提示想是不是http.py这个模块与Python内置的同名模块冲突了?所 ...

  10. 再谈 最速下降法/梯度法/Steepest Descent

    转载请注明出处:http://www.codelast.com/ 最速下降法(又称梯度法,或Steepest Descent),是无约束最优化领域中最简单的算法,单独就这种算法来看,属于早就“过时”了 ...