package com.griddynamics.jagger.rawdata.receiver;
import com.google.protobuf.InvalidProtocolBufferException;
import com.griddynamics.jagger.rawdata.protobuf.RawDataPackageProtos;
import org.apache.kafka.clients.consumer.Consumer;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.KafkaConsumer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import static com.google.common.collect.Lists.newArrayList;
import static com.griddynamics.jagger.rawdata.protobuf.RawDataPackageProtos.RawDataPackage.parseFrom;
import static com.griddynamics.jagger.rawdata.sender.RawDataSender.KAFKA_KEY_SEPARATOR;
import static java.util.Collections.singletonList;
import static org.apache.commons.lang.StringUtils.split;
@SuppressWarnings("unused")
public class RawDataReceiver {
private static final Logger LOGGER = LoggerFactory.getLogger(RawDataReceiver.class);
private final String groupId;
private final String kafkaTopic;
private final Properties kafkaProperties = new Properties();
private final Consumer<String, byte[]> consumer;
/**
* @param kafkaTopic The name of Kafka topic from which data must be received.
* @param kafkaServers A list of host/port pairs to use for establishing the initial connection to the Kafka cluster.
* This list should be in the form "host1:port1,host2:port2,...".
* @param groupId A string that uniquely identifies the group of consumer processes to which this consumer belongs.
* By setting the same group id multiple processes indicate that they are all part of the same consumer group.
*/
public RawDataReceiver(String kafkaTopic, String kafkaServers, String groupId) {
this.kafkaTopic = kafkaTopic;
this.groupId = groupId;
kafkaProperties.put("bootstrap.servers", kafkaServers);
kafkaProperties.put("group.id", groupId);
kafkaProperties.put("enable.auto.commit", "true");
kafkaProperties.put("auto.commit.interval.ms", "1000");
kafkaProperties.put("session.timeout.ms", "30000");
kafkaProperties.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
kafkaProperties.put("value.deserializer", "org.apache.kafka.common.serialization.ByteArrayDeserializer");
this.consumer = new KafkaConsumer<>(kafkaProperties);
consumer.subscribe(singletonList(kafkaTopic));
}
/**
* Fetches data from the topic {@link #kafkaTopic}.
*
* @param timeout The time, in milliseconds, spent waiting in poll if data is not available. If 0, returns
* immediately with any records that are available now. Must not be negative.
* @return List of raw data packages mapped by metricId.
* @throws InvalidProtocolBufferException if parsing of protobuf data failed.
* @see KafkaConsumer#poll(long)
*/
public Map<String, List<RawDataPackageProtos.RawDataPackage>> receiveData(long timeout) throws InvalidProtocolBufferException {
Map<String, List<RawDataPackageProtos.RawDataPackage>> rawDataByMetricId = new HashMap<>();
ConsumerRecords<String, byte[]> records = consumer.poll(timeout);
for (ConsumerRecord<String, byte[]> record : records) {
String nodeName = split(record.key(), KAFKA_KEY_SEPARATOR)[0];
String metricId = split(record.key(), KAFKA_KEY_SEPARATOR)[1];
RawDataPackageProtos.RawDataPackage rawDataPackage = parseFrom(record.value());
LOGGER.debug("Received: nodeName = {}, metricId = {}, offset = {}", nodeName, record.key(), record.offset());
if (rawDataByMetricId.get(metricId) == null)
rawDataByMetricId.put(metricId, newArrayList(rawDataPackage));
else
rawDataByMetricId.get(metricId).add(rawDataPackage);
}
return rawDataByMetricId;
}
/**
* @return group id of {@link #consumer}.
* @see #RawDataReceiver(String, String, String)
*/
public String getGroupId() {
return groupId;
}
/**
* @return name of Kafka topic.
*/
public String getKafkaTopic() {
return kafkaTopic;
}
/**
* @return copy of {@link #kafkaProperties}.
*/
public Properties getKafkaProperties() {
return new Properties(kafkaProperties);
}
}