package resa.metrics;
import backtype.storm.metric.api.IMetricsConsumer;
import backtype.storm.task.IErrorReporter;
import backtype.storm.task.TopologyContext;
import org.codehaus.jackson.map.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.Jedis;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* Write metrics to redis server.
* <p>
* Created by ding on 13-12-11.
*/
public class RedisMetricsCollector extends FilteredMetricsCollector {
public static class QueueElement {
public final String queueName;
public final String data;
public QueueElement(String queueName, String data) {
this.queueName = queueName;
this.data = data;
}
}
public static final String REDIS_HOST = "resa.metric.redis.host";
public static final String REDIS_PORT = "resa.metric.redis.port";
public static final String REDIS_QUEUE_NAME = "resa.metric.redis.queue-name";
private static final Logger LOG = LoggerFactory.getLogger(RedisMetricsCollector.class);
private transient Jedis jedis;
private String jedisHost;
private int jedisPort;
private String queueName;
private ObjectMapper objectMapper = new ObjectMapper();
@Override
public void prepare(Map stormConf, Object registrationArgument, TopologyContext context,
IErrorReporter errorReporter) {
super.prepare(stormConf, registrationArgument, context, errorReporter);
jedisHost = (String) stormConf.get(REDIS_HOST);
jedisPort = ((Number) stormConf.get(REDIS_PORT)).intValue();
queueName = (String) stormConf.get(REDIS_QUEUE_NAME);
// queue name is not exist, use topology id as default
if (queueName == null) {
queueName = context.getStormId() + "-metrics";
}
LOG.info("Write metrics to redis server " + jedisHost + ":" + jedisPort);
}
/* get a jedis instance, create a one if necessary */
private Jedis getJedisInstance() {
if (jedis == null) {
jedis = new Jedis(jedisHost, jedisPort);
LOG.info("connecting to redis server " + jedisHost);
}
return jedis;
}
private void closeJedis() {
if (jedis != null) {
try {
LOG.info("disconnecting redis server " + jedisHost);
jedis.disconnect();
} catch (Exception e) {
}
jedis = null;
}
}
@Override
protected void handleSelectedDataPoints(IMetricsConsumer.TaskInfo taskInfo,
Collection<DataPoint> dataPoints) {
List<QueueElement> data = dataPoints2QueueElement(taskInfo, dataPoints);
if (data == null) {
return;
}
// LOG.debug("data size is " + data.size());
// push to redis
for (QueueElement e : data) {
try {
getJedisInstance().rpush(e.queueName, e.data);
} catch (Exception e1) {
LOG.info("push data to redis failed", e1);
closeJedis();
}
}
}
protected List<QueueElement> dataPoints2QueueElement(IMetricsConsumer.TaskInfo taskInfo,
Collection<DataPoint> dataPoints) {
Map<String, Object> ret = dataPoints.stream().collect(Collectors.toMap(p -> p.name, p -> p.value));
//data format is "srcComponentId:taskId:timestamp->data point json"
String data = taskInfo.srcComponentId + ':' + taskInfo.srcTaskId + ':' + taskInfo.timestamp
+ "->" + object2Json(ret);
return Arrays.asList(createDefaultQueueElement(data));
}
private String object2Json(Object o) {
try {
return objectMapper.writeValueAsString(o);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
protected QueueElement createDefaultQueueElement(String data) {
return new QueueElement(queueName, data);
}
@Override
public void cleanup() {
closeJedis();
}
}