package com.sohu.cache.stats.app.impl; import java.util.List; import java.util.Map; 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 redis.clients.jedis.Jedis; import redis.clients.jedis.Protocol; import com.sohu.cache.constant.ImportAppResult; import com.sohu.cache.constant.InstanceStatusEnum; import com.sohu.cache.dao.InstanceDao; import com.sohu.cache.dao.InstanceStatsDao; import com.sohu.cache.entity.AppDesc; import com.sohu.cache.entity.InstanceInfo; import com.sohu.cache.entity.InstanceStats; import com.sohu.cache.entity.MachineInfo; import com.sohu.cache.machine.MachineCenter; import com.sohu.cache.redis.RedisCenter; import com.sohu.cache.stats.app.ImportAppCenter; import com.sohu.cache.util.ConstUtils; import com.sohu.cache.util.IdempotentConfirmer; import com.sohu.cache.web.service.AppService; /** * 导入应用 * * @author leifu * @Date 2016-4-16 * @Time 下午3:42:49 */ public class ImportAppCenterImpl implements ImportAppCenter { private Logger logger = LoggerFactory.getLogger(ImportAppCenterImpl.class); private AppService appService; private RedisCenter redisCenter; private MachineCenter machineCenter; private InstanceDao instanceDao; private InstanceStatsDao instanceStatsDao; @Override public ImportAppResult check(AppDesc appDesc, String appInstanceInfo) { // 1.检查是否应用信息为空 if (appDesc == null) { return ImportAppResult.fail("应用信息为空"); } // 2.检查应用名是否重复 String appName = appDesc.getName(); AppDesc existAppDesc = appService.getAppByName(appName); if (existAppDesc != null) { return ImportAppResult.fail(appName + ", 应用名重复"); } // 3.实例信息是否为空 if (StringUtils.isBlank(appInstanceInfo)) { return ImportAppResult.fail("实例详情为空"); } String[] appInstanceDetails = appInstanceInfo.split("\n"); // 4.检查实例信息格式是否正确 for (String appInstance : appInstanceDetails) { if (StringUtils.isBlank(appInstance)) { return ImportAppResult.fail("应用实例信息有空行"); } String[] instanceItems = appInstance.split(":"); if (instanceItems.length != 3) { return ImportAppResult.fail("应用实例信息" + appInstance + "格式错误,必须有2个冒号"); } String ip = instanceItems[0]; // 4.1.检查ip对应的机器是否存在 try { MachineInfo machineInfo = machineCenter.getMachineInfoByIp(ip); if (machineInfo == null) { return ImportAppResult.fail(appInstance + "中的ip不存在"); } else if (machineInfo.isOffline()) { return ImportAppResult.fail(appInstance + "中的ip已经被删除"); } } catch (Exception e) { return ImportAppResult.fail(appInstance + "中的ip不存在"); } // 4.2.检查端口是否为整数 String portStr = instanceItems[1]; boolean portIsDigit = NumberUtils.isDigits(portStr); if (!portIsDigit) { return ImportAppResult.fail(appInstance + "中的port不是整数"); } int port = NumberUtils.toInt(portStr); // 4.3.检查ip:port是否已经在instance_info表和instance_statistics中 int count = instanceDao.getCountByIpAndPort(ip, port); if (count > 0) { return ImportAppResult.fail(appInstance + "中ip:port已经在instance_info存在"); } InstanceStats instanceStats = instanceStatsDao.getInstanceStatsByHost(ip, port); if (instanceStats != null) { return ImportAppResult.fail(appInstance + "中ip:port已经在instance_statistics存在"); } // 4.4.检查Redis实例是否存活 String memoryOrMasterName = instanceItems[2]; int memoryOrMasterNameInt = NumberUtils.toInt(memoryOrMasterName); boolean isRun; if (memoryOrMasterNameInt > 0) { isRun = redisCenter.isRun(ip, port, appDesc.getPassword()); } else { isRun = redisCenter.isRun(ip, port); } if (!isRun) { return ImportAppResult.fail(appInstance + "中的节点不是存活的"); } // 4.5.检查内存是否为整数 boolean isSentinelNode = memoryOrMasterNameInt <= 0; if (isSentinelNode) { // 4.5.1 sentinel节点masterName判断 if (StringUtils.isEmpty(memoryOrMasterName)) { return ImportAppResult.fail(appInstance + "中的sentinel节点master为空"); } // 判断masterName String masterName = getSentinelMasterName(ip, port); if (StringUtils.isEmpty(masterName) || !memoryOrMasterName.equals(masterName)) { return ImportAppResult.fail(ip + ":" + port + ", masterName:" + masterName + "与所填" + memoryOrMasterName + "不一致"); } } else { // 4.5.2 内存必须是整数 boolean maxMemoryIsDigit = NumberUtils.isDigits(memoryOrMasterName); if (!maxMemoryIsDigit) { return ImportAppResult.fail(appInstance + "中的maxmemory不是整数"); } } } // 5. 节点之间关系是否正确,这个比较麻烦,还是依赖于用户填写的正确性。 return ImportAppResult.success(); } @Override public boolean importAppAndInstance(AppDesc appDesc, String appInstanceInfo) { boolean isSuccess = true; try { // 1.1 保存应用信息 appService.save(appDesc); long appId = appDesc.getAppId(); // 1.2 更新appKey appService.updateAppKey(appId); int type = appDesc.getType(); // 2.保存应用和用户的关系 appService.saveAppToUser(appId, appDesc.getUserId()); // 3.保存实例信息并开启统计 String[] appInstanceDetails = appInstanceInfo.split("\n"); // 4.检查实例信息格式是否正确 for (String appInstance : appInstanceDetails) { String[] instanceItems = appInstance.split(":"); String host = instanceItems[0]; int port = NumberUtils.toInt(instanceItems[1]); String memoryOrMasterName = instanceItems[2]; boolean isSentinelNode = NumberUtils.toInt(memoryOrMasterName) <= 0; if (isSentinelNode) { saveInstance(appId, host, port, 0, ConstUtils.CACHE_REDIS_SENTINEL, memoryOrMasterName); } else { if (ConstUtils.CACHE_REDIS_STANDALONE == type || ConstUtils.CACHE_REDIS_SENTINEL == type) { saveInstance(appId, host, port, NumberUtils.toInt(memoryOrMasterName), ConstUtils.CACHE_REDIS_STANDALONE, ""); } else if (ConstUtils.CACHE_TYPE_REDIS_CLUSTER == type) { saveInstance(appId, host, port, NumberUtils.toInt(memoryOrMasterName), ConstUtils.CACHE_TYPE_REDIS_CLUSTER, ""); } //deploy quartz redisCenter.deployRedisCollection(appId, host, port); redisCenter.deployRedisSlowLogCollection(appId, host, port); } } } catch (Exception e) { logger.error(e.getMessage(), e); isSuccess = false; } return isSuccess; } /** * 获取sentinel的masterName * @param ip * @param port * @return */ private String getSentinelMasterName(final String ip, final int port) { final StringBuilder masterName = new StringBuilder(); new IdempotentConfirmer() { private int timeOutFactor = 1; @Override public boolean execute() { Jedis jedis = null; try { // 预留 String password = null; jedis = redisCenter.getJedis(ip, port, password); jedis.getClient().setConnectionTimeout(Protocol.DEFAULT_TIMEOUT * (timeOutFactor++)); jedis.getClient().setSoTimeout(Protocol.DEFAULT_TIMEOUT * (timeOutFactor++)); List<Map<String, String>> mapList = jedis.sentinelMasters(); String targetKey = "name"; for (Map<String, String> map : mapList) { if (map.containsKey(targetKey)) { masterName.append(MapUtils.getString(map, targetKey, "")); } } return true; } catch (Exception e) { logger.warn("{}:{} error message is {} ", ip, port, e.getMessage()); return false; } finally { if (jedis != null) { jedis.close(); } } } }.run(); return masterName.toString(); } /** * 保存实例信息 * @param appId * @param host * @param port * @param maxMemory * @param type * @param cmd * @return */ private InstanceInfo saveInstance(long appId, String host, int port, int maxMemory, int type, String cmd) { InstanceInfo instanceInfo = new InstanceInfo(); instanceInfo.setAppId(appId); MachineInfo machineInfo = machineCenter.getMachineInfoByIp(host); instanceInfo.setHostId(machineInfo.getId()); instanceInfo.setConn(0); instanceInfo.setMem(maxMemory); instanceInfo.setStatus(InstanceStatusEnum.GOOD_STATUS.getStatus()); instanceInfo.setPort(port); instanceInfo.setType(type); instanceInfo.setCmd(cmd); instanceInfo.setIp(host); instanceDao.saveInstance(instanceInfo); return instanceInfo; } public void setAppService(AppService appService) { this.appService = appService; } public void setRedisCenter(RedisCenter redisCenter) { this.redisCenter = redisCenter; } public void setMachineCenter(MachineCenter machineCenter) { this.machineCenter = machineCenter; } public void setInstanceDao(InstanceDao instanceDao) { this.instanceDao = instanceDao; } public void setInstanceStatsDao(InstanceStatsDao instanceStatsDao) { this.instanceStatsDao = instanceStatsDao; } }