OpenTSDB之Java开发

164 阅读3分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

JAVA的调用

OpenTSDB提供三种方式的读写操作:telnet、http、post,但官方并没提供JAVA版的API。在GitHub上有的Java的opentsdb-client ,才使得我能对openTSDB的读写操作进行封装,从而分享至此:github.com/shifeng258/… 在此项目上包装开发或将其打包成SDK使用均可。本人将用前一种方式进行的。 增加的一个统一调用类OpentsdbClient

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.ygsoft.opentsdb.client.ExpectResponse;
import com.ygsoft.opentsdb.client.HttpClient;
import com.ygsoft.opentsdb.client.HttpClientImpl;
import com.ygsoft.opentsdb.client.builder.MetricBuilder;
import com.ygsoft.opentsdb.client.request.Query;
import com.ygsoft.opentsdb.client.request.QueryBuilder;
import com.ygsoft.opentsdb.client.request.SubQueries;
import com.ygsoft.opentsdb.client.response.Response;
import com.ygsoft.opentsdb.client.response.SimpleHttpResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.*;

/**
 * Opentsdb读写工具类
 */
public class OpentsdbClient {
    private static Logger log = LoggerFactory.getLogger(OpentsdbClient.class);
    /**
     * 取平均值的聚合器
     */
    public static String AGGREGATOR_AVG = "avg";
    /**
     * 取累加值的聚合器
     */
    public static String AGGREGATOR_SUM = "sum";
    private HttpClient httpClient;

    public OpentsdbClient(String opentsdbUrl) {
        this.httpClient = new HttpClientImpl(opentsdbUrl);
    }

    /**
     * 写入数据 
     * @param metric 指标 
     * @param timestamp 时间点 
     * @param value 
     * @param tagMap 
     * @return 
     * @throws Exception
     */
    public boolean putData(String metric, Date timestamp, long value, Map tagMap) throws Exception {
        long timsSecs = timestamp.getTime() / 1000;
        return this.putData(metric, timsSecs, value, tagMap);
    }

    /**
     * 写入数据 
     * @param metric 指标  
     * @param timestamp 时间点 
     * @param value 
     * @param tagMap 
     * @return 
     * @throws Exception
     */
    public boolean putData(String metric, Date timestamp, double value, Map tagMap) throws Exception {
        long timsSecs = timestamp.getTime() / 1000;
        return this.putData(metric, timsSecs, value, tagMap);
    }

    /**
     * 写入数据 
     * @param metric 指标 
     * @param timestamp 转化为秒的时间点 
     * @param value 
     * @param tagMap 
     * @return 
     * @throws Exception
     */
    public boolean putData(String metric, long timestamp, long value, Map tagMap) throws Exception {
        MetricBuilder builder = MetricBuilder.getInstance();
        builder.addMetric(metric).setDataPoint(timestamp, value).addTags(tagMap);
        try {
            log.debug("write quest:{}", builder.build());
            Response response = httpClient.pushMetrics(builder, ExpectResponse.SUMMARY);
            log.debug("response.statusCode: {}", response.getStatusCode());
            return response.isSuccess();
        } catch (Exception e) {
            log.error("put data to opentsdb error: ", e);
            throw e;
        }
    }

    /**
     * 写入数据 
     * @param metric 指标 
     * @param timestamp 转化为秒的时间点 
     * @param value 
     * @param tagMap 
     * @return 
     * @throws Exception
     */
    public boolean putData(String metric, long timestamp, double value, Map tagMap) throws Exception {
        MetricBuilder builder = MetricBuilder.getInstance();
        builder.addMetric(metric).setDataPoint(timestamp, value).addTags(tagMap);
        try {
            log.debug("write quest:{}", builder.build());
            Response response = httpClient.pushMetrics(builder, ExpectResponse.SUMMARY);
            log.debug("response.statusCode: {}", response.getStatusCode());
            return response.isSuccess();
        } catch (Exception e) {
            log.error("put data to opentsdb error: ", e);
            throw e;
        }
    }
    
    /**
     * 批量写入数据 
     * @param metric 指标  
     * @param timestamp 时间点 
     * @param value 
     * @param tagMap 
     * @return 
     * @throws Exception
     */
    public boolean putData(JSONArray jsonArr) throws Exception {
        return this.putDataBatch(jsonArr);
    }
    
