package com.sohu.cache.client.service.impl;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.sohu.cache.client.service.AppInstanceClientRelationService;
import com.sohu.cache.client.service.ClientReportCostDistriService;
import com.sohu.cache.client.service.ClientReportInstanceService;
import com.sohu.cache.dao.AppClientCostTimeStatDao;
import com.sohu.cache.dao.AppClientCostTimeTotalStatDao;
import com.sohu.cache.entity.AppClientCostTimeStat;
import com.sohu.cache.entity.AppClientCostTimeTotalStat;
import com.sohu.cache.entity.InstanceInfo;
import com.sohu.tv.jedis.stat.constant.ClientReportConstant;
import com.sohu.tv.jedis.stat.enums.ClientCollectDataTypeEnum;
import com.sohu.tv.jedis.stat.model.ClientReportBean;
/**
* 客户端上报耗时分布
*
* @author leifu
* @Date 2015年1月19日
* @Time 下午1:49:39
*/
public class ClientReportCostDistriServiceImpl implements ClientReportCostDistriService {
private final Logger logger = LoggerFactory.getLogger(ClientReportCostDistriServiceImpl.class);
/**
* 客户端耗时操作
*/
private AppClientCostTimeStatDao appClientCostTimeStatDao;
/**
* 基于应用的客户端耗时操作
*/
private AppClientCostTimeTotalStatDao appClientCostTimeTotalStatDao;
/**
* host:port与instanceInfo简单缓存
*/
private ClientReportInstanceService clientReportInstanceService;
/**
* 应用下节点和客户端关系
*/
private AppInstanceClientRelationService appInstanceClientRelationService;
@Override
public List<String> getAppDistinctCommand(Long appId, long startTime, long endTime) {
try {
return appClientCostTimeTotalStatDao.getAppDistinctCommand(appId, startTime, endTime);
} catch (Exception e) {
logger.error(e.getMessage(), e);
return Collections.emptyList();
}
}
@Override
public List<AppClientCostTimeStat> getAppCommandClientToInstanceStat(Long appId, String command, Long instanceId,
String clientIp, long startTime, long endTime) {
try {
return appClientCostTimeStatDao.getAppCommandClientToInstanceStat(appId, command, instanceId, clientIp,
startTime, endTime);
} catch (Exception e) {
logger.error(e.getMessage(), e);
return Collections.emptyList();
}
}
@Override
public List<AppClientCostTimeTotalStat> getAppClientCommandTotalStat(Long appId, String command, long startTime,
long endTime) {
try {
return appClientCostTimeTotalStatDao.getAppClientCommandStat(appId, command, startTime, endTime);
} catch (Exception e) {
logger.error(e.getMessage(), e);
return Collections.emptyList();
}
}
@Override
public void batchSave(ClientReportBean clientReportBean) {
try {
// 1.client上报
final String clientIp = clientReportBean.getClientIp();
final long collectTime = clientReportBean.getCollectTime();
final long reportTime = clientReportBean.getReportTimeStamp();
final List<Map<String, Object>> datas = clientReportBean.getDatas();
if (datas == null || datas.isEmpty()) {
logger.warn("datas field {} is empty", clientReportBean);
return;
}
// 2.结果集
List<AppClientCostTimeStat> appClientCostTimeStatList = new ArrayList<AppClientCostTimeStat>();
// 3.解析结果
for (Map<String, Object> map : datas) {
Integer clientDataType = MapUtils.getInteger(map, ClientReportConstant.CLIENT_DATA_TYPE, -1);
ClientCollectDataTypeEnum clientCollectDataTypeEnum = ClientCollectDataTypeEnum.MAP.get(clientDataType);
if (clientCollectDataTypeEnum == null) {
continue;
}
if (ClientCollectDataTypeEnum.COST_TIME_DISTRI_TYPE.equals(clientCollectDataTypeEnum)) {
AppClientCostTimeStat appClientCostTimeStat = generate(clientIp, collectTime, reportTime, map);
if (appClientCostTimeStat != null) {
appClientCostTimeStatList.add(appClientCostTimeStat);
}
}
}
if (CollectionUtils.isNotEmpty(appClientCostTimeStatList)) {
// 4.批量保存
appClientCostTimeStatDao.batchSave(appClientCostTimeStatList);
// 5.合并app统计结果
List<AppClientCostTimeTotalStat> appClientCostTimeTotalStatList = mergeAppClientCostTimeStat(appClientCostTimeStatList);
if (CollectionUtils.isNotEmpty(appClientCostTimeTotalStatList)) {
appClientCostTimeTotalStatDao.batchSave(appClientCostTimeTotalStatList);
}
// 6.保存应用下节点和客户端关系
appInstanceClientRelationService.batchSave(appClientCostTimeStatList);
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
/**
* 合并以app为单位的
* @param appClientCostTimeStatList
*/
private List<AppClientCostTimeTotalStat> mergeAppClientCostTimeStat(List<AppClientCostTimeStat> appClientCostTimeStatList) {
// 1. merge后的结果
List<AppClientCostTimeTotalStat> resultList = new ArrayList<AppClientCostTimeTotalStat>();
// 2. 以appid_command_collectTime为key,拆分appClientCostTimeStatList
Map<String, List<AppClientCostTimeStat>> map = new HashMap<String, List<AppClientCostTimeStat>>();
for (AppClientCostTimeStat appClientCostTimeStat : appClientCostTimeStatList) {
long appId = appClientCostTimeStat.getAppId();
String command = appClientCostTimeStat.getCommand();
long collectTime = appClientCostTimeStat.getCollectTime();
String key = appId + "_" + command + "_" + collectTime;
if (map.containsKey(key)) {
map.get(key).add(appClientCostTimeStat);
} else {
List<AppClientCostTimeStat> list = new ArrayList<AppClientCostTimeStat>();
list.add(appClientCostTimeStat);
map.put(key, list);
}
}
// 3.生成结果
for (Entry<String, List<AppClientCostTimeStat>> entry : map.entrySet()) {
String key = entry.getKey();
String[] items = key.split("_");
long appId = NumberUtils.toLong(items[0]);
String command = items[1];
long collectTime = NumberUtils.toLong(items[2]);
double totalCost = 0.0;
long totalCount = 0;
int median = 0;
int ninetyPercentMax = 0;
int ninetyNinePercentMax = 0;
int hundredMax = 0;
String maxInstanceHost = "";
int maxInstancePort = 0;
long maxInstanceId = 0;
String maxClientIp = "";
double mean = 0.0;
for (AppClientCostTimeStat appClientCostTimeStat : entry.getValue()) {
AppClientCostTimeTotalStat appClientCostTimeTotalStat = AppClientCostTimeTotalStat.getFromAppClientCostTimeStat(appClientCostTimeStat);
totalCost += appClientCostTimeTotalStat.getTotalCost();
totalCount += appClientCostTimeTotalStat.getTotalCount();
if (appClientCostTimeTotalStat.getMedian() > median) {
median = appClientCostTimeTotalStat.getMedian();
}
if (appClientCostTimeTotalStat.getNinetyPercentMax() > ninetyPercentMax) {
ninetyPercentMax = appClientCostTimeTotalStat.getNinetyPercentMax();
}
if (appClientCostTimeTotalStat.getNinetyNinePercentMax() > ninetyNinePercentMax) {
ninetyNinePercentMax = appClientCostTimeTotalStat.getNinetyNinePercentMax();
}
if (appClientCostTimeTotalStat.getHundredMax() > hundredMax) {
hundredMax = appClientCostTimeTotalStat.getHundredMax();
maxInstanceHost = appClientCostTimeTotalStat.getMaxInstanceHost();
maxInstancePort = appClientCostTimeTotalStat.getMaxInstancePort();
maxInstanceId = appClientCostTimeTotalStat.getMaxInstanceId();
maxClientIp = appClientCostTimeTotalStat.getMaxClientIp();
}
}
DecimalFormat df = new DecimalFormat("0.00");
totalCost = NumberUtils.toDouble(df.format(totalCost));
//平均值
if (totalCount > 0) {
mean = totalCost / totalCount;
mean = NumberUtils.toDouble(df.format(mean));
}
//添加到结果集
resultList.add(new AppClientCostTimeTotalStat(-1, appId, collectTime, new Date(), command,
totalCount, totalCost, median, mean, ninetyPercentMax, ninetyNinePercentMax, hundredMax,
maxInstanceHost, maxInstancePort, maxInstanceId, maxClientIp));
}
return resultList;
}
private AppClientCostTimeStat generate(String clientIp, long collectTime, long reportTime, Map<String, Object> map) {
try {
Integer count = MapUtils.getInteger(map, ClientReportConstant.COST_COUNT, 0);
String command = MapUtils.getString(map, ClientReportConstant.COST_COMMAND, "");
if (StringUtils.isBlank(command)) {
logger.warn("command is empty!");
return null;
}
String hostPort = MapUtils.getString(map, ClientReportConstant.COST_HOST_PORT, "");
if (StringUtils.isBlank(hostPort)) {
logger.warn("hostPort is empty", hostPort);
return null;
}
int index = hostPort.indexOf(":");
if (index <= 0) {
logger.warn("hostPort {} format is wrong", hostPort);
return null;
}
String host = hostPort.substring(0, index);
int port = NumberUtils.toInt(hostPort.substring(index + 1));
// 实例信息
InstanceInfo instanceInfo = clientReportInstanceService.getInstanceInfoByHostPort(host, port);
if (instanceInfo == null) {
// logger.warn("instanceInfo is empty, host is {}, port is {}", host, port);
return null;
}
long appId = instanceInfo.getAppId();
// 耗时分布详情
double mean = MapUtils.getDouble(map, ClientReportConstant.COST_TIME_MEAN, 0.0);
Integer median = MapUtils.getInteger(map, ClientReportConstant.COST_TIME_MEDIAN, 0);
Integer ninetyPercentMax = MapUtils.getInteger(map, ClientReportConstant.COST_TIME_90_MAX, 0);
Integer ninetyNinePercentMax = MapUtils.getInteger(map, ClientReportConstant.COST_TIME_99_MAX, 0);
Integer hunredMax = MapUtils.getInteger(map, ClientReportConstant.COST_TIME_100_MAX, 0);
AppClientCostTimeStat stat = new AppClientCostTimeStat();
stat.setAppId(appId);
stat.setClientIp(clientIp);
stat.setReportTime(new Date(reportTime));
stat.setCollectTime(collectTime);
stat.setCreateTime(new Date());
stat.setCommand(command);
stat.setCount(count);
stat.setInstanceHost(host);
stat.setInstancePort(port);
stat.setMean(NumberUtils.toDouble(new DecimalFormat("#.00").format(mean)));
stat.setMedian(median);
stat.setNinetyPercentMax(ninetyPercentMax);
stat.setNinetyNinePercentMax(ninetyNinePercentMax);
stat.setHundredMax(hunredMax);
stat.setInstanceId(instanceInfo.getId());
return stat;
} catch (Exception e) {
logger.error(e.getMessage(), e);
return null;
}
}
/**
* 1.获取最小的id
* 2.获取date的id
* 3.按照id批量删除
*/
@Override
public int deleteBeforeCollectTime(long collectTime) {
long startTime = System.currentTimeMillis();
int deleteCount = 0;
try {
int batchSize = 10000;
long minId = appClientCostTimeStatDao.getTableMinimumId();
long maxId = appClientCostTimeStatDao.getMinimumIdByCollectTime(collectTime);
if (minId > maxId) {
return deleteCount;
}
long startId = minId;
long endId = startId + batchSize;
while (startId < maxId) {
if (endId > maxId) {
endId = maxId;
}
deleteCount += appClientCostTimeStatDao.deleteByIds(startId, endId);
startId += batchSize;
endId += batchSize;
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
logger.warn("batch delete before collectTime {} cost time is {} ms", collectTime, (System.currentTimeMillis() - startTime));
return deleteCount;
}
public void setAppClientCostTimeStatDao(AppClientCostTimeStatDao appClientCostTimeStatDao) {
this.appClientCostTimeStatDao = appClientCostTimeStatDao;
}
public void setClientReportInstanceService(ClientReportInstanceService clientReportInstanceService) {
this.clientReportInstanceService = clientReportInstanceService;
}
public void setAppClientCostTimeTotalStatDao(AppClientCostTimeTotalStatDao appClientCostTimeTotalStatDao) {
this.appClientCostTimeTotalStatDao = appClientCostTimeTotalStatDao;
}
public void setAppInstanceClientRelationService(AppInstanceClientRelationService appInstanceClientRelationService) {
this.appInstanceClientRelationService = appInstanceClientRelationService;
}
}