package com.sohu.tv.jedis.stat.data;
import java.text.DecimalFormat;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.sohu.tv.cachecloud.client.basic.util.DateUtils;
import com.sohu.tv.jedis.stat.constant.ClientReportConstant;
import com.sohu.tv.jedis.stat.enums.ClientExceptionType;
import com.sohu.tv.jedis.stat.enums.ValueSizeDistriEnum;
import com.sohu.tv.jedis.stat.model.CostTimeDetailStatKey;
import com.sohu.tv.jedis.stat.model.CostTimeDetailStatModel;
import com.sohu.tv.jedis.stat.model.ExceptionModel;
import com.sohu.tv.jedis.stat.model.UsefulDataModel;
import com.sohu.tv.jedis.stat.model.ValueLengthModel;
import com.sohu.tv.jedis.stat.utils.AtomicLongMap;
import com.sohu.tv.jedis.stat.utils.NamedThreadFactory;
import com.sohu.tv.jedis.stat.utils.NumberUtil;
/**
* jedis有价值数据收集器(耗时,值分布,异常等)
*
* @author leifu
* @Date 2015年1月13日
* @Time 下午5:42:31
*/
public class UsefulDataCollector {
private final static Logger logger = LoggerFactory.getLogger(UsefulDataCollector.class);
/**
* 耗时详细统计
*/
private static ConcurrentHashMap<CostTimeDetailStatKey, AtomicLongMap<Integer>> DATA_COST_TIME_MAP_ALL = new ConcurrentHashMap<CostTimeDetailStatKey, AtomicLongMap<Integer>>();
/**
* 值分布统计
*/
private static ConcurrentHashMap<String, AtomicLongMap<ValueLengthModel>> DATA_VALUE_LENGTH_DISTRIBUTE_MAP_ALL = new ConcurrentHashMap<String, AtomicLongMap<ValueLengthModel>>();
/**
* 异常详细统计
*/
private static ConcurrentHashMap<String, AtomicLongMap<ExceptionModel>> DATA_EXCEPTION_MAP_ALL = new ConcurrentHashMap<String, AtomicLongMap<ExceptionModel>>();
/**
* 收集耗时统计(统计收集数据本身对于速度的影响)
*/
private static ConcurrentHashMap<String, AtomicLongMap<Long>> COLLECTION_COST_TIME_MAP_ALL = new ConcurrentHashMap<String, AtomicLongMap<Long>>();
/**
* 数据定时清理
*/
private final static ScheduledExecutorService jedisDataCleanScheduledExecutor = Executors.newScheduledThreadPool(2,
new NamedThreadFactory("jedisCleanScheduledExecutor", true));
private static ScheduledFuture<?> jedisDataCleanScheduleFuture;
private final static int delay = 10;
private final static int fixCycle = 60;
static {
init();
}
public static void init() {
Thread jedisCleanDataThread = new Thread(new Runnable() {
@Override
public void run() {
try {
// 清理2分钟前数据
Date date = DateUtils.addMinutes(new Date(), -2);
String dateSdf = ClientReportConstant.getCollectTimeSDf().format(date);
clearCostTime(dateSdf);
clearException(dateSdf);
clearValueLength(dateSdf);
clearCollectionCost(dateSdf);
} catch (Exception e) {
logger.error("jedisCleanData thread message is" + e.getMessage(), e);
}
}
});
jedisCleanDataThread.setDaemon(true);
// 启动定时任务
jedisDataCleanScheduleFuture = jedisDataCleanScheduledExecutor
.scheduleWithFixedDelay(jedisCleanDataThread, delay, fixCycle,
TimeUnit.SECONDS);
}
/**
* 关闭
*/
public static void close() {
try {
jedisDataCleanScheduleFuture.cancel(true);
} catch (Throwable t) {
logger.error(t.getMessage(), t);
}
}
/**
* 收集耗时和值分布
*
* @param costModel
*/
public static void collectCostAndValueDistribute(UsefulDataModel costModel) {
Long start = System.currentTimeMillis();
try {
// 基础数据
String currentMinute = ClientReportConstant.getCollectTimeSDf().format(new Date());
int cost = (int) costModel.getCost();
String command = costModel.getCommand();
String hostPort = costModel.getHostPort();
int valueBytesLength = costModel.getValueBytesLength();
// 耗时详细统计
CostTimeDetailStatKey costTimeDetailStatKey = new CostTimeDetailStatKey(currentMinute, command, hostPort);
if (DATA_COST_TIME_MAP_ALL.containsKey(costTimeDetailStatKey)) {
AtomicLongMap<Integer> stat = DATA_COST_TIME_MAP_ALL.get(costTimeDetailStatKey);
stat.getAndIncrement(cost);
} else {
AtomicLongMap<Integer> stat = AtomicLongMap.create();
stat.getAndIncrement(cost);
AtomicLongMap<Integer> currentStat = DATA_COST_TIME_MAP_ALL.putIfAbsent(costTimeDetailStatKey, stat);
if (currentStat != null) {
currentStat.getAndIncrement(cost);
}
}
// 值分布
ValueSizeDistriEnum redisValueSizeEnum = ValueSizeDistriEnum.getRightSizeBetween(valueBytesLength);
if (redisValueSizeEnum != null) {
ValueLengthModel valueLengthModel = new ValueLengthModel(redisValueSizeEnum, costModel.getCommand(),
costModel.getHostPort());
if (DATA_VALUE_LENGTH_DISTRIBUTE_MAP_ALL.containsKey(currentMinute)) {
DATA_VALUE_LENGTH_DISTRIBUTE_MAP_ALL.get(currentMinute).getAndIncrement(valueLengthModel);
} else {
AtomicLongMap<ValueLengthModel> dataValueLengthMap = AtomicLongMap.create();
dataValueLengthMap.getAndIncrement(valueLengthModel);
AtomicLongMap<ValueLengthModel> currentDataValueLengthMap = DATA_VALUE_LENGTH_DISTRIBUTE_MAP_ALL
.putIfAbsent(currentMinute, dataValueLengthMap);
if (currentDataValueLengthMap != null) {
currentDataValueLengthMap.getAndIncrement(valueLengthModel);
}
}
}
// 统计收集这件事本身的耗时
Long collectCostTime = System.currentTimeMillis() - start;
if (COLLECTION_COST_TIME_MAP_ALL.containsKey(currentMinute)) {
AtomicLongMap<Long> stat = COLLECTION_COST_TIME_MAP_ALL.get(currentMinute);
stat.getAndIncrement(collectCostTime);
} else {
AtomicLongMap<Long> stat = AtomicLongMap.create();
stat.getAndIncrement(collectCostTime);
AtomicLongMap<Long> currentStat = COLLECTION_COST_TIME_MAP_ALL.putIfAbsent(currentMinute, stat);
if (currentStat != null) {
currentStat.getAndIncrement(collectCostTime);
}
}
} catch (Exception e) {
logger.error("collect data error: " + e.getMessage());
}
}
/**
* 收集异常
*
* @param exception
* @param hostPort
* @param currentTime(保留)
*/
public static void collectException(Exception exception, String hostPort, long currentTime) {
collectException(exception, hostPort, currentTime, ClientExceptionType.REDIS_TYPE);
}
/**
* 收集异常
*
* @param exception
* @param hostPort
* @param currentTime
* @param clientExceptionType(区分jedis还是client)
*/
public static void collectException(Exception exception, String hostPort, long currentTime,
ClientExceptionType clientExceptionType) {
if (exception == null) {
return;
}
try {
// 当前分钟 yyyyMMddHHmm00
String currentMinute = ClientReportConstant.getCollectTimeSDf().format(new Date());
ExceptionModel jedisExceptionModel = new ExceptionModel();
String exceptionClassName = exception.getClass().getName();
jedisExceptionModel.setExceptionClass(exceptionClassName);
jedisExceptionModel.setHostPort(hostPort);
jedisExceptionModel.setClientExceptionType(clientExceptionType);
if (DATA_EXCEPTION_MAP_ALL.containsKey(currentMinute)) {
DATA_EXCEPTION_MAP_ALL.get(currentMinute).getAndIncrement(jedisExceptionModel);
} else {
AtomicLongMap<ExceptionModel> dataExcpetionMap = AtomicLongMap.create();
dataExcpetionMap.getAndIncrement(jedisExceptionModel);
AtomicLongMap<ExceptionModel> currentDataExcpetionMap = DATA_EXCEPTION_MAP_ALL.putIfAbsent(
currentMinute, dataExcpetionMap);
if (currentDataExcpetionMap != null) {
currentDataExcpetionMap.getAndIncrement(jedisExceptionModel);
}
}
} catch (Exception e) {
logger.error("collect exception error: " + e.getMessage());
}
}
/**
* 清除targetMinute之前的耗时
*
* @param targetMinute
*/
private static void clearCostTime(String targetMinute) {
try {
if (targetMinute == "" || "".equals(targetMinute)) {
return;
}
long targetMinuteLong = NumberUtil.toLong(targetMinute);
if (targetMinuteLong == 0) {
return;
}
for (CostTimeDetailStatKey key : DATA_COST_TIME_MAP_ALL.keySet()) {
long minute = NumberUtil.toLong(key.getCurrentMinute());
if (minute < targetMinuteLong) {
DATA_COST_TIME_MAP_ALL.remove(key);
}
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
/**
* 清除targetMinute之前的值分布
*
* @param targetMinute
*/
private static void clearValueLength(String targetMinute) {
try {
if (targetMinute == "" || "".equals(targetMinute)) {
return;
}
long targetMinuteLong = NumberUtil.toLong(targetMinute);
if (targetMinuteLong == 0) {
return;
}
for (String key : DATA_VALUE_LENGTH_DISTRIBUTE_MAP_ALL.keySet()) {
long minute = NumberUtil.toLong(key);
if (minute < targetMinuteLong) {
DATA_VALUE_LENGTH_DISTRIBUTE_MAP_ALL.remove(key);
}
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
/**
* 清除收集数据耗时本身
*
* @param targetMinute
*/
private static void clearCollectionCost(String targetMinute) {
try {
if (targetMinute == "" || "".equals(targetMinute)) {
return;
}
long targetMinuteLong = NumberUtil.toLong(targetMinute);
if (targetMinuteLong == 0) {
return;
}
for (String key : COLLECTION_COST_TIME_MAP_ALL.keySet()) {
long minute = NumberUtil.toLong(key);
if (minute < targetMinuteLong) {
COLLECTION_COST_TIME_MAP_ALL.remove(key);
}
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
/**
* 清除targetMinute之前的异常
*
* @param targetMinute
*/
private static void clearException(String targetMinute) {
try {
if (targetMinute == "" || "".equals(targetMinute)) {
return;
}
long targetMinuteLong = NumberUtil.toLong(targetMinute);
if (targetMinuteLong == 0) {
return;
}
for (String key : DATA_EXCEPTION_MAP_ALL.keySet()) {
long minute = NumberUtil.toLong(key);
if (minute < targetMinuteLong) {
DATA_EXCEPTION_MAP_ALL.remove(key);
}
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
/**
* 获取上一分钟的值分布
*
* @param currentMinuteStamp
*/
public static Map<ValueLengthModel, Long> getValueLengthLastMinute(String currentMinuteStamp) {
AtomicLongMap<ValueLengthModel> map = DATA_VALUE_LENGTH_DISTRIBUTE_MAP_ALL.get(currentMinuteStamp);
if (map == null || map.isEmpty()) {
return Collections.emptyMap();
}
return map.asMap();
}
/**
* 获取上一分钟的耗时
*
* @param currentMinuteStamp
*/
public static Map<CostTimeDetailStatKey, AtomicLongMap<Integer>> getCostTimeLastMinute(String currentMinuteStamp) {
Map<CostTimeDetailStatKey, AtomicLongMap<Integer>> result = new HashMap<CostTimeDetailStatKey, AtomicLongMap<Integer>>();
for (Entry<CostTimeDetailStatKey, AtomicLongMap<Integer>> entry : DATA_COST_TIME_MAP_ALL.entrySet()) {
CostTimeDetailStatKey costTimeDetailStatKey = entry.getKey();
if (costTimeDetailStatKey != null && costTimeDetailStatKey.getCurrentMinute() != null
&& costTimeDetailStatKey.getCurrentMinute().equals(currentMinuteStamp)) {
result.put(costTimeDetailStatKey, entry.getValue());
}
}
return result;
}
/**
* 获取上一分钟的异常
*
* @param currentMinuteStamp
*/
public static Map<ExceptionModel, Long> getExceptionLastMinute(String currentMinuteStamp) {
AtomicLongMap<ExceptionModel> map = DATA_EXCEPTION_MAP_ALL.get(currentMinuteStamp);
if (map == null || map.isEmpty()) {
return Collections.emptyMap();
}
return map.asMap();
}
/**
* 产生耗时详细分布
*
* @param statMap
*/
public static CostTimeDetailStatModel generateCostTimeDetailStatKey(AtomicLongMap<Integer> statMap) {
CostTimeDetailStatModel model = new CostTimeDetailStatModel();
model.setMean(getMeanValue(statMap));
model.setMedian(fillCostTimeDetailStatModel(model, statMap, 50));
model.setNinetyPercentMax(fillCostTimeDetailStatModel(model, statMap, 90));
model.setNinetyNinePercentMax(fillCostTimeDetailStatModel(model, statMap, 99));
model.setHundredMax(fillCostTimeDetailStatModel(model, statMap, 100));
// 上面已经设置过了
// model.setTotalCount(getTotalValue(statMap));
return model;
}
/**
* 获取平均值
*
* @param statMap
*/
private static double getMeanValue(AtomicLongMap<Integer> statMap) {
if (statMap == null || statMap.isEmpty()) {
return 0;
}
Map<Integer, Long> map = statMap.asMap();
Long totalCount = 0L;
Long totalValue = 0L;
for (Entry<Integer, Long> entry : map.entrySet()) {
totalCount += entry.getValue();
totalValue += entry.getKey() * entry.getValue();
}
DecimalFormat df = new DecimalFormat("#.00");
Double result = totalValue * 1.0 / totalCount * 1.0;
return NumberUtil.toDouble(df.format(result));
}
/**
* 计算Integer-Long结构排序后,百分之多少所在的对应数据
*
* @param statMap
* @param percent
* @return
*/
private static int fillCostTimeDetailStatModel(CostTimeDetailStatModel model, AtomicLongMap<Integer> statMap,
double percent) {
int wrongResultValue = 0;
if (percent > 100 || percent < 0) {
return wrongResultValue;
}
if (statMap == null || statMap.isEmpty()) {
return wrongResultValue;
}
Map<Integer, Long> sortKeyMap = new TreeMap<Integer, Long>(statMap.asMap());
Long totalSize = model.getTotalCount();
if (totalSize <= 0) {
for (Long count : sortKeyMap.values()) {
totalSize += count;
}
model.setTotalCount(totalSize);
}
return getPercentValue(totalSize, sortKeyMap, percent);
}
private static int getPercentValue(Long totalSize, Map<Integer, Long> sortKeyMap, double percent) {
// 计算百分比所在个数
Long targetLocation = (long) (totalSize * percent / 100.0);
Long count = 0L;
Integer key = 0;
for (Entry<Integer, Long> entry : sortKeyMap.entrySet()) {
key = entry.getKey();
count += entry.getValue();
if (count > targetLocation) {
break;
}
}
return key;
}
public static Map<CostTimeDetailStatKey, AtomicLongMap<Integer>> getDataCostTimeMapAll() {
return DATA_COST_TIME_MAP_ALL;
}
public static Map<String, AtomicLongMap<ValueLengthModel>> getDataValueLengthDistributeMapAll() {
return DATA_VALUE_LENGTH_DISTRIBUTE_MAP_ALL;
}
public static Map<String, AtomicLongMap<ExceptionModel>> getDataExceptionMapAll() {
return DATA_EXCEPTION_MAP_ALL;
}
public static Map<String, AtomicLongMap<Long>> getCollectionCostTimeMapAll() {
return COLLECTION_COST_TIME_MAP_ALL;
}
public static void setCOLLECTION_COST_TIME_MAP_ALL(ConcurrentHashMap<String, AtomicLongMap<Long>> cOLLECTION_COST_TIME_MAP_ALL) {
COLLECTION_COST_TIME_MAP_ALL = cOLLECTION_COST_TIME_MAP_ALL;
}
}