package com.amazonaws.services.kinesis.producer.demo;
import java.nio.ByteBuffer;
import java.util.Random;
import java.util.concurrent.BlockingQueue;
import java.util.stream.Collectors;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.amazonaws.services.kinesis.producer.KinesisProducer;
import com.amazonaws.services.kinesis.producer.KinesisProducerConfiguration;
import com.amazonaws.services.kinesis.producer.Metric;
import com.amazonaws.services.kinesis.producer.UserRecordFailedException;
import com.amazonaws.services.kinesis.producer.UserRecordResult;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
public class AdvancedKPLClickEventsToKinesis
extends AbstractClickEventsToKinesis {
private static final Random RANDOM = new Random();
private static final Log log = LogFactory.getLog(
AdvancedKPLClickEventsToKinesis.class);
private final KinesisProducer kinesis;
protected AdvancedKPLClickEventsToKinesis(
BlockingQueue<ClickEvent> inputQueue) {
super(inputQueue);
kinesis = new KinesisProducer(new KinesisProducerConfiguration()
.setRegion(REGION));
}
@Override
protected void runOnce() throws Exception {
ClickEvent event = inputQueue.take();
String partitionKey = event.getSessionId();
String payload = event.getPayload();
ByteBuffer data = ByteBuffer.wrap(payload.getBytes("UTF-8"));
while (kinesis.getOutstandingRecordsCount() > 1e4) {
Thread.sleep(1);
}
recordsPut.getAndIncrement();
ListenableFuture<UserRecordResult> f =
kinesis.addUserRecord(STREAM_NAME, partitionKey, data);
Futures.addCallback(f, new FutureCallback<UserRecordResult>() {
@Override
public void onSuccess(UserRecordResult result) {
long totalTime = result.getAttempts().stream()
.mapToLong(a -> a.getDelay() + a.getDuration())
.sum();
// Only log with a small probability, otherwise it'll be very
// spammy
if (RANDOM.nextDouble() < 1e-5) {
log.info(String.format(
"Succesfully put record, partitionKey=%s, "
+ "payload=%s, sequenceNumber=%s, "
+ "shardId=%s, took %d attempts, "
+ "totalling %s ms",
partitionKey, payload, result.getSequenceNumber(),
result.getShardId(), result.getAttempts().size(),
totalTime));
}
}
@Override
public void onFailure(Throwable t) {
if (t instanceof UserRecordFailedException) {
UserRecordFailedException e =
(UserRecordFailedException) t;
UserRecordResult result = e.getResult();
String errorList =
StringUtils.join(result.getAttempts().stream()
.map(a -> String.format(
"Delay after prev attempt: %d ms, "
+ "Duration: %d ms, Code: %s, "
+ "Message: %s",
a.getDelay(), a.getDuration(),
a.getErrorCode(),
a.getErrorMessage()))
.collect(Collectors.toList()), "\n");
log.error(String.format(
"Record failed to put, partitionKey=%s, "
+ "payload=%s, attempts:\n%s",
partitionKey, payload, errorList));
}
};
});
}
@Override
public long recordsPut() {
try {
return kinesis.getMetrics("UserRecordsPut").stream()
.filter(m -> m.getDimensions().size() == 2)
.findFirst()
.map(Metric::getSum)
.orElse(0.0)
.longValue();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public void stop() {
super.stop();
kinesis.flushSync();
kinesis.destroy();
}
}