package storm.applications.bolt;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import backtype.storm.tuple.Fields;
import backtype.storm.tuple.Tuple;
import backtype.storm.tuple.Values;
import static storm.applications.constants.MachineOutlierConstants.*;
import storm.applications.util.sort.Sorter;
/**
* Always trigger the top-K objects as abnormal.
* @author yexijiang
*
*/
public class TopKAlertTriggerBolt extends AbstractBolt {
private int k;
private long previousTimestamp;
private List<Tuple> streamList;
@Override
public void initialize() {
k = config.getInt(Conf.ALERT_TRIGGER_TOPK, 3);
previousTimestamp = 0;
streamList = new ArrayList<>();
}
@Override
public void execute(Tuple input) {
long timestamp = input.getLongByField(Field.TIMESTAMP);
// new batch
if (timestamp > previousTimestamp) {
// sort the tuples in stream list
Sorter.quicksort(streamList, new Comparator<Tuple>() {
@Override
public int compare(Tuple o1, Tuple o2) {
double score1 = o1.getDouble(1);
double score2 = o2.getDouble(1);
if (score1 < score2) {
return -1;
} else if (score1 > score2) {
return 1;
}
return 0;
}
});
// treat the top-K as abnormal
int realK = streamList.size() < k ? streamList.size() : k;
for (int i = 0; i < streamList.size(); ++i) {
Tuple streamProfile = streamList.get(i);
boolean isAbnormal = false;
// last three stream are marked as abnormal
if (i >= streamList.size() - 3) {
isAbnormal = true;
}
collector.emit(streamList, new Values(streamProfile.getString(0), streamProfile.getDouble(1), streamProfile.getLong(2), isAbnormal, streamProfile.getValue(3)));
}
previousTimestamp = timestamp;
// clear the cache
streamList.clear();
}
streamList.add(input);
collector.ack(input);
}
@Override
public Fields getDefaultFields() {
return new Fields(Field.ANOMALY_STREAM, Field.STREAM_ANOMALY_SCORE,
Field.TIMESTAMP, Field.IS_ABNORMAL, Field.OBSERVATION);
}
}