package com.sohu.cache.stats.app.impl;
import com.sohu.cache.constant.AppTopology;
import com.sohu.cache.constant.TimeDimensionalityEnum;
import com.sohu.cache.dao.AppDao;
import com.sohu.cache.dao.AppStatsDao;
import com.sohu.cache.dao.InstanceDao;
import com.sohu.cache.dao.InstanceStatsDao;
import com.sohu.cache.entity.*;
import com.sohu.cache.redis.RedisCenter;
import com.sohu.cache.stats.app.AppStatsCenter;
import com.sohu.cache.util.ConstUtils;
import com.sohu.cache.util.TypeUtil;
import com.sohu.cache.web.vo.AppDetailVO;
import com.sohu.cache.web.service.UserService;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;
import java.text.DecimalFormat;
import java.util.*;
/**
* 基于app的统计信息的接口:包括app详情、app配置以及基于app的统计
* @author leifu
* @Date 2015年3月2日
* @Time 下午1:50:09
*/
public class AppStatsCenterImpl implements AppStatsCenter {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private AppDao appDao;
private InstanceDao instanceDao;
private AppStatsDao appStatsDao;
private InstanceStatsDao instanceStatsDao;
private RedisCenter redisCenter;
private UserService userService;
private final static String COLLECT_DATE_FORMAT = "yyyyMMddHHmm";
@Override
public List<AppStats> getAppStatsListByMinuteTime(long appId, long beginTime, long endTime) {
Assert.isTrue(appId > 0);
Assert.isTrue(beginTime > 0 && endTime > 0);
List<AppStats> appStatsList = null;
try {
appStatsList = appStatsDao.getAppStatsList(appId, new TimeDimensionality(beginTime, endTime, COLLECT_DATE_FORMAT));
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
return appStatsList;
}
/**
* 通过时间区间查询app的分钟统计数据
*
* @param appId
* @param beginTime 时间,格式:yyyyMMddHHmm
* @param endTime 时间,格式:yyyyMMddHHmm
* @return
*/
@Override
public List<AppStats> getAppStatsList(final long appId, long beginTime, long endTime, TimeDimensionalityEnum timeDimensionalityEnum) {
Assert.isTrue(appId > 0);
Assert.isTrue(beginTime > 0 && endTime > 0);
List<AppStats> appStatsList = null;
try {
if (TimeDimensionalityEnum.MINUTE.equals(timeDimensionalityEnum)) {
appStatsList = appStatsDao.getAppStatsByMinute(appId, beginTime, endTime);
} else if(TimeDimensionalityEnum.HOUR.equals(timeDimensionalityEnum)) {
appStatsList = appStatsDao.getAppStatsByHour(appId, beginTime, endTime);
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
return appStatsList;
}
@Override
public List<AppCommandStats> getTop5AppCommandStatsList(final long appId, long begin, long end) {
Assert.isTrue(appId > 0);
Assert.isTrue(begin > 0L);
Assert.isTrue(end > 0L);
List<AppCommandStats> topAppCmdList = null;
try {
topAppCmdList = appStatsDao.getTopAppCommandGroupSum(appId, new TimeDimensionality(begin, end, COLLECT_DATE_FORMAT), 5);
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
return topAppCmdList;
}
@Override
public List<AppCommandStats> getTopLimitAppCommandStatsList(long appId, long begin, long end, int limit) {
Assert.isTrue(appId > 0);
Assert.isTrue(begin > 0L);
Assert.isTrue(end > 0L);
List<AppCommandStats> topAppCmdList = null;
try {
topAppCmdList = appStatsDao.getTopAppCommandStatsList(appId, new TimeDimensionality(begin, end, COLLECT_DATE_FORMAT), limit);
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
return topAppCmdList;
}
/**
* 查询应用的配置和节点信息
*
* @param appId
* @return
*/
@Override
public Map<AppTopology, Object> queryAppTopology(final long appId) {
Assert.isTrue(appId > 0);
Map<AppTopology, Object> appTopologyMap = new HashMap<AppTopology, Object>();
AppDesc appDesc = null;
double totalMemory = 0.0;
Set<Long> machineSet = new HashSet<Long>();
int masterCount = 0;
int slaveCount = 0;
List<InstanceInfo> instanceInfoList = null;
try {
appDesc = appDao.getAppDescById(appId);
instanceInfoList = instanceDao.getInstListByAppId(appId);
if (appDesc == null || instanceInfoList == null || instanceInfoList.isEmpty()) {
logger.error("get app and it's instances error, appId = {}", appId);
return null;
}
if (appDesc.getType() == ConstUtils.CACHE_TYPE_REDIS_CLUSTER) {
for (InstanceInfo instance : instanceInfoList) {
machineSet.add(instance.getHostId());
totalMemory += instance.getMem();
Boolean isMaster = redisCenter.isMaster(appId, instance.getIp(), instance.getPort());
if (isMaster == null) {
continue;
}
if (isMaster) {
masterCount++;
} else {
slaveCount++;
}
}
}
appTopologyMap.put(AppTopology.TOTAL_MEMORY, totalMemory / ConstUtils._1024);
appTopologyMap.put(AppTopology.MACHINE_COUNT, machineSet.size());
appTopologyMap.put(AppTopology.MASTER_COUNT, masterCount);
appTopologyMap.put(AppTopology.SLAVE_COUNT, slaveCount);
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
return appTopologyMap;
}
/**
* 查询应用指定时间段,指定命令名的结果集合
*
* @param appId 应用id
* @param beginTime 时间,格式:yyyyMMddHHmm
* @param endTime 时间,格式:yyyyMMddHHmm
* @param commandName 命令名
* @return
*/
@Override
public List<AppCommandStats> getCommandStatsList(long appId, long beginTime, long endTime, String commandName) {
return appStatsDao.getAppCommandStatsList(appId, commandName, new TimeDimensionality(beginTime, endTime, COLLECT_DATE_FORMAT));
}
/**
* 查询应用指定时间段,指定命令名的结果集合
*
* @param appId 应用id
* @param beginTime 时间,格式:yyyyMMddHHmm
* @param endTime 时间,格式:yyyyMMddHHmm
* @return
*/
@Override
public List<AppCommandStats> getCommandStatsList(long appId, long beginTime, long endTime) {
return appStatsDao.getAppAllCommandStatsList(appId, new TimeDimensionality(beginTime, endTime, COLLECT_DATE_FORMAT));
}
@Override
public List<AppCommandStats> getCommandStatsListV2(long appId, long beginTime, long endTime, TimeDimensionalityEnum timeDimensionalityEnum, String commandName) {
if (TimeDimensionalityEnum.MINUTE.equals(timeDimensionalityEnum)) {
return appStatsDao.getAppCommandStatsListByMinuteWithCommand(appId, beginTime, endTime, commandName);
} else if(TimeDimensionalityEnum.HOUR.equals(timeDimensionalityEnum)) {
return appStatsDao.getAppCommandStatsListByHourWithCommand(appId, beginTime, endTime, commandName);
}
return Collections.emptyList();
}
@Override
public List<AppCommandStats> getCommandStatsListV2(long appId, long beginTime, long endTime, TimeDimensionalityEnum timeDimensionalityEnum) {
if (TimeDimensionalityEnum.MINUTE.equals(timeDimensionalityEnum)) {
return appStatsDao.getAppAllCommandStatsListByMinute(appId, beginTime, endTime);
} else if(TimeDimensionalityEnum.HOUR.equals(timeDimensionalityEnum)) {
return appStatsDao.getAppAllCommandStatsListByHour(appId, beginTime, endTime);
}
return Collections.emptyList();
}
/**
* 查询应用指定命令的峰值
*
* @param appId 应用id
* @param beginTime 时间,格式:yyyyMMddHHmm
* @param endTime 时间,格式:yyyyMMddHHmm
* @param commandName 命令名
* @return
*/
@Override
public AppCommandStats getCommandClimax(long appId, Long beginTime, Long endTime, String commandName) {
TimeDimensionality td = new TimeDimensionality(beginTime, endTime, COLLECT_DATE_FORMAT);
AppCommandStats appCommandStats = appStatsDao.getCommandClimaxCount(appId, commandName, td);
if (appCommandStats == null) {
return null;
}
appCommandStats.setCommandName(commandName);
AppCommandStats appCommandStatsTemp = appStatsDao.getCommandClimaxCreateTime(appId, commandName, appCommandStats.getCommandCount(), td);
if (appCommandStatsTemp != null) {
appCommandStats.setCreateTime(appCommandStatsTemp.getCreateTime());
}
return appCommandStats;
}
/**
* 获取应用命令调用次数分布
*
* @param appId
* @param beginTime
* @param endTime
* @return
*/
@Override
public List<AppCommandGroup> getAppCommandGroup(long appId, Long beginTime, Long endTime) {
return appStatsDao.getAppCommandGroup(appId, new TimeDimensionality(beginTime, endTime, COLLECT_DATE_FORMAT));
}
/**
* 获取应用详细信息
*/
@Override
public AppDetailVO getAppDetail(long appId) {
AppDesc appDesc = appDao.getAppDescById(appId);
if (appDesc == null) {
return null;
}
AppDetailVO resultVO = new AppDetailVO();
resultVO.setAppDesc(appDesc);
Set<String> machines = new HashSet<String>();
List<InstanceInfo> instanceList = instanceDao.getInstListByAppId(appId);
if (instanceList == null || instanceList.isEmpty()) {
return resultVO;
}
long hits = 0L;
long miss = 0L;
long allUsedMemory = 0L;
long allMaxMemory = 0L;
List<InstanceStats> instanceStatsList = instanceStatsDao.getInstanceStatsByAppId(appId);
if(instanceStatsList != null && instanceStatsList.size() > 0){
Map<Long, InstanceStats> instanceStatMap = new HashMap<Long, InstanceStats>();
for (InstanceStats stats : instanceStatsList) {
instanceStatMap.put(stats.getInstId(), stats);
}
for (InstanceInfo instanceInfo : instanceList) {
if (instanceInfo.isOffline()) {
continue;
}
machines.add(instanceInfo.getIp());
InstanceStats instanceStats = instanceStatMap.get(Long.valueOf(instanceInfo.getId()));
if (instanceStats == null) {
continue;
}
boolean isMaster = isMaster(instanceStats);
long usedMemory = instanceStats.getUsedMemory();
long usedMemoryMB = usedMemory / 1024 / 1024;
allUsedMemory += usedMemory;
allMaxMemory += instanceStats.getMaxMemory();
hits += instanceStats.getHits();
miss += instanceStats.getMisses();
if (isMaster) {
resultVO.setMem(resultVO.getMem() + instanceInfo.getMem());
resultVO.setCurrentMem(resultVO.getCurrentMem() + usedMemoryMB);
resultVO.setCurrentObjNum(resultVO.getCurrentObjNum() + instanceStats.getCurrItems());
resultVO.setMasterNum(resultVO.getMasterNum() + 1);
//按instanceStats计算conn
resultVO.setConn(resultVO.getConn() + instanceStats.getCurrConnections());
} else {
resultVO.setSlaveNum(resultVO.getSlaveNum() + 1);
}
}
}
List<AppUser> userList = userService.getByAppId(appId);
if (userList != null && userList.size() > 0) {
resultVO.setAppUsers(userList);
}
resultVO.setMachineNum(machines.size());
if (allMaxMemory == 0L) {
resultVO.setMemUsePercent(0.0D);
} else {
double percent = 100 * (double) allUsedMemory / (allMaxMemory);
DecimalFormat df = new DecimalFormat("##.##");
resultVO.setMemUsePercent(Double.parseDouble(df.format(percent)));
}
if (miss == 0L) {
if (hits > 0) {
resultVO.setHitPercent(100.0D);
} else {
resultVO.setHitPercent(0.0D);
}
} else {
double percent = 100 * (double) hits / (hits + miss);
DecimalFormat df = new DecimalFormat("##.##");
resultVO.setHitPercent(Double.parseDouble(df.format(percent)));
}
return resultVO;
}
private boolean isMaster(InstanceStats instanceStats) {
return instanceStats.getRole() == 1 ? true : false;
}
@Override
public String executeCommand(long appId, String command) {
if (StringUtils.isBlank(command)) {
return "命令不能为空";
}
AppDesc appDesc = appDao.getAppDescById(appId);
if (appDesc == null) {
return "app not found";
}
if (TypeUtil.isRedisType(appDesc.getType())) {
return redisCenter.executeCommand(appDesc, command);
}
return "not support app";
}
@Override
public Map<String, Long> getInstanceSlowLogCountMapByAppId(Long appId, Date startDate, Date endDate) {
AppDesc appDesc = appDao.getAppDescById(appId);
if (appDesc == null) {
return Collections.emptyMap();
}
if (TypeUtil.isRedisType(appDesc.getType())) {
return redisCenter.getInstanceSlowLogCountMapByAppId(appId, startDate, endDate);
}
return Collections.emptyMap();
}
@Override
public List<InstanceSlowLog> getInstanceSlowLogByAppId(long appId, Date startDate, Date endDate) {
AppDesc appDesc = appDao.getAppDescById(appId);
if (appDesc == null) {
return Collections.emptyList();
}
if (TypeUtil.isRedisType(appDesc.getType())) {
return redisCenter.getInstanceSlowLogByAppId(appId, startDate, endDate);
}
return Collections.emptyList();
}
public void setAppDao(AppDao appDao) {
this.appDao = appDao;
}
public void setAppStatsDao(AppStatsDao appStatsDao) {
this.appStatsDao = appStatsDao;
}
public void setInstanceDao(InstanceDao instanceDao) {
this.instanceDao = instanceDao;
}
public void setRedisCenter(RedisCenter redisCenter) {
this.redisCenter = redisCenter;
}
public void setInstanceStatsDao(InstanceStatsDao instanceStatsDao) {
this.instanceStatsDao = instanceStatsDao;
}
public void setUserService(UserService userService) {
this.userService = userService;
}
}