package sniper.histogram.service; import com.alibaba.fastjson.JSON; import com.google.common.base.Charsets; import com.google.common.io.Files; import org.HdrHistogram.ConcurrentHistogram; import org.HdrHistogram.Histogram; import org.apache.commons.lang3.tuple.Pair; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sniper.histogram.dataObject.meta.BaseMeta; import sniper.histogram.dataObject.meta.Meta; import sniper.histogram.dataObject.result.HistogramResult; import javax.xml.bind.DatatypeConverter; import java.io.File; import java.io.IOException; import java.nio.ByteBuffer; import java.util.Arrays; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentNavigableMap; import java.util.concurrent.ConcurrentSkipListMap; import java.util.zip.Deflater; import static java.nio.ByteOrder.BIG_ENDIAN; /** * Created by peiliping on 16-7-15. */ public class BaseHistogramService { protected static Logger LOG = LoggerFactory.getLogger(BaseHistogramService.class); protected final ConcurrentHashMap<String, Pair<Meta, Histogram>> writerMap = new ConcurrentHashMap<>(); protected final ConcurrentSkipListMap<String, Pair<Meta, Histogram>> readerMap = new ConcurrentSkipListMap<>(); protected final long lowest; protected final long highest; protected final int precisions; public BaseHistogramService(long lowest, long highest, int precisions) { this.lowest = lowest; this.highest = highest; this.precisions = precisions; } public BaseHistogramService() { this(1, Long.MAX_VALUE, 3); } public void addRecord(String nameSpace, String metric, long timestamp, long value, long count) { String key = buildKey(nameSpace, metric, timestamp); Pair<Meta, Histogram> pair = writerMap.get(key); if (pair == null) { for (; ; ) { pair = Pair.of(buildMeta(nameSpace, metric, timestamp), buildHistogram(timestamp)); Pair<Meta, Histogram> old = writerMap.putIfAbsent(key, pair); if (old == null) { readerMap.put(key, pair); break; } } } pair.getRight().recordValueWithCount(value, count); while (true) { long old = pair.getLeft().getLastModifyTime().get(); if (timestamp < old || pair.getLeft().getLastModifyTime().compareAndSet(old, timestamp)) break; } } public void addRecord(String nameSpace, String metric, long timestamp, long value) { addRecord(nameSpace, metric, timestamp, value, 1); } protected String buildKey(String nameSpace, String metric, long timestamp) { return BaseMeta.builder().nameSpace(nameSpace).metric(metric).build().buildKey(); } protected Meta buildMeta(String nameSpace, String metric, long timestamp) { return BaseMeta.builder().nameSpace(nameSpace).metric(metric).build(); } protected Histogram buildHistogram(long startTime) { Histogram hsm = new ConcurrentHistogram(lowest, highest, precisions); hsm.setAutoResize(true); hsm.setStartTimeStamp(startTime); return hsm; } public ConcurrentNavigableMap<String, Pair<Meta, Histogram>> acquireAllData() { return readerMap.clone(); } public void shuffle(ConcurrentNavigableMap<String, Pair<Meta, Histogram>> part, String path) throws IOException { File file = new File(path); for (Map.Entry<String, Pair<Meta, Histogram>> val : part.entrySet()) { Meta baseMeta = val.getValue().getLeft(); Histogram hsm = val.getValue().getRight(); hsm.setEndTimeStamp(baseMeta.getLastModifyTime().get()); HistogramResult result = buildResult(baseMeta, hsm); Files.append(JSON.toJSONString(result) + "\n", file, Charsets.UTF_8); readerMap.remove(val.getKey()); writerMap.remove(val.getKey()); } } protected HistogramResult buildResult(Meta baseMeta, Histogram hsm) { return HistogramResult.builder().nameSpace(baseMeta.getNameSpace()).metric(baseMeta.getMetric()).timeWindow(null).startTime(hsm.getStartTimeStamp()) .endTime(hsm.getEndTimeStamp()).totalCount(hsm.getTotalCount()).mean(hsm.getMean()).min(hsm.getMinValue()).max(hsm.getMaxValue()).p1(hsm.getValueAtPercentile(1)) .p99(hsm.getValueAtPercentile(99)).footprint(buildFootPrint(hsm)).histogram(compressData(hsm)).build(); } protected String compressData(Histogram hsm) { ByteBuffer targetBuffer = ByteBuffer.allocate(hsm.getNeededByteBufferCapacity()).order(BIG_ENDIAN); targetBuffer.clear(); int compressedLength = hsm.encodeIntoCompressedByteBuffer(targetBuffer, Deflater.BEST_COMPRESSION); byte[] compressedArray = Arrays.copyOf(targetBuffer.array(), compressedLength); return DatatypeConverter.printBase64Binary(compressedArray); } protected long[] buildFootPrint(Histogram hsm) { long[] fp = new long[21]; for (int i = 0; i < 21; i++) fp[i] = hsm.getValueAtPercentile(i * 5); return fp; } }