    /**
     * 批量写入数据 
     * @param metric 指标 
     * @param timestamp 转化为秒的时间点 
     * @param value 
     * @param tagMap 
     * @return 
     * @throws Exception
     */
    public boolean putDataBatch(JSONArray jsonArr) throws Exception {
        MetricBuilder builder = MetricBuilder.getInstance();
        try {
        	for(int i = 0; i < jsonArr.size(); i++){
            	Map tagMap = new HashMap();
            	for(String key : jsonArr.getJSONObject(i).getJSONObject("tags").keySet()){
            		tagMap.put(key, jsonArr.getJSONObject(i).getJSONObject("tags").get(key));
            	}
    			String metric = jsonArr.getJSONObject(i).getString("metric").toString();
    			long timestamp = DateTimeUtil.parse(jsonArr.getJSONObject(i).getString("timestamp"), "yyyy/MM/dd HH:mm:ss").getTime() / 1000;
    			double value = Double.valueOf(jsonArr.getJSONObject(i).getString("value"));
    			builder.addMetric(metric).setDataPoint(timestamp, value).addTags(tagMap);
            }
        	
            log.debug("write quest:{}", builder.build());
            Response response = httpClient.pushMetrics(builder, ExpectResponse.SUMMARY);
            log.debug("response.statusCode: {}", response.getStatusCode());
            return response.isSuccess();
        } catch (Exception e) {
            log.error("put data to opentsdb error: ", e);
            throw e;
        }
    }

    /**
     * 查询数据,返回的数据为json格式,结构为: 
     * "[ 
     * " { 
     * " metric: mysql.innodb.row_lock_time, 
     * " tags: { 
     * " host: web01, 
     * " dc: beijing 
     * " }, 
     * " aggregateTags: [], 
     * " dps: { 
     * " 1435716527: 1234, 
     * " 1435716529: 2345 
     * " } 
     * " }, 
     * " { 
     * " metric: mysql.innodb.row_lock_time, 
     * " tags: { 
     * " host: web02, 
     * " dc: beijing 
     * " }, 
     * " aggregateTags: [], 
     * " dps: { 
     * " 1435716627: 3456 
     * " } 
     * " } 
     * "]"; 
     * @param metric 要查询的指标 
     * @param aggregator 查询的聚合类型, 如: OpentsdbClient.AGGREGATOR_AVG, OpentsdbClient.AGGREGATOR_SUM 
     * @param tagMap 查询的条件 
     * @param downsample 采样的时间粒度, 如: 1s,2m,1h,1d,2d 
     * @param startTime 查询开始时间,时间格式为yyyy/MM/dd HH:mm:ss 
     * @param endTime 查询结束时间,时间格式为yyyy/MM/dd HH:mm:ss
     */
    public String getData(String metric, Map tagMap, String aggregator, String downsample, String startTime, String endTime) throws IOException {
        QueryBuilder queryBuilder = QueryBuilder.getInstance();
        Query query = queryBuilder.getQuery();
        query.setStart(DateTimeUtil.parse(startTime, "yyyy/MM/dd HH:mm:ss").getTime() / 1000);
        query.setEnd(DateTimeUtil.parse(endTime, "yyyy/MM/dd HH:mm:ss").getTime() / 1000);
        List sqList = new ArrayList();
        SubQueries sq = new SubQueries();
        sq.addMetric(metric);
        sq.addTag(tagMap);
        sq.addAggregator(aggregator);
        sq.setDownsample(downsample + "-" + aggregator);
        sqList.add(sq);
        query.setQueries(sqList);
        try {
            log.debug("query request:{}", queryBuilder.build()); //这行起到校验作用
            SimpleHttpResponse spHttpResponse = httpClient.pushQueries(queryBuilder, ExpectResponse.DETAIL);
            log.debug("response.content: {}", spHttpResponse.getContent());
            if (spHttpResponse.isSuccess()) {
                return spHttpResponse.getContent();
            }
            return null;
        } catch (IOException e) {
            log.error("get data from opentsdb error: ", e);
            throw e;
        }
    }

    /**
     * 查询数据,返回tags与时序值的映射: Map> 
     * @param metric 要查询的指标 
     * @param aggregator 查询的聚合类型, 如: OpentsdbClient.AGGREGATOR_AVG, OpentsdbClient.AGGREGATOR_SUM 
     * @param tagMap 查询的条件 
     * @param downsample 采样的时间粒度, 如: 1s,2m,1h,1d,2d 
     * @param startTime 查询开始时间, 时间格式为yyyy/MM/dd HH:mm:ss 
     * @param endTime 查询结束时间, 时间格式为yyyy/MM/dd HH:mm:ss 
     * @param retTimeFmt 返回的结果集中,时间点的格式, 如:yyyy/MM/dd HH:mm:ss 或 yyyyMMddHH 等 
     * @return Map>
     */
    public Map getData(String metric, Map tagMap, String aggregator, String downsample, String startTime, String endTime, String retTimeFmt) throws IOException {
        String resContent = this.getData(metric, tagMap, aggregator, downsample, startTime, endTime);
        return this.convertContentToMap(resContent, retTimeFmt);
    }

