OpenTSDB的简单使用
OpenTSDB由于没有为Java提供专门的Java API,但OpenTSDB提供了REST API。所以我们需要利用提供的REST API来实现对OpenTSDB的使用。
REST API
数据添加
首先,数据添加的api为: http://服务地址:端口/api/put,请求方式为post。
数据结构为: metric(根据时间变化的指标)、timestamp、value(记录每个时刻的值)、tag(辅助标识)。
添加示例:
post http://服务地址:端口/api/put
单数据添加:
{
"metric": "cpu",
"timestamp": 1346846400,
"value": 18,
"tags": {
"host": "192.168.13.188"
}
}
上述请求可以描述为: 主机192.168.13.188在2012-09-05 20:00:00的cpu值为18。
多数据添加:
[
{
"metric": "cpu",
"timestamp": 1346846401,
"value": 18,
"tags": {
"host": "192.168.13.188"
}
},
{
"metric": "cpu",
"timestamp": 1346846402,
"value": 20,
"tags": {
"host": "192.168.13.188"
}
}
]
上述请求可以描述为: 主机192.168.13.188在2012-09-05 20:00:01和20:00:02的cpu值分别为18和20。
数据查询
OpenTSDB有多种不同的API,以满足各种查询。因为只是入门需求简单,所以这里用最简单的http://服务地址:端口/api/query。
这里我们希望通过查询API将刚刚添加的数据查询出来。
查询示例:
post http://服务地址:端口/api/query
{
"start": "1346846400",
"end" : "1346846403",
"queries": [
{
"aggregator": "none",
"metric": "cpu",
"tags": {
"host": "192.168.13.188"
}
}
],
"msResolution": true
}
上述请求可以描述为: 查询主机192.168.13.188在2012-09-05 20:00:01到20:00:03时间内cpu的值。
响应结果为:
[
{
"metric": "cpu",
"tags": {
"host": "192.168.13.188"
},
"aggregateTags": [],
"dps": {
"1346846400000": 18,
"1346846410000": 18,
"1346846420000": 20
}
}
]
dps为主机192.168.13.188的cpu在查询时刻内的cpu值。
metric | tag | 1346846400000 | 1346846410000 | 1346846420000 |
---|---|---|---|---|
cpu | host:192.168.13.188 | 18 | 18 | 20 |
封装Java API
我们可以利用httpclient来实现对OpenTSDB的REST API进行请求,以完成数据的添加和数据查询。
-
在Java项目中引入对httpclient的依赖,我这里使用了maven进行依赖引入。
<dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.12</version> </dependency>
-
将OpenTSDB数据添加和数据查询的请求体构造为Java实体类,方便后期请求体的构造。
数据添加实体类构造示例:
@Data //该注解需要插件支持,用于自动生成getter/setter等方法 public class PutData<T> { private String metric; private long timestamp; private T value; private Map<String, String> tags; }
数据查询实体类构造示例:
@Data public class QueryDate { private String start; private String end; private List<Query> queries; private boolean msResolution; /* 其他参数暂时没用到先不进行拓展 */ } @Data public class Query { private String aggregator; private String metric; private Map<String, String> tags; /* 其他参数暂时没用到先不进行拓展 */ }
数据响应实体类构造示例:
@Data public class RespOut { //响应状态码 200、301、404等 private int code; /*响应体(JSON文本) *考虑到响应结果种类繁多,还是根据需要进行解析比较方便 */ private String responseBody; }
-
创建一个自定义一个客户端类,用于对REST API请求进行支持。
客户端类示例:
@Component("openTSDBClient")//该注解需要spring支持,将该类作为组件 public class OpenTSDBClient { @Value("${com.ffcs.itm.opentsdb.host}")//该注解需要spring,用于从配置文件中读取配置值 private String host; @Value("${com.ffcs.itm.opentsdb.port}") private int port; /** *数据添加 *将传入的putData构造为JSON作为数据添加的请求体,发送请求 *@param PutData 数据添加实体 *@return RespOut 响应结果 */ public RespOut put(PutData putData) { CloseableHttpClient client = HttpClients.createDefault(); String body = ""; try { ObjectMapper mapper = new ObjectMapper(); body = mapper.writeValueAsString(putData); } catch (JsonProcessingException e) { System.err.println("Object transform String is fail!"); } return getPostResponse(client, buildHttpPost(body, APIPath.PUT)); } /** *数据查询 *将传入的queryData构造为JSON作为数据查询的请求体,发送请求 *@param QueryDate 数据查询实体 *@return RespOut 响应结果 */ public RespOut query(QueryDate queryDate){ CloseableHttpClient client = HttpClients.createDefault(); String body = ""; try { ObjectMapper mapper = new ObjectMapper(); body = mapper.writeValueAsString(queryDate); } catch (JsonProcessingException e) { System.err.println("Object transform String is fail!"); } return getPostResponse(client, buildHttpPost(body, APIPath.QUERY)); } /** *post请求构造 *@param String 请求体 *@param APIPath 请求路径 *@return HttpPost 构造的post请求 */ private HttpPost buildHttpPost(@NotNull String jsonBody, APIPath apiPath) { URI requestURI = null; try { requestURI = new URIBuilder().setScheme("http").setHost(host).setPort(port).setPath(apiPath.toString()) .addParameter("details", "true").build(); } catch (URISyntaxException e) { System.err.println("URI build is fail!"); } StringEntity body = null; body = new StringEntity(jsonBody, ContentType.APPLICATION_JSON); HttpPost httpPost = new HttpPost(requestURI); httpPost.setEntity(body); httpPost.setHeader("Content-Type", "application/json;charset=utf8"); return httpPost; } /** *获取响应结果 *@param CloseableHttpClient 可关闭的httpclient *@param HttpPost Post请求 *@return RespOut 响应结果 */ private RespOut getPostResponse(@NotNull CloseableHttpClient client, @NotNull HttpPost post) { RespOut res = new RespOut(); CloseableHttpResponse response = null; try { response = client.execute(post); res.setCode(response.getStatusLine().getStatusCode()); res.setResponseBody(EntityUtils.toString(response.getEntity())); System.err.println(res.getResponseBody()); response.close(); } catch (IOException e) { System.err.println("Connention time out: " + host); } finally { try { client.close(); } catch (IOException e) { System.err.println("client close is fail"); } } return res; } }
-
客户端类使用
数据添加使用示例:
这里将自定义的客户端类作为底层的数据访问接口,上层根据具体业务需要进行接口调用或组合完成更复杂的操作。
@Repository("cpuDao") //该注解需要spring支持,用于将该类作为数据访问层 public class CPUDao implements ICPUDao { @Autowired //该注解需要spring支持,用于将类进行自动装载(类似new,但这里为单例模式) private OpenTSDBClient openTSDBClient; /** *cpu数据添加 *@param <CPU extends PutData> *@return boolean 数据添加情况 */ @Override public boolean add(CPU data) { boolean flag = false; RespOut res = openTSDBClient.put(data);//数据添加 if(200 == res.getCode()){ flag = true; } return flag; } @Override public RespOut query(QueryDate date) { return openTSDBClient.query(date);//数据查询 } }
总结
OpenTSDB功能最强大的应该是数据统计功能,也就是上面的数据查询。但目前由于刚接触OpenTSDB,API的作用了解不多。所以这里只说一下在使用过程中发现的/api/query的一个局限性,该API似乎是没有分页功能的,所以程序在获取数据后需要对数据进行分页处理,数据大时,分页处理可能会耗费大量时间。
如果存在错误,欢迎指正,谢谢!