package com.sohu.cache.client.heartbeat; import com.sohu.cache.client.service.ClientVersionService; import com.sohu.cache.constant.ClientStatusEnum; import com.sohu.cache.dao.AppDao; import com.sohu.cache.dao.InstanceDao; import com.sohu.cache.entity.AppDesc; import com.sohu.cache.entity.InstanceInfo; import com.sohu.cache.util.ConstUtils; import com.sohu.cache.util.ObjectConvert; import com.sohu.cache.web.util.IpUtil; import com.google.common.collect.Lists; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import java.util.ArrayList; import java.util.List; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; /** * redis 客户端连接类 */ @Controller @RequestMapping(value = "/cache/client") public class RedisClientController { private final Logger logger = LoggerFactory.getLogger(RedisClientController.class); @Resource private AppDao appDao; @Resource private InstanceDao instanceDao; @Resource(name = "clientVersionService") private ClientVersionService clientVersionService; /** * 通过appId返回RedisCluster实例信息 * * @param appId */ @RequestMapping(value = "/redis/cluster/{appId}.json") public void getClusterByAppIdAndKey(HttpServletRequest request, @PathVariable long appId, Model model) { if (!handleRedisApp(appId, request, model, ConstUtils.CACHE_TYPE_REDIS_CLUSTER, false)) { return; } getRedisClusterInfo(request, appId, model); } /** * 通过appId返回RedisCluster实例信息(要求有appkey) * * @param appId */ @RequestMapping(value = "/redis/cluster/safe/{appId}.json") public void getClusterAppById(HttpServletRequest request, @PathVariable long appId, Model model) { if (!handleRedisApp(appId, request, model, ConstUtils.CACHE_TYPE_REDIS_CLUSTER, true)) { return; } getRedisClusterInfo(request, appId, model); } private void getRedisClusterInfo(HttpServletRequest request, long appId, Model model) { String clientVersion = request.getParameter("clientVersion"); if (!checkClientVersion(appId, clientVersion, model)) { return; } List<InstanceInfo> instanceList = instanceDao.getInstListByAppId(appId); if (instanceList == null || instanceList.isEmpty()) { model.addAttribute("status", ClientStatusEnum.ERROR.getStatus()); model.addAttribute("message", "ERROR: appId:" + appId + "实例集合为空 "); return; } String shardsInfo = ObjectConvert.assembleInstance(instanceList); if (StringUtils.isBlank(shardsInfo)) { model.addAttribute("status", ClientStatusEnum.ERROR.getStatus()); model.addAttribute("message", "ERROR: appId:" + appId + "shardsInfo为空 "); return; } // AppDesc appDesc = appDao.getAppDescById(appId); int shardNum = shardsInfo.split(" ").length; model.addAttribute("appId", appId); model.addAttribute("shardNum", shardNum); model.addAttribute("shardInfo", shardsInfo); // model.addAttribute("password", appDesc.getPassword()); //保存版本信息 try { clientVersionService.saveOrUpdateClientVersion(appId, IpUtil.getIpAddr(request), clientVersion); } catch (Exception e) { logger.error("redisCluster heart error:" + e.getMessage(), e); } } /** * 通过appId返回RedisSentinel实例信息 * * @param appId */ @RequestMapping(value = "/redis/sentinel/{appId}.json") public void getSentinelAppById(HttpServletRequest request, @PathVariable long appId, Model model) { if (!handleRedisApp(appId, request, model, ConstUtils.CACHE_REDIS_SENTINEL, false)) { return; } getRedisSentinelInfo(request, appId, model); } /** * 通过appId返回RedisSentinel实例信息(要求有appkey) * * @param appId */ @RequestMapping(value = "/redis/sentinel/safe/{appId}.json") public void getSentinelByAppIdAndKey(HttpServletRequest request, @PathVariable long appId, Model model) { if (!handleRedisApp(appId, request, model, ConstUtils.CACHE_REDIS_SENTINEL, true)) { return; } getRedisSentinelInfo(request, appId, model); } private void getRedisSentinelInfo(HttpServletRequest request, long appId, Model model) { String clientVersion = request.getParameter("clientVersion"); if (!checkClientVersion(appId, clientVersion, model)) { return; } List<InstanceInfo> instanceList = instanceDao.getInstListByAppId(appId); if (instanceList == null || instanceList.isEmpty()) { model.addAttribute("status", ClientStatusEnum.ERROR.getStatus()); model.addAttribute("message", "appId: " + appId + " 实例集合为空 "); return; } String masterName = null; List<String> sentinelList = new ArrayList<String>(); for (InstanceInfo instance : instanceList) { if (instance.isOffline()) { continue; } if (instance.getType() == ConstUtils.CACHE_REDIS_SENTINEL && masterName == null && StringUtils.isNotBlank(instance.getCmd())) { masterName = instance.getCmd(); } if (instance.getType() == ConstUtils.CACHE_REDIS_SENTINEL) { sentinelList.add(instance.getIp() + ":" + instance.getPort()); } } String sentinels = StringUtils.join(sentinelList, " "); // AppDesc appDesc = appDao.getAppDescById(appId); model.addAttribute("sentinels", sentinels); model.addAttribute("masterName", masterName); model.addAttribute("appId", appId); model.addAttribute("status", ClientStatusEnum.GOOD.getStatus()); // model.addAttribute("password", appDesc.getPassword()); //保存版本信息 try { clientVersionService.saveOrUpdateClientVersion(appId, IpUtil.getIpAddr(request), clientVersion); } catch (Exception e) { logger.error("redisSentinel heart error:" + e.getMessage(), e); } } /** * 通过appId返回RedisStandalone实例信息 * * @param appId */ @RequestMapping(value = "/redis/standalone/{appId}.json") public void getStandaloneAppById(HttpServletRequest request, @PathVariable long appId, Model model) { if (!handleRedisApp(appId, request, model, ConstUtils.CACHE_REDIS_STANDALONE, false)) { return; } getRedisStandaloneInfo(request, appId, model); } /** * 通过appId返回RedisStandalone实例信息 * * @param appId */ @RequestMapping(value = "/redis/standalone/safe/{appId}.json") public void getStandaloneByAppIdAndKey(HttpServletRequest request, @PathVariable long appId, Model model) { if (!handleRedisApp(appId, request, model, ConstUtils.CACHE_REDIS_STANDALONE, true)) { return; } getRedisStandaloneInfo(request, appId, model); } private void getRedisStandaloneInfo(HttpServletRequest request, long appId, Model model) { String clientVersion = request.getParameter("clientVersion"); if (!checkClientVersion(appId, clientVersion, model)) { return; } List<InstanceInfo> instanceList = instanceDao.getInstListByAppId(appId); String standalone = null; for (InstanceInfo instanceInfo : instanceList) { if (instanceInfo.isOffline()) { continue; } standalone = instanceInfo.getIp() + ":" + instanceInfo.getPort(); } // AppDesc appDesc = appDao.getAppDescById(appId); model.addAttribute("standalone", standalone); model.addAttribute("status", ClientStatusEnum.GOOD.getStatus()); // model.addAttribute("password", appDesc.getPassword()); //保存版本信息 try { clientVersionService.saveOrUpdateClientVersion(appId, IpUtil.getIpAddr(request), clientVersion); } catch (Exception e) { logger.error("redisStandalone heart error:" + e.getMessage(), e); } } /** * 检查客户端相关参数 * @param appId 应用id * @param request * @param model * @param type 应用类型 * @param isCheckAppKey 是否检测appKey * @return */ private boolean handleRedisApp(long appId, HttpServletRequest request, Model model, int type, boolean isCheckAppKey) { AppDesc appDesc = appDao.getAppDescById(appId); if (appDesc == null) { model.addAttribute("status", ClientStatusEnum.ERROR.getStatus()); model.addAttribute("message", String.format("appId:%s 不存在", appId)); return false; } else if (appDesc.getType() != type) { model.addAttribute("status", ClientStatusEnum.ERROR.getStatus()); model.addAttribute("message", String.format("appId:%s 类型不符,期望类型:%s,实际类型%s,请联系管理员!", appId, type, appDesc.getType())); return false; } else if (isCheckAppKey) { String appKey = request.getParameter("appKey"); if (StringUtils.isBlank(appKey)) { model.addAttribute("status", ClientStatusEnum.ERROR.getStatus()); model.addAttribute("message", String.format("appId=%s,appKey参数为空", appId)); return false; } if (!appKey.equals(appDesc.getAppKey())) { model.addAttribute("status", ClientStatusEnum.ERROR.getStatus()); model.addAttribute("message", String.format("appId=%s,appKey:%s错误,与服务端不匹配", appId, appKey)); return false; } } return true; } private boolean checkClientVersion(long appId, String clientVersion, Model model) { /** 检查客户端的版本 **/ List<String> goodVersions = Lists.newArrayList(ConstUtils.GOOD_CLIENT_VERSIONS.split(ConstUtils.COMMA)); List<String> warnVersions = Lists.newArrayList(ConstUtils.WARN_CLIENT_VERSIONS.split(ConstUtils.COMMA)); boolean versionOk = true; if (goodVersions.contains(clientVersion)) { model.addAttribute("status", ClientStatusEnum.GOOD.getStatus()); model.addAttribute("message", "appId:" + appId + " client is up to date, Cheers!"); } else if (warnVersions.contains(clientVersion)) { model.addAttribute("status", ClientStatusEnum.WARN.getStatus()); model.addAttribute("message", "WARN: client is NOT the newest, please update!"); } else { model.addAttribute("status", ClientStatusEnum.ERROR.getStatus()); model.addAttribute("message", "ERROR: client is TOO old or NOT recognized, please update NOW!"); versionOk = false; } return versionOk; } }