|
|
@@ -0,0 +1,452 @@
|
|
|
+package com.xunmei.deploy.service.impl;
|
|
|
+
|
|
|
+import com.alibaba.fastjson.JSON;
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
|
|
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|
|
+import com.github.zafarkhaja.semver.Version;
|
|
|
+import com.xunmei.deploy.dao.*;
|
|
|
+import com.xunmei.deploy.domain.FrontTask;
|
|
|
+import com.xunmei.deploy.domain.HostInfo;
|
|
|
+import com.xunmei.deploy.domain.HostZipInfo;
|
|
|
+import com.xunmei.deploy.domain.SysConf;
|
|
|
+import com.xunmei.deploy.service.AsyncService;
|
|
|
+import com.xunmei.deploy.service.BeringService;
|
|
|
+import com.xunmei.deploy.service.FrontTaskService;
|
|
|
+import com.xunmei.deploy.util.RedisPrefix;
|
|
|
+import com.xunmei.deploy.util.RedisTemplateUtil;
|
|
|
+import com.xunmei.deploy.util.UTCTimeUtils;
|
|
|
+import com.xunmei.deploy.vo.MachineInfo;
|
|
|
+import com.xunmei.deploy.vo.OrgVo;
|
|
|
+import com.xunmei.deploy.vo.TokenCache;
|
|
|
+import com.xunmei.deploy.vo.TokenVo;
|
|
|
+import com.xunmei.deploy.vo.heart.AppRunningInfo;
|
|
|
+import com.xunmei.deploy.vo.heart.HeartBeat;
|
|
|
+import com.xunmei.deploy.vo.heart.HeartResponse;
|
|
|
+import com.xunmei.deploy.vo.heart.HeartTimeVo;
|
|
|
+import org.apache.commons.lang3.StringUtils;
|
|
|
+import org.slf4j.Logger;
|
|
|
+import org.slf4j.LoggerFactory;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.beans.factory.annotation.Value;
|
|
|
+import org.springframework.scheduling.annotation.Scheduled;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+import org.springframework.transaction.annotation.Transactional;
|
|
|
+import org.springframework.util.DigestUtils;
|
|
|
+import javax.annotation.Resource;
|
|
|
+import java.rmi.ServerException;
|
|
|
+import java.util.Collections;
|
|
|
+import java.util.Iterator;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Set;
|
|
|
+
|
|
|
+@Service
|
|
|
+public class BeringServiceImpl extends ServiceImpl<HostInfoDao, HostInfo> implements BeringService {
|
|
|
+ private Logger logger = LoggerFactory.getLogger(getClass());
|
|
|
+ @Value("${apps.version}")
|
|
|
+ private String defaultVersion;
|
|
|
+ private static final long MAX_HEART_TIME = 5 * 60 * 1000;
|
|
|
+ @Resource
|
|
|
+ private RedisTemplateUtil redisTemplateUtil;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private SysConfDao sysConfDao;
|
|
|
+ @Autowired
|
|
|
+ private FrontTaskDao frontTaskDao;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private HostInfoDao hostInfoDao;
|
|
|
+ @Autowired
|
|
|
+ private HostZipInfoDao hostZipInfoDao;
|
|
|
+ @Autowired
|
|
|
+ private UpgradeBatchInfoDao upgradeBatchInfoDao;
|
|
|
+ @Autowired
|
|
|
+ private OrgVoDao orgVoDao;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private FrontTaskService frontTaskService;
|
|
|
+ @Autowired
|
|
|
+ private AsyncService asyncService;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取令牌
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public HostInfo generateAccessToken(TokenVo tokenVo) throws Exception {
|
|
|
+
|
|
|
+ //1.先判断请求参数是否合法
|
|
|
+ if(tokenVo == null){
|
|
|
+ logger.error("主机获取令牌失败:参数不能为空!");
|
|
|
+ throw new ServerException("主机获取令牌失败:参数不能为空!");
|
|
|
+ }
|
|
|
+
|
|
|
+ if(StringUtils.isEmpty(tokenVo.getClient_id())){
|
|
|
+ logger.error("主机获取令牌失败:主机ID不能为空!");
|
|
|
+ throw new ServerException("主机获取令牌失败:主机ID不能为空!");
|
|
|
+ }
|
|
|
+
|
|
|
+ MachineInfo machineInfo = tokenVo.getMachineInfo();
|
|
|
+ if(machineInfo == null){
|
|
|
+ logger.error("主机获取令牌失败:计算机信息不能为空!");
|
|
|
+ throw new ServerException("主机获取令牌失败:计算机信息不能为空!");
|
|
|
+ }
|
|
|
+
|
|
|
+ if(StringUtils.isEmpty(tokenVo.getGrant_type())){
|
|
|
+ logger.error("主机获取令牌失败:认证类型不能为空!");
|
|
|
+ throw new ServerException("主机获取令牌失败:认证类型不能为空!");
|
|
|
+ }
|
|
|
+ if(!tokenVo.getGrant_type().equals("client_credentials")){
|
|
|
+ logger.error("主机获取令牌失败:请求参数认证类型错误!");
|
|
|
+ throw new ServerException("主机获取令牌失败:请求参数认证类型错误!");
|
|
|
+ }
|
|
|
+
|
|
|
+ //验证秘钥是否合法
|
|
|
+ List<String> macs = machineInfo.getMacs();
|
|
|
+ if (null == macs || macs.size() <= 0){
|
|
|
+ logger.error("主机获取令牌失败:计算机macs信息不能为空!");
|
|
|
+ throw new ServerException("主机获取令牌失败:计算机macs信息不能为空!");
|
|
|
+ }
|
|
|
+
|
|
|
+ Collections.sort(macs);
|
|
|
+ String macss = StringUtils.join(macs.toArray(), "");
|
|
|
+
|
|
|
+ String str = macss + "zmoon";
|
|
|
+
|
|
|
+ //md5加密--->按照生成规则生成后的秘钥
|
|
|
+ String code = DigestUtils.md5DigestAsHex(str.getBytes());
|
|
|
+ if(!code.equals(tokenVo.getClient_secret())){
|
|
|
+ logger.error("主机获取令牌失败:主机秘钥认证失败!");
|
|
|
+ throw new ServerException("主机获取令牌失败:主机秘钥认证失败!");
|
|
|
+ }
|
|
|
+
|
|
|
+ //2.验证该秘钥是否在数据库中已经存在
|
|
|
+ TokenCache tokenCache = null;
|
|
|
+ String result = redisTemplateUtil.get(RedisPrefix.CACHE_TOKENS + ":" + tokenVo.getClient_secret());
|
|
|
+ if (StringUtils.isNotBlank(result)){
|
|
|
+ tokenCache = JSON.parseObject(result, TokenCache.class);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ HostInfo hostInfo = baseMapper.selectById(tokenVo.getClient_secret());
|
|
|
+ //秘钥验证成功且数据库无该秘钥,判断为数据库丢失数据,需要重新注册
|
|
|
+ if (hostInfo == null){
|
|
|
+ //判断是否允许重新注册
|
|
|
+ SysConf sysConf = sysConfDao.getByCode("register_again");
|
|
|
+ if ("1".equals(sysConf.getValue())){
|
|
|
+ logger.info("数据库注册数据丢失,程序允许主机重新注册!\n"+ JSON.toJSONString(machineInfo));
|
|
|
+ hostInfo = new HostInfo();
|
|
|
+ hostInfo.setId(code);
|
|
|
+ hostInfo.setHostName(machineInfo.getMachineName());
|
|
|
+ hostInfo.setHostIp(StringUtils.join(machineInfo.getIpAddresses().toArray(),","));
|
|
|
+ hostInfo.setHostSystem(machineInfo.getOsPlatform());
|
|
|
+ hostInfo.setHostFrame(machineInfo.getOsArchitecture());
|
|
|
+ hostInfo.setHostMac(StringUtils.join(macs.toArray(),","));
|
|
|
+ hostInfo.setHostOrg(null);
|
|
|
+ hostInfo.setHostStatus(1);
|
|
|
+ hostInfo.setIsPush(0);
|
|
|
+ baseMapper.insert(hostInfo);
|
|
|
+
|
|
|
+ //删除frontTask中数据
|
|
|
+ QueryWrapper<FrontTask> wrapper = new QueryWrapper<>();
|
|
|
+ wrapper.eq("host_id",code);
|
|
|
+ frontTaskDao.delete(wrapper);
|
|
|
+
|
|
|
+ //添加缓存信息
|
|
|
+ TokenCache tc = new TokenCache();
|
|
|
+ tc.setClientId(hostInfo.getId());
|
|
|
+ tc.setClientSecret(hostInfo.getId());
|
|
|
+ redisTemplateUtil.set(RedisPrefix.CACHE_TOKENS + ":" +hostInfo.getId(),tc,2 * 60 * 60);
|
|
|
+ }else {
|
|
|
+ logger.error("数据库注册数据丢失,程序不允许主机重新注册!\n"+ JSON.toJSONString(machineInfo));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ long time = System.currentTimeMillis();
|
|
|
+ //返回有效的token
|
|
|
+ //判断缓存中令牌是否存在
|
|
|
+ if(null != tokenCache && StringUtils.isNotEmpty(tokenCache.getAccessToken())){
|
|
|
+ //判断令牌的有效期是否过期
|
|
|
+ //验证该token是否在系统中存在
|
|
|
+ Long tokenCreateTime = tokenCache.getTokenDate();
|
|
|
+ int expiresIn = tokenCache.getExpiresIn();
|
|
|
+ //与当前时间判断 是否超过9授权时间,如果超过,则重新生成token
|
|
|
+ long timeDiff = (time - tokenCreateTime) / 1000;
|
|
|
+ //令牌时间过期
|
|
|
+ if(timeDiff > expiresIn){
|
|
|
+ //先删除之前tokentimes的缓存
|
|
|
+ redisTemplateUtil.del(RedisPrefix.CACHE_TOKEN_TIMES + ":" +tokenCache.getAccessToken());
|
|
|
+ //重新生成token 并更新数据库与缓存
|
|
|
+ String key = tokenCache.getClientSecret();
|
|
|
+ key = key + time;
|
|
|
+ String newMd5 = DigestUtils.md5DigestAsHex(key.getBytes());
|
|
|
+ tokenCache.setAccessToken(newMd5);
|
|
|
+ tokenCache.setExpiresIn(7200);
|
|
|
+ tokenCache.setTokenDate(time);
|
|
|
+ redisTemplateUtil.set(RedisPrefix.CACHE_TOKENS + ":" + tokenCache.getClientId(),tokenCache,2 * 60 * 60);
|
|
|
+ redisTemplateUtil.set(RedisPrefix.CACHE_TOKEN_TIMES + ":" + tokenCache.getAccessToken(),tokenCache,2 * 60 * 60);
|
|
|
+
|
|
|
+ hostInfo.setTokenCreateTime(time);
|
|
|
+ hostInfo.setExpiresIn(7200);
|
|
|
+ hostInfo.setAccessToken(newMd5);
|
|
|
+ hostInfo.setHostName(tokenVo.getMachineInfo().getMachineName());
|
|
|
+ logger.info("token不为空,时效超时,请求里面的ip 为:"+tokenVo.getMachineInfo().getIpAddresses().toString());
|
|
|
+ hostInfo.setHostIp(tokenVo.getMachineInfo().getIpAddresses().toString());
|
|
|
+ baseMapper.updateById(hostInfo);
|
|
|
+ }
|
|
|
+ }else{
|
|
|
+ //生成新的令牌 更新数据库与缓存
|
|
|
+ String t = tokenVo.getClient_secret()+time;
|
|
|
+ String md5Token = DigestUtils.md5DigestAsHex(t.getBytes());
|
|
|
+ hostInfo.setAccessToken(md5Token);
|
|
|
+ hostInfo.setExpiresIn(7200);
|
|
|
+ hostInfo.setScope("* identity");
|
|
|
+ hostInfo.setTokenType("Bearer");
|
|
|
+ hostInfo.setTokenCreateTime(System.currentTimeMillis());
|
|
|
+ logger.info("token为空,请求里面的ip 为:"+tokenVo.getMachineInfo().getIpAddresses().toString());
|
|
|
+ hostInfo.setHostName(tokenVo.getMachineInfo().getMachineName());
|
|
|
+ hostInfo.setHostIp(tokenVo.getMachineInfo().getIpAddresses().toString());
|
|
|
+ baseMapper.updateById(hostInfo);
|
|
|
+ //tokenTimes 缓存新增一条
|
|
|
+ TokenCache tc = new TokenCache();
|
|
|
+ tc.setClientId(hostInfo.getId());
|
|
|
+ tc.setClientSecret(hostInfo.getId());
|
|
|
+ tc.setAccessToken(hostInfo.getAccessToken());
|
|
|
+ tc.setExpiresIn(hostInfo.getExpiresIn());
|
|
|
+ tc.setTokenDate(hostInfo.getTokenCreateTime());
|
|
|
+
|
|
|
+ redisTemplateUtil.set(RedisPrefix.CACHE_TOKENS + ":" + tokenVo.getClient_id(),tc,2 * 60 * 60);
|
|
|
+ redisTemplateUtil.set(RedisPrefix.CACHE_TOKEN_TIMES + ":" + md5Token,tc,2 * 60 * 60);
|
|
|
+ }
|
|
|
+ return hostInfo;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public HeartResponse heartBeat(String authorization, HeartBeat heartBeat) throws Exception {
|
|
|
+
|
|
|
+ logger.info("心跳携带令牌:{}", authorization);
|
|
|
+ HeartResponse response = new HeartResponse();
|
|
|
+ //参数严重
|
|
|
+ String time = heartBeat.getTime();
|
|
|
+ if(StringUtils.isBlank(time)){
|
|
|
+ logger.error("白令海节点计算机的UTC时间不能为空!");
|
|
|
+ throw new RuntimeException("白令海节点计算机的UTC时间不能为空!");
|
|
|
+ }
|
|
|
+
|
|
|
+ String version = heartBeat.getVersion();
|
|
|
+ if(StringUtils.isBlank(version)){
|
|
|
+ logger.error("白令海自身版本号不能为空!");
|
|
|
+ throw new RuntimeException("白令海自身版本号不能为空!");
|
|
|
+ }
|
|
|
+
|
|
|
+ Boolean allowPrerelsease = heartBeat.getAllowPrerelease();
|
|
|
+ if(allowPrerelsease == null){
|
|
|
+ logger.error("白令海允许升级到预发布版本不能为空!");
|
|
|
+ throw new RuntimeException("白令海允许升级到预发布版本不能为空!");
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ String description = heartBeat.getDescription();
|
|
|
+ if(StringUtils.isBlank(description)){
|
|
|
+ logger.error("白令海节点描述不能为空!");
|
|
|
+ throw new RuntimeException("白令海节点描述不能为空!");
|
|
|
+ }
|
|
|
+
|
|
|
+ //根据token获取主机信息
|
|
|
+ TokenCache tokenCache = null;
|
|
|
+ String result = redisTemplateUtil.get(RedisPrefix.CACHE_TOKEN_TIMES + ":" + authorization);
|
|
|
+ if (StringUtils.isNotBlank(result)){
|
|
|
+ tokenCache = JSON.parseObject(result, TokenCache.class);
|
|
|
+ }
|
|
|
+
|
|
|
+ if(tokenCache == null){
|
|
|
+ logger.error(authorization + "令牌不合法!");
|
|
|
+ throw new RuntimeException(authorization + "令牌不合法!");
|
|
|
+ }
|
|
|
+
|
|
|
+ //获取秘钥
|
|
|
+ String clientId = tokenCache.getClientId();
|
|
|
+
|
|
|
+ HostInfo hostInfo = hostInfoDao.selectById(clientId);
|
|
|
+ if (null == hostInfo){
|
|
|
+ logger.error("主机{}在数据库不存在", clientId);
|
|
|
+ }
|
|
|
+ logger.info("主机{},心跳获取参数:{}", hostInfo.getId(), JSON.toJSONString(heartBeat));
|
|
|
+ //查询hostZipInfo信息是否存在,如果不存在,按照默认新建
|
|
|
+ QueryWrapper<HostZipInfo> qw = new QueryWrapper<>();
|
|
|
+ qw.eq("host_id",hostInfo.getId());
|
|
|
+
|
|
|
+ HostZipInfo hostZipInfo = hostZipInfoDao.selectOne(qw);
|
|
|
+ if(hostZipInfo == null){
|
|
|
+ String orgId = upgradeBatchInfoDao.getOrgId(hostInfo.getId());
|
|
|
+
|
|
|
+ if(StringUtils.isNotEmpty(orgId) || "YCAF".equals(hostInfo.getHostName())){
|
|
|
+ OrgVo org = orgVoDao.getByOrgId(orgId);
|
|
|
+ if(org == null && !"YCAF".equals(hostInfo.getHostName())){
|
|
|
+ throw new RuntimeException("心跳异常:组织机构不存在,该机器组织机构id"+orgId);
|
|
|
+ }
|
|
|
+ hostZipInfo = new HostZipInfo();
|
|
|
+ hostZipInfo.setStatus(1);
|
|
|
+ hostZipInfo.setZipVersion(defaultVersion);
|
|
|
+ hostZipInfo.setTargetVersion(defaultVersion);
|
|
|
+ hostZipInfo.setHostId(hostInfo.getId());
|
|
|
+
|
|
|
+ hostZipInfo.setHostIp(hostInfo.getHostIp());
|
|
|
+ if (org != null){
|
|
|
+ hostZipInfo.setOrgId(org.getOrgId()+"");
|
|
|
+ hostZipInfo.setOrgName(org.getOrgName());
|
|
|
+ }else {
|
|
|
+ hostZipInfo.setOrgId(null);
|
|
|
+ hostZipInfo.setOrgName(null);
|
|
|
+ }
|
|
|
+ hostZipInfo.setHostName(hostInfo.getHostName());
|
|
|
+
|
|
|
+
|
|
|
+ hostZipInfoDao.insert(hostZipInfo);
|
|
|
+ }
|
|
|
+ }else{
|
|
|
+ hostZipInfo.setHostName(hostInfo.getHostName());
|
|
|
+ hostZipInfo.setHostIp(hostInfo.getHostIp());
|
|
|
+ hostZipInfo.setHostId(hostInfo.getId());
|
|
|
+ String orgId = upgradeBatchInfoDao.getOrgId(hostInfo.getId());
|
|
|
+ if(StringUtils.isNotEmpty(orgId)){
|
|
|
+ OrgVo org = orgVoDao.getByOrgId(orgId);
|
|
|
+ if(org == null){
|
|
|
+ throw new RuntimeException("心跳异常:组织机构不存在,该机器组织机构id"+orgId);
|
|
|
+ }
|
|
|
+ hostZipInfo.setOrgId(orgId);
|
|
|
+ hostZipInfo.setOrgName(org.getOrgName());
|
|
|
+ }
|
|
|
+ hostZipInfoDao.updateById(hostZipInfo);
|
|
|
+ }
|
|
|
+
|
|
|
+ //获取到心跳信息,获取缓存中是否有心跳信息。
|
|
|
+ HeartTimeVo heartTimeVo = null;
|
|
|
+ String heartResult = redisTemplateUtil.get(RedisPrefix.CACHE_HEARTS + ":" + clientId);
|
|
|
+ if (org.apache.commons.lang3.StringUtils.isNotBlank(heartResult)){
|
|
|
+ heartTimeVo = JSON.parseObject(heartResult, HeartTimeVo.class);
|
|
|
+ }
|
|
|
+ if(heartTimeVo == null){
|
|
|
+ //缓存中不存在该token信息,缓存中添加心跳信息
|
|
|
+ heartTimeVo = new HeartTimeVo();
|
|
|
+
|
|
|
+ heartTimeVo.setClientId(tokenCache.getClientId());
|
|
|
+ heartTimeVo.setHeartTime(System.currentTimeMillis());
|
|
|
+ logger.info("第一次心跳向心跳缓存中添加信息:key:{},value:{}",clientId,JSON.toJSONString(heartTimeVo));
|
|
|
+ //缓存中添加
|
|
|
+ redisTemplateUtil.set(RedisPrefix.CACHE_HEARTS + ":" + clientId,heartTimeVo,2 * 60 * 60);
|
|
|
+ }else{
|
|
|
+ //存在该心跳,1.更新心态缓存
|
|
|
+ long now = System.currentTimeMillis();
|
|
|
+ heartTimeVo.setHeartTime(now);
|
|
|
+ if(hostInfo.getHostStatus() == 2){
|
|
|
+ //如果是离线状态,改为在线状态
|
|
|
+ hostInfo.setHostStatus(1);
|
|
|
+ hostInfoDao.updateById(hostInfo);
|
|
|
+ heartTimeVo.setHostStatus(1);
|
|
|
+ }
|
|
|
+ redisTemplateUtil.set(RedisPrefix.CACHE_HEARTS + ":" + clientId,heartTimeVo,2 * 60 * 60);
|
|
|
+ }
|
|
|
+
|
|
|
+ //调用是否生成任务清单判断,线程池开启线程
|
|
|
+ frontTaskService.validateAppInfoList(clientId,heartBeat.getVersion());
|
|
|
+ //获取缓存中数据,告知白代理需要来弄一波任务清单推送了哦
|
|
|
+ List<FrontTask> hasTask = null;
|
|
|
+ String frontTaskResult = redisTemplateUtil.get(RedisPrefix.CACHE_FRONT_TASKS + ":" + clientId);
|
|
|
+ if (StringUtils.isNotBlank(frontTaskResult)){
|
|
|
+ hasTask = JSON.parseArray(frontTaskResult,FrontTask.class);
|
|
|
+ }
|
|
|
+ response.setHasTask(false);
|
|
|
+ if(hasTask != null && hasTask.size() > 0 ){
|
|
|
+ logger.info("判断{}需要推送任务清单:{}",clientId,JSON.toJSONString(hasTask));
|
|
|
+ response.setHasTask(true);
|
|
|
+ }
|
|
|
+ response.setName("部署中心");
|
|
|
+ response.setVersion("1.0.0");
|
|
|
+ if(hostInfo.getCoreTimeStamp() == 0){
|
|
|
+ response.setDeployTimeStamp(heartBeat.getDeployTimeStamp());
|
|
|
+ }else{
|
|
|
+ response.setDeployTimeStamp(UTCTimeUtils.getLong2UtcStr(hostInfo.getCoreTimeStamp()));
|
|
|
+ }
|
|
|
+ response.setTime(UTCTimeUtils.getUTCTimeStr());
|
|
|
+ response.setAgentLastVersion(getNewlyAgentVersion(hostInfo));
|
|
|
+
|
|
|
+ List<AppRunningInfo> appRunningInfos = heartBeat.getApps();
|
|
|
+ if (null != appRunningInfos && appRunningInfos.size() > 0){
|
|
|
+ asyncService.asyncSaveOrUpdateBatch(appRunningInfos,hostInfo.getId());
|
|
|
+ }
|
|
|
+
|
|
|
+ logger.info("心跳返回结果:{}",JSON.toJSONString(response));
|
|
|
+ return response;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 定时任务消费心跳
|
|
|
+ */
|
|
|
+ public void scheduleHearts(){
|
|
|
+ String lockKey = RedisPrefix.HEART_LOCK_PREFIX;
|
|
|
+ boolean isLock = !redisTemplateUtil.hasKey(lockKey) && redisTemplateUtil.set(lockKey, lockKey, 10);
|
|
|
+ if(!isLock) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ Set<String> hallKeys = redisTemplateUtil.getKeys(RedisPrefix.CACHE_HEARTS + ":*");
|
|
|
+
|
|
|
+ Iterator<String> iterator = hallKeys.iterator();
|
|
|
+
|
|
|
+ while (iterator.hasNext()){
|
|
|
+ String key = iterator.next();
|
|
|
+ String heartResult = redisTemplateUtil.get(key);
|
|
|
+ HeartTimeVo hv = null;
|
|
|
+ if (org.apache.commons.lang3.StringUtils.isNotBlank(heartResult)){
|
|
|
+ hv = JSON.parseObject(heartResult, HeartTimeVo.class);
|
|
|
+ }
|
|
|
+ if (null == hv){
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ Long heartTime = hv.getHeartTime();
|
|
|
+
|
|
|
+ long newTime = System.currentTimeMillis();
|
|
|
+
|
|
|
+ long mid = newTime - heartTime;
|
|
|
+
|
|
|
+ //log.info("主机:{},30秒一次查看心跳时间,上一次心跳时间:{},当前时间:{},时间差:{}秒",hv.getClientId(),heartTime,newTime,mid/1000);
|
|
|
+
|
|
|
+ if(mid > MAX_HEART_TIME){
|
|
|
+ //时间已经超过最大心跳时间,我们认为主机已经离线了
|
|
|
+ String clientId = hv.getClientId();
|
|
|
+
|
|
|
+ HostInfo hostInfo = hostInfoDao.selectById(clientId);
|
|
|
+
|
|
|
+ hostInfo.setHostStatus(2);
|
|
|
+
|
|
|
+ hostInfoDao.updateById(hostInfo);
|
|
|
+
|
|
|
+ hv.setHostStatus(2);
|
|
|
+ //离线后删除心跳缓存
|
|
|
+ redisTemplateUtil.del(key);
|
|
|
+ logger.info("主机:{},上一次心跳时间:{},当前时间:{},时间差:{}秒,修改主机为离线",hv.getClientId(),heartTime,newTime,mid/1000);
|
|
|
+ //log.info("主机:{},分析结果:心跳超过5分钟,时间差:{}秒,修改主机状态为离线且删除心跳缓存",mid/1000,hv.getClientId());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取白令海的最新版本号
|
|
|
+ */
|
|
|
+ private String getNewlyAgentVersion(HostInfo hostInfo){
|
|
|
+ //需要升级的版本为空 直接返回agent自身版本
|
|
|
+ if (StringUtils.isEmpty(hostInfo.getUploadVersion())){
|
|
|
+ return hostInfo.getAgentVersion();
|
|
|
+ }
|
|
|
+
|
|
|
+ //对比agent自身版本 和 要升级的版本
|
|
|
+ Version agentVersion = Version.valueOf(hostInfo.getAgentVersion());
|
|
|
+ Version uploadVersion = Version.valueOf(hostInfo.getUploadVersion());
|
|
|
+ if (agentVersion.compareTo(uploadVersion) < 0){
|
|
|
+ return hostInfo.getUploadVersion();
|
|
|
+ }
|
|
|
+ return hostInfo.getAgentVersion();
|
|
|
+ }
|
|
|
+}
|