见我博客:openTSDB详解之Telnet Style API
见我博客【待完善】
接下来我主要分析一下使用Java API将数据写入到openTSDB中。因为openTSDB官方并没有提供Java API,所以这个API需要使用我们自己开发。但是整个开源世界何其广阔,在你需要使用java往openTSDB写入数据时,早就有前辈遇到这个问题。所以我就从网上下载了一个包,然后继续倒腾。接下来我就针对这个包的代码进行写入数据过程的分析。
public class OpentsdbClientTest {
private static Logger log = LoggerFactory.getLogger(OpentsdbClientTest.class);
static OpentsdbClient client = new OpentsdbClient("http://192.168.211.4:4399");
public static void testPutData() {
try {
Map tagMap = new HashMap();
tagMap.put("aaa", "bbb");
client.putData("metric-t", DateTimeUtil.parse("20180902 23:10", "yyyyMMdd HH:mm"), 330l, tagMap);
client.putData("metric-t", DateTimeUtil.parse("20180902 23:06", "yyyyMMdd HH:mm"), 440l, tagMap);
client.putData("metric-t", DateTimeUtil.parse("20180902 22:50", "yyyyMMdd HH:mm"), 460l, tagMap);
} catch (Exception e) {
e.printStackTrace();
}
}
}
OpentsdbClientTest是一个测试类,用于测试将数据写入到openTSDB中。
testPutData()
方法进行数据的写入测试。testPutData()
方法中有一个Map对象tagMap。这个是用于记录tag对。这里我随便使用了一组tag。接着在client端使用putData(…)方法。其中putData(…)方法如下:/**
* 写入数据 *
* @param metric 指标
* @param timestamp 时间戳
* @param value type is long
* @param tagMap
* @return
* @throws Exception
*/
public boolean putData(String metric, Date timestamp, long value, Map tagMap) throws Exception {
/*
1.Returns the number of milliseconds since January 1, 1970, 00:00:00 GMT represented by this Date object.
2.but why did timestamp divide 1000?[译者注:这个地方我不大理解!!!]
*/
long timsSecs = timestamp.getTime() / 1000;
//call the simple function putData(...), but the parameter is Long instead of Date.
return this.putData(metric, timsSecs, value, tagMap);
}
这个putData(…)方法将Date 型的timestamp转型成long型。并再次调用下面的putData(…)方法:
/**
* 写入数据
* @param metric 指标
* @param timestamp 转化为秒的时间点
* @param value
* @param tagMap
* @return
* @throws Exception
*/
public boolean putData(String metric, long timestamp, long value, Map tagMap) throws Exception {
/* 1.Builder used to create the JSON to push metrics to openTSDB.
*/
MetricBuilder builder = MetricBuilder.getInstance();
//addMetric(metric) return on Metric's instance
//设置metric的数据值、标签
builder.addMetric(metric).setDataPoint(timestamp, value).addTags(tagMap);
try {
log.debug("write quest:{}", builder.build());
/*1.first,putMetrics()
*/
Response response = httpClient.pushMetrics(builder, ExpectResponse.SUMMARY);
//sometimes : 22:00:48.898 [main] DEBUG enn.cn.dataimport.OpentsdbClient - response.statusCode: 200
log.debug("response.statusCode: {}", response.getStatusCode());
return response.isSuccess();
} catch (Exception e) {
log.error("put data to opentsdb error: ", e);
throw e;
}
}
这里有两个主要的方法调用:
MetricBuilder
构建一个builder实例,这个builder用于构建一个metric。使用addmetric()方法,将传入到方法中的metric添加到其中,然后使用setDataPoint()设置这个metric的数据点,最后为这个数据点添加一个tag 对。public Response pushMetrics(MetricBuilder builder,
ExpectResponse expectResponse) throws IOException {
//checkNotNull(): Ensures that an object reference passed as a parameter to the calling method is not null.
//see above: import static com.google.common.base.Preconditions.checkNotNull;
checkNotNull(builder);
/*1.TODO 错误处理,比如IOException或者failed>0,写到队列或者文件后续重试。*/
SimpleHttpResponse response = httpClient
.doPost(buildUrl(serviceUrl, PUT_POST_API, expectResponse),
builder.build());
return getResponse(response);
}
因为导入了checkNotNull()
所在的方法包,所以可以直接使用checkNotNull(builder)
方法。那这里的httpClient
又是个什么呢?看下面的这个定义:
private PoolingHttpClient httpClient = new PoolingHttpClient();
as the name,PoolingHttpClient is a http client pool。需要记住这个httpClient是一个连接到远端服务器[在本文里,就是192.168.211.4:4399这个服务器的端口]的client端即可。
然后使用doPost
方法将整个builder
发送出去。如下介绍两个地方:
doPost()
方法CloseableHttpClient
实例对象httpClient
,其定义方式如下:private CloseableHttpClient httpClient = null;
public PoolingHttpClient() {
···
httpClient = HttpClients.custom()
.setKeepAliveStrategy(keepAliveStrategy)
.setConnectionManager(connManager)
.setDefaultRequestConfig(config).build();
···
}
再往下探索,发现这个HttpClients
类是一个工厂方法,在其类的API介绍中,有这么一句话:Factory methods for CloseableHttpClient instances
。好了,关于httpClient就介绍到这里。这个httpClient就是实现http协议的一个客户端【这么理解就大致ok】。接着看doPost方法,如下:
public SimpleHttpResponse doPost(String url, String data)
throws IOException {
/*
1.StringEntity: A self contained, repeatable entity that obtains its content from a String.
【一个自包含的、可重复的实体,这个实体包涵可以从字符串中得到的内容。【译者注:比如下面的这个构造函数就是使用String 对象作为参数传递进去构造出
一个StringEntity】】
2.HttpPost: HTTP POST method.
*/
StringEntity requestEntity = new StringEntity(data);
HttpPost postMethod = new HttpPost(url);
postMethod.setEntity(requestEntity);
//这里没有写httpClient.execute(),就说明执行这个方法的对象就是当前这个类的对象[PoolingHttpClient]
HttpResponse response = execute(postMethod);
int statusCode = response.getStatusLine().getStatusCode();//查看返回值
/**
* 1.SimpleHttpResponse is a custom class.
*/
SimpleHttpResponse simpleResponse = new SimpleHttpResponse();
simpleResponse.setStatusCode(statusCode);//set statusCode
/*1.HttpEntity: An entity that can be sent or received with an HTTP message. */
HttpEntity entity = response.getEntity();
if (entity != null) {
// should return: application/json; charset=UTF-8
String ctype = entity.getContentType().getValue();
String charset = getResponseCharset(ctype);
//EntityUtils:Static helpers for dealing with HttpEntitys.
String content = EntityUtils.toString(entity, charset);
simpleResponse.setContent(content);
}
return simpleResponse;
}
该方法有两个参数:分别是url和data,均为String类型。
首先,将需要post的data封装到一个StringEntity中,得到一个叫做requestEntity的对象,接着声明一个HttpPost的对象,注入参数中url。然后将刚才的那个requestEntity放到postMethod这个对象里。最后执行execute()方法,其中的参数是postMethod。
但是发出了一个Http Post之后,总会有响应的吧,那该怎么接收这个响应呢?定义了一个HttpResponse对象response,该对象指向返回实例。
这里自定义了一个SimpleHttpResponse类,为的是只取响应的返回码和返回内容。最后返回这个simpleResponse对象。
SimpleHttpResponse response = httpClient.doPost(
buildUrl(serviceUrl,
PUT_POST_API,
expectResponse),
builder.build());
接着看buildUrl(serviceUrl,PUT_POST_API,expectResponse)
语句。查看buildUrl方法:
private String buildUrl(String serviceUrl, String postApiEndPoint,
ExpectResponse expectResponse) {
//what is postApiEndPoint?
String url = serviceUrl + postApiEndPoint;
switch (expectResponse) {
case SUMMARY:
url += "?summary";
break;
case DETAIL:
url += "?details";
break;
default:
break;
}
return url;
}
这里使用serviceUrl和postApiEndPoint构造一个实际的url,并且根据expectResponse去判断是否需要何种url,然后拼接成一个真正的url【笔者注:这里的SUMMARY/DETAIL是什么意思?=> 我猜测应该是Opentsdb通过http获取数据的一种方式】
builder.build()
方法 /**
* Returns the JSON string built by the builder. This is the JSON that can
* be used by the client add metrics.
*
* @return JSON
* @throws IOException
* if metrics cannot be converted to JSON
*/
public String build() throws IOException {
// verify that there is at least one tag for each metric
for (Metric metric : metrics) {
checkState(metric.getTags().size() > 0, metric.getName()
+ " must contain at least one tag.");
}
/*
1.toJson(): This method serializes the specified object into its equivalent Json representation.
*/
return mapper.toJson(metrics);
}
写入到openTSDB之后,可以打开浏览器查看写入效果,如下:
有需要jar包的可留言告知博主。