    public Map convertContentToMap(String resContent, String retTimeFmt) {
        Map tagsValuesMap = new HashMap();
        if (resContent == null || "".equals(resContent.trim())) {
            return tagsValuesMap;
        }
        JSONArray array = (JSONArray) JSONObject.parse(resContent);
        if (array != null) {
            for (int i = 0; i < array.size(); i++) {
                JSONObject obj = (JSONObject) array.get(i);
                JSONObject tags = (JSONObject) obj.get("tags");
                JSONObject dps = (JSONObject) obj.get("dps"); //timeValueMap.putAll(dps); Map timeValueMap = new HashMap(); for (Iterator it = dps.keySet().iterator(); it.hasNext(); ) { String timstamp = it.next(); Date datetime = new Date(Long.parseLong(timstamp)*1000); timeValueMap.put(DateTimeUtil.format(datetime, retTimeFmt), dps.get(timstamp)); } tagsValuesMap.put(tags.toString(), timeValueMap); } } return tagsValuesMap; }
            }
        }
        return tagsValuesMap;
    }
}

增加一个时间处理类

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class DateTimeUtil {
    public static Date parse(String date,String fm){
        Date res=null;
        try {
            SimpleDateFormat sft=new SimpleDateFormat(fm);
            res=sft.parse(date);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return res;
    }

}

为了建立HTTP的长连接,将PoolingHttpClient进行单例模式的修改(防止调用时频繁的实例化引起的资源消耗)

public class PoolingHttpClient {

	...
	
	/*单例模式修改*/
	private static PoolingHttpClient poolingHttpClient;
	
	private PoolingHttpClient() {
		// Increase max total connection
		connManager.setMaxTotal(maxTotalConnections);
		// Increase default max connection per route
		connManager.setDefaultMaxPerRoute(maxConnectionsPerRoute);

		// config timeout
		RequestConfig config = RequestConfig.custom()
				.setConnectTimeout(connectTimeout)
				.setConnectionRequestTimeout(waitTimeout)
				.setSocketTimeout(readTimeout).build();

		httpClient = HttpClients.custom()
				.setKeepAliveStrategy(keepAliveStrategy)
				.setConnectionManager(connManager)
				.setDefaultRequestConfig(config).build();

		// detect idle and expired connections and close them
		IdleConnectionMonitorThread staleMonitor = new IdleConnectionMonitorThread(
				connManager);
		staleMonitor.start();
	}

	public static PoolingHttpClient getInstance() {
    	if (null == poolingHttpClient) {
	    	//加锁保证线程安全
    		synchronized (PoolingHttpClient.class) {
    			if (null == poolingHttpClient) {
    				poolingHttpClient = new PoolingHttpClient();
    			}
			}
    	}
    	
    	return poolingHttpClient;
    }
	/*单例模式修改结束*/
	
	public SimpleHttpResponse doPost(String url, String data)
			throws IOException {
		StringEntity requestEntity = new StringEntity(data);
		HttpPost postMethod = new HttpPost(url);
		postMethod.setEntity(requestEntity);

		HttpResponse response = execute(postMethod);
		int statusCode = response.getStatusLine().getStatusCode();

		SimpleHttpResponse simpleResponse = new SimpleHttpResponse();
		simpleResponse.setStatusCode(statusCode);

		HttpEntity entity = response.getEntity();
		if (entity != null) {
			// should return: application/json; charset=UTF-8
			String ctype = entity.getContentType().getValue();
			String charset = getResponseCharset(ctype);
			String content = EntityUtils.toString(entity, charset);
			simpleResponse.setContent(content);
		}
		
		/*增加线程回收开始*/
		EntityUtils.consume(entity);
		postMethod.releaseConnection();
		/*增加线程回收结束*/
		return simpleResponse;
	}
	
	...
	
}

修改HttpClientImpl对单例模式的PoolingHttpClient调用

public class HttpClientImpl implements HttpClient {

    ...

    //private PoolingHttpClient httpClient = new PoolingHttpClient();
    private PoolingHttpClient httpClient = PoolingHttpClient.getInstance();
    
    ...
}