content: 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;
      }
  
  
  
  }
  