diff --git a/README.md b/README.md index 600a8eed..8738201c 100644 --- a/README.md +++ b/README.md @@ -8,15 +8,15 @@ * [七、图书分享](#cc7) * [八、支持与帮助](#cc8) - + ## 一、CacheCloud是什么 -CacheCloud是一个Redis云管理平台:支持Redis多种架构(Standalone、Sentinel、Cluster)高效管理、有效降低大规模redis运维成本,提升资源管控能力和利用率。平台提供快速搭建/迁移,运维管理,弹性伸缩,统计监控,客户端整合接入等功能。 +CacheCloud是一个Redis云管理平台:支持Redis多种架构(Standalone、Sentinel、Cluster)高效管理、有效降低大规模redis运维成本,提升资源管控能力和利用率。平台提供快速搭建/迁移,运维管理,弹性伸缩,统计监控,客户端整合接入等功能。 ![cachecloud云平台](cachecloud-web/src/main/resources/static/img/readme/cachecloud.png) - + ## 二、CacheCloud功能架构 @@ -24,11 +24,11 @@ CacheCloud是一个Redis云管理平台:支持Redis多种架构(Standalone、S + 客户端接入:Java-SDK接入、客户端监控、其他语言接入; + 运维管理:宿主环境、资源管理、应用审计、应用运维、应用质量监控、应用拓扑诊断; + 弹性伸缩:资源评估、垂直伸缩、水平伸缩、外部接入; -+ 统计监控:指标采集、应用统计、节点统计、机器统计、监控报警、问题诊断; ++ 统计监控:指标采集、应用统计、节点统计、机器统计、监控报警、问题诊断; - + ## 三、CacheCloud使用规模 @@ -37,7 +37,7 @@ CacheCloud是一个Redis云管理平台:支持Redis多种架构(Standalone、S + 300+ app Total / 3000+ Instances Total + 200+ Machines Total - + ## 四、阿里云ecs试用版本 @@ -45,26 +45,26 @@ CacheCloud是一个Redis云管理平台:支持Redis多种架构(Standalone、S + 用户名/登录密码:cachecloud_user:cachecloud_user + 开源版本试用截止时间:2021-01-18,如果大家有空闲公网资源可以贡献,请[联系我们](#cc8) - + ## 五、FAQ快速接入 + [快速开始](cachecloud-web/src/main/resources/static/wiki/quickstart/index.md) + [客户端接入](cachecloud-web/src/main/resources/static/wiki/access/client.md) - + ## 六、服务用户 - + ## 七、图书 -由CacheCloud团队撰写的实体书《Redis开发与运维》(机械工业出版)已在各大网店售卖,本书包含了CacheCloud团队在Redis规模化开发运维大量经验,以及Cachecloud系统使用介绍。 +由CacheCloud团队撰写的实体书《Redis开发与运维》(机械工业出版)已在各大网店售卖,本书包含了CacheCloud团队在Redis规模化开发运维大量经验,以及Cachecloud系统使用介绍。 -### 相关样章分享 +### 相关样章分享 + 1.[Redis架构之防雪崩设计](https://mp.weixin.qq.com/s/TBCEwLVAXdsTszRVpXhVug) + 2.[Redis的内存优化](https://cachecloud.github.io/2017/02/16/Redis%E5%86%85%E5%AD%98%E4%BC%98%E5%8C%96/) @@ -73,17 +73,17 @@ CacheCloud是一个Redis云管理平台:支持Redis多种架构(Standalone、S + 5.[Redis热点key寻找与优化](https://cachecloud.github.io/2017/02/20/Redis%E7%83%AD%E7%82%B9key%E5%AF%BB%E6%89%BE%E4%B8%8E%E4%BC%98%E5%8C%96/) + 6.[Redis无限全量复制问题分析与优化](https://cachecloud.github.io/2016/11/24/%E5%85%A8%E9%87%8F%E5%A4%8D%E5%88%B6%E9%97%AE%E9%A2%98/) - + ## 八、支持与帮助 + QQ群:534429768 -+ Redis开发运维公众号:关注Redis开发运维实战,发布Cachecloud最新动态,帮大家减轻工作负担。 ++ Redis开发运维公众号:关注Redis开发运维实战,发布Cachecloud最新动态,帮大家减轻工作负担。 + 微信:如果大家有公网资源可以联系我,会加入到开源版本服务资源部署试用,提高大家的用户体验。 - + 如果你觉得CacheCloud对你有帮助,欢迎Star。 diff --git a/cachecloud-client/cachecloud-client-redis/pom.xml b/cachecloud-client/cachecloud-client-redis/pom.xml index 8ae8ca50..d4925f04 100644 --- a/cachecloud-client/cachecloud-client-redis/pom.xml +++ b/cachecloud-client/cachecloud-client-redis/pom.xml @@ -42,6 +42,8 @@ logback-classic test + + diff --git a/cachecloud-custom/src/main/java/com/sohu/cache/alert/EmailComponent.java b/cachecloud-custom/src/main/java/com/sohu/cache/alert/EmailComponent.java index 42ff3c0c..524d5bd8 100644 --- a/cachecloud-custom/src/main/java/com/sohu/cache/alert/EmailComponent.java +++ b/cachecloud-custom/src/main/java/com/sohu/cache/alert/EmailComponent.java @@ -16,8 +16,8 @@ public interface EmailComponent { * @return */ boolean sendMail(String title, String content, List emailList, List ccList); - - + + /** * 发送邮件 * @param title @@ -35,7 +35,7 @@ public interface EmailComponent { */ boolean sendMailToAdmin(String title, String content); - + /** * 获取管理员邮件组 * @return diff --git a/cachecloud-web/sql/update2.0-2.1.sql b/cachecloud-web/sql/update2.0-2.1.sql new file mode 100644 index 00000000..af48176b --- /dev/null +++ b/cachecloud-web/sql/update2.0-2.1.sql @@ -0,0 +1,29 @@ +create or replace table app_import( + id bigint auto_increment + primary key, + app_id bigint null comment '目标应用id', + instance_info text null comment '源redis实例信息', + redis_password varchar (200) null comment '源redis密码', + status int null comment '迁移状态:PREPARE(0, "准备", "应用导入-未开始"), + START(1, "进行中...", "应用导入-开始"), + ERROR(2, "error", "应用导入-出错"), + VERSION_BUILD_START(11, "进行中...", "新建redis版本-进行中"), + VERSION_BUILD_ERROR(12, "error", "新建redis版本-出错"), + VERSION_BUILD_END(20, "成功", "新建redis版本-完成"), + APP_BUILD_INIT(21, "准备就绪", "新建redis应用-准备就绪"), + APP_BUILD_START(22, "进行中...", "新建redis应用-进行中"), + APP_BUILD_ERROR(23, "error", "新建redis应用-出错"), + APP_BUILD_END(30, "成功", "新建redis应用-完成"), + MIGRATE_INIT(31, "准备就绪", "数据迁移-准备就绪"), + MIGRATE_START(32, "进行中...", "数据迁移-进行中"), + MIGRATE_ERROR(33, "error", "数据迁移-出错"), + MIGRATE_END(3, "成功", "应用导入-成功")', + step int null comment '导入阶段', + create_time timestamp default CURRENT_TIMESTAMP not null, + update_time timestamp default CURRENT_TIMESTAMP null, + migrate_id bigint null comment '数据迁移id', + mem_size int null comment '目标应用内存大小,单位G', + redis_version_name varchar (20) null comment '目标应用redis版本,格式:redis-x.x.x', + app_build_task_id bigint null comment '目标应用部署任务id', + source_type int null comment '源redis类型:7:cluster, 6:sentinel, 5:standalone' +); \ No newline at end of file diff --git a/cachecloud-web/src/main/java/com/sohu/cache/client/command/AppClientCommand.java b/cachecloud-web/src/main/java/com/sohu/cache/client/command/AppClientCommand.java index 0042671a..20e0949a 100644 --- a/cachecloud-web/src/main/java/com/sohu/cache/client/command/AppClientCommand.java +++ b/cachecloud-web/src/main/java/com/sohu/cache/client/command/AppClientCommand.java @@ -145,6 +145,12 @@ public Map getRedisStandaloneInfo(boolean clientRequest) { if (CollectionUtils.isEmpty(instanceList)) { instanceList = instanceDao.getInstListByAppId(appId); } + String shardsInfo = ObjectConvert.assembleInstance(instanceList); + if (StringUtils.isBlank(shardsInfo)) { + model.put("status", ClientStatusEnum.ERROR.getStatus()); + model.put("message", "ERROR: appId:" + appId + "shardsInfo为空 "); + return model; + } String standalone = null; for (InstanceInfo instanceInfo : instanceList) { if (instanceInfo.isOffline()) { @@ -175,6 +181,12 @@ public Map getRedisSentinelInfo(boolean clientRequest) { model.put("message", "appId: " + appId + " 实例集合为空 "); return model; } + String shardsInfo = ObjectConvert.assembleInstance(instanceList); + if (StringUtils.isBlank(shardsInfo)) { + model.put("status", ClientStatusEnum.ERROR.getStatus()); + model.put("message", "ERROR: appId:" + appId + "shardsInfo为空 "); + return model; + } String masterName = null; List sentinelList = new ArrayList(); for (InstanceInfo instance : instanceList) { diff --git a/cachecloud-web/src/main/java/com/sohu/cache/configuration/AsyncConfiguration.java b/cachecloud-web/src/main/java/com/sohu/cache/configuration/AsyncConfiguration.java new file mode 100644 index 00000000..595fcc46 --- /dev/null +++ b/cachecloud-web/src/main/java/com/sohu/cache/configuration/AsyncConfiguration.java @@ -0,0 +1,21 @@ +package com.sohu.cache.configuration; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.concurrent.ForkJoinPool; + +@Configuration +@Slf4j +public class AsyncConfiguration { + + private int parallelism=256; + + @Bean + public ForkJoinPool forkJoinPool() { + log.info("availableProcessors:{}, parallelism:{}", Runtime.getRuntime().availableProcessors(), parallelism); + ForkJoinPool forkJoinPool = new ForkJoinPool(parallelism); + return forkJoinPool; + } +} \ No newline at end of file diff --git a/cachecloud-web/src/main/java/com/sohu/cache/constant/AppAuditType.java b/cachecloud-web/src/main/java/com/sohu/cache/constant/AppAuditType.java index d9350e6d..9fda43cd 100644 --- a/cachecloud-web/src/main/java/com/sohu/cache/constant/AppAuditType.java +++ b/cachecloud-web/src/main/java/com/sohu/cache/constant/AppAuditType.java @@ -9,17 +9,19 @@ public enum AppAuditType { APP_AUDIT(0, "申请集群"), APP_SCALE(1, "集群容量变更"), - APP_MODIFY_CONFIG(2,"集群修改配置"), + APP_MODIFY_CONFIG(2, "集群修改配置"), REGISTER_USER_APPLY(3, "用户注册"), INSTANCE_MODIFY_CONFIG(4, "实例修改配置"), - APP_MONITOR_CONFIG(5,"全局报警配置修改"), + APP_MONITOR_CONFIG(5, "全局报警配置修改"), KEY_ANALYSIS(6, "键值分析"), FLUSHALL_DATA(7, "清理数据"), - APP_DIAGNOSTIC(8,"应用诊断"), + APP_DIAGNOSTIC(8, "应用诊断"), APP_OFFLINE(10, "应用下线"), - APP_MIGRATE(11,"应用数据迁移"); + APP_MIGRATE(11, "应用数据迁移"), + APP_IMPORT(12, "应用导入"); private final static Map MAP = new HashMap(); + static { for (AppAuditType appAuditType : AppAuditType.values()) { MAP.put(appAuditType.getValue(), appAuditType); diff --git a/cachecloud-web/src/main/java/com/sohu/cache/constant/MachineInfoEnum.java b/cachecloud-web/src/main/java/com/sohu/cache/constant/MachineInfoEnum.java index 1e76bdeb..d590f5d2 100644 --- a/cachecloud-web/src/main/java/com/sohu/cache/constant/MachineInfoEnum.java +++ b/cachecloud-web/src/main/java/com/sohu/cache/constant/MachineInfoEnum.java @@ -81,4 +81,35 @@ public String getInfo() { } + public static enum MachineEnum { + HOST("host"), + CONTAINER("container"); + + private String value; + + MachineEnum(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + } + + public static enum MachineTypeEnum { + HOST(1), + CONTAINER(2), + ALL(3); + + private int value; + + MachineTypeEnum(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + } + } diff --git a/cachecloud-web/src/main/java/com/sohu/cache/dao/AppDataMigrateStatusDao.java b/cachecloud-web/src/main/java/com/sohu/cache/dao/AppDataMigrateStatusDao.java index 48bf2fe8..f7c777f9 100644 --- a/cachecloud-web/src/main/java/com/sohu/cache/dao/AppDataMigrateStatusDao.java +++ b/cachecloud-web/src/main/java/com/sohu/cache/dao/AppDataMigrateStatusDao.java @@ -20,6 +20,8 @@ public interface AppDataMigrateStatusDao { AppDataMigrateStatus get(@Param("id") long id); + AppDataMigrateStatus getByMigrateId(@Param("migrateId") long migrateId); + int updateStatus(@Param("id") long id, @Param("status") int status); int getMigrateTaskCount(@Param("appDataMigrateSearch") AppDataMigrateSearch appDataMigrateSearch); diff --git a/cachecloud-web/src/main/java/com/sohu/cache/dao/AppImportDao.java b/cachecloud-web/src/main/java/com/sohu/cache/dao/AppImportDao.java new file mode 100644 index 00000000..d94f088f --- /dev/null +++ b/cachecloud-web/src/main/java/com/sohu/cache/dao/AppImportDao.java @@ -0,0 +1,21 @@ +package com.sohu.cache.dao; + + +import com.sohu.cache.entity.AppImport; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * @Author: rucao + * @Date: 2021/1/7 下午6:03 + */ +public interface AppImportDao { + AppImport get(@Param("id") Long id); + + int save(AppImport appImport); + + int update(AppImport appImport); + + List getAppImports(@Param("status") int status); +} diff --git a/cachecloud-web/src/main/java/com/sohu/cache/entity/AppDataMigrateSearch.java b/cachecloud-web/src/main/java/com/sohu/cache/entity/AppDataMigrateSearch.java index 8cd9b066..ac551cfd 100644 --- a/cachecloud-web/src/main/java/com/sohu/cache/entity/AppDataMigrateSearch.java +++ b/cachecloud-web/src/main/java/com/sohu/cache/entity/AppDataMigrateSearch.java @@ -10,6 +10,10 @@ */ @Data public class AppDataMigrateSearch { + /** + * 迁移id + */ + private Long migrateId; /** * 源应用id */ diff --git a/cachecloud-web/src/main/java/com/sohu/cache/entity/AppImport.java b/cachecloud-web/src/main/java/com/sohu/cache/entity/AppImport.java new file mode 100644 index 00000000..a466e14d --- /dev/null +++ b/cachecloud-web/src/main/java/com/sohu/cache/entity/AppImport.java @@ -0,0 +1,26 @@ +package com.sohu.cache.entity; + +import lombok.Data; + +import java.util.Date; + +/** + * @Author: rucao + * @Date: 2021/1/7 下午6:00 + */ +@Data +public class AppImport { + private long id; + private long appId; + private int memSize; + private int sourceType; + private String redisVersionName; + private String instanceInfo; + private String redisPassword; + private int status; + private int step; + private long appBuildTaskId; + private long migrateId; + private Date createTime; + private Date updateTime; +} diff --git a/cachecloud-web/src/main/java/com/sohu/cache/entity/InstanceInfo.java b/cachecloud-web/src/main/java/com/sohu/cache/entity/InstanceInfo.java index cc8bab13..04d6679d 100644 --- a/cachecloud-web/src/main/java/com/sohu/cache/entity/InstanceInfo.java +++ b/cachecloud-web/src/main/java/com/sohu/cache/entity/InstanceInfo.java @@ -4,8 +4,12 @@ import com.sohu.cache.util.ConstUtils; import com.sohu.cache.web.enums.BooleanEnum; import lombok.Data; +import redis.clients.jedis.Module; + import java.io.Serializable; import java.util.Date; +import java.util.List; + /** * 实例信息 * User: lingguo @@ -57,6 +61,9 @@ public class InstanceInfo implements Serializable { private String roleDesc; private int groupId; private Date updateTime; + + private List modules; + public String getTypeDesc() { if (type <= 0) { typeDesc = ""; diff --git a/cachecloud-web/src/main/java/com/sohu/cache/entity/MachineStats.java b/cachecloud-web/src/main/java/com/sohu/cache/entity/MachineStats.java index 70f723ba..31dac467 100644 --- a/cachecloud-web/src/main/java/com/sohu/cache/entity/MachineStats.java +++ b/cachecloud-web/src/main/java/com/sohu/cache/entity/MachineStats.java @@ -51,6 +51,10 @@ public class MachineStats{ private MachineMemInfo machineMemInfo; + private Map moduleInfo; + + private String versionInfo; + private Map diskUsageMap; /** diff --git a/cachecloud-web/src/main/java/com/sohu/cache/machine/MachineCenter.java b/cachecloud-web/src/main/java/com/sohu/cache/machine/MachineCenter.java index 361a38cf..fd0a850f 100644 --- a/cachecloud-web/src/main/java/com/sohu/cache/machine/MachineCenter.java +++ b/cachecloud-web/src/main/java/com/sohu/cache/machine/MachineCenter.java @@ -6,6 +6,7 @@ import com.sohu.cache.web.enums.MachineMemoryDistriEnum; import com.sohu.cache.web.vo.MachineStatsVo; +import java.util.Date; import java.util.List; import java.util.Map; @@ -174,22 +175,6 @@ public interface MachineCenter { public Map getK8sMachineMap(); - /** - * 获取机器安装redis不同版本情况 - */ - public List getMachineInstallRedisStat(List versionList); - - /** - *

- * Description: 获取有效机器列表 - *

- * - * @author chenshi - * @version 1.0 - * @date 2018/9/25 - */ - public List getAllEffectiveMachines(); - /** *

* Description: 获取有效机房 @@ -245,10 +230,16 @@ public interface MachineCenter { */ public Boolean isK8sMachine(String host); + public Map getAllMachineEnv(Date searchDate,int type); + + public Map getExceptionMachineEnv(Date searchDate); + /** * 获取机器列表的第一台机器资源 * @return */ public String getFirstMachineIp(); + public List checkMachineModule(List machineStatsList); + } diff --git a/cachecloud-web/src/main/java/com/sohu/cache/machine/impl/MachineCenterImpl.java b/cachecloud-web/src/main/java/com/sohu/cache/machine/impl/MachineCenterImpl.java index 7ec5332a..e76eb1dd 100644 --- a/cachecloud-web/src/main/java/com/sohu/cache/machine/impl/MachineCenterImpl.java +++ b/cachecloud-web/src/main/java/com/sohu/cache/machine/impl/MachineCenterImpl.java @@ -7,6 +7,7 @@ import com.sohu.cache.async.KeyCallable; import com.sohu.cache.constant.InstanceStatusEnum; import com.sohu.cache.constant.MachineConstant; +import com.sohu.cache.constant.MachineInfoEnum; import com.sohu.cache.constant.MachineInfoEnum.TypeEnum; import com.sohu.cache.dao.*; import com.sohu.cache.entity.*; @@ -16,6 +17,7 @@ import com.sohu.cache.protocol.MachineProtocol; import com.sohu.cache.redis.RedisCenter; import com.sohu.cache.redis.enums.DirEnum; +import com.sohu.cache.ssh.SSHService; import com.sohu.cache.ssh.SSHUtil; import com.sohu.cache.stats.instance.InstanceStatsCenter; import com.sohu.cache.task.BaseTask; @@ -23,8 +25,10 @@ import com.sohu.cache.task.constant.ResourceEnum; import com.sohu.cache.util.*; import com.sohu.cache.web.enums.BooleanEnum; +import com.sohu.cache.web.enums.CheckEnum; import com.sohu.cache.web.enums.MachineMemoryDistriEnum; -import com.sohu.cache.web.enums.RedisVersionEnum; +import com.sohu.cache.web.enums.ModuleEnum; +import com.sohu.cache.web.vo.MachineEnv; import com.sohu.cache.web.vo.MachineStatsVo; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.MapUtils; @@ -45,8 +49,9 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.text.DecimalFormat; +import java.text.SimpleDateFormat; import java.util.*; -import java.util.concurrent.TimeUnit; +import java.util.concurrent.*; import java.util.stream.Collectors; import static com.google.common.base.Preconditions.checkArgument; @@ -76,14 +81,18 @@ public class MachineCenterImpl implements MachineCenter { private ResourceDao resourceDao; @Autowired private MachineRoomDao machineRoomDao; + @Autowired + private SSHService sshService; + @Autowired + protected AsyncService asyncService; + @Autowired + private ForkJoinPool forkJoinPool; /** * 邮箱报警 */ @Autowired private EmailComponent emailComponent; - @Autowired - private AsyncService asyncService; @PostConstruct public void init() { @@ -743,58 +752,6 @@ public Map getK8sMachineMap() { return k8sMachineMaps; } - public List getMachineInstallRedisStat(List resourceList){ - - List allMachines = machineDao.getAllMachines(); - //1.遍历机器安装情况 - Map installStats = new HashMap(); - if (allMachines != null && allMachines.size() > 0) { - for (MachineInfo machine : allMachines) { - String version_install = machine.getVersionInstall(); - if (!StringUtils.isEmpty(version_install)) { - for (String installinfo : version_install.split(";")) { - Integer count = MapUtils.getInteger(installStats, installinfo, 0) + 1; - installStats.put(installinfo, count); - } - } - } - } - - //2. app stat - List> appVersionStats = appDao.getVersionStat(); - - Map appStats = new HashMap(); - if (appVersionStats != null && appVersionStats.size() > 0) { - for (Map appVersion : appVersionStats) { - appStats.put(MapUtils.getString(appVersion, "version_id"), MapUtils.getInteger(appVersion, "num", 0)); - } - } - //3.安装信息写入redisVersionStat - List redisVersionStatList = new ArrayList(); - for (SystemResource redisResource : resourceList) { - RedisVersionStat redisVersionStat = new RedisVersionStat(redisResource); - redisVersionStat.setInstallNum(MapUtils.getInteger(installStats, - redisResource.getName() + ConstUtils.POUND + RedisVersionEnum.Redis_installed.getValue(), 0)); - redisVersionStat.setUninstallNum(MapUtils.getInteger(installStats, - redisResource.getName() + ConstUtils.POUND + RedisVersionEnum.Redis_uninstalled.getValue(), 0)); - redisVersionStat.setInstallExceptionNum(MapUtils.getInteger(installStats, - redisResource.getName() + ConstUtils.POUND + RedisVersionEnum.Redis_installException.getValue(), 0)); - redisVersionStat.setTotalMachineNum(allMachines.size()); - if (!CollectionUtils.isEmpty(allMachines)) { - redisVersionStat.setInstallRatio(redisVersionStat.getInstallNum() * 100 / allMachines.size()); - } else { - redisVersionStat.setInstallRatio(0); - } - redisVersionStat.setAppUsedNum(MapUtils.getIntValue(appStats, redisResource.getId() + "")); - redisVersionStatList.add(redisVersionStat); - } - return redisVersionStatList; - } - - public List getAllEffectiveMachines() { - return machineDao.getAllMachines(); - } - public List getEffectiveRoom() { return machineRoomDao.getEffectiveRoom(); } @@ -950,7 +907,7 @@ public String getInstanceRemoteBasePath(long appId, int port, InstanceTypeEnum i public String getMachineRelativeDir(String host, int dirType) { MachineInfo machineInfo = machineDao.getMachineInfoByIp(host); - if (machineInfo != null && machineInfo.isK8sMachine(machineInfo.getK8sType())) { + if (machineInfo != null && machineInfo.isK8sMachine(machineInfo.getK8sType())) { return MachineProtocol.getK8sDir(host, dirType); } return MachineProtocol.getDir(dirType); @@ -964,11 +921,262 @@ public Boolean isK8sMachine(String host) { return false; } - public String getFirstMachineIp(){ + public Map getExceptionMachineEnv(Date searchDate) { + + Map exceptionMap = new HashMap(); + Map allMachineEnvMap = getAllMachineEnv(searchDate, MachineInfoEnum.MachineTypeEnum.ALL.getValue()); + // 过滤需要监控的数据 + List> containerlist = (List>) allMachineEnvMap.get(MachineInfoEnum.MachineEnum.CONTAINER.getValue()); + List> hostlist = (List>) allMachineEnvMap.get(MachineInfoEnum.MachineEnum.HOST.getValue()); + + exceptionMap.put(MachineInfoEnum.MachineEnum.CONTAINER.getValue(), containerlist.stream().filter(map -> MapUtils.getInteger(map, "status") != CheckEnum.CONSISTENCE.getValue()).collect(Collectors.toList())); + exceptionMap.put(MachineInfoEnum.MachineEnum.HOST.getValue(), hostlist.stream().filter(map -> MapUtils.getInteger(map, "status") != CheckEnum.CONSISTENCE.getValue()).collect(Collectors.toList())); + return exceptionMap; + } + + public Map getAllMachineEnv(Date searchDate, int type) { + + Map resultMap = new HashMap(); + + List allMachines = machineDao.getAllMachines(); + Set iplist = new HashSet(); + Set hostlist = new HashSet(); + + SimpleDateFormat dateFormat = new SimpleDateFormat("dd MMM yyyy", Locale.ENGLISH); + if (!CollectionUtils.isEmpty(allMachines)) { + for (MachineInfo machineInfo : allMachines) { + iplist.add(machineInfo.getIp()); + hostlist.add(machineInfo.getRealIp()); + } + } + /** + * 检测容器: + * 1.内存分配策略 + * 2.thp大内存页配置 + * 3.内存swap配置 + * 4.容器nproc配置 + */ + String container_cmd = + "cat /proc/sys/vm/overcommit_memory;" + + "cat /proc/sys/vm/swappiness;" + + "cat /sys/kernel/mm/transparent_hugepage/enabled;" + + "cat /sys/kernel/mm/transparent_hugepage/defrag;" + + "cat /etc/security/limits.d/*-nproc.conf | grep '* soft nproc'" + + ""; + /** + * 检测宿主机: + * 1.检测用户连接的进程数 大于>=1024 + * 2.检测宿主机所有实例aof写盘阻塞 >=3次 + * 3.检测 somaxconn 512 + * 4.检测 sshpass安装 + * 5.运行redis实例总数 + * 6.ulimit 打开文件句柄检测 + * 7.磁盘/内存使用情况 + */ + String machine_cmd = + "cat /proc/sys/net/core/somaxconn;" + + "cat /data/redis/logs/*/* | grep '" + dateFormat.format(searchDate) + "' | grep 'slow down Redis' | wc -l;" + + "ps -u cachecloud -L | wc -l;" + + "sshpass -V | head -1;" + + "ulimit -n;" + + "echo 0;" + +// "lsof | grep cachecloud | wc -l;" + + "df -h | grep '/dev' | grep '/data' | awk '{print $5\"(\"$3\"/\"$2\")\"}';" + + "ps -ef | grep redis | wc -l;" + + ""; + + + List> containerInfo = new ArrayList<>(); + List> machineInfo = new ArrayList<>(); + long phase1 = System.currentTimeMillis(); + if (type == MachineInfoEnum.MachineTypeEnum.CONTAINER.getValue() || type == MachineInfoEnum.MachineTypeEnum.ALL.getValue()) { + if (!CollectionUtils.isEmpty(iplist)) { + ForkJoinTask>> container_task = forkJoinPool.submit(() -> iplist.parallelStream().collect(Collectors.toMap(containerIp -> containerIp, containerIp -> new MachinetaskCallable(containerIp, container_cmd, sshService, MachineInfoEnum.MachineEnum.CONTAINER.getValue()).call()))); + try { + Map> container_result = container_task.get(30, TimeUnit.SECONDS); + if (!MapUtils.isEmpty(container_result)) { + for (Map.Entry> container : container_result.entrySet()) { + Map res = container.getValue(); + if (!MapUtils.isEmpty(res)) { + containerInfo.add(res); + } + } + } + logger.info("container result size:{}", container_result.size()); + } catch (InterruptedException e) { + e.printStackTrace(); + } catch (ExecutionException e) { + e.printStackTrace(); + } catch (TimeoutException e) { + e.printStackTrace(); + } + } + } + long phase2 = System.currentTimeMillis(); + logger.info("container check env cost time:{} ms", phase2 - phase1); + + if (type == MachineInfoEnum.MachineTypeEnum.HOST.getValue() || type == MachineInfoEnum.MachineTypeEnum.ALL.getValue()) { + if (!CollectionUtils.isEmpty(hostlist)) { + ForkJoinTask>> machine_task = forkJoinPool.submit(() -> hostlist.parallelStream().collect(Collectors.toMap(machineIp -> machineIp, machineIp -> new MachinetaskCallable(machineIp, machine_cmd, sshService, MachineInfoEnum.MachineEnum.HOST.getValue()).call()))); + try { + Map> host_result = machine_task.get(30, TimeUnit.SECONDS); + if (!MapUtils.isEmpty(host_result)) { + for (Map.Entry> host : host_result.entrySet()) { + Map res = host.getValue(); + if (!MapUtils.isEmpty(res)) { + machineInfo.add(res); + } + } + } + logger.info("machine result size:{}", host_result.size()); + } catch (InterruptedException e) { + e.printStackTrace(); + } catch (ExecutionException e) { + e.printStackTrace(); + } catch (TimeoutException e) { + e.printStackTrace(); + } + } + } + logger.info("host check env cost time:{} ms", System.currentTimeMillis() - phase2); + + resultMap.put(MachineInfoEnum.MachineEnum.CONTAINER.getValue(), containerInfo); + resultMap.put(MachineInfoEnum.MachineEnum.HOST.getValue(), machineInfo); + return resultMap; + } + + private class MachinetaskCallable implements Callable> { + + private String ip; + private String cmd; + private SSHService sshService; + private String type; + + public MachinetaskCallable(String ip, String cmd, SSHService sshService, String type) { + this.ip = ip; + this.cmd = cmd; + this.sshService = sshService; + this.type = type; + } + + + @Override + public Map call() { + + Map machineResult = new HashMap(); + String info = null; + try { + info = sshService.execute(ip, cmd); + machineResult.put("ip", ip); + if (!StringUtil.isBlank(info)) { + if (type.equals(MachineInfoEnum.MachineEnum.CONTAINER.getValue())) { + MachineEnv containerEnv = convertContainer(info); + if (containerEnv != null) { + machineResult.put("envs", containerEnv); + machineResult.put("status", MachineEnv.checkContainer(containerEnv)); + } else { + machineResult.put("status", CheckEnum.EXCEPTION.getValue()); + machineResult.put("envs", MachineEnv.getDefaultEnv()); + } + } else if (type.equals(MachineInfoEnum.MachineEnum.HOST.getValue())) { + MachineEnv hostEnv = convertHost(info); + if (hostEnv != null) { + machineResult.put("envs", hostEnv); + machineResult.put("status", MachineEnv.checkHost(hostEnv)); + } else { + machineResult.put("status", CheckEnum.EXCEPTION.getValue()); + machineResult.put("envs", MachineEnv.getDefaultEnv()); + } + } + } else { + machineResult.put("status", CheckEnum.EXCEPTION.getValue()); + machineResult.put("envs", MachineEnv.getDefaultEnv()); + } + } catch (SSHException e) { + logger.error("MachinetaskCallable ip:{} error msg :{}",ip,e.getMessage()); + machineResult.put("status", CheckEnum.EXCEPTION.getValue()); + machineResult.put("envs", MachineEnv.getDefaultEnv()); + } + + return machineResult; + } + } + + + public MachineEnv convertContainer(String cmdResult) { + + String[] envs = cmdResult.split("\n"); + String nproc = ""; + try { + nproc = StringUtils.isBlank(envs[4]) ? "" : envs[4]; + } catch (Exception e) { + logger.error("MachineEnv convertContainer cmdResult:{} error {}:", cmdResult, e.getMessage()); + } + return new MachineEnv(envs[0], envs[1], envs[2], envs[3], nproc); + + } + + public MachineEnv convertHost(String cmdResult) { + + int fsync_delay_times = -1; + int nproc_threads = -1; + int unlimit = -1; + int unlimit_used = -1; + int instanceNum = -1; + try { + String[] envs = cmdResult.split("\n"); + fsync_delay_times = StringUtils.isBlank(envs[1]) ? -1 : Integer.parseInt(envs[1]); + nproc_threads = StringUtils.isBlank(envs[2]) ? -1 : Integer.parseInt(envs[2]); + unlimit = StringUtils.isBlank(envs[4]) ? -1 : Integer.parseInt(envs[4]); + unlimit_used = StringUtils.isBlank(envs[5]) ? -1 : Integer.parseInt(envs[5]); + instanceNum = StringUtils.isBlank(envs[7]) ? -1 : Integer.parseInt(envs[7]); + return new MachineEnv(envs[0], fsync_delay_times, nproc_threads, envs[3], unlimit_used, unlimit, envs[6], instanceNum); + } catch (Exception e) { + logger.error("convertMachine error :{} {}", cmdResult, e.getMessage(), e); + return new MachineEnv("-1", fsync_delay_times, nproc_threads, "", unlimit_used, unlimit, "", instanceNum); + + } + } + + public String getFirstMachineIp() { List machines = machineDao.getAllMachines(); - if(!CollectionUtils.isEmpty(machines)){ + if (!CollectionUtils.isEmpty(machines)) { return machines.get(0).getIp(); } return null; } + + public List checkMachineModule(List machineStatsList) { + + if (!CollectionUtils.isEmpty(machineStatsList)) { + for (MachineStats machineStats : machineStatsList) { + + String moduleBasePath = ConstUtils.MODULE_BASE_PATH; + String cmd = String.format("cd %s && ls -l | grep .so", moduleBasePath); + String cmd2 = String.format("cat /etc/redhat-release"); + + try { + String ip = machineStats.getInfo().getIp(); + String executeResult = sshService.execute(ip, cmd); + logger.info("ip :{} ,exe cmd :{},module info:{}", ip, cmd, executeResult); + + Map moduleInfo = new HashMap(); + for(String moduleName : ConstUtils.MODULE_LIST){ + if(!StringUtil.isBlank(executeResult)) { + moduleInfo.put(moduleName, executeResult.contains(moduleName)); + }else{ + moduleInfo.put(moduleName, false); + } + } + + String version = sshService.execute(ip, cmd2); + machineStats.setVersionInfo(version); + machineStats.setModuleInfo(moduleInfo); + } catch (SSHException e) { + e.printStackTrace(); + } + } + } + return machineStatsList; + } } diff --git a/cachecloud-web/src/main/java/com/sohu/cache/redis/RedisCenter.java b/cachecloud-web/src/main/java/com/sohu/cache/redis/RedisCenter.java index fee1c718..3e914810 100644 --- a/cachecloud-web/src/main/java/com/sohu/cache/redis/RedisCenter.java +++ b/cachecloud-web/src/main/java/com/sohu/cache/redis/RedisCenter.java @@ -459,4 +459,18 @@ public List collectRedisSlowLog(long appId, long collectTime, S */ public List checkNutCrackerHashIsSame(long appId, boolean isDelete); + /** + * 检查实例安装插件情况 + * @param appId + */ + public List checkInstanceModule(long appId); + + public Map loadModule(long appId, String moduleName); + + public Map unloadModule(long appId, String moduleName); + + /** + * 检查主从节点是否有redis插件 + */ + public boolean checkAndLoadModule(long appId, String host, int port, String currentHost, int currentPort); } diff --git a/cachecloud-web/src/main/java/com/sohu/cache/redis/impl/AssistRedisServiceImpl.java b/cachecloud-web/src/main/java/com/sohu/cache/redis/impl/AssistRedisServiceImpl.java index e63270f5..07ee9294 100755 --- a/cachecloud-web/src/main/java/com/sohu/cache/redis/impl/AssistRedisServiceImpl.java +++ b/cachecloud-web/src/main/java/com/sohu/cache/redis/impl/AssistRedisServiceImpl.java @@ -10,6 +10,7 @@ import redis.clients.jedis.JedisPool; import redis.clients.jedis.Protocol; import redis.clients.jedis.Tuple; +import redis.clients.jedis.exceptions.JedisConnectionException; import redis.clients.jedis.params.SetParams; import redis.clients.jedis.serializable.ProtostuffSerializer; @@ -24,13 +25,13 @@ public class AssistRedisServiceImpl implements AssistRedisService { private Logger logger = LoggerFactory.getLogger(AssistRedisServiceImpl.class); - @Value("${cachecloud.redis.main.host}") + @Value("${cachecloud.redis.main.host:127.0.0.1}") private String mainHost; - @Value("${cachecloud.redis.main.port}") + @Value("${cachecloud.redis.main.port:6379}") private int mainPort; - @Value("${cachecloud.redis.main.password}") + @Value("${cachecloud.redis.main.password:}") private String mainPassword; private JedisPool jedisPoolMain; @@ -51,8 +52,11 @@ public void init() { private Jedis getFromJedisPool() { try { return jedisPoolMain.getResource(); + } catch (JedisConnectionException ce){ + logger.warn("Please Make sure the file:application-${profile}.yml connection pool is configured correctly ! cachecloud.redis.main.host:{} cachecloud.redis.main.port:{} cachecloud.redis.main.password:{}",mainHost,mainPort,mainPassword); + return null; } catch (Exception e) { - logger.error(e.getMessage(), e); + logger.warn(e.getMessage(),e); return null; } } @@ -65,7 +69,7 @@ public boolean rpush(String key, String item) { jedis.rpush(key, item); return true; } catch (Exception e) { - logger.error("rpush {} {} error " + e.getMessage(), key, item, e); + logger.warn("rpush {} {} error " + e.getMessage(), key, item, e); return false; } finally { if (jedis != null) { @@ -81,7 +85,7 @@ public List lrange(String key, int start, int end) { jedis = getFromJedisPool(); return jedis.lrange(key, start, end); } catch (Exception e) { - logger.error("lrange {} {} {} error " + e.getMessage(), key, start, end, e); + logger.warn("lrange {} {} {} error " + e.getMessage(), key, start, end); return Collections.emptyList(); } finally { if (jedis != null) { @@ -98,7 +102,7 @@ public boolean rpushList(String key, List items) { jedis.rpush(key, items.toArray(new String[items.size()])); return true; } catch (Exception e) { - logger.error("rpushList {} {} error " + e.getMessage(), key, items, e); + logger.warn("rpushList {} {} error " + e.getMessage(), key, items); return false; } finally { if (jedis != null) { @@ -115,7 +119,7 @@ public boolean saddSet(String key, Set items) { jedis.sadd(key, items.toArray(new String[items.size()])); return true; } catch (Exception e) { - logger.error("saddList {} {} error " + e.getMessage(), key, items, e); + logger.warn("saddList {} {} error " + e.getMessage(), key, items); return false; } finally { if (jedis != null) { @@ -131,7 +135,7 @@ public Set smembers(String key) { jedis = getFromJedisPool(); return jedis.smembers(key); } catch (Exception e) { - logger.error("smembers {} error " + e.getMessage(), key, e); + logger.warn("smembers {} error " + e.getMessage(), key); return Collections.emptySet(); } finally { if (jedis != null) { @@ -148,7 +152,7 @@ public boolean srem(String key, String item) { jedis.srem(key, item); return true; } catch (Exception e) { - logger.error("srem {} {} error " + e.getMessage(), key, item, e); + logger.warn("srem {} {} error " + e.getMessage(), key, item); return false; } finally { if (jedis != null) { @@ -175,7 +179,7 @@ public boolean set(String key, T value) { jedis.set(key.getBytes(Charset.forName("UTF-8")), bytes); return true; } catch (Exception e) { - logger.error("set {} error " + e.getMessage(), key, e); + logger.warn("set {} error " + e.getMessage(), key, e); return false; } finally { if (jedis != null) { @@ -197,7 +201,7 @@ public boolean set(String key, T value, int seconds) { jedis.setex(key.getBytes(Charset.forName("UTF-8")), seconds, bytes); return true; } catch (Exception e) { - logger.error("setex {} {} error " + e.getMessage(), key, seconds, e); + logger.warn("setex {} {} error " + e.getMessage(), key, seconds); return false; } finally { if (jedis != null) { @@ -213,7 +217,7 @@ public boolean setNx(String key, String value) { jedis = getFromJedisPool(); result = jedis.setnx(key, value); } catch (Exception e) { - logger.error("setnx {} {} error:{} ", key, value, e.getMessage(), e); + logger.warn("setnx {} {} error:{} ", key, value, e.getMessage()); } finally { if (jedis != null) { jedis.close(); @@ -229,7 +233,7 @@ public String set(String key, String value, SetParams params) { jedis = getFromJedisPool(); return jedis.set(key, value, params); } catch (Exception e) { - logger.error("set {} {} {} error " + e.getMessage(), key, value, params, e); + logger.warn("set {} {} {} error " + e.getMessage(), key, value, params); return null; } finally { if (jedis != null) { @@ -249,7 +253,7 @@ public boolean setWithNoSerialize(String key, T value) { jedis.set(key, value.toString()); return true; } catch (Exception e) { - logger.error("setWithNoSerialize {} error " + e.getMessage(), key, e); + logger.warn("setWithNoSerialize {} error " + e.getMessage(), key); return false; } finally { if (jedis != null) { @@ -269,7 +273,7 @@ public boolean setWithNoSerialize(String key, T value, int seconds) { jedis.setex(key, seconds, value.toString()); return true; } catch (Exception e) { - logger.error("setWithNoSerialize {} error " + e.getMessage(), key, e); + logger.warn("setWithNoSerialize {} error " + e.getMessage(), key); return false; } finally { if (jedis != null) { @@ -285,7 +289,7 @@ public String getWithNoSerialize(String key) { jedis = getFromJedisPool(); return jedis.get(key); } catch (Exception e) { - logger.error("getWithNoSerialize {} error " + e.getMessage(), key, e); + logger.warn("getWithNoSerialize {} error " + e.getMessage(), key); return null; } finally { if (jedis != null) { @@ -302,7 +306,7 @@ public boolean remove(String key) { jedis.del(key); return true; } catch (Exception e) { - logger.error("remove {} error " + e.getMessage(), key, e); + logger.warn("remove {} error " + e.getMessage(), key); return false; } finally { if (jedis != null) { @@ -319,7 +323,7 @@ public boolean zadd(String key, long score, String member) { jedis.zadd(key, score, member); return true; } catch (Exception e) { - logger.error("zadd {} {} {} error " + e.getMessage(), key, score, member, e); + logger.warn("zadd {} {} {} error " + e.getMessage(), key, score, member); return false; } finally { if (jedis != null) { @@ -336,7 +340,7 @@ public boolean hset(String key, String field, String value) { jedis.hset(key, field, value); return true; } catch (Exception e) { - logger.error("hset {} {} {} error " + e.getMessage(), key, field, value, e); + logger.warn("hset {} {} {} error " + e.getMessage(), key, field, value); return false; } finally { if (jedis != null) { @@ -354,7 +358,7 @@ public boolean hmset(String key, Map map) { jedis.hmset(key, map); return true; } catch (Exception e) { - logger.error("hset {} {} error " + e.getMessage(), key, map, e); + logger.warn("hset {} {} error " + e.getMessage(), key, map); return false; } finally { if (jedis != null) { @@ -370,7 +374,7 @@ public Map hgetAll(String key) { jedis = getFromJedisPool(); return jedis.hgetAll(key); } catch (Exception e) { - logger.error("hgetAll {} error " + e.getMessage(), key, e); + logger.warn("hgetAll {} error " + e.getMessage(), key); return Collections.emptyMap(); } finally { if (jedis != null) { @@ -391,7 +395,7 @@ public T get(String key) { T t = protostuffSerializer.deserialize(bytes); return t; } catch (Exception e) { - logger.error("get {} error " + e.getMessage(), key, e); + logger.warn("get {} error " + e.getMessage(), key); return null; } finally { if (jedis != null) { @@ -408,7 +412,7 @@ public boolean del(String key) { jedis.del(key); return true; } catch (Exception e) { - logger.error("del {} error " + e.getMessage(), key, e); + logger.warn("del {} error " + e.getMessage(), key); return false; } finally { if (jedis != null) { @@ -424,7 +428,7 @@ public void zincrby(String key, double score, String member) { jedis = getFromJedisPool(); jedis.zincrby(key, score, member); } catch (Exception e) { - logger.error("zincrby {} {} {} error " + e.getMessage(), key, score, member, e); + logger.warn("zincrby {} {} {} error " + e.getMessage(), key, score, member); } finally { if (jedis != null) { jedis.close(); @@ -439,7 +443,7 @@ public Set zrangeWithScores(String key, long start, long end) { jedis = getFromJedisPool(); return jedis.zrangeWithScores(key, start, end); } catch (Exception e) { - logger.error("zrangeWithScores {} {} {}error " + e.getMessage(), key, start, end, e); + logger.warn("zrangeWithScores {} {} {}error " + e.getMessage(), key, start, end); return null; } finally { if (jedis != null) { diff --git a/cachecloud-web/src/main/java/com/sohu/cache/redis/impl/RedisCenterImpl.java b/cachecloud-web/src/main/java/com/sohu/cache/redis/impl/RedisCenterImpl.java index 43c0e55f..577cef9b 100644 --- a/cachecloud-web/src/main/java/com/sohu/cache/redis/impl/RedisCenterImpl.java +++ b/cachecloud-web/src/main/java/com/sohu/cache/redis/impl/RedisCenterImpl.java @@ -14,12 +14,14 @@ import com.sohu.cache.redis.RedisCenter; import com.sohu.cache.redis.enums.RedisInfoEnum; import com.sohu.cache.redis.enums.RedisReadOnlyCommandEnum; +import com.sohu.cache.ssh.SSHService; import com.sohu.cache.stats.instance.InstanceStatsCenter; import com.sohu.cache.task.BaseTask; import com.sohu.cache.task.constant.InstanceInfoEnum.InstanceTypeEnum; import com.sohu.cache.util.*; import com.sohu.cache.web.enums.BooleanEnum; import com.sohu.cache.web.enums.ClientTypeEnum; +import com.sohu.cache.web.enums.SuccessEnum; import com.sohu.cache.web.service.AppService; import com.sohu.cache.web.service.WebClientComponent; import com.sohu.cache.web.util.DateUtil; @@ -90,6 +92,8 @@ public class RedisCenterImpl implements RedisCenter { private InstanceLatencyHistoryDao instanceLatencyHistoryDao; @Autowired private WebClientComponent webClientComponent; + @Autowired + SSHService sshService; @PostConstruct public void init() { @@ -2628,101 +2632,302 @@ public List checkNutCrackerHashIsSame(long appId, boolean isDelete return null; } - private class RedisKeyCallable extends KeyCallable { - private final long appId; - private final long collectTime; - private final String host; - private final int port; - private final Map> infoMap; - private final Map clusterInfoMap; - - private RedisKeyCallable(long appId, long collectTime, String host, int port, - Map> infoMap, Map clusterInfoMap) { - super(buildFutureKey(appId, collectTime, host, port)); - this.appId = appId; - this.collectTime = collectTime; - this.host = host; - this.port = port; - this.infoMap = infoMap; - this.clusterInfoMap = clusterInfoMap; - } - - @Override - public Boolean execute() { - //比对currentInfoMap和lastInfoMap,计算差值 - long lastCollectTime = ScheduleUtil.getLastCollectTime(collectTime); - Map lastInfoMap = instanceStatsCenter - .queryStandardInfoMap(lastCollectTime, host, port, ConstUtils.REDIS); - - if (lastInfoMap == null || lastInfoMap.isEmpty()) { - logger.error("[redis-lastInfoMap] : lastCollectTime = {} appId={} host:port = {}:{} is null", - lastCollectTime, appId, host, port); - } - //基本统计累加差值 - Table baseDiffTable = getAccumulationDiff(infoMap, lastInfoMap); - fillAccumulationMap(infoMap, baseDiffTable); - - //命令累加差值 - Table commandDiffTable = getCommandsDiff(infoMap, lastInfoMap); - fillAccumulationMap(infoMap, commandDiffTable); - - //内存碎片率差值计算 - //Table otherDiffTable = getDoubleAccumulationDiff(infoMap, lastInfoMap); - //fillDoubleAccumulationMap(infoMap, otherDiffTable); - fillMemFragRatioMap(infoMap); - - Map currentInfoMap = new LinkedHashMap(); - for (Map.Entry> entry : infoMap.entrySet()) { - currentInfoMap.put(entry.getKey().getValue(), entry.getValue()); - } - currentInfoMap.put(ConstUtils.COLLECT_TIME, collectTime); - instanceStatsCenter.saveStandardStats(currentInfoMap, clusterInfoMap, host, port, ConstUtils.REDIS); - - // 更新实例在db中的状态 - InstanceStats instanceStats = getInstanceStats(appId, host, port, infoMap); - if (instanceStats != null) { - instanceStatsDao.updateInstanceStats(instanceStats); - } - - BooleanEnum isMaster = isMaster(infoMap); - if (isMaster == BooleanEnum.TRUE) { - Table diffTable = HashBasedTable.create(); - diffTable.putAll(baseDiffTable); - diffTable.putAll(commandDiffTable); - - long allCommandCount = 0L; - //更新命令统计 - List commandStatsList = getCommandStatsList(appId, collectTime, diffTable); - for (AppCommandStats commandStats : commandStatsList) { - //排除无效命令且存储有累加的数据 - if (RedisExcludeCommand.isExcludeCommand(commandStats.getCommandName()) - || commandStats.getCommandCount() <= 0L) { - continue; - } - allCommandCount += commandStats.getCommandCount(); + public List checkInstanceModule(long appId) { + + // 实例列表 + List instanceList = appService.getAppInstanceInfo(appId); + if (!CollectionUtils.isEmpty(instanceList)) { + for (InstanceInfo instanceInfo : instanceList) { + if (!CollectionUtils.isEmpty(instanceList)) { + String host = instanceInfo.getIp(); + int port = instanceInfo.getPort(); + int type = instanceInfo.getType(); + Jedis jedis = null; try { - // todo 数据库(on duplicate key update)竞争优化 - appStatsDao.mergeMinuteCommandStatus(commandStats); - appStatsDao.mergeHourCommandStatus(commandStats); + if (type == ConstUtils.CACHE_REDIS_STANDALONE || type == ConstUtils.CACHE_TYPE_REDIS_CLUSTER) { + jedis = getJedis(appId, host, port); + List modules = jedis.moduleList(); + instanceInfo.setModules(modules); + logger.info("checkInstanceModule {}:{} module info :{}", host, port, modules); + } } catch (Exception e) { - logger.error(e.getMessage() + appId, e); + logger.error("checkInstanceModule {}:{} error , message:{}", host, port, e.getMessage(), e); + } finally { + if (jedis != null) { + jedis.close(); + } + } + } + } + } + return instanceList; + } + + public Map loadModule(long appId, String moduleName) { + Map resultMap = new HashMap(); + int status = SuccessEnum.SUCCESS.value(); + String message = ""; + try { + List instanceList = appService.getAppInstanceInfo(appId); + if (!CollectionUtils.isEmpty(instanceList)) { + for (InstanceInfo instanceInfo : instanceList) { + if (!instanceInfo.isOffline()) { + String host = instanceInfo.getIp(); + int port = instanceInfo.getPort(); + int type = instanceInfo.getType(); + String module_path = ConstUtils.MODULE_BASE_PATH + moduleName; + Jedis jedis = null; + try { + if (type == ConstUtils.CACHE_REDIS_STANDALONE || type == ConstUtils.CACHE_TYPE_REDIS_CLUSTER) { + jedis = getJedis(appId, host, port); + List modules = jedis.moduleList(); + // 未load module + if (!existModule(modules, moduleName)) { + String result = jedis.moduleLoad(module_path); + logger.info(" {}:{} load module path:{} result:{}", host, port, module_path, result); + //写配置文件 + refreshConfig(appId, host, port, module_path); + } + } + } catch (Exception e) { + logger.error(" {}:{} load module path:{} error , message:{}", host, port, module_path, e.getMessage(), e); + status = SuccessEnum.ERROR.value(); + message += String.format("%s:%s load module:%s error \n", host, port, module_path); + } finally { + if (jedis != null) { + jedis.close(); + } + } } } - //写入app分钟统计 - AppStats appStats = getAppStats(appId, collectTime, diffTable, infoMap); + } + + } catch (Exception e) { + logger.error("appid:{} load moduleName :{} error :{}", appId, moduleName, e.getMessage()); + status = SuccessEnum.FAIL.value(); + } + resultMap.put("status", status); + resultMap.put("message", message); + return resultMap; + } + + public boolean refreshConfig(long appid, String host, int port, String modulePath) { + + try { + AppDesc appDesc = appService.getByAppId(appid); + boolean iscluster = false; + if (appDesc.getType() == ConstUtils.CACHE_TYPE_REDIS_CLUSTER) { + iscluster = true; + } + String configName = RedisProtocol.getConfig(port, iscluster); + String filePath = MachineProtocol.CONF_DIR + configName; + if (machineCenter.isK8sMachine(host)) { + filePath = MachineProtocol.getK8sConfDir(host) + configName; + } + + String cmd = String.format("echo \"loadmodule %s\" >> %s", modulePath, filePath); + + String result = sshService.execute(host, cmd); + logger.info("appid:{} {}:{} load module:{} refresh config result:{}", appid, host, port, modulePath, result); + + } catch (Exception e) { + logger.error("appid:{} {}:{} load module:{} refresh config error :{}", appid, host, port, modulePath, e.getMessage(), e); + return false; + } + return true; + } + + public boolean existModule(List modules, String moduleName) { + if (!CollectionUtils.isEmpty(modules)) { + for (Module module : modules) { + if (module.getName().equals(ConstUtils.MODULE_MAP.get(moduleName))) { + logger.info("module:{} alread load in redis!", moduleName); + return true; + } + } + } + return false; + } + + public boolean checkAndLoadModule(long appId, String masterHost, int masterPort, String slaveHost, int slavePort) { + + Jedis jedis = null; + Jedis currentJedis = null; + try { + //原redis实例 + jedis = getJedis(appId, masterHost, masterPort); + //变更redis实例 + currentJedis = getJedis(appId, slaveHost, slavePort); + List modules = jedis.moduleList(); + // 未load module + if(!CollectionUtils.isEmpty(modules)){ + for(Module module : modules){ + String moduleFileName = MapUtils.getString(ConstUtils.MODULE_MAP, module.getName()); + if(!StringUtils.isEmpty(moduleFileName)){ + // 装载redis插件 + String modulePath = String.format("%s%s", ConstUtils.MODULE_BASE_PATH, moduleFileName); + String result = currentJedis.moduleLoad(modulePath); + logger.info(" {}:{} load module path:{} result:{}", slaveHost, slavePort, modulePath, result); + // 写配置文件 + refreshConfig(appId, slaveHost, slavePort, modulePath); + } + } + } + } catch(Exception e) { + logger.error(" {}:{} load module error , message:{}", slaveHost, slaveHost, e.getMessage(), e); + } finally { + if (jedis != null) { + jedis.close(); + } + if (currentJedis != null){ + currentJedis.close(); + } + } + return true; +} + + public Map unloadModule(long appId, String moduleName) { + Map resultMap = new HashMap(); + int status = SuccessEnum.SUCCESS.value(); + String message = ""; + try { + List instanceList = appService.getAppInstanceInfo(appId); + if (!CollectionUtils.isEmpty(instanceList)) { + for (InstanceInfo instanceInfo : instanceList) { + if (!instanceInfo.isOffline()) { + String host = instanceInfo.getIp(); + int port = instanceInfo.getPort(); + int type = instanceInfo.getType(); + Jedis jedis = null; + try { + if (type == ConstUtils.CACHE_REDIS_STANDALONE || type == ConstUtils.CACHE_TYPE_REDIS_CLUSTER) { + jedis = getJedis(appId, host, port); + List modules = jedis.moduleList(); + if (!CollectionUtils.isEmpty(modules)) { + for (Module module : modules) { + if (module.getName().equals(moduleName)) { + String result = jedis.moduleUnload(module.getName()); + logger.info("checkInstanceModule {}:{} unload module:{} result:{}", host, port, moduleName, result); + } + } + } + } + } catch (Exception e) { + logger.error("checkInstanceModule {}:{} unload module:{} error , message:{}", host, port, moduleName, e.getMessage(), e); + status = SuccessEnum.ERROR.value(); + message += String.format("%s:%s unload module:%s error \n", host, port, moduleName); + } finally { + if (jedis != null) { + jedis.close(); + } + } + } + } + } + } catch (Exception e) { + logger.error("appid:{} unload moduleName :{} error :{}", appId, moduleName, e.getMessage()); + status = SuccessEnum.FAIL.value(); + } + resultMap.put("status", status); + resultMap.put("message", message); + return resultMap; + } + +private class RedisKeyCallable extends KeyCallable { + private final long appId; + private final long collectTime; + private final String host; + private final int port; + private final Map> infoMap; + private final Map clusterInfoMap; + + private RedisKeyCallable(long appId, long collectTime, String host, int port, + Map> infoMap, Map clusterInfoMap) { + super(buildFutureKey(appId, collectTime, host, port)); + this.appId = appId; + this.collectTime = collectTime; + this.host = host; + this.port = port; + this.infoMap = infoMap; + this.clusterInfoMap = clusterInfoMap; + } + + @Override + public Boolean execute() { + //比对currentInfoMap和lastInfoMap,计算差值 + long lastCollectTime = ScheduleUtil.getLastCollectTime(collectTime); + Map lastInfoMap = instanceStatsCenter + .queryStandardInfoMap(lastCollectTime, host, port, ConstUtils.REDIS); + + if (lastInfoMap == null || lastInfoMap.isEmpty()) { + logger.error("[redis-lastInfoMap] : lastCollectTime = {} appId={} host:port = {}:{} is null", + lastCollectTime, appId, host, port); + } + //基本统计累加差值 + Table baseDiffTable = getAccumulationDiff(infoMap, lastInfoMap); + fillAccumulationMap(infoMap, baseDiffTable); + + //命令累加差值 + Table commandDiffTable = getCommandsDiff(infoMap, lastInfoMap); + fillAccumulationMap(infoMap, commandDiffTable); + + //内存碎片率差值计算 + //Table otherDiffTable = getDoubleAccumulationDiff(infoMap, lastInfoMap); + //fillDoubleAccumulationMap(infoMap, otherDiffTable); + fillMemFragRatioMap(infoMap); + + Map currentInfoMap = new LinkedHashMap(); + for (Map.Entry> entry : infoMap.entrySet()) { + currentInfoMap.put(entry.getKey().getValue(), entry.getValue()); + } + currentInfoMap.put(ConstUtils.COLLECT_TIME, collectTime); + instanceStatsCenter.saveStandardStats(currentInfoMap, clusterInfoMap, host, port, ConstUtils.REDIS); + + // 更新实例在db中的状态 + InstanceStats instanceStats = getInstanceStats(appId, host, port, infoMap); + if (instanceStats != null) { + instanceStatsDao.updateInstanceStats(instanceStats); + } + + BooleanEnum isMaster = isMaster(infoMap); + if (isMaster == BooleanEnum.TRUE) { + Table diffTable = HashBasedTable.create(); + diffTable.putAll(baseDiffTable); + diffTable.putAll(commandDiffTable); + + long allCommandCount = 0L; + //更新命令统计 + List commandStatsList = getCommandStatsList(appId, collectTime, diffTable); + for (AppCommandStats commandStats : commandStatsList) { + //排除无效命令且存储有累加的数据 + if (RedisExcludeCommand.isExcludeCommand(commandStats.getCommandName()) + || commandStats.getCommandCount() <= 0L) { + continue; + } + allCommandCount += commandStats.getCommandCount(); try { - appStats.setCommandCount(allCommandCount); // todo 数据库(on duplicate key update)竞争优化 - appStatsDao.mergeMinuteAppStats(appStats); - appStatsDao.mergeHourAppStats(appStats); + appStatsDao.mergeMinuteCommandStatus(commandStats); + appStatsDao.mergeHourCommandStatus(commandStats); } catch (Exception e) { logger.error(e.getMessage() + appId, e); } - logger.debug("collect redis info done, appId: {}, instance: {}:{}, time: {}", appId, host, port, - collectTime); } - - return true; + //写入app分钟统计 + AppStats appStats = getAppStats(appId, collectTime, diffTable, infoMap); + try { + appStats.setCommandCount(allCommandCount); + // todo 数据库(on duplicate key update)竞争优化 + appStatsDao.mergeMinuteAppStats(appStats); + appStatsDao.mergeHourAppStats(appStats); + } catch (Exception e) { + logger.error(e.getMessage() + appId, e); + } + logger.debug("collect redis info done, appId: {}, instance: {}:{}, time: {}", appId, host, port, + collectTime); } + + return true; } +} } \ No newline at end of file diff --git a/cachecloud-web/src/main/java/com/sohu/cache/redis/impl/RedisConfigTemplateServiceImpl.java b/cachecloud-web/src/main/java/com/sohu/cache/redis/impl/RedisConfigTemplateServiceImpl.java index eed58893..0ea05628 100644 --- a/cachecloud-web/src/main/java/com/sohu/cache/redis/impl/RedisConfigTemplateServiceImpl.java +++ b/cachecloud-web/src/main/java/com/sohu/cache/redis/impl/RedisConfigTemplateServiceImpl.java @@ -82,6 +82,8 @@ public class RedisConfigTemplateServiceImpl implements RedisConfigTemplateServic private static int WAITING_RESOURCE_SECOND = 5; + private static int WAITING_RETRY_TIMES = 10; + private static int DOWNLOAD_SECONDS = 60 * 5 * 1000; private static String DOWNLOAD_CMD = "cd %s && wget %s && tar -xvf %s && rm -rf %s "; @@ -441,8 +443,8 @@ public Boolean checkAndInstallRedisResource(String host, SystemResource redisRes String redisDir = ConstUtils.getRedisDir(redisResource.getName()); if (!checkMachineRedisVersion(host, redisDir)) { installRedisOnMachine(host, redisResource); - // 验证安装是否成功,最多重试3次 - for (int retry = 1; retry <= 3; retry++) { + // 验证安装是否成功,最多重试10次 + for (int retry = 1; retry <= WAITING_RETRY_TIMES; retry++) { if (checkMachineRedisVersion(host, redisDir)) { logger.info("checkAndInstallRedisResource machine:{} install {} ok!", host, redisResource.getName()); redisInstallFlag = true; @@ -467,8 +469,8 @@ public Boolean checkAndInstallRedisTool(String host, SystemResource redisResourc String redisDir = ConstUtils.getRedisDir(redisResource.getName()); if (!checkMachineRedisTool(host, redisDir)) { installRedisOnMachine(host, redisResource); - // 验证安装是否成功,最多重试3次 - for (int retry = 1; retry <= 3; retry++) { + // 验证安装是否成功,最多重试10次 + for (int retry = 1; retry <= WAITING_RETRY_TIMES; retry++) { if (checkMachineRedisTool(host, redisDir)) { logger.info("checkAndInstallRedisResource machine:{} install {} ok!", host, redisResource.getName()); redisInstallFlag = true; diff --git a/cachecloud-web/src/main/java/com/sohu/cache/redis/impl/RedisDeployCenterImpl.java b/cachecloud-web/src/main/java/com/sohu/cache/redis/impl/RedisDeployCenterImpl.java index ee91159a..b9b9c9bb 100644 --- a/cachecloud-web/src/main/java/com/sohu/cache/redis/impl/RedisDeployCenterImpl.java +++ b/cachecloud-web/src/main/java/com/sohu/cache/redis/impl/RedisDeployCenterImpl.java @@ -443,6 +443,7 @@ private boolean runInstance(AppDesc appDesc, String host, Integer port, int maxM configs.add(RedisConfigEnum.REQUIREPASS.getKey() + ConstUtils.SPACE + password); configs.add(RedisConfigEnum.MASTERAUTH.getKey() + ConstUtils.SPACE + password); } + printConfig(configs); String fileName; String runShell; @@ -488,7 +489,6 @@ public boolean bornConfigAndRunNode(AppDesc appDesc, InstanceInfo instanceInfo, long appId = appDesc.getAppId(); String password = appDesc.getPasswordMd5(); // 获取redis路径 -// RedisVersion redisVersion = redisConfigTemplateService.getRedisVersionById(appDesc.getVersionId()); SystemResource redisResource = resourceService.getResourceById(appDesc.getVersionId()); String redisDir = redisResource == null ? ConstUtils.REDIS_DEFAULT_DIR : ConstUtils.getRedisDir(redisResource.getName()); // 生成配置 @@ -1006,6 +1006,10 @@ public boolean addSlave(long appId, int instanceId, final String slaveHost) { logger.error("{}:{} getNodeId failed", masterHost, masterPort); return false; } + + // 检查主节点是否有加载redis插件 + redisCenter.checkAndLoadModule(appId, masterHost, masterPort, slaveHost, slavePort); + boolean isClusterReplicate = new IdempotentConfirmer() { @Override public boolean execute() { @@ -1702,6 +1706,9 @@ public boolean slaveOf(final long appId, final String masterHost, final int mast final int slavePort) { final Jedis slave = redisCenter.getJedis(appId, slaveHost, slavePort, Protocol.DEFAULT_TIMEOUT * 3, Protocol.DEFAULT_TIMEOUT * 3); try { + // 检查主节点是否有加载redis插件 + redisCenter.checkAndLoadModule(appId, masterHost, masterPort, slaveHost, slavePort); + boolean isSlave = new IdempotentConfirmer() { @Override public boolean execute() { diff --git a/cachecloud-web/src/main/java/com/sohu/cache/schedule/jobs/CleanupDayDimensionalityJob.java b/cachecloud-web/src/main/java/com/sohu/cache/schedule/jobs/CleanupDayDimensionalityJob.java index d7fda64e..1d6bdc0f 100644 --- a/cachecloud-web/src/main/java/com/sohu/cache/schedule/jobs/CleanupDayDimensionalityJob.java +++ b/cachecloud-web/src/main/java/com/sohu/cache/schedule/jobs/CleanupDayDimensionalityJob.java @@ -22,25 +22,29 @@ public class CleanupDayDimensionalityJob extends CacheBaseJob { private static final long serialVersionUID = 8815839394475276540L; - private static final String CLEAN_APP_HOUR_COMMAND_STATISTICS = "delete from app_hour_command_statistics where create_time < ?"; + private static int BATCH_SIZE = 1000; - private static final String CLEAN_APP_MINUTE_COMMAND_STATISTICS = "delete from app_minute_command_statistics where create_time < ?"; + private static final String CLEAN_APP_HOUR_COMMAND_STATISTICS = "delete from app_hour_command_statistics where create_time < ? limit " + BATCH_SIZE; - private static final String CLEAN_APP_HOUR_STATISTICS = "delete from app_hour_statistics where create_time < ?"; + private static final String CLEAN_APP_MINUTE_COMMAND_STATISTICS = "delete from app_minute_command_statistics where create_time < ? limit " + BATCH_SIZE; - private static final String CLEAN_APP_MINUTE_STATISTICS = "delete from app_minute_statistics where create_time < ?"; + private static final String CLEAN_APP_HOUR_STATISTICS = "delete from app_hour_statistics where create_time < ? limit " + BATCH_SIZE; + + private static final String CLEAN_APP_MINUTE_STATISTICS = "delete from app_minute_statistics where create_time < ? limit " + BATCH_SIZE; /** * 清除客户端耗时汇总数据 */ - private static final String CLEAN_APP_CLIENT_MINUTE_COST_TOTAL = "delete from app_client_costtime_minute_stat_total where collect_time < ?"; + private static final String CLEAN_APP_CLIENT_MINUTE_COST_TOTAL = "delete from app_client_costtime_minute_stat_total where collect_time < ? limit " + BATCH_SIZE; //清除服务器统计数据 - private static final String CLEAN_SERVER_STAT_STATISTICS = "delete from server_stat where cdate < ?"; + private static final String CLEAN_SERVER_STAT_STATISTICS = "delete from server_stat where cdate < ? limit " + BATCH_SIZE; /** * 清除实例基础统计 */ - private static final String CLEAN_INSTANCE_MINUTE_STATS = "delete from instance_minute_stats where collect_time < ?"; + private static final String CLEAN_INSTANCE_MINUTE_STATS = "delete from instance_minute_stats where collect_time < ? limit " + BATCH_SIZE; + + JdbcTemplate jdbcTemplate = null; @Override public void action(JobExecutionContext context) { @@ -51,7 +55,7 @@ public void action(JobExecutionContext context) { try { SchedulerContext schedulerContext = context.getScheduler().getContext(); ApplicationContext applicationContext = (ApplicationContext) schedulerContext.get(APPLICATION_CONTEXT_KEY); - JdbcTemplate jdbcTemplate = applicationContext.getBean("jdbcTemplate", JdbcTemplate.class); + jdbcTemplate = applicationContext.getBean("jdbcTemplate", JdbcTemplate.class); Calendar calendar = Calendar.getInstance(); calendar.setTime(new Date()); @@ -59,21 +63,36 @@ public void action(JobExecutionContext context) { // 清除应用&命令统计数据(保存31天) calendar.add(Calendar.DAY_OF_MONTH, -31); Date time = calendar.getTime(); - int cleanCount = jdbcTemplate.update(CLEAN_APP_HOUR_COMMAND_STATISTICS, time); + long cleanCount = 0; + cleanCount = scrollDelete(CLEAN_APP_HOUR_COMMAND_STATISTICS, time); logger.warn("clean_app_hour_command_statistics count={}", cleanCount); - cleanCount = jdbcTemplate.update(CLEAN_APP_MINUTE_COMMAND_STATISTICS, time); + cleanCount = scrollDelete(CLEAN_APP_MINUTE_COMMAND_STATISTICS, time); logger.warn("clean_app_minute_command_statistics count={}", cleanCount); - cleanCount = jdbcTemplate.update(CLEAN_APP_HOUR_STATISTICS, time); + cleanCount = scrollDelete(CLEAN_APP_HOUR_STATISTICS, time); logger.warn("clean_app_hour_statistics count={}", cleanCount); - cleanCount = jdbcTemplate.update(CLEAN_APP_MINUTE_STATISTICS, time); + cleanCount = scrollDelete(CLEAN_APP_MINUTE_STATISTICS, time); logger.warn("clean_app_minute_statistics count={}", cleanCount); + //清除服务器统计数据 + calendar.setTime(new Date()); + calendar.add(Calendar.DAY_OF_MONTH, -7); + String date = new SimpleDateFormat("yyyy-MM-dd").format(calendar.getTime()); + cleanCount = scrollDelete(CLEAN_SERVER_STAT_STATISTICS, date); + logger.warn("clean_server_stat_total count={}", cleanCount); + + long timeFormat = NumberUtils.toLong(new SimpleDateFormat("yyyyMMddHHmm00").format(calendar.getTime())); + //清除进程级别统计数据(保存5天) + long start = System.currentTimeMillis(); + timeFormat = NumberUtils.toLong(new SimpleDateFormat("yyyyMMddHHmm").format(DateUtils.addDays(new Date(), -5))); + cleanCount = scrollDelete(CLEAN_INSTANCE_MINUTE_STATS, timeFormat); + logger.warn("clean_instance_minute_stats timeFormat={} count={} cost={}s", timeFormat, cleanCount, (System.currentTimeMillis() - start) / 1000); + //清除客户端耗时数据(保存2天) ClientReportCostDistriService clientReportCostDistriService = applicationContext.getBean( "clientReportCostDistriService", ClientReportCostDistriService.class); calendar.setTime(new Date()); calendar.add(Calendar.DAY_OF_MONTH, -2); - long timeFormat = NumberUtils.toLong(new SimpleDateFormat("yyyyMMddHHmm00").format(calendar.getTime())); + timeFormat = NumberUtils.toLong(new SimpleDateFormat("yyyyMMddHHmm00").format(calendar.getTime())); cleanCount = clientReportCostDistriService.deleteBeforeCollectTime(timeFormat); logger.warn("clean_app_client_costtime_minute_stat count={}", cleanCount); @@ -93,22 +112,25 @@ public void action(JobExecutionContext context) { cleanCount = clientReportValueDistriService.deleteBeforeCollectTime(timeFormat); logger.warn("clean_app_client_value_minute_stats count={}", cleanCount); - //清除服务器统计数据 - calendar.setTime(new Date()); - calendar.add(Calendar.DAY_OF_MONTH, -7); - String date = new SimpleDateFormat("yyyy-MM-dd").format(calendar.getTime()); - cleanCount = jdbcTemplate.update(CLEAN_SERVER_STAT_STATISTICS, date); - logger.warn("clean_server_stat_total count={}", cleanCount); - - //清除进程级别统计数据(保存5天) - long start = System.currentTimeMillis(); - timeFormat = NumberUtils.toLong(new SimpleDateFormat("yyyyMMddHHmm").format(DateUtils.addDays(new Date(), -5))); - cleanCount = jdbcTemplate.update(CLEAN_INSTANCE_MINUTE_STATS, timeFormat); - logger.warn("clean_instance_minute_stats timeFormat={} count={} cost={}s", timeFormat, cleanCount,(System.currentTimeMillis()-start)/1000); } catch (Exception e) { logger.error(e.getMessage(), e); } } + /** + * 滚动删除表数据 + */ + private long scrollDelete(String sql, Object time) { + long totalCount = 0; + while (true) { + int cleanCount = jdbcTemplate.update(sql, time); + totalCount += cleanCount; + if (cleanCount == 0) { + break; + } + } + return totalCount; + } + } diff --git a/cachecloud-web/src/main/java/com/sohu/cache/stats/admin/impl/CoreAppsStatCenterImpl.java b/cachecloud-web/src/main/java/com/sohu/cache/stats/admin/impl/CoreAppsStatCenterImpl.java index ea0e70e4..87f0405c 100644 --- a/cachecloud-web/src/main/java/com/sohu/cache/stats/admin/impl/CoreAppsStatCenterImpl.java +++ b/cachecloud-web/src/main/java/com/sohu/cache/stats/admin/impl/CoreAppsStatCenterImpl.java @@ -4,6 +4,7 @@ import com.sohu.cache.dao.AppDao; import com.sohu.cache.dao.ResourceDao; import com.sohu.cache.entity.AppDesc; +import com.sohu.cache.machine.MachineCenter; import com.sohu.cache.stats.admin.CoreAppsStatCenter; import com.sohu.cache.util.StringUtil; import com.sohu.cache.web.service.AppService; @@ -39,6 +40,8 @@ public class CoreAppsStatCenterImpl implements CoreAppsStatCenter { private EmailComponent emailComponent; @Autowired private Configuration configuration; + @Autowired + private MachineCenter machineCenter; @Override public boolean sendExpAppsStatDataEmail(String searchDate) { @@ -60,7 +63,10 @@ public boolean sendExpAppsStatDataEmail(String searchDate) { Map>> appClientGatherStatGroup = appService.getFilterAppClientStatGather(-1, searchDate); - noticeExpAppsDaily(searchDate, appDescMap, appClientGatherStatGroup); + // 获取异常的宿主或容器信息 + Map exceptionMachineEnv = machineCenter.getExceptionMachineEnv(DateUtils.addDays(new Date(), -1)); + + noticeExpAppsDaily(searchDate, appDescMap, appClientGatherStatGroup,exceptionMachineEnv); return true; } catch (Exception e) { log.error(e.getMessage(), e); @@ -68,15 +74,19 @@ public boolean sendExpAppsStatDataEmail(String searchDate) { } } - public void noticeExpAppsDaily(String searchDate, Map appDescMap, Map>> appClientGatherStatGroup) { + public void noticeExpAppsDaily(String searchDate, Map appDescMap, Map>> appClientGatherStatGroup,Map exceptionMachineEnv) { String title = String.format("【CacheCloud】%s应用日报", searchDate); Map context = new HashMap<>(); context.put("appDescMap", appDescMap); context.put("appClientGatherStatGroup", appClientGatherStatGroup); + context.put("exceptionMachineEnv", exceptionMachineEnv); context.put("searchDate", searchDate); String mailContent = FreemakerUtils.createText("expAppsDaily.ftl", configuration, context); log.info("noticeExpAppsDaily sendMailToAdmin, title:{}, mailContent:{}", title, mailContent); + // 发送管理员 emailComponent.sendMailToAdmin(title, mailContent); log.info("noticeExpAppsDaily success"); } + + } diff --git a/cachecloud-web/src/main/java/com/sohu/cache/stats/app/ImportAppCenter.java b/cachecloud-web/src/main/java/com/sohu/cache/stats/app/ImportAppCenter.java index c4291cc0..bb009f5d 100644 --- a/cachecloud-web/src/main/java/com/sohu/cache/stats/app/ImportAppCenter.java +++ b/cachecloud-web/src/main/java/com/sohu/cache/stats/app/ImportAppCenter.java @@ -5,6 +5,7 @@ /** * 导入应用 + * * @author leifu * @Date 2016-4-16 * @Time 下午3:42:49 @@ -13,16 +14,15 @@ public interface ImportAppCenter { /** * 检查应用和实例 - * - * @param appDesc + * * @param appInstanceInfo * @return */ - ImportAppResult check(AppDesc appDesc, String appInstanceInfo,String password); + ImportAppResult check(int type, String appInstanceInfo, String password); /** * 导入应用和相关实例 - * + * * @param appDesc * @param appInstanceInfo * @return diff --git a/cachecloud-web/src/main/java/com/sohu/cache/stats/app/RedisMigrateToolCenter.java b/cachecloud-web/src/main/java/com/sohu/cache/stats/app/RedisMigrateToolCenter.java index 75b0cea8..8aa08609 100644 --- a/cachecloud-web/src/main/java/com/sohu/cache/stats/app/RedisMigrateToolCenter.java +++ b/cachecloud-web/src/main/java/com/sohu/cache/stats/app/RedisMigrateToolCenter.java @@ -3,6 +3,7 @@ import com.sohu.cache.constant.AppDataMigrateEnum; import com.sohu.cache.constant.AppDataMigrateResult; import com.sohu.cache.constant.CommandResult; +import com.sohu.cache.entity.AppDataMigrateStatus; import com.sohu.cache.entity.SystemResource; /** @@ -41,8 +42,8 @@ AppDataMigrateResult check(String migrateMachineIp, AppDataMigrateEnum sourceRed * @param targetSourcePass * @return */ - boolean migrate(String migrateMachineIp, AppDataMigrateEnum sourceRedisMigrateEnum, String sourceServers, - AppDataMigrateEnum targetRedisMigrateEnum, String targetServers, long sourceAppId, long targetAppId, String redisSourcePass, String targetSourcePass, long userId, SystemResource resource); + AppDataMigrateStatus migrate(String migrateMachineIp, AppDataMigrateEnum sourceRedisMigrateEnum, String sourceServers, + AppDataMigrateEnum targetRedisMigrateEnum, String targetServers, long sourceAppId, long targetAppId, String redisSourcePass, String targetSourcePass, long userId, SystemResource resource); /** * 比较源和目标的样本数据 diff --git a/cachecloud-web/src/main/java/com/sohu/cache/stats/app/RedisShakeCenter.java b/cachecloud-web/src/main/java/com/sohu/cache/stats/app/RedisShakeCenter.java index 0b224c74..e886c5d3 100644 --- a/cachecloud-web/src/main/java/com/sohu/cache/stats/app/RedisShakeCenter.java +++ b/cachecloud-web/src/main/java/com/sohu/cache/stats/app/RedisShakeCenter.java @@ -3,6 +3,7 @@ import com.sohu.cache.constant.AppDataMigrateEnum; import com.sohu.cache.constant.AppDataMigrateResult; import com.sohu.cache.constant.CommandResult; +import com.sohu.cache.entity.AppDataMigrateStatus; import com.sohu.cache.entity.SystemResource; /** @@ -40,13 +41,13 @@ AppDataMigrateResult check(String migrateMachineIp, AppDataMigrateEnum sourceRed * @param userId * @return */ - boolean migrate(String migrateMachineIp, int source_rdb_parallel, int parallel, - AppDataMigrateEnum sourceRedisMigrateEnum, String sourceServers, - AppDataMigrateEnum targetRedisMigrateEnum, String targetServers, - long sourceAppId, long targetAppId, - String redisSourcePass, String redisTargetPass, - String redisSourceVersion, String redisTargetVersion, - long userId, SystemResource resource); + AppDataMigrateStatus migrate(String migrateMachineIp, int source_rdb_parallel, int parallel, + AppDataMigrateEnum sourceRedisMigrateEnum, String sourceServers, + AppDataMigrateEnum targetRedisMigrateEnum, String targetServers, + long sourceAppId, long targetAppId, + String redisSourcePass, String redisTargetPass, + String redisSourceVersion, String redisTargetVersion, + long userId, SystemResource resource); /** * 关闭迁移 diff --git a/cachecloud-web/src/main/java/com/sohu/cache/stats/app/impl/AppStatsCenterImpl.java b/cachecloud-web/src/main/java/com/sohu/cache/stats/app/impl/AppStatsCenterImpl.java index b56191fd..1f57eec5 100644 --- a/cachecloud-web/src/main/java/com/sohu/cache/stats/app/impl/AppStatsCenterImpl.java +++ b/cachecloud-web/src/main/java/com/sohu/cache/stats/app/impl/AppStatsCenterImpl.java @@ -293,14 +293,19 @@ public AppDetailVO getAppDetail(long appId) { //2.判断版本是否可以升级 List versionList = resourceDao.getResourceList(ResourceEnum.REDIS.getValue()); for (SystemResource version : versionList) { - // 大版本同一版本且大于当前版本号 - int versionTag = Integer.parseInt(version.getName().replaceAll("redis-","").replaceAll("\\.","")); - int currentTag = Integer.parseInt(redisResource.getName().replaceAll("redis-","").replaceAll("\\.","")); - // 支持小版本号升级 - String versionStr = redisResource.getName().substring(0, redisResource.getName().lastIndexOf(".")); - if (version.getName().indexOf(versionStr) > -1 && versionTag > currentTag) { - appDesc.setIsVersionUpgrade(1); - break; + try { + // 大版本同一版本且大于当前版本号 + int versionTag = Integer.parseInt(version.getName().replaceAll("redis-","").replaceAll("\\.","")); + int currentTag = Integer.parseInt(redisResource.getName().replaceAll("redis-","").replaceAll("\\.","")); + // 支持小版本号升级 + String versionStr = redisResource.getName().substring(0, redisResource.getName().lastIndexOf(".")); + if (version.getName().indexOf(versionStr) > -1 && versionTag > currentTag) { + appDesc.setIsVersionUpgrade(1); + break; + } + } catch (Exception e) { + logger.error("parse version:{} {} exception : {}", redisResource.getName(), version.getName(), e.getMessage(), e); + appDesc.setIsVersionUpgrade(0); } } } else { diff --git a/cachecloud-web/src/main/java/com/sohu/cache/stats/app/impl/ImportAppCenterImpl.java b/cachecloud-web/src/main/java/com/sohu/cache/stats/app/impl/ImportAppCenterImpl.java index a122f7f4..fba83ed7 100644 --- a/cachecloud-web/src/main/java/com/sohu/cache/stats/app/impl/ImportAppCenterImpl.java +++ b/cachecloud-web/src/main/java/com/sohu/cache/stats/app/impl/ImportAppCenterImpl.java @@ -53,54 +53,38 @@ public class ImportAppCenterImpl implements ImportAppCenter { private InstanceStatsDao instanceStatsDao; @Override - public ImportAppResult check(AppDesc appDesc, String appInstanceInfo, String password) { - // 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.实例信息是否为空 + public ImportAppResult check(int type, String appInstanceInfo, String password) { + // 1.实例信息是否为空 if (StringUtils.isBlank(appInstanceInfo)) { return ImportAppResult.fail("实例详情为空"); } String[] appInstanceDetails = appInstanceInfo.split("\n"); - // 4.检查实例信息格式是否正确 + String masterNameInput = ""; + // 2.检查实例信息格式是否正确 for (String appInstance : appInstanceDetails) { if (StringUtils.isBlank(appInstance)) { return ImportAppResult.fail("应用实例信息有空行"); } String[] instanceItems = appInstance.split(":"); - if (instanceItems.length != 3) { - return ImportAppResult.fail("应用实例信息" + appInstance + "格式错误,必须有2个冒号"); + if (instanceItems.length != 2) { + return ImportAppResult.fail("应用实例信息" + appInstance + "格式错误,必须以冒号分隔"); } + + // 2.1检查端口是否为整数 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) { + if ((!portIsDigit) && (type != ConstUtils.CACHE_REDIS_SENTINEL)) { return ImportAppResult.fail(appInstance + "中的port不是整数"); + } else if ((!portIsDigit) && (type == ConstUtils.CACHE_REDIS_SENTINEL)) { + masterNameInput = instanceItems[0]; + continue; } int port = NumberUtils.toInt(portStr); - // 4.3.检查ip:port是否已经在instance_info表和instance_statistics中 + // 2.2检查ip:port是否已经在instance_info表和instance_statistics中 int count = instanceDao.getCountByIpAndPort(ip, port); if (count > 0) { return ImportAppResult.fail(appInstance + "中ip:port已经在instance_info存在"); @@ -109,11 +93,9 @@ public ImportAppResult check(AppDesc appDesc, String appInstanceInfo, String pas if (instanceStats != null) { return ImportAppResult.fail(appInstance + "中ip:port已经在instance_statistics存在"); } - // 4.4.检查Redis实例是否存活 - String memoryOrMasterName = instanceItems[2]; - int memoryOrMasterNameInt = NumberUtils.toInt(memoryOrMasterName); + // 3.2检查Redis实例是否存活 boolean isRun; - if (memoryOrMasterNameInt > 0) { + if (StringUtils.isNotEmpty(password)) { // 外部导入密码以外部密码为主(cc内部采用一定规则加密) isRun = redisCenter.isRun(ip, port, password); } else { @@ -123,30 +105,16 @@ public ImportAppResult check(AppDesc appDesc, String appInstanceInfo, String pas 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 + //3.3判断sentinel模式下,masterName是否正确 + if (StringUtils.isNotEmpty(masterNameInput) && (type == ConstUtils.CACHE_REDIS_SENTINEL)) { String masterName = getSentinelMasterName(ip, port); - if (StringUtils.isEmpty(masterName) || !memoryOrMasterName.equals(masterName)) { + if (StringUtils.isEmpty(masterName) || !masterNameInput.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不是整数"); + + masterNameInput + "不一致"); } } - } - - // 5. 节点之间关系是否正确,这个比较麻烦,还是依赖于用户填写的正确性。 + } return ImportAppResult.success(); } diff --git a/cachecloud-web/src/main/java/com/sohu/cache/stats/app/impl/RedisMigrateToolCenterImpl.java b/cachecloud-web/src/main/java/com/sohu/cache/stats/app/impl/RedisMigrateToolCenterImpl.java index fb545287..6816011e 100644 --- a/cachecloud-web/src/main/java/com/sohu/cache/stats/app/impl/RedisMigrateToolCenterImpl.java +++ b/cachecloud-web/src/main/java/com/sohu/cache/stats/app/impl/RedisMigrateToolCenterImpl.java @@ -190,7 +190,7 @@ private AppDataMigrateResult checkMigrateConfig(String migrateMachineIp, AppData } @Override - public boolean migrate(String migrateMachineIp, AppDataMigrateEnum sourceRedisMigrateEnum, String sourceServers, + public AppDataMigrateStatus migrate(String migrateMachineIp, AppDataMigrateEnum sourceRedisMigrateEnum, String sourceServers, AppDataMigrateEnum targetRedisMigrateEnum, String targetServers, long sourceAppId, long targetAppId, String redisSourcePass, String redisTargetPass, long userId, SystemResource resource) { // 1. 生成配置 @@ -203,7 +203,7 @@ public boolean migrate(String migrateMachineIp, AppDataMigrateEnum sourceRedisMi String logFileName = "rmt-" + timestamp + ".log"; boolean uploadConfig = createRemoteFile(migrateMachineIp, confileFileName, configContent); if (!uploadConfig) { - return false; + return null; } // 3. 开始执行: 指定的配置名、目录、日志名 String cmd = ConstUtils.getRedisMigrateToolCmd(resource.getName()) + " -c " + ConstUtils.getRedisMigrateToolDir() + confileFileName @@ -213,7 +213,7 @@ public boolean migrate(String migrateMachineIp, AppDataMigrateEnum sourceRedisMi SSHUtil.execute(migrateMachineIp, cmd); } catch (Exception e) { logger.error(e.getMessage(), e); - return false; + return null; } // 4. 记录执行记录 AppDataMigrateStatus appDataMigrateStatus = new AppDataMigrateStatus(); @@ -234,7 +234,7 @@ public boolean migrate(String migrateMachineIp, AppDataMigrateEnum sourceRedisMi appDataMigrateStatus.setStatus(AppDataMigrateStatusEnum.PREPARE.getStatus()); appDataMigrateStatusDao.save(appDataMigrateStatus); - return true; + return appDataMigrateStatus; } /** diff --git a/cachecloud-web/src/main/java/com/sohu/cache/stats/app/impl/RedisShakeCenterImpl.java b/cachecloud-web/src/main/java/com/sohu/cache/stats/app/impl/RedisShakeCenterImpl.java index f57e5882..6eabcc18 100644 --- a/cachecloud-web/src/main/java/com/sohu/cache/stats/app/impl/RedisShakeCenterImpl.java +++ b/cachecloud-web/src/main/java/com/sohu/cache/stats/app/impl/RedisShakeCenterImpl.java @@ -72,7 +72,7 @@ public AppDataMigrateResult check(String migrateMachineIp, AppDataMigrateEnum so } @Override - public boolean migrate(String migrateMachineIp, int source_rdb_parallel, int parallel, + public AppDataMigrateStatus migrate(String migrateMachineIp, int source_rdb_parallel, int parallel, AppDataMigrateEnum sourceRedisMigrateEnum, String sourceServers, AppDataMigrateEnum targetRedisMigrateEnum, String targetServers, long sourceAppId, long targetAppId, @@ -90,7 +90,7 @@ public boolean migrate(String migrateMachineIp, int source_rdb_parallel, int par String logFileName = fileName + timestamp + ".log"; boolean uploadConfig = createRemoteFile(migrateMachineIp, confileFileName, configContent, resource); if (!uploadConfig) { - return false; + return null; } // 3. 开始执行: 指定的配置名、目录、日志名 String cmd = ConstUtils.getRedisShakeLinuxCmd(resource.getName()) + " -conf=" + ConstUtils.getRedisShakeConfDir(resource.getName()) + confileFileName + " -type=sync"; @@ -100,7 +100,7 @@ public boolean migrate(String migrateMachineIp, int source_rdb_parallel, int par SSHUtil.execute(migrateMachineIp, cmd); } catch (Exception e) { log.error(e.getMessage(), e); - return false; + return null; } // 4. 记录执行记录 @@ -123,7 +123,7 @@ public boolean migrate(String migrateMachineIp, int source_rdb_parallel, int par appDataMigrateStatus.setStatus(AppDataMigrateStatusEnum.PREPARE.getStatus()); appDataMigrateStatusDao.save(appDataMigrateStatus); - return true; + return appDataMigrateStatus; } @Override diff --git a/cachecloud-web/src/main/java/com/sohu/cache/stats/instance/InstanceDeployCenter.java b/cachecloud-web/src/main/java/com/sohu/cache/stats/instance/InstanceDeployCenter.java index afd3e733..dd21da1a 100644 --- a/cachecloud-web/src/main/java/com/sohu/cache/stats/instance/InstanceDeployCenter.java +++ b/cachecloud-web/src/main/java/com/sohu/cache/stats/instance/InstanceDeployCenter.java @@ -74,4 +74,6 @@ boolean modifyInstanceConfig(long appId, Long appAuditId, String host, int port, * @return */ List checkAndStartExceptionInstance(String ip, Boolean isAlert); + + } diff --git a/cachecloud-web/src/main/java/com/sohu/cache/stats/instance/impl/InstanceAlertConfigServiceImpl.java b/cachecloud-web/src/main/java/com/sohu/cache/stats/instance/impl/InstanceAlertConfigServiceImpl.java index d9a13164..20ba2ed2 100644 --- a/cachecloud-web/src/main/java/com/sohu/cache/stats/instance/impl/InstanceAlertConfigServiceImpl.java +++ b/cachecloud-web/src/main/java/com/sohu/cache/stats/instance/impl/InstanceAlertConfigServiceImpl.java @@ -333,7 +333,7 @@ public int compare(InstanceAlertValueResult o1, InstanceAlertValueResult o2) { Map context = new HashMap<>(); context.put("instanceAlertValueResultList", instanceAlertValueResultList); String emailContent = FreemakerUtils.createText("instanceAlert.ftl", configuration, context); - emailComponent.sendMailToAdmin(emailTitle, emailContent); + emailComponent.sendMailToAdmin(emailTitle, emailContent.replaceAll("\t","")); // 5.发送给客户端定制报警 for (Map.Entry> appAlert : appAlertMap.entrySet()) { diff --git a/cachecloud-web/src/main/java/com/sohu/cache/task/constant/ResourceEnum.java b/cachecloud-web/src/main/java/com/sohu/cache/task/constant/ResourceEnum.java index 93849c78..4ab568da 100644 --- a/cachecloud-web/src/main/java/com/sohu/cache/task/constant/ResourceEnum.java +++ b/cachecloud-web/src/main/java/com/sohu/cache/task/constant/ResourceEnum.java @@ -8,11 +8,12 @@ public enum ResourceEnum { ALL(0, "所有资源"), Repository(1, "仓库管理"), SCRIPT(2, "脚本管路"), - REDIS(3, "redis资源管理"), + REDIS(3, "Redis资源管理"), SSHKEY(4, "sshkey管理"), DOCKERFILE(5, "镜像管理"), DIR(6, "目录管理"), - TOOL(7, "迁移工具管理"); + TOOL(7, "迁移工具管理"), + MODULE(8, "Redis模块"); private int value; diff --git a/cachecloud-web/src/main/java/com/sohu/cache/task/tasks/RedisClusterAppDeployTask.java b/cachecloud-web/src/main/java/com/sohu/cache/task/tasks/RedisClusterAppDeployTask.java index 51b8dd25..049cf43e 100644 --- a/cachecloud-web/src/main/java/com/sohu/cache/task/tasks/RedisClusterAppDeployTask.java +++ b/cachecloud-web/src/main/java/com/sohu/cache/task/tasks/RedisClusterAppDeployTask.java @@ -121,10 +121,10 @@ public TaskFlowStatusEnum init() { //审核id auditId = MapUtils.getLongValue(paramMap, TaskConstants.AUDIT_ID_KEY); - if (auditId <= 0) { - logger.error(marker, "task {} auditId {} is wrong", taskId, auditId); - return TaskFlowStatusEnum.ABORT; - } +// if (auditId <= 0) { +// logger.error(marker, "task {} auditId {} is wrong", taskId, auditId); +// return TaskFlowStatusEnum.ABORT; +// } //maxMemory maxMemory = MapUtils.getIntValue(paramMap, TaskConstants.REDIS_SERVER_MAX_MEMORY_KEY); @@ -433,7 +433,9 @@ public TaskFlowStatusEnum setPasswd() { */ public TaskFlowStatusEnum updateAudit() { try { - appAuditDao.updateAppAudit(auditId, AppCheckEnum.APP_ALLOCATE_RESOURCE.value()); + if (auditId > 0){ + appAuditDao.updateAppAudit(auditId, AppCheckEnum.APP_ALLOCATE_RESOURCE.value()); + } return TaskFlowStatusEnum.SUCCESS; } catch (Exception e) { logger.error(marker, e.getMessage(), e); diff --git a/cachecloud-web/src/main/java/com/sohu/cache/task/tasks/RedisSentinelAppDeployTask.java b/cachecloud-web/src/main/java/com/sohu/cache/task/tasks/RedisSentinelAppDeployTask.java index 64ddc751..f5415f73 100644 --- a/cachecloud-web/src/main/java/com/sohu/cache/task/tasks/RedisSentinelAppDeployTask.java +++ b/cachecloud-web/src/main/java/com/sohu/cache/task/tasks/RedisSentinelAppDeployTask.java @@ -132,10 +132,10 @@ public TaskFlowStatusEnum init() { //审核id auditId = MapUtils.getLongValue(paramMap, TaskConstants.AUDIT_ID_KEY); - if (auditId <= 0) { - logger.error(marker, "task {} auditId {} is wrong", taskId, auditId); - return TaskFlowStatusEnum.ABORT; - } +// if (auditId <= 0) { +// logger.error(marker, "task {} auditId {} is wrong", taskId, auditId); +// return TaskFlowStatusEnum.ABORT; +// } //maxMemory maxMemory = MapUtils.getIntValue(paramMap, TaskConstants.REDIS_SERVER_MAX_MEMORY_KEY); @@ -215,7 +215,7 @@ public TaskFlowStatusEnum checkResourceAllow() { return TaskFlowStatusEnum.ABORT; } MachineInfo machineInfo = machineDao.getMachineInfoByIp(redisServerIp); - if(machineInfo == null){ + if (machineInfo == null) { logger.error(marker, "redis server machine info is null"); return TaskFlowStatusEnum.ABORT; } @@ -346,7 +346,7 @@ public TaskFlowStatusEnum saveSentinelInstanceNodes() { return TaskFlowStatusEnum.ABORT; } // 保存sentinel实例信息 - if(!StringUtils.isEmpty(masterName)){ + if (!StringUtils.isEmpty(masterName)) { for (RedisSentinelNode redisSentinelNode : redisSentinelNodes) { Integer instanceId = saveInstance(appId, redisSentinelNode.getIp(), redisSentinelNode.getPort(), 0, InstanceTypeEnum.REDIS_SENTINEL, InstanceStatusEnum.NEW_STATUS, masterName); @@ -354,7 +354,7 @@ public TaskFlowStatusEnum saveSentinelInstanceNodes() { return TaskFlowStatusEnum.ABORT; } } - }else { + } else { logger.error(marker, "appId {} masterName {} is empty", appId, masterName); return TaskFlowStatusEnum.ABORT; } @@ -555,7 +555,7 @@ public TaskFlowStatusEnum deployCollection() { return TaskFlowStatusEnum.SUCCESS; } - public TaskFlowStatusEnum setPasswd(){ + public TaskFlowStatusEnum setPasswd() { try { if (appId > 0) { @@ -579,9 +579,10 @@ public TaskFlowStatusEnum setPasswd(){ /** * 清理sentinel实例状态 + * * @return */ - public TaskFlowStatusEnum sentinelReset(){ + public TaskFlowStatusEnum sentinelReset() { try { redisDeployCenter.sentinelReset(appId); @@ -600,7 +601,9 @@ public TaskFlowStatusEnum sentinelReset(){ */ public TaskFlowStatusEnum updateAudit() { try { - appAuditDao.updateAppAudit(auditId, AppCheckEnum.APP_ALLOCATE_RESOURCE.value()); + if (auditId > 0) { + appAuditDao.updateAppAudit(auditId, AppCheckEnum.APP_ALLOCATE_RESOURCE.value()); + } return TaskFlowStatusEnum.SUCCESS; } catch (Exception e) { logger.error(marker, e.getMessage(), e); diff --git a/cachecloud-web/src/main/java/com/sohu/cache/task/tasks/RedisStandaloneAppDeployTask.java b/cachecloud-web/src/main/java/com/sohu/cache/task/tasks/RedisStandaloneAppDeployTask.java index 5d6d8017..6e70a546 100644 --- a/cachecloud-web/src/main/java/com/sohu/cache/task/tasks/RedisStandaloneAppDeployTask.java +++ b/cachecloud-web/src/main/java/com/sohu/cache/task/tasks/RedisStandaloneAppDeployTask.java @@ -29,13 +29,14 @@ *

* Description:RedisStandalone部署任务流 *

+ * * @author chenshi * @version 1.0 * @date 2019/1/9 */ @Component("RedisStandaloneAppDeployTask") @Scope(SCOPE_PROTOTYPE) -public class RedisStandaloneAppDeployTask extends BaseTask{ +public class RedisStandaloneAppDeployTask extends BaseTask { /** * 应用id */ @@ -114,10 +115,10 @@ public TaskFlowStatusEnum init() { //审核id auditId = MapUtils.getLongValue(paramMap, TaskConstants.AUDIT_ID_KEY); - if (auditId <= 0) { - logger.error(marker, "task {} auditId {} is wrong", taskId, auditId); - return TaskFlowStatusEnum.ABORT; - } +// if (auditId <= 0) { +// logger.error(marker, "task {} auditId {} is wrong", taskId, auditId); +// return TaskFlowStatusEnum.ABORT; +// } //maxMemory maxMemory = MapUtils.getIntValue(paramMap, TaskConstants.REDIS_SERVER_MAX_MEMORY_KEY); @@ -219,7 +220,7 @@ public TaskFlowStatusEnum generateInstanceNodes() { } // 设置master节点 for (RedisServerNode redisServerNode : redisServerNodes) { - if(redisServerNode.isMaster()){ + if (redisServerNode.isMaster()) { redisServerNodes.remove(redisServerNode); } } @@ -365,7 +366,9 @@ public TaskFlowStatusEnum setPasswd() { */ public TaskFlowStatusEnum updateAudit() { try { - appAuditDao.updateAppAudit(auditId, AppCheckEnum.APP_ALLOCATE_RESOURCE.value()); + if (auditId > 0) { + appAuditDao.updateAppAudit(auditId, AppCheckEnum.APP_ALLOCATE_RESOURCE.value()); + } return TaskFlowStatusEnum.SUCCESS; } catch (Exception e) { logger.error(marker, e.getMessage(), e); diff --git a/cachecloud-web/src/main/java/com/sohu/cache/task/util/AppWechatUtil.java b/cachecloud-web/src/main/java/com/sohu/cache/task/util/AppWechatUtil.java index 09c41b80..a98fa8b3 100644 --- a/cachecloud-web/src/main/java/com/sohu/cache/task/util/AppWechatUtil.java +++ b/cachecloud-web/src/main/java/com/sohu/cache/task/util/AppWechatUtil.java @@ -7,6 +7,7 @@ import com.sohu.cache.entity.AppUser; import com.sohu.cache.stats.app.AppStatsCenter; import com.sohu.cache.util.ConstUtils; +import com.sohu.cache.util.EnvUtil; import com.sohu.cache.web.service.AppService; import com.sohu.cache.web.service.UserService; import org.apache.commons.lang.StringUtils; @@ -14,6 +15,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; import org.springframework.stereotype.Component; import java.text.SimpleDateFormat; @@ -44,6 +46,8 @@ public class AppWechatUtil { @Autowired private AppStatsCenter appStatsCenter; + @Autowired + private Environment environment; /** * 应用状态通知 @@ -52,6 +56,9 @@ public class AppWechatUtil { * @param appAudit */ public void noticeAppResult(AppDesc appDesc, AppAudit appAudit) { + if (EnvUtil.isDev(environment)) { + return; + } try { long userId = appDesc.getUserId(); AppUser appUser = userService.get(userId); @@ -61,18 +68,18 @@ public void noticeAppResult(AppDesc appDesc, AppAudit appAudit) { StringBuffer appCreateWeChatContent = new StringBuffer(); appCreateWeChatContent - .append(String.format("
申请类型: %s
", appAudit.getTypeDesc())); - appCreateWeChatContent.append(String.format("申请描述: %s
", appAudit.getInfo())); - appCreateWeChatContent.append(String.format("申请时间: %s
", appAudit.getCreateTimeFormat())); - appCreateWeChatContent.append(String.format("申请人员: %s
", applyAppUser.getChName())); + .append(String.format("申请类型: %s \n", appAudit.getTypeDesc())); + appCreateWeChatContent.append(String.format("申请描述: %s \n", appAudit.getInfo())); + appCreateWeChatContent.append(String.format("申请时间: %s \n", appAudit.getCreateTimeFormat())); + appCreateWeChatContent.append(String.format("申请人员: %s \n", applyAppUser.getChName())); appCreateWeChatContent - .append(String.format("
申请状态: %s
", appAudit.getStatusDesc())); - appCreateWeChatContent.append(String.format("集群名称: %s
", appDesc.getName())); + .append(String.format("申请状态: %s \n", appAudit.getStatusDesc())); + appCreateWeChatContent.append(String.format("集群名称: %s\n", appDesc.getName())); //appCreateWeChatContent.append(String.format("集群容量: %s GB
", appDesc.getForecastMem())); //appCreateWeChatContent.append(String.format("集群机房: %s(%s)
", machineRoomName, machineLogicName)); if (StringUtils.isNotBlank(appAudit.getRefuseReason())) { appCreateWeChatContent - .append(String.format("
处理描述: %s
", appAudit.getRefuseReason())); + .append(String.format("处理描述: %s \n", appAudit.getRefuseReason())); } Set weChatSet = new HashSet(); //weChatSet.addAll(ConstUtils.getAdminWeChatList()); @@ -93,6 +100,10 @@ public void noticeAppResult(AppDesc appDesc, AppAudit appAudit) { * @param operateUser */ public void noticeAppClaim(AppDesc appDesc, AppUser operateUser) { + if (EnvUtil.isDev(environment)) { + return; + } + try { // String machineRoomName = appDesc.getMachineRoom(); // String machineLogicName; @@ -104,11 +115,11 @@ public void noticeAppClaim(AppDesc appDesc, AppUser operateUser) { // } StringBuffer appCreateWeChatContent = new StringBuffer(); - appCreateWeChatContent.append(String.format("
事件类型: %s
", "集群认领")); + appCreateWeChatContent.append(String.format("事件类型: %s \n", "集群认领")); appCreateWeChatContent.append(String.format("认领人员: %s
", operateUser.getChName())); appCreateWeChatContent.append(String - .format("认领时间: %s
", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()))); - appCreateWeChatContent.append(String.format("集群名称: %s
", appDesc.getName())); + .format("认领时间: %s \n", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()))); + appCreateWeChatContent.append(String.format("集群名称: %s \n", appDesc.getName())); //appCreateWeChatContent.append(String.format("集群类型: %s
", appDesc.getDbTypeDesc())); //appCreateWeChatContent.append(String.format("集群机房: %s(%s)
", machineRoomName, machineLogicName)); @@ -124,14 +135,17 @@ public void noticeAppClaim(AppDesc appDesc, AppUser operateUser) { } public void noticeRmtSyncFinish(long sourceAppId, long targetAppId) { + if (EnvUtil.isDev(environment)) { + return; + } try { AppDesc sourceAppDesc = appService.getByAppId(sourceAppId); AppDesc targetAppDesc = appService.getByAppId(targetAppId); StringBuffer content = new StringBuffer(); - content.append("
扩容同步完成
"); - content.append(String.format("源集群 : %s
", sourceAppDesc.getName())); - content.append(String.format("目集群 : %s
", targetAppDesc.getName())); + content.append("扩容同步完成\n"); + content.append(String.format("源集群 : %s \n", sourceAppDesc.getName())); + content.append(String.format("目集群 : %s \n", targetAppDesc.getName())); content.append("可以执行redis.sh --update"); weChatComponent.sendWeChatToAdmin(ConstUtils.NOTICE_TITLE, content.toString()); } catch (Exception e) { @@ -140,9 +154,12 @@ public void noticeRmtSyncFinish(long sourceAppId, long targetAppId) { } public void noticeTaskAbort(long taskId, String stepName) { + if (EnvUtil.isDev(environment)) { + return; + } try { StringBuffer content = new StringBuffer(); - content.append(String.format("
任务id=%s,stepName=%s中断
,请查看", taskId, stepName)); + content.append(String.format("任务id=%s,stepName=%s中断,请查看", taskId, stepName)); weChatComponent.sendWeChatToAdmin(ConstUtils.NOTICE_TITLE, content.toString()); } catch (Exception e) { logger.error(e.getMessage(), e); @@ -150,11 +167,14 @@ public void noticeTaskAbort(long taskId, String stepName) { } public void noticeAppScaleStop(long sourceAppId, long targetAppId) { + if (EnvUtil.isDev(environment)) { + return; + } try { AppDesc sourceAppDesc = appService.getByAppId(sourceAppId); AppDesc targetAppDesc = appService.getByAppId(targetAppId); StringBuffer content = new StringBuffer(); - content.append(String.format("
集群%s->%s的rmt被强制中断,请查看cc任务日志
", + content.append(String.format("集群%s->%s的rmt被强制中断,请查看cc任务日志", sourceAppDesc.getName(), targetAppDesc.getName())); weChatComponent.sendWeChatToAdmin(ConstUtils.NOTICE_TITLE, content.toString()); } catch (Exception e) { @@ -163,9 +183,12 @@ public void noticeAppScaleStop(long sourceAppId, long targetAppId) { } public void noticeRmtUseMaster(long sourceAppId, long targetAppId) { + if (EnvUtil.isDev(environment)) { + return; + } try { StringBuffer content = new StringBuffer(); - content.append(String.format("
集群迁移%s->%s,使用了master做source
", sourceAppId, + content.append(String.format("集群迁移%s->%s,使用了master做source", sourceAppId, targetAppId)); weChatComponent.sendWeChatToAdmin(ConstUtils.NOTICE_TITLE, content.toString()); } catch (Exception e) { @@ -180,6 +203,9 @@ public void noticeRmtUseMaster(long sourceAppId, long targetAppId) { * @param port */ public void noticeWildInstance(String ip, int port) { + if (EnvUtil.isDev(environment)) { + return; + } try { StringBuffer content = new StringBuffer(); content.append(String.format("%s:%s is not in instance_info", ip, port)); diff --git a/cachecloud-web/src/main/java/com/sohu/cache/util/ConstUtils.java b/cachecloud-web/src/main/java/com/sohu/cache/util/ConstUtils.java index 6752aa7e..dde2b6fe 100644 --- a/cachecloud-web/src/main/java/com/sohu/cache/util/ConstUtils.java +++ b/cachecloud-web/src/main/java/com/sohu/cache/util/ConstUtils.java @@ -1,8 +1,12 @@ package com.sohu.cache.util; +import com.sohu.cache.web.enums.ModuleEnum; import com.sohu.cache.web.enums.SshAuthTypeEnum; +import org.apache.commons.collections.map.HashedMap; +import java.util.ArrayList; import java.util.List; +import java.util.Map; /** * cachecloud常量 @@ -140,6 +144,23 @@ public class ConstUtils { public static final String DEFAULT_PUBLIC_USERNAME = "cachecloud"; public static final String MEMCACHE_USER = "memcached_server"; public static final String MEMCACHE_KEY_PEM = "/home/redis_server/cachecloud/cachecloud/memcache_server_key/id_rsa"; + /** + * module info + */ + public static final String MODULE_BASE_PATH = "/opt/cachecloud/module/"; + public static List MODULE_LIST = new ArrayList(); + public static Map MODULE_MAP = new HashedMap(); + static { + // 布隆过滤器 & redis search + MODULE_LIST.add(ModuleEnum.BLOOMFILTER_SO.getValue()); + MODULE_LIST.add(ModuleEnum.REDISSEARCH_SO.getValue()); + // 存redis moduleName & 模块文件名 一一对应关系 + MODULE_MAP.put(ModuleEnum.BLOOMFILTER_SO.getValue(),ModuleEnum.BLOOMFILTER_NAME.getValue()); + MODULE_MAP.put(ModuleEnum.REDISSEARCH_SO.getValue(),ModuleEnum.REDISSEARCH_NAME.getValue()); + MODULE_MAP.put(ModuleEnum.BLOOMFILTER_NAME.getValue(),ModuleEnum.BLOOMFILTER_SO.getValue()); + MODULE_MAP.put(ModuleEnum.REDISSEARCH_NAME.getValue(),ModuleEnum.REDISSEARCH_SO.getValue()); + } + /** * 管理员相关 */ diff --git a/cachecloud-web/src/main/java/com/sohu/cache/web/controller/AppController.java b/cachecloud-web/src/main/java/com/sohu/cache/web/controller/AppController.java index 14bb33cb..aae0da0b 100644 --- a/cachecloud-web/src/main/java/com/sohu/cache/web/controller/AppController.java +++ b/cachecloud-web/src/main/java/com/sohu/cache/web/controller/AppController.java @@ -913,6 +913,18 @@ public ModelAndView doAppInit(HttpServletRequest request, return new ModelAndView("app/jobIndex/appInitIndex"); } + @RequestMapping(value = "/import") + public ModelAndView doAppImport(HttpServletRequest request, + HttpServletResponse response, Model model) { + List userList = userService.getAllUser(); + List roomList = machineCenter.getEffectiveRoom(); + List versionList = resourceService.getResourceList(ResourceEnum.REDIS.getValue()); + model.addAttribute("userList", userList); + model.addAttribute("roomList", roomList); + model.addAttribute("versionList", versionList); + + return new ModelAndView("app/jobIndex/appImportIndex"); + } @RequestMapping(value = "/jobs") public ModelAndView doAppJobs(HttpServletRequest request, @@ -1170,6 +1182,62 @@ public ModelAndView submitJobApplication(HttpServletRequest request, } + @RequestMapping(value = "import/submit", method = RequestMethod.POST) + public ModelAndView doAppImportSubmit(HttpServletRequest request, + HttpServletResponse response, Model model, AppDesc appDesc, String memSize) { + AppUser appUser = getUserInfo(request); + + //创建appImport + AppImport appImport = new AppImport(); + //appDesc入库 + if (appDesc != null) { + Timestamp now = new Timestamp(new Date().getTime()); + appDesc.setCreateTime(now); + appDesc.setPassedTime(now); + appDesc.setVerId(1); + appDesc.setStatus((short) AppStatusEnum.STATUS_INITIALIZE.getStatus()); + appDesc.setHitPrecentAlertValue(0); + appDesc.setIsAccessMonitor(AppUserAlertEnum.NO.value()); + appService.save(appDesc); + // 保存应用和用户的关系 + String officers = appDesc.getOfficer(); + if (!StringUtils.isEmpty(officers)) { + for (String officerId : officers.split(",")) { + if (!StringUtils.isEmpty(officerId)) { + appService.saveAppToUser(appDesc.getAppId(), Long.parseLong(officerId)); + } + } + } + // 更新appKey + long appId = appDesc.getAppId(); + appService.updateAppKey(appId); + } + appImport.setAppId(appDesc.getAppId()); + appImport.setMemSize(NumberUtils.toInt(memSize)); + appImport.setSourceType(NumberUtils.toInt(request.getParameter("sourceType"))); + appImport.setInstanceInfo(request.getParameter("appInstanceInfo")); + appImport.setRedisPassword(request.getParameter("password")); + appImport.setStatus(0); + appImportDao.save(appImport); + + //appAudit入库 + AppAudit appAudit = new AppAudit(); + appAudit.setAppId(appDesc.getAppId()); + appAudit.setUserId(appUser.getId()); + appAudit.setUserName(appUser.getName()); + appAudit.setInfo("迁移到应用:" + appDesc.getAppId() + " " + appDesc.getName()); + appAudit.setModifyTime(new Date()); + appAudit.setStatus(AppCheckEnum.APP_WATING_CHECK.value()); + appAudit.setType(AppAuditType.APP_IMPORT.getValue()); + appAudit.setParam1(String.valueOf(appImport.getId())); + Date now = new Date(); + appAudit.setCreateTime(now); + appAudit.setModifyTime(now); + appAuditDao.insertAppAudit(appAudit); + + return new ModelAndView("redirect:/admin/app/jobs"); + } + /** * 添加应用 * diff --git a/cachecloud-web/src/main/java/com/sohu/cache/web/controller/AppDataMigrateController.java b/cachecloud-web/src/main/java/com/sohu/cache/web/controller/AppDataMigrateController.java index 1f6e045f..f5d16ecf 100644 --- a/cachecloud-web/src/main/java/com/sohu/cache/web/controller/AppDataMigrateController.java +++ b/cachecloud-web/src/main/java/com/sohu/cache/web/controller/AppDataMigrateController.java @@ -12,6 +12,8 @@ import com.sohu.cache.stats.app.RedisShakeCenter; import com.sohu.cache.task.constant.ResourceEnum; import com.sohu.cache.util.ConstUtils; +import com.sohu.cache.web.enums.SuccessEnum; +import com.sohu.cache.web.service.AppImportService; import com.sohu.cache.web.service.AppService; import com.sohu.cache.web.service.ResourceService; import com.sohu.cache.web.util.Page; @@ -25,6 +27,7 @@ import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import java.util.*; import java.util.stream.Collectors; @@ -65,6 +68,32 @@ public class AppDataMigrateController extends BaseController { private AppUserDao appUserDao; @Autowired private ResourceService resourceService; + @Autowired + private AppImportService appImportService; + + + @RequestMapping("/index") + public ModelAndView index(HttpServletRequest request, HttpServletResponse response, Model model, + String tabTag, + AppDataMigrateSearch appDataMigrateSearch) { + List adminList = appUserDao.getAdminList(); + model.addAttribute("adminList", adminList); + + // 分页相关 + int totalCount = appDataMigrateCenter.getMigrateTaskCount(appDataMigrateSearch); + int pageNo = NumberUtils.toInt(request.getParameter("pageNo"), 1); + Page page = new Page(pageNo, 15, totalCount); + appDataMigrateSearch.setPage(page); + + List appDataMigrateStatusList = appDataMigrateCenter.search(appDataMigrateSearch); + model.addAttribute("page", page); + model.addAttribute("appDataMigrateStatusList", appDataMigrateStatusList); + model.addAttribute("appDataMigrateSearch", appDataMigrateSearch); + model.addAttribute("tabTag", tabTag); + model.addAttribute("appMigrateActive", SuccessEnum.SUCCESS.value()); + + return new ModelAndView("manage/migrate/list"); + } /** * 初始化界面 @@ -72,7 +101,7 @@ public class AppDataMigrateController extends BaseController { * @return */ @RequestMapping(value = "/init") - public ModelAndView init(Model model) { + public ModelAndView init(HttpServletRequest request, Model model) { List machineInfoList = machineCenter.getMachineInfoByType(MachineInfoEnum.TypeEnum.REDIS_MIGRATE_TOOL); Map machineInfoMap = machineInfoList.stream().collect(Collectors.toMap( machineInfo -> machineInfo.getIp(), @@ -80,6 +109,19 @@ public ModelAndView init(Model model) { List resourcelist = resourceService.getResourceList(ResourceEnum.TOOL.getValue()); model.addAttribute("resourcelist", resourcelist); model.addAttribute("machineInfoMap", machineInfoMap); + + long importId = NumberUtils.toLong(request.getParameter("importId")); + if (importId > 0) { + AppImport appImport = appImportService.get(importId); + model.addAttribute("importId", importId); + model.addAttribute("targetAppId", appImport.getAppId()); + model.addAttribute("sourceServers", appImport.getInstanceInfo()); + model.addAttribute("redisSourcePass", appImport.getRedisPassword()); + model.addAttribute("sourceType", appImport.getSourceType()); + model.addAttribute("sourceDataType", 0); + model.addAttribute("redisSourceVersion", appImport.getRedisVersionName()); + } + return new ModelAndView("migrate/init"); } @@ -124,12 +166,12 @@ public ModelAndView check(HttpServletRequest request, Model model) { return null; } // 检验机器安装环境 - redisConfigTemplateService.checkAndInstallRedisTool(migrateMachineIp,resource); + redisConfigTemplateService.checkAndInstallRedisTool(migrateMachineIp, resource); AppDataMigrateResult redisMigrateResult = null; if (resource.getName().indexOf("redis-shake") > -1) { redisMigrateResult = redisShakeCenter.check(migrateMachineIp, sourceRedisMigrateEnum, sourceServers, targetRedisMigrateEnum, targetServers, redisSourcePass, redisTargetPass, resource); - }else if(resource.getName().indexOf("redis-migrate-tool") > -1){ + } else if (resource.getName().indexOf("redis-migrate-tool") > -1) { redisMigrateResult = redisMigrateToolCenter.check(migrateMachineIp, sourceRedisMigrateEnum, sourceServers, targetRedisMigrateEnum, targetServers, redisSourcePass, redisTargetPass, resource); } model.addAttribute("status", redisMigrateResult.getStatus()); @@ -178,20 +220,21 @@ public ModelAndView start(HttpServletRequest request, Model model) { if (resource == null) { return null; } - + AppDataMigrateStatus appDataMigrateStatus = new AppDataMigrateStatus(); if (resource.getName().indexOf("redis-shake") > -1) { - redisShakeCenter.migrate(migrateMachineIp, source_rdb_parallel, parallel, + appDataMigrateStatus = redisShakeCenter.migrate(migrateMachineIp, source_rdb_parallel, parallel, sourceRedisMigrateEnum, sourceServers, targetRedisMigrateEnum, targetServers, sourceAppId, targetAppId, redisSourcePass, redisTargetPass, redisSourceVersion, redisTargetVersion, - userId,resource); - }else if(resource.getName().indexOf("redis-migrate-tool") > -1){ - redisMigrateToolCenter.migrate(migrateMachineIp, sourceRedisMigrateEnum, sourceServers, - targetRedisMigrateEnum, targetServers, sourceAppId, targetAppId, redisSourcePass, redisTargetPass, userId,resource); + userId, resource); + } else if (resource.getName().indexOf("redis-migrate-tool") > -1) { + appDataMigrateStatus = redisMigrateToolCenter.migrate(migrateMachineIp, sourceRedisMigrateEnum, sourceServers, + targetRedisMigrateEnum, targetServers, sourceAppId, targetAppId, redisSourcePass, redisTargetPass, userId, resource); } model.addAttribute("status", 1); + model.addAttribute("migrateId", appDataMigrateStatus.getMigrateId()); return new ModelAndView(""); } @@ -275,7 +318,7 @@ public ModelAndView showProcess(HttpServletRequest request, Model model) { * @return */ @RequestMapping(value = "/checkData") - public ModelAndView checkData(HttpServletRequest request,Model model) { + public ModelAndView checkData(HttpServletRequest request, Model model) { long id = NumberUtils.toLong(request.getParameter("id")); int migrateTool = NumberUtils.toInt(request.getParameter("migrateTool"), 0); int comparemode = NumberUtils.toInt(request.getParameter("comparemode"), 3); @@ -318,7 +361,7 @@ public ModelAndView checkData(HttpServletRequest request,Model model) { * @return */ @RequestMapping(value = "/checkData/log") - public ModelAndView checkDatalog(HttpServletRequest request,Model model) { + public ModelAndView checkDatalog(HttpServletRequest request, Model model) { //任务id:查到任务相关信息 long id = NumberUtils.toLong(request.getParameter("id")); int pageSize = NumberUtils.toInt(request.getParameter("pageSize"), 0); @@ -340,29 +383,6 @@ private boolean isUsefulLine(String line) { return false; } - /** - * 查看迁移列表(包含历史) - * - * @return - */ - @RequestMapping(value = "/list") - public ModelAndView list(HttpServletRequest request,Model model, AppDataMigrateSearch appDataMigrateSearch) { - List adminList = appUserDao.getAdminList(); - model.addAttribute("adminList", adminList); - - // 分页相关 - int totalCount = appDataMigrateCenter.getMigrateTaskCount(appDataMigrateSearch); - int pageNo = NumberUtils.toInt(request.getParameter("pageNo"), 1); - Page page = new Page(pageNo, 15, totalCount); - appDataMigrateSearch.setPage(page); - - List appDataMigrateStatusList = appDataMigrateCenter.search(appDataMigrateSearch); - model.addAttribute("page", page); - model.addAttribute("appDataMigrateStatusList", appDataMigrateStatusList); - model.addAttribute("appDataMigrateSearch", appDataMigrateSearch); - return new ModelAndView("migrate/list"); - } - /** * 通过应用id获取可用的Redis实例信息 * diff --git a/cachecloud-web/src/main/java/com/sohu/cache/web/controller/AppManageController.java b/cachecloud-web/src/main/java/com/sohu/cache/web/controller/AppManageController.java index 55d6f58a..0ba9419b 100644 --- a/cachecloud-web/src/main/java/com/sohu/cache/web/controller/AppManageController.java +++ b/cachecloud-web/src/main/java/com/sohu/cache/web/controller/AppManageController.java @@ -1,7 +1,6 @@ package com.sohu.cache.web.controller; import com.sohu.cache.constant.*; -import com.sohu.cache.dao.AppUserDao; import com.sohu.cache.dao.InstanceReshardProcessDao; import com.sohu.cache.entity.*; import com.sohu.cache.machine.MachineCenter; @@ -9,7 +8,6 @@ import com.sohu.cache.redis.RedisDeployCenter; import com.sohu.cache.stats.app.AppDailyDataCenter; import com.sohu.cache.stats.app.AppDeployCenter; -import com.sohu.cache.stats.instance.InstanceDeployCenter; import com.sohu.cache.task.TaskService; import com.sohu.cache.task.constant.ResourceEnum; import com.sohu.cache.util.ConstUtils; @@ -29,7 +27,6 @@ import org.apache.commons.lang.time.DateUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; @@ -72,9 +69,6 @@ public class AppManageController extends BaseController { @Resource(name = "redisDeployCenter") private RedisDeployCenter redisDeployCenter; - @Resource(name = "instanceDeployCenter") - private InstanceDeployCenter instanceDeployCenter; - @Resource(name = "appDailyDataCenter") private AppDailyDataCenter appDailyDataCenter; @@ -84,9 +78,6 @@ public class AppManageController extends BaseController { @Resource(name = "appService") private AppService appService; - @Autowired - private AppUserDao appUserDao; - @Resource private TaskService taskService; @@ -462,9 +453,19 @@ public ModelAndView doAddAppScaleApply(HttpServletRequest request, */ @RequestMapping(value = "/initAppDeploy") public ModelAndView doInitAppDeploy(HttpServletRequest request, HttpServletResponse response, Model model, Long appAuditId) { - // 申请原因 - AppAudit appAudit = appService.getAppAuditById(appAuditId); - model.addAttribute("appAudit", appAudit); + long appId; + AppDesc appDesc; + if (appAuditId == null) { + appId = NumberUtils.toLong(request.getParameter("appId")); + appDesc = appService.getByAppId(appId); + } else { + // 申请原因 + AppAudit appAudit = appService.getAppAuditById(appAuditId); + appId = appAudit.getAppId(); + model.addAttribute("appAudit", appAudit); + appDesc = appAudit.getAppDesc(); + } + // 获取所有Redis版本 List allRedisVersion = resourceService.getResourceList(ResourceEnum.REDIS.getValue()); // 机器列表 @@ -472,9 +473,8 @@ public ModelAndView doInitAppDeploy(HttpServletRequest request, HttpServletRespo // 获取机器信息 Map machineInstanceCountMap = machineCenter.getMachineInstanceCountMap(); - AppDesc appDesc = appAudit.getAppDesc(); if (appDesc != null) { - model.addAttribute("version",appDesc.getVersionName()); + model.addAttribute("version", appDesc.getVersionName()); } List roomList = machineCenter.getEffectiveRoom(); model.addAttribute("roomList", roomList); @@ -482,10 +482,11 @@ public ModelAndView doInitAppDeploy(HttpServletRequest request, HttpServletRespo model.addAttribute("machineList", machineList); model.addAttribute("machineInstanceCountMap", machineInstanceCountMap); model.addAttribute("appAuditId", appAuditId); - model.addAttribute("appId", appAudit.getAppId()); - model.addAttribute("md5password", AuthUtil.getAppIdMD5(String.valueOf(appAudit.getAppId()))); - model.addAttribute("appDesc", appService.getByAppId(appAudit.getAppId())); + model.addAttribute("appId", appId); + model.addAttribute("md5password", AuthUtil.getAppIdMD5(String.valueOf(appId))); + model.addAttribute("appDesc", appService.getByAppId(appId)); model.addAttribute("versionList", allRedisVersion); + model.addAttribute("importId", request.getParameter("importId")); return new ModelAndView("manage/appAudit/deploy/initAppDeploy"); } @@ -662,7 +663,11 @@ public ModelAndView doAddAppDeployTask(HttpServletRequest request, sendMessage(response, json.toString()); return null; } - appAuditDao.updateAppAuditOperateUser(appAuditId, appUser.getId()); + if (appAuditId != null) { + appAuditDao.updateAppAuditOperateUser(appAuditId, appUser.getId()); + } else { + appAuditId = -1l; + } //2.根据应用类型获取部署拓扑信息 switch (type) { case 2: // 部署task :redis cluster @@ -852,6 +857,10 @@ public ModelAndView doAddAuditStatus(HttpServletRequest request, HttpServletResp return new ModelAndView("redirect:/data/migrate/init"); } + if (AppCheckEnum.APP_ALLOCATE_RESOURCE.value().equals(status) && AppAuditType.APP_IMPORT.getValue() == type) { + return new ModelAndView("redirect:/import/app/init?importId=" + appAudit.getParam1()); + } + write(response, String.valueOf(SuccessEnum.SUCCESS.value())); return null; } @@ -938,6 +947,48 @@ public ModelAndView appMachine(HttpServletRequest request, HttpServletResponse r return new ModelAndView("manage/appOps/appMachine"); } + @RequestMapping("/module") + public ModelAndView appModule(Model model, Long appId) { + if (appId != null && appId > 0) { + List appMachineList = appService.getAppMachine(appId); + //1.检测机器模块装载情况 + machineCenter.checkMachineModule(appMachineList); + model.addAttribute("appMachineList", appMachineList); + //2.检测应用实例的插件集成情况 + List instanceList = redisCenter.checkInstanceModule(appId); + model.addAttribute("instanceList", instanceList); + AppDesc appDesc = appService.getByAppId(appId); + model.addAttribute("appDesc", appDesc); + model.addAttribute("basePath", ConstUtils.MODULE_BASE_PATH); + model.addAttribute("moduleMap", ConstUtils.MODULE_MAP); + } + return new ModelAndView("manage/appOps/appModule"); + } + + @RequestMapping("/loadModule") + public ModelAndView loadModule(HttpServletRequest request, HttpServletResponse response, Long appId, String moduleName) { + Map map = new HashMap(); + AppUser appUser = getUserInfo(request); + if (appId != null && appId > 0) { + map = redisCenter.loadModule(appId, moduleName); + } + logger.warn("user {} loadModule, appId:{}, result is {}", appUser.getName(), appId, map); + sendMessage(response, JSONObject.fromObject(map).toString()); + return null; + } + + @RequestMapping("/unloadModule") + public ModelAndView unloadModule(HttpServletRequest request, HttpServletResponse response, Long appId, String moduleName) { + Map map = new HashMap(); + AppUser appUser = getUserInfo(request); + if (appId != null && appId > 0) { + map = redisCenter.unloadModule(appId, moduleName); + } + logger.warn("user {} unloadModule, appId:{}, result is {}", appUser.getName(), appId, map); + sendMessage(response, JSONObject.fromObject(map).toString()); + return null; + } + /** * 应用实例运维 * diff --git a/cachecloud-web/src/main/java/com/sohu/cache/web/controller/AppStatController.java b/cachecloud-web/src/main/java/com/sohu/cache/web/controller/AppStatController.java index 8c19fbc4..36187eee 100644 --- a/cachecloud-web/src/main/java/com/sohu/cache/web/controller/AppStatController.java +++ b/cachecloud-web/src/main/java/com/sohu/cache/web/controller/AppStatController.java @@ -1,8 +1,8 @@ package com.sohu.cache.web.controller; +import com.sohu.cache.constant.MachineInfoEnum; import com.sohu.cache.dao.AppClientStatisticGatherDao; import com.sohu.cache.dao.AppDao; -import com.sohu.cache.dao.ResourceDao; import com.sohu.cache.entity.AppClientStatisticGather; import com.sohu.cache.entity.AppDesc; import com.sohu.cache.entity.InstanceInfo; @@ -26,6 +26,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.text.ParseException; +import java.text.SimpleDateFormat; import java.util.*; import java.util.stream.Collectors; @@ -48,7 +49,6 @@ public class AppStatController extends BaseController { @Autowired TopologyExamTask topologyExamTask; - @RequestMapping(value = "/list") public ModelAndView doAppStatsList(HttpServletRequest request, HttpServletResponse response, Model model) { @@ -123,22 +123,45 @@ public ModelAndView doAppStatsListForServer(HttpServletRequest request, searchDate = timeBetween.getFormatStartDate(); model.addAttribute("searchDate", searchDate); - //appDescList - List appDescList = appDao.getOnlineApps(); - appDescList.forEach(appDesc -> { - String versionName = Optional.ofNullable(resourceService.getResourceById(appDesc.getVersionId())).map(ver -> ver.getName()).orElse(""); - appDesc.setVersionName(versionName); - }); - model.addAttribute("appDescList", appDescList); - - //appDetailVOMap - Map appDetailVOMap = appStatsCenter.getOnlineAppDetails(); - model.addAttribute("appDetailVOMap", appDetailVOMap); - - //appClientGatherStatMap - Map> appClientGatherStatMap = appService.getAppClientStatGather(appId, searchDate); - model.addAttribute("appClientGatherStatMap", appClientGatherStatMap); - + if(tabId == 1 || tabId == 2 || tabId == 3){ + //appDescList + List appDescList = appDao.getOnlineApps(); + appDescList.forEach(appDesc -> { + String versionName = Optional.ofNullable(resourceService.getResourceById(appDesc.getVersionId())).map(ver -> ver.getName()).orElse(""); + appDesc.setVersionName(versionName); + }); + model.addAttribute("appDescList", appDescList); + + //appDetailVOMap + Map appDetailVOMap = appStatsCenter.getOnlineAppDetails(); + model.addAttribute("appDetailVOMap", appDetailVOMap); + + //appClientGatherStatMap + Map> appClientGatherStatMap = appService.getAppClientStatGather(appId, searchDate); + model.addAttribute("appClientGatherStatMap", appClientGatherStatMap); + } + //机器环境检查 + if(tabId == 4 ) { + SimpleDateFormat searchFormat = new SimpleDateFormat("yyyy-MM-dd"); + Map machineEnvMap = null; + try { + machineEnvMap = machineCenter.getAllMachineEnv(searchFormat.parse(searchDate), MachineInfoEnum.MachineTypeEnum.CONTAINER.getValue()); + } catch (ParseException e) { + logger.error("machineCenter get container date:{} error :{}",searchDate,e.getMessage()); + } + model.addAttribute("machineEnvMap", machineEnvMap); + } + // 宿主环境检查 + if(tabId == 5) { + SimpleDateFormat searchFormat = new SimpleDateFormat("yyyy-MM-dd"); + Map machineEnvMap = null; + try { + machineEnvMap = machineCenter.getAllMachineEnv(searchFormat.parse(searchDate), MachineInfoEnum.MachineTypeEnum.HOST.getValue()); + } catch (ParseException e) { + logger.error("machineCenter get host date:{} error :{}",searchDate,e.getMessage()); + } + model.addAttribute("machineEnvMap", machineEnvMap); + } model.addAttribute("appStatServerActive", SuccessEnum.SUCCESS.value()); return new ModelAndView("manage/appStat/listServer"); diff --git a/cachecloud-web/src/main/java/com/sohu/cache/web/controller/AppToolController.java b/cachecloud-web/src/main/java/com/sohu/cache/web/controller/AppToolController.java index 45661b1c..3bb36686 100644 --- a/cachecloud-web/src/main/java/com/sohu/cache/web/controller/AppToolController.java +++ b/cachecloud-web/src/main/java/com/sohu/cache/web/controller/AppToolController.java @@ -242,8 +242,7 @@ public ModelAndView diagnosticResult(HttpServletRequest request, json.put("result", result); } else if (type == DiagnosticTypeEnum.HOT_KEY.getType()) { String result = diagnosticToolService.getHotkeyDiagnosticData(redisKey); - String formatResult = result.replaceAll("(\\r\\n|\\n|\\n\\r)", "
"); - json.put("result", formatResult); + json.put("result", result == null ? "" : result.replaceAll("(\\r\\n|\\n|\\n\\r)", "
")); } } json.put("status", String.valueOf(SuccessEnum.SUCCESS.value())); diff --git a/cachecloud-web/src/main/java/com/sohu/cache/web/controller/BaseController.java b/cachecloud-web/src/main/java/com/sohu/cache/web/controller/BaseController.java index 6236c591..edfdff20 100644 --- a/cachecloud-web/src/main/java/com/sohu/cache/web/controller/BaseController.java +++ b/cachecloud-web/src/main/java/com/sohu/cache/web/controller/BaseController.java @@ -4,6 +4,7 @@ import com.sohu.cache.constant.AppUserTypeEnum; import com.sohu.cache.dao.AppAuditDao; import com.sohu.cache.dao.AppAuditLogDao; +import com.sohu.cache.dao.AppImportDao; import com.sohu.cache.dao.InstanceDao; import com.sohu.cache.entity.*; import com.sohu.cache.machine.MachineCenter; @@ -80,6 +81,9 @@ public class BaseController { @Resource protected AppAuditDao appAuditDao; + @Resource + protected AppImportDao appImportDao; + @Resource protected AppAuditLogDao appAuditLogDao; diff --git a/cachecloud-web/src/main/java/com/sohu/cache/web/controller/ImportAppController.java b/cachecloud-web/src/main/java/com/sohu/cache/web/controller/ImportAppController.java index 6bea37e7..068baa6e 100644 --- a/cachecloud-web/src/main/java/com/sohu/cache/web/controller/ImportAppController.java +++ b/cachecloud-web/src/main/java/com/sohu/cache/web/controller/ImportAppController.java @@ -1,28 +1,38 @@ package com.sohu.cache.web.controller; +import com.sohu.cache.async.AsyncThreadPoolFactory; +import com.sohu.cache.constant.AppStatusEnum; import com.sohu.cache.constant.ImportAppResult; -import com.sohu.cache.entity.AppDesc; -import com.sohu.cache.entity.AppUser; +import com.sohu.cache.constant.InstanceStatusEnum; +import com.sohu.cache.dao.AppDataMigrateStatusDao; +import com.sohu.cache.dao.TaskQueueDao; +import com.sohu.cache.entity.*; import com.sohu.cache.stats.app.ImportAppCenter; -import com.sohu.cache.web.service.UserService; +import com.sohu.cache.task.entity.TaskQueue; +import com.sohu.cache.util.TypeUtil; +import com.sohu.cache.web.enums.AppImportStatusEnum; +import com.sohu.cache.web.enums.BooleanEnum; +import com.sohu.cache.web.enums.SuccessEnum; +import com.sohu.cache.web.service.AppImportService; import org.apache.commons.lang.math.NumberUtils; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.servlet.ModelAndView; +import redis.clients.jedis.Jedis; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import java.util.Arrays; import java.util.Date; import java.util.List; +import java.util.function.Function; +import java.util.stream.Collectors; /** * 已经存在Redis导入 - * - * @author leifu - * @Date 2016-4-16 - * @Time 下午2:31:14 */ @Controller @RequestMapping("/import/app") @@ -30,22 +40,232 @@ public class ImportAppController extends BaseController { @Resource(name = "importAppCenter") private ImportAppCenter importAppCenter; - @Resource - private UserService userService; - - @RequestMapping(value = "/init") - public ModelAndView init(HttpServletRequest request, HttpServletResponse response, Model model) { - List userList = userService.getAllUser(); - model.addAttribute("userList", userList); - return new ModelAndView("import/init"); + @Autowired + private AppImportService appImportService; + @Autowired + private AppDataMigrateStatusDao appDataMigrateStatusDao; + @Autowired + private TaskQueueDao taskQueueDao; + + @RequestMapping("/index") + public ModelAndView index(HttpServletRequest request, HttpServletResponse response, Model model, String tabTag) { + model.addAttribute("tabTag", tabTag); + model.addAttribute("appImportActive", SuccessEnum.SUCCESS.value()); + List appImportList = appImportService.getImportAppList(-1); + model.addAttribute("appImportList", appImportList); + model.addAttribute("appImportStatusMap", Arrays.stream(AppImportStatusEnum.values()).collect(Collectors.toMap(AppImportStatusEnum::getStatus, Function.identity()))); + + + return new ModelAndView("manage/appImport/list"); + } + + @RequestMapping(value = "init") + public ModelAndView init(Model model, long importId) { + //1.获取导入信息 + AppImport appImport = appImportService.get(importId); + if (appImport == null) { + return new ModelAndView(""); + } + Long appId = appImport.getAppId(); + AppDesc appDesc = appService.getByAppId(appId); + int oldStatus = appImport.getStatus(); + int status = oldStatus; + + if (status == AppImportStatusEnum.PREPARE.getStatus()) { + //导入申请未处理阶段 + if (appDesc.getVersionId() != -1) { + appImport.setRedisVersionName(resourceService.getResourceById(appDesc.getVersionId()).getName()); + } + model.addAttribute("appDesc", appDesc); + } + if (status > 10 && status < 20) { + //创建版本阶段 + if (appDesc.getVersionId() == -1) { + SystemResource resource = resourceService.getResourceByName(appImport.getRedisVersionName()); + if (resource != null) { + appDesc.setVersionId(resource.getId()); + appService.update(appDesc); + status = AppImportStatusEnum.VERSION_BUILD_END.getStatus(); + model.addAttribute("hasRedisVersion", 1); + } else { + model.addAttribute("hasRedisVersion", 0); + } + } else { + status = AppImportStatusEnum.VERSION_BUILD_END.getStatus(); + model.addAttribute("hasRedisVersion", 1); + } + } + if (status >= 20 && status < 30) { + //创建应用阶段 + long appBuildTaskId = appImport.getAppBuildTaskId(); + if (appBuildTaskId > 0) { + TaskQueue appBuildTask = taskQueueDao.getById(appBuildTaskId); + if (appBuildTask != null && (appBuildTask.getStatus() == 2 || appBuildTask.getStatus() == 3)) { + status = AppImportStatusEnum.APP_BUILD_ERROR.getStatus(); + } else if (appBuildTask != null && appBuildTask.getStatus() == 4) { + appDesc.setStatus(AppStatusEnum.STATUS_PUBLISHED.getStatus()); + appService.update(appDesc); + status = AppImportStatusEnum.APP_BUILD_END.getStatus(); + } else { + status = AppImportStatusEnum.APP_BUILD_START.getStatus(); + } + } else { + status = AppImportStatusEnum.APP_BUILD_INIT.getStatus(); + } + } + if (status >= 30 && status < 40) { + //数据迁移阶段 + long migrateId = appImport.getMigrateId(); + if (migrateId > 0) { + AppDataMigrateStatus appDataMigrateStatus = appDataMigrateStatusDao.getByMigrateId(migrateId); + int migrateStatus = appDataMigrateStatus.getStatus(); + if (migrateStatus == 1) { + status = AppImportStatusEnum.MIGRATE_END.getStatus(); + } else if (migrateStatus == 2) { + status = AppImportStatusEnum.MIGRATE_ERROR.getStatus(); + } else { + status = AppImportStatusEnum.MIGRATE_START.getStatus(); + } + } + } + if (status == AppImportStatusEnum.MIGRATE_END.getStatus()) { + model.addAttribute("appId", appId); + } + + if (oldStatus != status) { + appImport.setStatus(status); + appImportService.update(appImport); + } + model.addAttribute("appImport", appImport); + model.addAttribute("appImportStatusMap", Arrays.stream(AppImportStatusEnum.values()).collect(Collectors.toMap(AppImportStatusEnum::getStatus, Function.identity()))); + return new ModelAndView("/manage/appImport/appImport"); + } + + @RequestMapping(value = "/preRebuildApp") + public ModelAndView preRebuildApp(HttpServletRequest request, HttpServletResponse response, Model model) { + long importId = NumberUtils.toLong(request.getParameter("importId")); + long appId = NumberUtils.toLong(request.getParameter("appId")); + AppImport appImport = appImportService.get(importId); + appImport.setStatus(AppImportStatusEnum.APP_BUILD_INIT.getStatus()); + appImport.setAppBuildTaskId(0); + + AppDesc appDesc = appService.getByAppId(appId); + if (appDesc != null) { + //offline instances + List instanceInfos = instanceDao.getInstListByAppId(appId); + int type = appDesc.getType(); + if (instanceInfos != null) { + instanceInfos.parallelStream().map(instanceInfo -> instanceOffline(appId, instanceInfo, type)).collect(Collectors.toList()); + } + appDesc.setStatus(AppStatusEnum.STATUS_INITIALIZE.getStatus()); + if (appService.update(appDesc) > 0 && appImportService.update(appImport) > 0) { + model.addAttribute("success", 1); + } + } + + appImportService.update(appImport); + return new ModelAndView(""); + } + + @RequestMapping(value = "/preReMigrate") + public ModelAndView preReMigrate(HttpServletRequest request, HttpServletResponse response, Model model) { + long importId = NumberUtils.toLong(request.getParameter("importId")); + long appId = NumberUtils.toLong(request.getParameter("appId")); + AppImport appImport = appImportService.get(importId); + appImport.setStatus(AppImportStatusEnum.APP_BUILD_END.getStatus()); + appImport.setMigrateId(0); + + AppDesc appDesc = appService.getByAppId(appId); + if (appDesc != null) { + //todo 清空数据可能耗时太长 + if (cleanAppData(appId) && appImportService.update(appImport) > 0) { + model.addAttribute("success", 1); + } + } + + appImportService.update(appImport); + return new ModelAndView(""); + } + + private boolean cleanAppData(long appId) { + List instanceList = instanceDao.getInstListByAppId(appId); + // 开始清除 + for (InstanceInfo instance : instanceList) { + if (instance.getStatus() != InstanceStatusEnum.GOOD_STATUS.getStatus()) { + continue; + } + String host = instance.getIp(); + int port = instance.getPort(); + // master + 非sentinel节点 + BooleanEnum isMater = redisCenter.isMaster(appId, host, port); + if (isMater == BooleanEnum.TRUE && !TypeUtil.isRedisSentinel(instance.getType())) { + //异步线程处理 + AsyncThreadPoolFactory.DEFAULT_ASYNC_THREAD_POOL.execute(new Runnable() { + @Override + public void run() { + Jedis jedis = redisCenter.getJedis(appId, host, port); + jedis.getClient().setConnectionTimeout(1000); + jedis.getClient().setSoTimeout(60000); + try { + logger.warn("{}:{} start clear data", host, port); + long start = System.currentTimeMillis(); + String result = jedis.flushAll(); + logger.warn("{}:{} finish clear data :{}, cost time:{} ms", host, port, result, + (System.currentTimeMillis() - start)); + } catch (Exception e) { + logger.error("clear redis: " + e.getMessage(), e); + } finally { + jedis.close(); + } + } + }); + } + } + return true; + } + + private boolean instanceOffline(long appId, InstanceInfo instanceInfo, int type) { + final String ip = instanceInfo.getIp(); + final int port = instanceInfo.getPort(); + boolean isShutdown = TypeUtil.isRedisType(type) ? redisCenter.shutdown(appId, ip, port) : true; + if (isShutdown) { + instanceInfo.setStatus(InstanceStatusEnum.OFFLINE_STATUS.getStatus()); + instanceDao.update(instanceInfo); + } else { + return false; + } + return true; + } + + @RequestMapping(value = "/goOn") + public ModelAndView goOn(HttpServletRequest request, HttpServletResponse response, Model model) { + long importId = NumberUtils.toLong(request.getParameter("importId")); + int status = NumberUtils.toInt(request.getParameter("status")); + long migrateId = NumberUtils.toLong(request.getParameter("migrateId")); + long appBuildTaskId = NumberUtils.toLong(request.getParameter("appBuildTaskId")); + AppImport appImport = appImportService.get(importId); + if (status > 0) { + appImport.setStatus(status); + } + if (migrateId > 0) { + appImport.setMigrateId(migrateId); + } + if (appBuildTaskId > 0) { + appImport.setAppBuildTaskId(appBuildTaskId); + } + appImportService.update(appImport); + model.addAttribute("success", 1); + model.addAttribute("message", "成功"); + return new ModelAndView(""); } @RequestMapping(value = "/check") public ModelAndView check(HttpServletRequest request, HttpServletResponse response, Model model) { - AppDesc appDesc = genAppDesc(request); + + int type = NumberUtils.toInt(request.getParameter("type")); String appInstanceInfo = request.getParameter("appInstanceInfo"); String password = request.getParameter("password"); - ImportAppResult importAppResult = importAppCenter.check(appDesc, appInstanceInfo, password); + ImportAppResult importAppResult = importAppCenter.check(type, appInstanceInfo, password); model.addAttribute("status", importAppResult.getStatus()); model.addAttribute("message", importAppResult.getMessage()); return new ModelAndView(""); diff --git a/cachecloud-web/src/main/java/com/sohu/cache/web/controller/ResourceController.java b/cachecloud-web/src/main/java/com/sohu/cache/web/controller/ResourceController.java index c2646907..edd286f6 100644 --- a/cachecloud-web/src/main/java/com/sohu/cache/web/controller/ResourceController.java +++ b/cachecloud-web/src/main/java/com/sohu/cache/web/controller/ResourceController.java @@ -77,6 +77,9 @@ public ModelAndView tab(@PathVariable("tab") String tab, String searchName, Mode case "dir": resource_type = ResourceEnum.DIR.getValue(); break; + case "module": + resource_type = ResourceEnum.MODULE.getValue(); + break; default: break; } diff --git a/cachecloud-web/src/main/java/com/sohu/cache/web/enums/AppImportStatusEnum.java b/cachecloud-web/src/main/java/com/sohu/cache/web/enums/AppImportStatusEnum.java new file mode 100644 index 00000000..1e186d45 --- /dev/null +++ b/cachecloud-web/src/main/java/com/sohu/cache/web/enums/AppImportStatusEnum.java @@ -0,0 +1,43 @@ +package com.sohu.cache.web.enums; + +/** + * @Author: rucao + * @Date: 2021/1/11 下午5:45 + */ +public enum AppImportStatusEnum { + PREPARE(0, "准备", "应用导入-未开始"), + START(1, "进行中...", "应用导入-开始"), + ERROR(2, "error", "应用导入-出错"), + + VERSION_BUILD_START(11, "进行中...", "新建redis版本-进行中"), + VERSION_BUILD_ERROR(12, "error", "新建redis版本-出错"), + VERSION_BUILD_END(20, "成功", "新建redis版本-完成"), + + APP_BUILD_INIT(21, "准备就绪", "新建redis应用-准备就绪"), + APP_BUILD_START(22, "进行中...", "新建redis应用-进行中"), + APP_BUILD_ERROR(23, "error", "新建redis应用-出错"), + APP_BUILD_END(30, "成功", "新建redis应用-完成"), + + MIGRATE_INIT(31, "准备就绪", "数据迁移-准备就绪"), + MIGRATE_START(32, "进行中...", "数据迁移-进行中"), + MIGRATE_ERROR(33, "error", "数据迁移-出错"), + MIGRATE_END(3, "成功", "应用导入-成功"), + ; + private int status; + private String desc; + private String info; + + AppImportStatusEnum(int status, String desc, String info) { + this.status = status; + this.desc = desc; + this.info = info; + } + + public int getStatus() { + return status; + } + + public String getInfo() { + return info; + } +} diff --git a/cachecloud-web/src/main/java/com/sohu/cache/web/enums/CheckEnum.java b/cachecloud-web/src/main/java/com/sohu/cache/web/enums/CheckEnum.java new file mode 100644 index 00000000..8d283937 --- /dev/null +++ b/cachecloud-web/src/main/java/com/sohu/cache/web/enums/CheckEnum.java @@ -0,0 +1,24 @@ +package com.sohu.cache.web.enums; + +/** + * Created by chenshi on 2021/1/12. + */ +public enum CheckEnum { + + //正常 + CONSISTENCE(1), + //异常 + INCONSISTENCE(2), + // + EXCEPTION(3); + + private int value; + + CheckEnum(int value) { + this.value = value; + } + + public int getValue() { + return value; + } +} diff --git a/cachecloud-web/src/main/java/com/sohu/cache/web/enums/ModuleEnum.java b/cachecloud-web/src/main/java/com/sohu/cache/web/enums/ModuleEnum.java new file mode 100644 index 00000000..a87177e2 --- /dev/null +++ b/cachecloud-web/src/main/java/com/sohu/cache/web/enums/ModuleEnum.java @@ -0,0 +1,20 @@ +package com.sohu.cache.web.enums; + +/** + * Created by chenshi on 2021/3/8. + */ +public enum ModuleEnum { + + BLOOMFILTER_NAME("bf"), REDISSEARCH_NAME("search"), + BLOOMFILTER_SO("redisbloom.so"),REDISSEARCH_SO("redisearch.so"); + + private String value; + + ModuleEnum(String value) { + this.value = value; + } + + public String getValue() { + return value; + } +} diff --git a/cachecloud-web/src/main/java/com/sohu/cache/web/service/AppImportService.java b/cachecloud-web/src/main/java/com/sohu/cache/web/service/AppImportService.java new file mode 100644 index 00000000..5a4ccdff --- /dev/null +++ b/cachecloud-web/src/main/java/com/sohu/cache/web/service/AppImportService.java @@ -0,0 +1,21 @@ +package com.sohu.cache.web.service; + +import com.sohu.cache.entity.AppImport; + +import java.util.List; + +/** + * @Author: rucao + * @Date: 2021/1/8 上午10:48 + */ +public interface AppImportService { + List getImportAppList(Integer status); + + int save(AppImport appImport); + + int update(AppImport appImport); + + AppImport appImport(Long id); + + AppImport get(Long id); +} diff --git a/cachecloud-web/src/main/java/com/sohu/cache/web/service/AppService.java b/cachecloud-web/src/main/java/com/sohu/cache/web/service/AppService.java index c8241f65..5ddd4ca6 100644 --- a/cachecloud-web/src/main/java/com/sohu/cache/web/service/AppService.java +++ b/cachecloud-web/src/main/java/com/sohu/cache/web/service/AppService.java @@ -223,6 +223,13 @@ AppAudit saveInstanceChangeConfig(AppDesc appDesc, AppUser appUser, Long instanc */ List getAppMachineDetail(Long appId); + /** + * 获取应用拥有实例的机器 + * @param appId + * @return + */ + public List getAppMachine(Long appId); + /** * 根据应用id获取审批记录 * diff --git a/cachecloud-web/src/main/java/com/sohu/cache/web/service/WebClientComponent.java b/cachecloud-web/src/main/java/com/sohu/cache/web/service/WebClientComponent.java index fbeb519b..2957b08d 100644 --- a/cachecloud-web/src/main/java/com/sohu/cache/web/service/WebClientComponent.java +++ b/cachecloud-web/src/main/java/com/sohu/cache/web/service/WebClientComponent.java @@ -14,7 +14,7 @@ */ @Component public class WebClientComponent { - @Value("${cachecloud.web.clients}") + @Value("${cachecloud.web.clients:127.0.0.1}") private String[] clients; public List getWebClientIps() { diff --git a/cachecloud-web/src/main/java/com/sohu/cache/web/service/impl/AppImportServiceImpl.java b/cachecloud-web/src/main/java/com/sohu/cache/web/service/impl/AppImportServiceImpl.java new file mode 100644 index 00000000..67677e56 --- /dev/null +++ b/cachecloud-web/src/main/java/com/sohu/cache/web/service/impl/AppImportServiceImpl.java @@ -0,0 +1,73 @@ +package com.sohu.cache.web.service.impl; + +import com.sohu.cache.dao.AppImportDao; +import com.sohu.cache.entity.AppImport; +import com.sohu.cache.web.service.AppImportService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; + +/** + * @Author: rucao + * @Date: 2021/1/8 上午11:43 + */ +@Slf4j +@Service +public class AppImportServiceImpl implements AppImportService { + @Autowired + private AppImportDao appImportDao; + + @Override + public List getImportAppList(Integer status) { + List appImportList = new ArrayList<>(); + try { + appImportList = appImportDao.getAppImports(status); + } catch (Exception e) { + log.error(e.getMessage(), e); + } + return appImportList; + } + + @Override + public int save(AppImport appImport) { + try { + return appImportDao.save(appImport); + } catch (Exception e) { + log.error(e.getMessage(), e); + } + return -1; + } + + @Override + public int update(AppImport appImport) { + try { + return appImportDao.update(appImport); + } catch (Exception e) { + log.error(e.getMessage(), e); + } + return -1; + } + + @Override + public AppImport appImport(Long id) { + AppImport appImport = get(id); + if (appImport == null) { + + } + + return null; + } + + @Override + public AppImport get(Long id) { + try { + return appImportDao.get(id); + } catch (Exception e) { + log.error(e.getMessage(), e); + } + return null; + } +} diff --git a/cachecloud-web/src/main/java/com/sohu/cache/web/service/impl/AppServiceImpl.java b/cachecloud-web/src/main/java/com/sohu/cache/web/service/impl/AppServiceImpl.java index ecfa16cf..b8a1f2e7 100644 --- a/cachecloud-web/src/main/java/com/sohu/cache/web/service/impl/AppServiceImpl.java +++ b/cachecloud-web/src/main/java/com/sohu/cache/web/service/impl/AppServiceImpl.java @@ -481,6 +481,59 @@ public List getAppMachineDetail(Long appId) { return machineDetailVOList; } + @Override + public List getAppMachine(Long appId) { + //应用信息 + Assert.isTrue(appId != null && appId > 0L); + AppDesc appDesc = appDao.getAppDescById(appId); + if (appDesc == null) { + logger.error("appDesc:id={} is not exist"); + return Collections.emptyList(); + } + + //应用实例列表 + List appInstanceList = getAppInstanceInfo(appId); + if (CollectionUtils.isEmpty(appInstanceList)) { + return Collections.emptyList(); + } + + //防止重复 + Set instanceMachineHosts = new HashSet(); + //结果列表 + List machineDetailVOList = new ArrayList(); + //应用的机器信息 + for (InstanceInfo instanceInfo : appInstanceList) { + if(!instanceInfo.isOffline()){ + String ip = instanceInfo.getIp(); + if (instanceMachineHosts.contains(ip)) { + continue; + } else { + instanceMachineHosts.add(ip); + } + MachineStats machineStats = machineStatsDao.getMachineStatsByIp(ip); + if (machineStats == null) { + continue; + } + //已经分配的内存 + int memoryHost = instanceDao.getMemoryByHost(ip); + machineStats.setMemoryAllocated(memoryHost); + //机器信息 + MachineInfo machineInfo = machineCenter.getMachineInfoByIp(ip); + if (machineInfo == null) { + continue; + } + //下线机器不展示 + if (machineInfo.isOffline()) { + continue; + } + machineStats.setInfo(machineInfo); + machineDetailVOList.add(machineStats); + } + } + return machineDetailVOList; + } + + @Override public AppAudit getAppAuditById(Long appAuditId) { return appAuditDao.getAppAudit(appAuditId); diff --git a/cachecloud-web/src/main/java/com/sohu/cache/web/vo/MachineEnv.java b/cachecloud-web/src/main/java/com/sohu/cache/web/vo/MachineEnv.java new file mode 100644 index 00000000..54d83e9e --- /dev/null +++ b/cachecloud-web/src/main/java/com/sohu/cache/web/vo/MachineEnv.java @@ -0,0 +1,139 @@ +package com.sohu.cache.web.vo; + +import com.sohu.cache.web.enums.CheckEnum; +import lombok.Data; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +/** + * Created by chenshi on 2021/1/12. + */ +@Data +public class MachineEnv { + + private static Logger logger = LoggerFactory.getLogger(MachineEnv.class); + /** + * 容器环境参数 + */ + //内存分配策略设置为1:表示内核允许分配所有的物理内存 + private String overcommit_memory; + private final static String overcommit_memory_warnning = "1"; + //swap策略设置为0:关闭swap,避免内存io转换磁盘io导致阻塞 + private String swappines = "0"; + private final static String swappines_warnning = "0"; + //thp设置:never,防止fork过程中消耗大内存拷贝导致阻塞 + private String transparent_hugepage_enable; + private final static String transparent_hugepage_enable_warnning = "always madvise [never]"; + private String transparent_hugepage_defrag; + private final static String transparent_hugepage_defrag_warnning = "always defer madvise [never]"; + private final static String thp_judge = "[never]"; + //nproc用户线程数: * soft nproc 4096 + private String nproc; + private final static String nproc_warnning = "* soft nproc 4096"; + + /** + * 宿主环境参数 + */ + //tcp连接队列数:512 + private String somaxconn; + private final static String somaxconn_warnning = "511"; + //redis fsync slow log + private int fsync_delay_times; + private final static int fsync_delay_times_warnning = 10; + //cachecloud用户最大线程数量,当前宿主环境为4096 + private int nproc_threads = 1024; + private final static int nproc_threads_warnning = 1024; + // ssh pass版本 + private String sshPass; + private final static String sshPass_warnning = ""; + // 文件句柄数量 + private int unlimit_used; + private int unlimit; + private final static int unlimit_warnning = 40000; + // 磁盘信息 + private String diskUsed; + private final static int diskUsed_warnning = 80; + // 实例数量 + private int instanceNum; + + /** + * 容器资源 + */ + public MachineEnv(String overcommit_memory, String swappines, String transparent_hugepage_enable, String transparent_hugepage_defrag, String nproc) { + this.overcommit_memory = overcommit_memory; + this.swappines = swappines; + this.transparent_hugepage_enable = transparent_hugepage_enable; + this.transparent_hugepage_defrag = transparent_hugepage_defrag; + this.nproc = nproc; + } + + /** + * 宿主资源 + */ + public MachineEnv(String somaxconn, int fsync_delay_times, int nproc_threads, String sshPass, int unlimit_used, int unlimit, String diskUsed, int instanceNum) { + this.somaxconn = somaxconn; + this.fsync_delay_times = fsync_delay_times; + this.nproc_threads = nproc_threads; + this.sshPass = sshPass; + this.unlimit_used = unlimit_used; + this.unlimit = unlimit; + this.diskUsed = diskUsed; + this.instanceNum = instanceNum; + } + + public static MachineEnv getDefaultEnv() { + return new MachineEnv("-1","-1","-1","-1","-1","-1",-1,-1,"-1",-1,-1,"-1",-1); + } + + public MachineEnv(String overcommit_memory, String swappines, String transparent_hugepage_enable, String transparent_hugepage_defrag, String nproc, String somaxconn, int fsync_delay_times, int nproc_threads, String sshPass, int unlimit_used, int unlimit, String diskUsed, int instanceNum) { + this.overcommit_memory = overcommit_memory; + this.swappines = swappines; + this.transparent_hugepage_enable = transparent_hugepage_enable; + this.transparent_hugepage_defrag = transparent_hugepage_defrag; + this.nproc = nproc; + this.somaxconn = somaxconn; + this.fsync_delay_times = fsync_delay_times; + this.nproc_threads = nproc_threads; + this.sshPass = sshPass; + this.unlimit_used = unlimit_used; + this.unlimit = unlimit; + this.diskUsed = diskUsed; + this.instanceNum = instanceNum; + } + + public static int checkContainer(MachineEnv env) { + + try { + if (env.overcommit_memory.equals(overcommit_memory_warnning) && env.transparent_hugepage_enable.contains(thp_judge) && + env.transparent_hugepage_defrag.contains(thp_judge) && env.swappines.equals(swappines_warnning) && + env.nproc.equals(nproc_warnning)) { + return CheckEnum.CONSISTENCE.getValue(); + } + return CheckEnum.INCONSISTENCE.getValue(); + } catch (Exception e) { + logger.error("MachineEnvUtil checkContainer error env:{} {}", env, e.getMessage()); + return CheckEnum.EXCEPTION.getValue(); + } + } + + public static int checkHost(MachineEnv env) { + try { + int diskuse_precent = 0; + try { + diskuse_precent = Integer.parseInt(env.diskUsed.split("%")[0]); + } catch (Exception e) { + logger.error("disk used {} parse error:{}", env.diskUsed,e.getMessage()); + } + + if (env.nproc_threads <= nproc_threads_warnning && env.fsync_delay_times <= fsync_delay_times_warnning && + env.somaxconn.equals(somaxconn_warnning) && env.unlimit_used < unlimit_warnning && diskuse_precent < diskUsed_warnning) { + return CheckEnum.CONSISTENCE.getValue(); + } + return CheckEnum.INCONSISTENCE.getValue(); + } catch (Exception e) { + logger.error("MachineEnvUtil checkMachine error env:{} {}", env, e.getMessage(), e); + return CheckEnum.EXCEPTION.getValue(); + } + } +} diff --git a/cachecloud-web/src/main/resources/mapper/AppDataMigrateStatusDao.xml b/cachecloud-web/src/main/resources/mapper/AppDataMigrateStatusDao.xml index c0319f88..f32d91e6 100644 --- a/cachecloud-web/src/main/resources/mapper/AppDataMigrateStatusDao.xml +++ b/cachecloud-web/src/main/resources/mapper/AppDataMigrateStatusDao.xml @@ -73,6 +73,11 @@ + + + update app_data_migrate_status set status = #{status}, end_time=now() where id = #{id} diff --git a/cachecloud-web/src/main/resources/mapper/AppImportDao.xml b/cachecloud-web/src/main/resources/mapper/AppImportDao.xml new file mode 100644 index 00000000..463f6999 --- /dev/null +++ b/cachecloud-web/src/main/resources/mapper/AppImportDao.xml @@ -0,0 +1,40 @@ + + + + + + id,app_id,mem_size,source_type,redis_version_name,instance_info,redis_password,status,step,app_build_task_id,migrate_id + + + + + + insert into app_import + () + values + (#{id},#{appId},#{memSize},#{sourceType},#{redisVersionName},#{instanceInfo},#{redisPassword},#{status},#{step},#{appBuildTaskId},#{migrateId}) + + + + update app_import + set app_id=#{appId},mem_size=#{memSize},source_type=#{sourceType}, redis_version_name=#{redisVersionName},instance_info=#{instanceInfo}, redis_password=#{redisPassword}, status=#{status}, step=#{step},app_build_task_id=#{appBuildTaskId}, migrate_id=#{migrateId} + where id=#{id} + + + + diff --git a/cachecloud-web/src/main/resources/spring/spring-quartz.xml b/cachecloud-web/src/main/resources/spring/spring-quartz.xml index 85b40b07..14eb234a 100644 --- a/cachecloud-web/src/main/resources/spring/spring-quartz.xml +++ b/cachecloud-web/src/main/resources/spring/spring-quartz.xml @@ -272,8 +272,8 @@ - - + + diff --git a/cachecloud-web/src/main/resources/static/img/function/appTool/idle-res.png b/cachecloud-web/src/main/resources/static/img/function/appTool/idle-res.png new file mode 100644 index 00000000..3388627b Binary files /dev/null and b/cachecloud-web/src/main/resources/static/img/function/appTool/idle-res.png differ diff --git a/cachecloud-web/src/main/resources/static/img/function/appTool/idle.png b/cachecloud-web/src/main/resources/static/img/function/appTool/idle.png new file mode 100644 index 00000000..5af67593 Binary files /dev/null and b/cachecloud-web/src/main/resources/static/img/function/appTool/idle.png differ diff --git a/cachecloud-web/src/main/resources/static/img/function/appTool/index.png b/cachecloud-web/src/main/resources/static/img/function/appTool/index.png new file mode 100644 index 00000000..992ecc74 Binary files /dev/null and b/cachecloud-web/src/main/resources/static/img/function/appTool/index.png differ diff --git a/cachecloud-web/src/main/resources/static/img/function/appTool/memory-res.png b/cachecloud-web/src/main/resources/static/img/function/appTool/memory-res.png new file mode 100644 index 00000000..d05149d6 Binary files /dev/null and b/cachecloud-web/src/main/resources/static/img/function/appTool/memory-res.png differ diff --git a/cachecloud-web/src/main/resources/static/img/function/appTool/memory.png b/cachecloud-web/src/main/resources/static/img/function/appTool/memory.png new file mode 100644 index 00000000..efb3d4dc Binary files /dev/null and b/cachecloud-web/src/main/resources/static/img/function/appTool/memory.png differ diff --git a/cachecloud-web/src/main/resources/static/img/function/appTool/scan-res.png b/cachecloud-web/src/main/resources/static/img/function/appTool/scan-res.png new file mode 100644 index 00000000..9e06d2e8 Binary files /dev/null and b/cachecloud-web/src/main/resources/static/img/function/appTool/scan-res.png differ diff --git a/cachecloud-web/src/main/resources/static/img/function/appTool/scan.png b/cachecloud-web/src/main/resources/static/img/function/appTool/scan.png new file mode 100644 index 00000000..406650f0 Binary files /dev/null and b/cachecloud-web/src/main/resources/static/img/function/appTool/scan.png differ diff --git a/cachecloud-web/src/main/resources/static/img/function/appTool/slot-res1.png b/cachecloud-web/src/main/resources/static/img/function/appTool/slot-res1.png new file mode 100644 index 00000000..58eb9fa2 Binary files /dev/null and b/cachecloud-web/src/main/resources/static/img/function/appTool/slot-res1.png differ diff --git a/cachecloud-web/src/main/resources/static/img/function/appTool/slot.png b/cachecloud-web/src/main/resources/static/img/function/appTool/slot.png new file mode 100644 index 00000000..4e218602 Binary files /dev/null and b/cachecloud-web/src/main/resources/static/img/function/appTool/slot.png differ diff --git a/cachecloud-web/src/main/resources/static/img/function/client/app-analysis-application.png b/cachecloud-web/src/main/resources/static/img/function/client/app-analysis-application.png index 6f463331..3789364a 100644 Binary files a/cachecloud-web/src/main/resources/static/img/function/client/app-analysis-application.png and b/cachecloud-web/src/main/resources/static/img/function/client/app-analysis-application.png differ diff --git a/cachecloud-web/src/main/resources/static/img/function/client/app-daily.png b/cachecloud-web/src/main/resources/static/img/function/client/app-daily.png new file mode 100644 index 00000000..d47d408b Binary files /dev/null and b/cachecloud-web/src/main/resources/static/img/function/client/app-daily.png differ diff --git a/cachecloud-web/src/main/resources/static/img/function/client/client-instancelist.png b/cachecloud-web/src/main/resources/static/img/function/client/client-instancelist.png deleted file mode 100644 index dc8750e4..00000000 Binary files a/cachecloud-web/src/main/resources/static/img/function/client/client-instancelist.png and /dev/null differ diff --git a/cachecloud-web/src/main/resources/static/img/function/client/client-instancelists.png b/cachecloud-web/src/main/resources/static/img/function/client/client-instancelists.png new file mode 100644 index 00000000..570a10a7 Binary files /dev/null and b/cachecloud-web/src/main/resources/static/img/function/client/client-instancelists.png differ diff --git a/cachecloud-web/src/main/resources/static/img/function/client/client-topology.png b/cachecloud-web/src/main/resources/static/img/function/client/client-topology.png index 8ea37139..9015465e 100644 Binary files a/cachecloud-web/src/main/resources/static/img/function/client/client-topology.png and b/cachecloud-web/src/main/resources/static/img/function/client/client-topology.png differ diff --git a/cachecloud-web/src/main/resources/static/img/function/client/instance-connInfo.png b/cachecloud-web/src/main/resources/static/img/function/client/instance-connInfo.png index 46f54b90..685f7865 100644 Binary files a/cachecloud-web/src/main/resources/static/img/function/client/instance-connInfo.png and b/cachecloud-web/src/main/resources/static/img/function/client/instance-connInfo.png differ diff --git a/cachecloud-web/src/main/resources/static/img/function/client/instance-latency.png b/cachecloud-web/src/main/resources/static/img/function/client/instance-latency.png new file mode 100644 index 00000000..ea28cd8d Binary files /dev/null and b/cachecloud-web/src/main/resources/static/img/function/client/instance-latency.png differ diff --git a/cachecloud-web/src/main/resources/static/img/function/client/instance-latency1.png b/cachecloud-web/src/main/resources/static/img/function/client/instance-latency1.png new file mode 100644 index 00000000..a5772be8 Binary files /dev/null and b/cachecloud-web/src/main/resources/static/img/function/client/instance-latency1.png differ diff --git a/cachecloud-web/src/main/resources/static/img/function/job/appImport.png b/cachecloud-web/src/main/resources/static/img/function/job/appImport.png new file mode 100644 index 00000000..2244bd87 Binary files /dev/null and b/cachecloud-web/src/main/resources/static/img/function/job/appImport.png differ diff --git a/cachecloud-web/src/main/resources/static/img/function/job/appMigrate.png b/cachecloud-web/src/main/resources/static/img/function/job/appMigrate.png new file mode 100644 index 00000000..9281e52a Binary files /dev/null and b/cachecloud-web/src/main/resources/static/img/function/job/appMigrate.png differ diff --git a/cachecloud-web/src/main/resources/static/img/function/operation/operation-app.png b/cachecloud-web/src/main/resources/static/img/function/operation/operation-app.png new file mode 100644 index 00000000..41187cca Binary files /dev/null and b/cachecloud-web/src/main/resources/static/img/function/operation/operation-app.png differ diff --git a/cachecloud-web/src/main/resources/static/img/function/server/app-operation-sentinel.png b/cachecloud-web/src/main/resources/static/img/function/server/app-operation-sentinel.png deleted file mode 100644 index 9102e7d8..00000000 Binary files a/cachecloud-web/src/main/resources/static/img/function/server/app-operation-sentinel.png and /dev/null differ diff --git a/cachecloud-web/src/main/resources/static/img/function/server/app-operation-sentinel1.png b/cachecloud-web/src/main/resources/static/img/function/server/app-operation-sentinel1.png new file mode 100644 index 00000000..904f671f Binary files /dev/null and b/cachecloud-web/src/main/resources/static/img/function/server/app-operation-sentinel1.png differ diff --git a/cachecloud-web/src/main/resources/static/img/function/server/import-list.png b/cachecloud-web/src/main/resources/static/img/function/server/import-list.png new file mode 100644 index 00000000..afd5289a Binary files /dev/null and b/cachecloud-web/src/main/resources/static/img/function/server/import-list.png differ diff --git a/cachecloud-web/src/main/resources/static/img/function/server/import-step1.png b/cachecloud-web/src/main/resources/static/img/function/server/import-step1.png new file mode 100644 index 00000000..54c84bb1 Binary files /dev/null and b/cachecloud-web/src/main/resources/static/img/function/server/import-step1.png differ diff --git a/cachecloud-web/src/main/resources/static/img/function/server/import-step3.png b/cachecloud-web/src/main/resources/static/img/function/server/import-step3.png new file mode 100644 index 00000000..35ff0db0 Binary files /dev/null and b/cachecloud-web/src/main/resources/static/img/function/server/import-step3.png differ diff --git a/cachecloud-web/src/main/resources/static/img/function/server/import-step3err.png b/cachecloud-web/src/main/resources/static/img/function/server/import-step3err.png new file mode 100644 index 00000000..a00488e5 Binary files /dev/null and b/cachecloud-web/src/main/resources/static/img/function/server/import-step3err.png differ diff --git a/cachecloud-web/src/main/resources/static/img/function/server/import-step4.png b/cachecloud-web/src/main/resources/static/img/function/server/import-step4.png new file mode 100644 index 00000000..51f30594 Binary files /dev/null and b/cachecloud-web/src/main/resources/static/img/function/server/import-step4.png differ diff --git a/cachecloud-web/src/main/resources/static/img/function/server/import-step4err.png b/cachecloud-web/src/main/resources/static/img/function/server/import-step4err.png new file mode 100644 index 00000000..4152a5bd Binary files /dev/null and b/cachecloud-web/src/main/resources/static/img/function/server/import-step4err.png differ diff --git a/cachecloud-web/src/main/resources/static/img/function/server/import-step5.png b/cachecloud-web/src/main/resources/static/img/function/server/import-step5.png new file mode 100644 index 00000000..3d6b4149 Binary files /dev/null and b/cachecloud-web/src/main/resources/static/img/function/server/import-step5.png differ diff --git a/cachecloud-web/src/main/resources/static/img/function/server/server-machine-add.png b/cachecloud-web/src/main/resources/static/img/function/server/server-machine-add.png index 5408c7a9..14838204 100644 Binary files a/cachecloud-web/src/main/resources/static/img/function/server/server-machine-add.png and b/cachecloud-web/src/main/resources/static/img/function/server/server-machine-add.png differ diff --git a/cachecloud-web/src/main/resources/static/img/function/server/server-machine.png b/cachecloud-web/src/main/resources/static/img/function/server/server-machine.png index e77dab7c..eb6ddc47 100644 Binary files a/cachecloud-web/src/main/resources/static/img/function/server/server-machine.png and b/cachecloud-web/src/main/resources/static/img/function/server/server-machine.png differ diff --git a/cachecloud-web/src/main/resources/static/img/function/server/server-room-add.png b/cachecloud-web/src/main/resources/static/img/function/server/server-room-add.png new file mode 100644 index 00000000..fe3c42b6 Binary files /dev/null and b/cachecloud-web/src/main/resources/static/img/function/server/server-room-add.png differ diff --git a/cachecloud-web/src/main/resources/static/img/function/server/server-room.png b/cachecloud-web/src/main/resources/static/img/function/server/server-room.png new file mode 100644 index 00000000..859c2828 Binary files /dev/null and b/cachecloud-web/src/main/resources/static/img/function/server/server-room.png differ diff --git a/cachecloud-web/src/main/resources/static/img/function/server/system-alter.png b/cachecloud-web/src/main/resources/static/img/function/server/system-alter.png new file mode 100644 index 00000000..2a1ed127 Binary files /dev/null and b/cachecloud-web/src/main/resources/static/img/function/server/system-alter.png differ diff --git a/cachecloud-web/src/main/resources/static/img/function/server/topology.png b/cachecloud-web/src/main/resources/static/img/function/server/topology.png new file mode 100644 index 00000000..84fe17e5 Binary files /dev/null and b/cachecloud-web/src/main/resources/static/img/function/server/topology.png differ diff --git a/cachecloud-web/src/main/resources/static/img/operate/appMigrate/app-migrate1.png b/cachecloud-web/src/main/resources/static/img/operate/appMigrate/app-migrate1.png index 2cde89b2..584b78f1 100644 Binary files a/cachecloud-web/src/main/resources/static/img/operate/appMigrate/app-migrate1.png and b/cachecloud-web/src/main/resources/static/img/operate/appMigrate/app-migrate1.png differ diff --git a/cachecloud-web/src/main/resources/static/img/operate/appMigrate/app-migrate2.png b/cachecloud-web/src/main/resources/static/img/operate/appMigrate/app-migrate2.png index d92d38ca..d6204264 100644 Binary files a/cachecloud-web/src/main/resources/static/img/operate/appMigrate/app-migrate2.png and b/cachecloud-web/src/main/resources/static/img/operate/appMigrate/app-migrate2.png differ diff --git a/cachecloud-web/src/main/resources/static/img/operate/appMigrate/app-migrate3.png b/cachecloud-web/src/main/resources/static/img/operate/appMigrate/app-migrate3.png index 2c6d3892..f4ae8a89 100644 Binary files a/cachecloud-web/src/main/resources/static/img/operate/appMigrate/app-migrate3.png and b/cachecloud-web/src/main/resources/static/img/operate/appMigrate/app-migrate3.png differ diff --git a/cachecloud-web/src/main/resources/static/img/operate/appMigrate/app-migrate4.png b/cachecloud-web/src/main/resources/static/img/operate/appMigrate/app-migrate4.png index d4150923..359f40b8 100644 Binary files a/cachecloud-web/src/main/resources/static/img/operate/appMigrate/app-migrate4.png and b/cachecloud-web/src/main/resources/static/img/operate/appMigrate/app-migrate4.png differ diff --git a/cachecloud-web/src/main/resources/static/img/operate/appMigrate/app-migrate5.png b/cachecloud-web/src/main/resources/static/img/operate/appMigrate/app-migrate5.png index 0267b5ce..177d7632 100644 Binary files a/cachecloud-web/src/main/resources/static/img/operate/appMigrate/app-migrate5.png and b/cachecloud-web/src/main/resources/static/img/operate/appMigrate/app-migrate5.png differ diff --git a/cachecloud-web/src/main/resources/static/img/operate/appMigrate/app-migrate6.png b/cachecloud-web/src/main/resources/static/img/operate/appMigrate/app-migrate6.png index 44d81876..5cdb393a 100644 Binary files a/cachecloud-web/src/main/resources/static/img/operate/appMigrate/app-migrate6.png and b/cachecloud-web/src/main/resources/static/img/operate/appMigrate/app-migrate6.png differ diff --git a/cachecloud-web/src/main/resources/static/img/operate/appMigrate/app-migrate7.png b/cachecloud-web/src/main/resources/static/img/operate/appMigrate/app-migrate7.png index 1a58ab91..38d7239d 100644 Binary files a/cachecloud-web/src/main/resources/static/img/operate/appMigrate/app-migrate7.png and b/cachecloud-web/src/main/resources/static/img/operate/appMigrate/app-migrate7.png differ diff --git a/cachecloud-web/src/main/resources/static/img/operate/appMigrate/app-migrate8.png b/cachecloud-web/src/main/resources/static/img/operate/appMigrate/app-migrate8.png index 7a906b5f..14fd4650 100644 Binary files a/cachecloud-web/src/main/resources/static/img/operate/appMigrate/app-migrate8.png and b/cachecloud-web/src/main/resources/static/img/operate/appMigrate/app-migrate8.png differ diff --git a/cachecloud-web/src/main/resources/static/img/operate/index.png b/cachecloud-web/src/main/resources/static/img/operate/index.png new file mode 100644 index 00000000..fa5c7a27 Binary files /dev/null and b/cachecloud-web/src/main/resources/static/img/operate/index.png differ diff --git a/cachecloud-web/src/main/resources/static/img/readme/CacheCloud-structure.png b/cachecloud-web/src/main/resources/static/img/readme/CacheCloud-structure.png new file mode 100644 index 00000000..abf141b9 Binary files /dev/null and b/cachecloud-web/src/main/resources/static/img/readme/CacheCloud-structure.png differ diff --git a/cachecloud-web/src/main/resources/static/wiki/access/client.md b/cachecloud-web/src/main/resources/static/wiki/access/client.md index 81bcaf38..f920583a 100644 --- a/cachecloud-web/src/main/resources/static/wiki/access/client.md +++ b/cachecloud-web/src/main/resources/static/wiki/access/client.md @@ -1,3 +1,5 @@ +# 客户端接入 +
## 一、Java接入方法 diff --git a/cachecloud-web/src/main/resources/static/wiki/access/config.md b/cachecloud-web/src/main/resources/static/wiki/access/config.md index f12877fb..9f8a45a6 100644 --- a/cachecloud-web/src/main/resources/static/wiki/access/config.md +++ b/cachecloud-web/src/main/resources/static/wiki/access/config.md @@ -1,4 +1,4 @@ -## 系统全局配置说明 +## 系统配置说明 ### 一、系统配置说明 @@ -82,17 +82,16 @@ cachecloud会定期对机器重要指标进行检测进行报警(报警实现请 #### 7. 相关工具 -- nmon安装目录: -- redis-migrate-tool安装路径: -- redis-shake安装路径: -- redis-full-check安装路径: +- redis-migrate-tool安装路径; +- redis-shake安装路径; +- redis-full-check安装路径。 #### ### 三、注意 -系统配置的初始化值,需要导入2.0.sql(目录:cachecloud-web/sql)中的相关表和数据: +系统配置的初始化值,需要导入2.0.sql(目录:cachecloud-web/sql)中的相关表和数据: ``` CREATE TABLE `system_config` ( diff --git a/cachecloud-web/src/main/resources/static/wiki/access/resource.md b/cachecloud-web/src/main/resources/static/wiki/access/resource.md index b95cfa8c..d66ce42e 100644 --- a/cachecloud-web/src/main/resources/static/wiki/access/resource.md +++ b/cachecloud-web/src/main/resources/static/wiki/access/resource.md @@ -13,6 +13,8 @@ CacheCloud系统需要依赖一些脚本,Redis资源包,迁移工具,公 - Redis资源管理: 管理redis资源包不同版本及推送下载; - 迁移工具资源管理:管理迁移工具不同资源包版本及推送下载; + + ### 二、仓库配置 @@ -23,18 +25,24 @@ CacheCloud系统需要依赖一些脚本,Redis资源包,迁移工具,公 资源下载地址:可访问资源地址;例如:http://%.%.%.com/software/cachecloud/resource + + ### 三、目录管理 对不同类型资源创建不同目录,需要做推送后及在远程仓库创建对应资源目录(格式:/${dir}); + + ### 四、脚本管理 对系统依赖脚本进行版本管理,可在编译内容和推送脚本到仓库上; + + ### 五、Redis资源管理 @@ -66,6 +74,8 @@ Redis资源管理支持对Redis大小版本做统一管理和版本控制(3.0.x 升级完后,若为升级版本5.0.7为当前管理的最终小版本号,则应用按钮会显示为`最新版本`。 + + ### 六、迁移工具资源管理 diff --git a/cachecloud-web/src/main/resources/static/wiki/access/resource.toc.md b/cachecloud-web/src/main/resources/static/wiki/access/resource.toc.md new file mode 100644 index 00000000..4d41b884 --- /dev/null +++ b/cachecloud-web/src/main/resources/static/wiki/access/resource.toc.md @@ -0,0 +1,8 @@ +##### 目录 + +* [一、目的](#cc1) +* [二、仓库配置](#cc2) +* [三、目录管理](#cc3) +* [四、脚本管理](#cc4) +* [五、Redis资源管理](#cc5) +* [六、迁移工具资源管理](#cc6) \ No newline at end of file diff --git a/cachecloud-web/src/main/resources/static/wiki/architecture/index.md b/cachecloud-web/src/main/resources/static/wiki/architecture/index.md index e7dad67e..76efb834 100644 --- a/cachecloud-web/src/main/resources/static/wiki/architecture/index.md +++ b/cachecloud-web/src/main/resources/static/wiki/architecture/index.md @@ -2,10 +2,10 @@ CacheCloud系统架构介绍,主要包含以下方面: -+ 1.[服务架构](service.md) ++ 1.[服务架构](service) + 1.1 类型架构 + 1.2 伸缩架构 -+ 2.[技术架构](tech.md) ++ 2.[技术架构](tech) diff --git a/cachecloud-web/src/main/resources/static/wiki/function/client-analysis.md b/cachecloud-web/src/main/resources/static/wiki/function/client-analysis.md new file mode 100644 index 00000000..10b9b0de --- /dev/null +++ b/cachecloud-web/src/main/resources/static/wiki/function/client-analysis.md @@ -0,0 +1,9 @@ +## 键值分析 + +键值分析主要用来分析应用BigKey/过期键/键值分布等情况。 + + + +点击”键值分析“,提交分析申请。 + + diff --git a/cachecloud-web/src/main/resources/static/wiki/function/client-appStats.md b/cachecloud-web/src/main/resources/static/wiki/function/client-appStats.md new file mode 100644 index 00000000..4583e79b --- /dev/null +++ b/cachecloud-web/src/main/resources/static/wiki/function/client-appStats.md @@ -0,0 +1,76 @@ +## 应用统计信息 + +当您的应用开始使用后,您可以按照如下步骤查看应用统计数据和各实例统计数据。 + +### 一、全局统计 + +- 在页面详情页,默认展示应用统计信息,可以看到全局信息、命令统计、各命令峰值信息,全命令统计、命中统计、网络流量等全天统计曲线。按照顺序, + + 全局信息:展示应用的全局信息,包括内存使用率、当前连接数、应用redis版本、应用类型、主从节点数、命中率、当前对象数、当前状态及分布的机器数; + + 命令统计:展示当前应用执行最频繁的5个命令的分布情况; + + 各命令峰值信息:展示当前应用执行最频繁的5个命令的峰值统计; + + 全命令统计:展示当前应用的命令执行次数趋势图; + + 命中统计:展示当前应用的命中次数趋势图; + + 网络流量:展示应用的输入输出流量趋势图,亦可查看应用下各实例的网络输入输出流量; + + CPU消耗:展示应用的CPU消耗情况趋势图,亦可查看应用下各实例的CPU消耗情况趋势; + + 内存使用量:展示应用的内存使用情况趋势图,亦可查看应用下各实例的内存碎片率情况; + + 客户端连接统计:展示该应用下客户端连接数趋势图; + + 键个数统计:展示该应用下键个数趋势图。 + +![](../../img/function/client/client-statsInfo.jpg) + +![](../../img/function/client/client-statsInfo1.jpg) + +![](../../img/function/client/client-statsInfo2.jpg) + + - 扩容/缩容,在应用统计信息页面,点击申请扩容,弹出如图弹框,按照要求填写对应信息,提交。提交成功后,将收到进度邮件。 + + ![](../../img/function/client/client-capacity.png) + + - 客户端统计 + +- 命令曲线,命令曲线展示了该应用下执行最频繁的5个命令的执行次数趋势比较图,可以点击不同命令查看个命令的趋势图。 + +![](../../img/function/client/client-codeImg.jpg)[]() + +### 二、客户端统计 + +应用首页查看客户端统计,主要客户命令调用,异常统计情况。 +某个客户端调用/流量/耗时 +具体命令调用/流量/耗时 + + + +- 客户命令调用统计,展示应用下命令调用全局统计情况,包括命令调用次数、命令平均耗时、输入/输出流量趋势。 + +![](../../img/function/client/command-statistic.png) +![](../../img/function/client/command-statistic1.png) +![](../../img/function/client/command-statistic2.png) + +**点击“客户端详情”,查看不同客户端下各命令的调用情况。** + +![](../../img/function/client/command-statistic-client.png) + + +![](../../img/function/client/command-statistic-all.png) + +- 客户端异常情况统计:展示应用下异常情况全局统计情况,包括异常次数、异常平均耗时趋势。 + +![](../../img/function/client/exception-statistic.png) + +**异常分为客户端连接异常及命令调用超时异常,可分别点击“客户端连接异常详情”和“客户端命令超时详情”查看不同客户端下对应的异常情况。** + +**1.客户端连接异常详情** + +![](../../img/function/client/exception-statistic-client-conn.png) + +**2.客户端命令超时详情** + +![](../../img/function/client/exception-statistic-client.png) + +![](../../img/function/client/exception-statistic-client1.png) + + 点击图中点查看超时命令详情,包括命令的执行时间,命令明文,参数明文和字节数信息,如下图: + +![](../../img/function/client/exception-statistic-client-cmd.png) + + \ No newline at end of file diff --git a/cachecloud-web/src/main/resources/static/wiki/function/client-cmd.md b/cachecloud-web/src/main/resources/static/wiki/function/client-cmd.md new file mode 100644 index 00000000..cd33c3d7 --- /dev/null +++ b/cachecloud-web/src/main/resources/static/wiki/function/client-cmd.md @@ -0,0 +1,5 @@ +## 命令曲线 + +命令曲线展示了该应用下执行最频繁的5个命令的执行次数趋势比较图,可以点击不同命令查看个命令的趋势图。 + +![](../../img/function/client/client-codeImg.jpg) \ No newline at end of file diff --git a/cachecloud-web/src/main/resources/static/wiki/function/client-cmdexe.md b/cachecloud-web/src/main/resources/static/wiki/function/client-cmdexe.md new file mode 100644 index 00000000..ccc16001 --- /dev/null +++ b/cachecloud-web/src/main/resources/static/wiki/function/client-cmdexe.md @@ -0,0 +1,5 @@ +## 命令执行 + +在应用或实例页面,点击命令执行,打开命令执行页面,支持在console中执行只读命令。 + +![](../../img/function/client/client-appCmd.png) \ No newline at end of file diff --git a/cachecloud-web/src/main/resources/static/wiki/function/client-conn.md b/cachecloud-web/src/main/resources/static/wiki/function/client-conn.md new file mode 100644 index 00000000..58ca0a2c --- /dev/null +++ b/cachecloud-web/src/main/resources/static/wiki/function/client-conn.md @@ -0,0 +1,10 @@ +## 连接信息 + +支持查看当前客户端连接信息,包括客户端ip、连接数、客户端类型,点击”查看实例连接信息“可查看具体redis实例下的连接信息。 + + + +- 应用客户端:该分类下展示用户端连接信息,可通过查看此连接确认是否有用户使用该应用,为应用迁移/下线提供极大便利; +- cc客户端:该分类下展示CacheCloud客户端的连接信息; +- redis客户端:该分类下展示redis实例连接信息; +- 所有客户端:该分类下展示所有客户端连接信息。 \ No newline at end of file diff --git a/cachecloud-web/src/main/resources/static/wiki/function/client-daily.md b/cachecloud-web/src/main/resources/static/wiki/function/client-daily.md new file mode 100644 index 00000000..cefdae28 --- /dev/null +++ b/cachecloud-web/src/main/resources/static/wiki/function/client-daily.md @@ -0,0 +1,5 @@ +## 日报统计 + +日报统计汇总了全天的应用使用情况,分为客户端相关和服务端相关指标统计,帮助用户了解应用的使用情况,发现问题等。 + + diff --git a/cachecloud-web/src/main/resources/static/wiki/function/client-desc.md b/cachecloud-web/src/main/resources/static/wiki/function/client-desc.md new file mode 100644 index 00000000..6bf358ab --- /dev/null +++ b/cachecloud-web/src/main/resources/static/wiki/function/client-desc.md @@ -0,0 +1,9 @@ +## 应用详情&权限管理 + +应用详情可以运维应用详情,配置应用报警,管理应用下的用户。 + +![](../../img/function/client/client-appDetail.png) + +- 该界面展示应用的基本信息, +- 修改应用信息:支持修改应用名、应用描述信息及项目负责人; +- 添加用户:负责人有权限为应用添加用户。 \ No newline at end of file diff --git a/cachecloud-web/src/main/resources/static/wiki/function/client-instances.md b/cachecloud-web/src/main/resources/static/wiki/function/client-instances.md new file mode 100644 index 00000000..d4a9abd7 --- /dev/null +++ b/cachecloud-web/src/main/resources/static/wiki/function/client-instances.md @@ -0,0 +1,14 @@ +## 实例列表&应用拓扑 + +### 一、实例列表 + +- 实例列表,展示应用下各实例的具体情况,可以看到该应用的具体实例分布情况,包括ip、端口,实例状态,角色以及关联实例的id,根据应用类型的不同,可能会有区别。 + + + + +### 二、应用拓扑 + +- 应用拓扑,展示应用实例在各机器上的分布情况。 + + diff --git a/cachecloud-web/src/main/resources/static/wiki/function/client-latency.md b/cachecloud-web/src/main/resources/static/wiki/function/client-latency.md new file mode 100644 index 00000000..86b3f4b9 --- /dev/null +++ b/cachecloud-web/src/main/resources/static/wiki/function/client-latency.md @@ -0,0 +1,12 @@ +## 延迟监控 + +延迟监控统计了应用下发生的”延迟事件“。根据发生事件点,将”延迟事件“和”慢查询命令“对应,更好的辅助用户发现、定位redis的使用问题。 + +![](../../img/function/client/instance-latency.png) + +- 延迟事件统计:统计了各延迟事件数量的趋势图,点击图中点可查看延迟事件详情,如下图:查看某个时间点下某个redis实例发生的延迟事件,关联的慢查询情况等。 +![](../../img/function/client/instance-latency1.png) + +- redis实例延迟&慢查询统计:统计每个redis实例下发生的延迟事件数和慢查询数。 +- 各实例慢查询情况:展示详细的实例下慢查询情况。 + diff --git a/cachecloud-web/src/main/resources/static/wiki/function/client-register.md b/cachecloud-web/src/main/resources/static/wiki/function/client-register.md new file mode 100644 index 00000000..549a370a --- /dev/null +++ b/cachecloud-web/src/main/resources/static/wiki/function/client-register.md @@ -0,0 +1,11 @@ +## 账户申请 + +用户首次使用CacheCloud执行所有操作之前,用户首先需要申请一个系统账号。当需要开通账户时,填写相关信息。管理员收到申请邮件时确认开通。 + +- 进入CacheCloud首页,点击注册按钮 + +![](../../img/function/client/client-login.png) + +- 填写用户相关信息并提交申请 + +![](../../img/function/client/client-register.png) diff --git a/cachecloud-web/src/main/resources/static/wiki/function/client.md b/cachecloud-web/src/main/resources/static/wiki/function/client.md index 580207a4..e7aff05d 100644 --- a/cachecloud-web/src/main/resources/static/wiki/function/client.md +++ b/cachecloud-web/src/main/resources/static/wiki/function/client.md @@ -1,198 +1,11 @@ -客户端模块介绍用户使用CacheCloud平台功能,包括客户端接入、应用/实例监控数据曲线展示、shell命令执行平台及报警权限设置等功能。 +客户端模块介绍普通用户使用CacheCloud平台功能,包括账户申请、应用管理和我的申请三大模块。 -## 一、总体使用流程 -用户使用CacheCloud平台总体流程如下: - -![总体使用流程](../../img/function/client/client-process.png) - - - -## 二、账户申请 - -在执行所有操作之前,用户首先需要申请一个系统账号。当需要开通账户时,填写相关信息,管理员收到申请邮件时确认开通。 - -- 进入CacheCloud首页,点击注册按钮 - -![](../../img/function/client/client-login.png) - -- 填写用户相关信息 - -![](../../img/function/client/client-register.png) - - - -## 三、应用申请 -- 首次登录系统,如下图所示,点击右上角自己的姓名,在弹出的下拉框中选择应用申请。 - -![](../../img/function/client/client-application-tag.png) - -- 弹出应用申请界面,如下图所示,按要求填写应用需求,提交申请即可。提交申请后,您将收到应用申请的邮件提醒及处理进度。 - -![](../../img/function/client/client-application.png) - - - -## 四、应用接入Demo -- 当您的应用申请流程全部完成后(收到应用已经开通的邮件),您可以开始使用您的应用。登录系统后,您会看到您申请及负责的应用列表,例如登录后,可以看到一个id为998的应用。如下图所示,点击应用id或应用名,进入应用详情页面。 - -![](../../img/function/client/client-apps.png) - - -- 点击接入代码tab页,如下图所示,查看使用[代码demo](#access),参考demo代码进行测试和使用。 - -![](../../img/function/client/client-code.png) - - - - -## 五、统计信息 -当您的应用开始使用后,您可以按照如下步骤查看应用统计数据和各实例统计数据。如第四章中所述,进入应用详情页面后,可以看到应用统计信息、拓扑结构、应用详情、命令分析,命令执行、接入代码、慢查询等。接入代码已经在上一节中做过阐述,本章中,我们将依次查看应用统计信息等其他功能。 - -- 在页面详情页,默认展示应用统计信息,可以看到全局信息、命令统计、各命令峰值信息,全命令统计、命中统计、网络流量等全天统计曲线。按照顺序, - + 全局信息:展示应用的全局信息,包括内存使用率、当前连接数、应用redis版本、应用类型、主从节点数、命中率、当前对象数、当前状态及分布的机器数; - + 命令统计:展示当前应用执行最频繁的5个命令的分布情况; - + 各命令峰值信息:展示当前应用执行最频繁的5个命令的峰值统计; - + 全命令统计:展示当前应用的命令执行次数趋势图; - + 命中统计:展示当前应用的命中次数趋势图; - + 网络流量:展示应用的输入输出流量趋势图,亦可查看应用下各实例的网络输入输出流量; - + CPU消耗:展示应用的CPU消耗情况趋势图,亦可查看应用下各实例的CPU消耗情况趋势; - + 内存使用量:展示应用的内存使用情况趋势图,亦可查看应用下各实例的内存碎片率情况; - + 客户端连接统计:展示该应用下客户端连接数趋势图; - + 键个数统计:展示该应用下键个数趋势图。 - -![](../../img/function/client/client-statsInfo.jpg) - -![](../../img/function/client/client-statsInfo1.jpg) - -![](../../img/function/client/client-statsInfo2.jpg) - - - 扩容/缩容,在应用统计信息页面,点击申请扩容,弹出如图弹框,按照要求填写对应信息,提交。提交成功后,将收到进度邮件。 - - ![](../../img/function/client/client-capacity.png) - - - 客户端统计 - -- 命令曲线,命令曲线展示了该应用下执行最频繁的5个命令的执行次数趋势比较图,可以点击不同命令查看个命令的趋势图。 - -![](../../img/function/client/client-codeImg.jpg) - - - -## 六、应用详情、拓扑、扩容/缩容、命令执行 - -在应用详情页面,还剩下实例列表、应用详情、命令执行、应用拓扑页面,本节中将描述这部分功能。 - -- 实例列表,展示应用下各实例的具体情况,可以看到该应用的具体实例分布情况,包括ip、端口,实例状态,角色以及关联实例的id,根据应用类型的不同,可能会有区别。 - -![](../../img/function/client/client-instancelist.png) - -- 应用拓扑,展示应用实例在各机器上的分布情况。 - -![](../../img/function/client/client-topology.png) - -- 应用详情,运维应用详情,配置应用报警,管理用户下的用户。 - -![](../../img/function/client/client-appDetail.png) - -- 接入代码,参考[“应用接入Demo”](#cc4) - - - -## 七、实例信息查看及配置修改 - -- 实例统计信息:在实例列表界面中,点击实例id可以跳转到实例信息页面,默认展示实例的统计信息,在实例页面可以看到菜单。 +- 账户申请:用户首次使用CacheCloud执行所有操作之前,用户首先需要注册一个系统账号; +- 应用管理:用户可以查看、管理自己名下的redis应用,包括应用的统计信息、应用详情、实例列表、连接信息、命令曲线、延迟监控和日报统计等; +- 我的申请: 用户可以查询、申请对自己名下redis应用的相关操作,保留申请应用、导入应用、数据清理、诊断应用等操作。 -![](../../img/function/client/instance-stats.png) - -- 其中拓扑结构,命令曲线,命令执行参考应用相关操作。 - -- 慢查询分析:Redis实例中,可以对慢查询进行查看分析,点击慢查询分析,如下图,可以看到实例的慢查询操作。 - -![](../../img/function/client/instance-slowlog.png) - -- 配置查询:点击配置查询可以对当前配置信息进行查询,如下图,根据应用需求,可以对配置信息进行修改,点击申请修改配置,如下图,按照要求填写信息,提交后会收到处理进度邮件。 - -![](../../img/function/client/instance-config.png) - -![](../../img/function/client/instance-configEdit.png) - -- 连接信息:查看当前客户端连接信息。 - -![](../../img/function/client/instance-connInfo.png) - -- 故障警报:展示当前实例故障情况。 - - - -## 八、CacheCloud Shell Console - -在应用或实例页面,点击命令执行,如下图,打开命令执行tab页面,可以在console中执行只读命令。 - -![](../../img/function/client/client-appCmd.png) - - - -## 九、权限管理及报警阀值设置 - -在应用对应页面,点击应用详情,打开本应用的详情页面,可以看到应用详情,报警指标,用户管理。如下图, -- 对应用信息进行修改; -- 对需要接收该appId报警信息的人员进行管理; -- 对报警指标进行设置和管理。 - -![](../../img/function/client/client-appDetail.png) - - - - -## 十、客户端使用统计 - -应用首页查看客户端统计,主要客户命令调用,异常统计情况。 -某个客户端调用/流量/耗时 -具体命令调用/流量/耗时 - - - -- 客户命令调用统计,展示应用下命令调用全局统计情况,包括命令调用次数、命令平均耗时、输入/输出流量趋势。 - -![](../../img/function/client/command-statistic.png) -![](../../img/function/client/command-statistic1.png) -![](../../img/function/client/command-statistic2.png) - -**点击“客户端详情”,查看不同客户端下各命令的调用情况。** - -![](../../img/function/client/command-statistic-client.png) - - -![](../../img/function/client/command-statistic-all.png) - -- 客户端异常情况统计:展示应用下异常情况全局统计情况,包括异常次数、异常平均耗时趋势。 - -![](../../img/function/client/exception-statistic.png) - -**异常分为客户端连接异常及命令调用超时异常,可分别点击“客户端连接异常详情”和“客户端命令超时详情”查看不同客户端下对应的异常情况。** - -**1.客户端连接异常详情** - -![](../../img/function/client/exception-statistic-client-conn.png) - -**2.客户端命令超时详情** - -![](../../img/function/client/exception-statistic-client.png) - -![](../../img/function/client/exception-statistic-client1.png) - - 点击图中点查看超时命令详情,包括命令的执行时间,命令明文,参数明文和字节数信息,如下图: - -![](../../img/function/client/exception-statistic-client-cmd.png) - - - -## 十一、应用分析 - -键值分析主要用来分析应用BigKey/过期键/键值分布等情况。 - -![](../../img/function/client/app-analysis.png) +用户使用CacheCloud平台总体流程如下: -![](../../img/function/client/app-analysis-application.png) +![总体使用流程](../../img/function/client/client-process.png) \ No newline at end of file diff --git a/cachecloud-web/src/main/resources/static/wiki/function/client.toc.md b/cachecloud-web/src/main/resources/static/wiki/function/client.toc.md deleted file mode 100644 index d62ad6b5..00000000 --- a/cachecloud-web/src/main/resources/static/wiki/function/client.toc.md +++ /dev/null @@ -1,13 +0,0 @@ -##### 目录 - -* [一、总体使用流程](#cc1) -* [二、账户申请](#cc2) -* [三、应用申请](#cc3) -* [四、应用接入demo](#cc4) -* [五、统计信息](#cc5) -* [六、应用详情查询、扩容](#cc6) -* [七、实例信息查看及配置修改](#cc7) -* [八、CacheCloud Shell Console](#cc8) -* [九、权限管理及报警阀值设置](#cc9) -* [十、客户端使用统计(new)](#cc10) -* [十一、应用分析(new)](#cc11) \ No newline at end of file diff --git a/cachecloud-web/src/main/resources/static/wiki/function/index.md b/cachecloud-web/src/main/resources/static/wiki/function/index.md index 875bbbde..76ce461f 100644 --- a/cachecloud-web/src/main/resources/static/wiki/function/index.md +++ b/cachecloud-web/src/main/resources/static/wiki/function/index.md @@ -1,10 +1,13 @@ ## CacheCloud功能介绍 -### 主要功能分以下五个模块: +### CacheCloud平台提供以下五个功能模块: + wiki管理:系统介绍、FAQ、系统接入、系统功能、功能手册、常见问题 + Redis搭建:环境初始化、实例部署安装、类型架构支持 + 客户端接入:Java-SDK接入、客户端监控、其他语言接入; + 运维管理:宿主环境、资源管理、应用审计、应用运维、应用质量监控、应用诊断扥西 + 弹性伸缩:资源评估、垂直伸缩、水平伸缩、外部接入; + 统计监控:指标采集、应用统计、节点统计、机器统计、监控报警、问题诊断; -![cachecloud功能架构](cachecloud-web/src/main/resources/static/img/readme/CacheCloud功能架构.png) \ No newline at end of file + +根据用户类型可分为:前台-客户端功能(普通用户),后台-运维端功能(管理员)。 + + \ No newline at end of file diff --git a/cachecloud-web/src/main/resources/static/wiki/function/job.md b/cachecloud-web/src/main/resources/static/wiki/function/job.md index 3a2bdb87..541a6f18 100644 --- a/cachecloud-web/src/main/resources/static/wiki/function/job.md +++ b/cachecloud-web/src/main/resources/static/wiki/function/job.md @@ -1,6 +1,8 @@ -## 工单流程 -Cacheloud系统提供完善的工单申请-审批流程。用户可以提交自己名下相关应用的工单申请,由管理员驳回/处理、通过后,一条完整的工单流程执行完毕。 -### 1.工单申请 +## 我的申请 +Cacheloud系统提供完善的工单申请-审批流程。用户可以提交自己名下相关应用的工单申请,由管理员驳回/处理、通过后,一条完整的工单流程执行完毕。 + +### 我的工单 + - 点击"我的申请"进入工单列表界面: @@ -9,47 +11,49 @@ Cacheloud系统提供完善的工单申请-审批流程。用户可以提交自 -- "创建工单"按不同类型分为:申请应用,数据清理(全库清理/删除数据),下线应用,诊断应用,键值分析,扩容/缩容,修改应用配置和修改报警。 +### 创建工单 + +- "创建工单"按不同类型分为:申请应用,应用导入,数据清理(全库清理/删除数据),下线应用,数据迁移,诊断应用,键值分析,扩容/缩容,修改应用配置和修改报警等。 a.应用申请: + + b.导入应用:用户将现有redis实例导入cachecloud平台,进行集中管理。 + + - b.数据清理:分为全库清理和删除数据 + c.数据清理:分为全库清理和删除数据 - c.下线应用: + d.下线应用: + + e.数据迁移:将redis数据迁移到新的应用下 + + - d.诊断应用: + f.诊断应用: - e.键值分析: + g.键值分析: - f.扩容/缩容: + h.扩容/缩容: - g.修改应用配置: + i.修改应用配置: - h.修改报警: + g.修改报警: - - -### 2.工单审批 - -- 工单审批界面展示了"工单处理汇总情况"和"审批列表": - - 工单状态分为:待审、处理中、通过、驳回,工单受理后显示处理人。 - - \ No newline at end of file + \ No newline at end of file diff --git a/cachecloud-web/src/main/resources/static/wiki/function/operation-alert.md b/cachecloud-web/src/main/resources/static/wiki/function/operation-alert.md new file mode 100644 index 00000000..d6b315ad --- /dev/null +++ b/cachecloud-web/src/main/resources/static/wiki/function/operation-alert.md @@ -0,0 +1,10 @@ +## 报警配置 + +管理redis报警阈值,支持对应用整体/单个实例进行报警配置。 + + + + + + + diff --git a/cachecloud-web/src/main/resources/static/wiki/function/operation-app.md b/cachecloud-web/src/main/resources/static/wiki/function/operation-app.md new file mode 100644 index 00000000..51c7be1d --- /dev/null +++ b/cachecloud-web/src/main/resources/static/wiki/function/operation-app.md @@ -0,0 +1,120 @@ +###### 目录 +* [1. 应用运维](#cc1) + - [管理应用实例](#cc1-1) + - [应用机器列表](#cc1-2) + - [应用详情和审批列表](#cc1-3) + - [应用密码修改](#cc1-4) + - [应用拓扑诊断](#cc1-5) +* [2. 应用迁移](#cc2) +* [3. 应用下线](#cc3) +* [4. 版本升级](#cc4) + +## 应用运维 + +管理员在应用运维中可查看应用列表,了解各应用的使用情况,如版本、类型、内存详细、碎片率等,并对应用进行一系列的管理操作。 + + + + +### 应用运维 +点击”应用运维“操作,可管理应用实例、查看应用机器列表,应用详情和审批列表,修改应用密码等。 + + +#### 管理应用实例 + +**1. Redis Sentinel类型应用** + +a. 一键添加sentinel节点 +b. 一键Failover +c. 上下线实例 +d. 添加slave节点 +e. 查看操作日志 + + + + + +**2. Redis Cluster应用类型** + +a. 添加Slave +b.一键Failover +c.上下线实例 +d. 查看操作日志 + + + + +#### 应用机器列表 + +查看该应用分布机器的使用情况,包括内存使用率、已分配内存、cpu使用率等信息。 + + + + + +#### 应用详情和审批列表 + + + + +#### 应用密码修改 + + + + +#### 应用拓扑诊断 + +提供对应用拓扑分布规范的检查功能,便于发现问题,做出调整。 + + + + +#### 2.应用迁移 + +在CacheCloud后台的“应用运维”页面,选择要进行迁移的应用,点击“应用迁移”。 这种迁移方式不会更换应用的appId,通过主从节点的failover实现,是对客户端无感知迁移。注意:此操作通过failover实现节点切换,仅支持同redis版本/小版本应用的迁移升级。 + + + +应用迁移整体包含八个主要步骤: + ++ (1) 应用信息:查看源应用的实例IP,角色和Redis版本等信息,选择迁移的目标机房和迁移机器; + + + ++ (2) 应用迁移计划:这一步主要查看节点的变更信息,新增实例(Slave)的IP和端口号,点击继续进行新老Salve节点的替换; + + + ++ (3) 新老Salve节点替换: 替换完成之后查看最新实例信息,点击继续,进行主从切换; + + + ++ (4) 主从Failover: 完成主从节点切换,点击继续,添加新的Slave; + + + ++ (5) 添加Slave: 添加新的Slave; + + + ++ (6) 新实例状态检测:检查新实例的连接状态,是否异常,点击继续,下线老的Slave; + + + ++ (7) 下线Slave: 下线老的Slave; + + + ++ (8) 迁移完成: 完成迁移。 + + + + +#### 3.应用下线 + +下线当前应用,销毁所有存活节点。点击“应用下线”,跳转到应用下线任务流,可查看下线情况。 + + +#### 4.版本升级 + +升级应用的redis版本。详细操作可参考:[运维手册-Redis版本管理.应用版本升级](../access/redisVersion.md)。 \ No newline at end of file diff --git a/cachecloud-web/src/main/resources/static/wiki/function/operation-diagnostic.md b/cachecloud-web/src/main/resources/static/wiki/function/operation-diagnostic.md new file mode 100644 index 00000000..cd0acf68 --- /dev/null +++ b/cachecloud-web/src/main/resources/static/wiki/function/operation-diagnostic.md @@ -0,0 +1,57 @@ +## 应用诊断工具 + +提供给应用诊断的便捷工具,方便管理员排查应用的问题。 + +### redis-cli工具 + +集成redis-cli工具,选择应用和实例,可执行redis-cli命令。 + + + +### scan检测 + +提交scan诊断,选择应用、实例等,扫描匹配的key: + + + +扫描完成后,可查看结果: + + + +### memoryUsed诊断 + +提交memoryUsed诊断,选择应用、实例、内存占用量,扫描出大key: + + + +执行完成后,可查看结果: + + + +### idlekey诊断 + +提交idlekey诊断,选择应用、实例、空闲天数,扫描出key: + + + +执行完成后,可查看结果: + + + +### hotkeys/bigkeys/memkeys诊断 + +采样诊断应用的hotkeys/bigkeys/memkeys,并给出统计结果。 + +### 删除任务 + +执行匹配key的删除任务,并统计删除数量。 + +### 集群slot分析 + +提交redis集群的slot分布,选择应用、实例,分析出slot上key分布: + + + +分析完成后,可查看结果:slot分布和误差较大的slot + + \ No newline at end of file diff --git a/cachecloud-web/src/main/resources/static/wiki/function/operation-import.md b/cachecloud-web/src/main/resources/static/wiki/function/operation-import.md new file mode 100644 index 00000000..6b42ba5a --- /dev/null +++ b/cachecloud-web/src/main/resources/static/wiki/function/operation-import.md @@ -0,0 +1,25 @@ +## 应用导入 + +在使用Cachecloud平台前,用户已有自己部署的redis实例,该功能支持将redis实例导入Cachecloud平台进行运维管理。用户提交的“导入应用”工单信息会记录在此,管理员点击“导入”或者"查看"可以追踪redis实例导入应用的情况。 + + + +应用导入共5个步骤,引导用户完成redis实例到Cachecloud应用的导入。 + +1. 确认导入配置:应用导入前的准备工作,确认源redis实例信息和目标应用信息无误后,点击“开始导入”。 + + +2. 创建Redis版本:系统自动检测平台中是否有满足用户需求的版本号,如果没有,会提示“redis-x.x.x存在”,管理员先“【创建版本】”;如果有满足的版本,直接进入下一步“新建应用”。 + +3. 新建应用:管理员“部署应用”。 + +系统自动检测应用部署状态,如果部署出错,会提示管理员“【修复】 或 【重新部署】”应用。当应用部署成功,会进行到下一步“数据迁移”。 + + +4. 数据迁移:进行源redis实例到目标应用间的数据迁移。 + +系统自动检测数据迁移状态,如果迁移出错,会提示管理员“【修复】 或 【重新迁移】”。当应用数据迁移完毕,会跳转到“应用导入完成提示”。 + + +5. 应用导入完成:应用导入成功,用户可以通过Cachecloud平台管理该应用,旧的redis实例可做下线处理。 + \ No newline at end of file diff --git a/cachecloud-web/src/main/resources/static/wiki/function/operation-job.md b/cachecloud-web/src/main/resources/static/wiki/function/operation-job.md new file mode 100644 index 00000000..478e9a22 --- /dev/null +++ b/cachecloud-web/src/main/resources/static/wiki/function/operation-job.md @@ -0,0 +1,145 @@ +## 工单审批 + +##### 目录 + +* [1. 工单类型](#cc1-1) +* [2. 开通应用](#cc1-2) +* [3. 应用导入](#cc1-3) +* [4. 扩容申请](#cc1-4) +* [5. 数据迁移](#cc1-5) +* [6. 数据清理](#cc1-6) +* [7. 应用诊断](#cc1-7) +* [8. 配置修改申请](#cc1-8) +* [9. 注册用户申请](#cc1-9) +* [10. 键值分析](#cc1-10) + + + +Cacheloud系统提供完善的工单申请-审批流程。用户可以提交自己名下相关应用的工单申请,由管理员驳回/处理、通过后,一条完整的工单流程执行完毕。 + +工单审批界面展示了"工单处理汇总情况"和"审批列表",用户提交的申请在此进行审批处理。工单状态分为:待审、处理中、通过、驳回,工单受理后显示处理人。 + + + + + +### 1. 工单类型 + +如下图所示,来自用户的工单有以下几种: + ++ 应用申请:用户需要申请开通redis standalone、redis sentinel和redis cluster; ++ 导入应用:用户将现有redis实例导入cachecloud平台,进行集中管理; ++ 应用扩容:用户需要对当前的应用进行内存容量扩容; ++ 应用数据迁移:新老应用间数据迁移; ++ 清理数据:删除固定前缀的key; ++ 应用诊断:诊断应用问题,包括扫描key,诊断bigkey/hotkey等,集群slot分析; ++ 应用下线:对无用应用进行下线处理,回收资源; ++ 应用配置修改:用户希望对当前的redis配置做调整; ++ 键值分析:用户申请分析应用BigKey/过期键/键值分布等情况; ++ 注册用户申请:管理员只需要开通或驳回就可以了。 + + + +### 2. 开通应用 + + (1). 不同类型的redis,开通使用不同的格式。 + (2). 一键开通中唯一需要的就是机器的IP。 + 添加机器时,要综合考虑,用户提交关于客户端的基本信息:QPS、容量、机房、主从等信息,决定选用的什么配置、什么机房的机器。 + +#### (1) redis-standalone开通 +- 如下图,按照步骤填选: + + + +- 点击自动生成“部署预览”,确认部署信息后,点击“开始部署”: + + + +#### (2) redis-sentinel开通 + +步骤同上 + +![](../../img/function/server/server-sentinel.png) + +#### (3) redis-cluster开通 + +步骤同上 + +![](../../img/function/server/server-cluster.png) + +详细操作可参考:[运维手册-应用部署](../../wiki/operate/appDeploy.md)。 + +待应用导入开通,点击”通过“,该工单审批完成。 + + + +### 3. 应用导入 + +在使用Cachecloud平台前,用户已有自己部署的redis实例,该功能支持将redis实例导入Cachecloud平台进行运维管理。用户提交的“导入应用”工单信息会记录在此,管理员点击“导入”或者"查看"可以追踪redis实例导入应用的情况。 + + + +应用导入共5个步骤,引导用户完成redis实例到Cachecloud应用的导入,具体操作请参考:[运维功能-应用导入](../../wiki/function/operation-import.md)。 + +待应用导入完毕,点击”通过“,该工单审批完成。 + + + +### 4. 扩容申请 + +- 垂直扩容 +在“扩容配置”一栏填写扩容后单实例最大内存即可 + + + +- 水平扩容 + +水平扩容相对麻烦且费时一些,在开通时候管理员尽量根据用户提交的信息(QPS,容量等),尽量提前预支一些实例,如果还是抗不住,就可做水平扩容。 + +a. 添加一个redis-cluster节点,格式为masterIp:memSize:slaveIp,并meet到集群中; + +b. 迁移slot: 迁移slot速度较慢,CacheCloud支持slot断点续传的功能。 + + + + + +### 5. 数据迁移 + +支持redis数据同步工作,点击”审批处理“跳转到数据迁移界面,具体操作请参考:[运维功能-数据迁移](../../wiki/operate/migrateTool.md)。 +待数据迁移完毕,点击”通过“,该工单审批完成。 + + + +### 6. 数据清理 + +待数据清理完毕,点击”通过“,该工单审批完成。 + + + +### 7. 应用诊断 + +待应用诊断完毕,点击”通过“,该工单审批完成。 + + + +### 8. 配置修改申请 +填写需要修改的配置项和配置值。 + + + + + +### 9. 键值分析 +点击“分析”,进行键值分析 + + + +跳转到键值分析任务流 + + + + + +### 10. 注册用户申请 +直接点击通过或者驳回即可。 diff --git a/cachecloud-web/src/main/resources/static/wiki/function/operation-machine.md b/cachecloud-web/src/main/resources/static/wiki/function/operation-machine.md new file mode 100644 index 00000000..0deb1ae7 --- /dev/null +++ b/cachecloud-web/src/main/resources/static/wiki/function/operation-machine.md @@ -0,0 +1,26 @@ +## 机器管理 + +提供对机器机房的管理运维操作。 + +### 机器管理 + +新机器除了要用脚本进行初始化安装CacheCloud的环境以外,还要统一进行管理,CacheCloud后台提供了机器的增删改查功能,还有一些指标的(cpu,网络,负载)监控功能。 + + + +支持批量添加、管理机器。 + + + + +注:CacheCloud之所以没有提供完整的机器监控功能,是因为各个公司一般都有自己专门的机器运维和监控工具,其中或自己开发或使用像ganglia、nagios等软件搭建。 + +### 机房管理 + +同时支持添加管理机器。 + + + +支持批量添加、管理机器。 + + \ No newline at end of file diff --git a/cachecloud-web/src/main/resources/static/wiki/function/operation-migrate.md b/cachecloud-web/src/main/resources/static/wiki/function/operation-migrate.md new file mode 100644 index 00000000..a8eeb563 --- /dev/null +++ b/cachecloud-web/src/main/resources/static/wiki/function/operation-migrate.md @@ -0,0 +1,120 @@ +## 数据迁移 + +”数据迁移“帮助完成应用间的数据迁移、同步功能。 + +### 一、CacheCloud数据迁移工具能做什么? + +Cachecloud数据迁移工具支持redis数据同步工作,可以完成如下功能: + ++ 支持任意两种类型的source和target进行数据迁移,如RDB文件、Redis Standalone、Redis Sentinel、Redis Cluster、CacheCloud应用; ++ 迁移过程中,源集群不影响对外提供服务; ++ 高效性,多线程并发迁移; ++ 迁移过程便捷,流程可视化; ++ 集成redis-migrate-tool、redis-shake redis数据同步的工具。 ++ 数据迁移具有实时性,所以使用合理可以基本保证数据一致性(原理可以参考第二小节); ++ 数据校验功能。 + + + +### 二、CacheCloud数据迁移工具是如何实现的? + +CacheCloud数据迁移工具底层使用了redis-migrate-tool和redis-shake两种主流redis数据同步的工具。所以这里有必要对这两种数据迁移工具做简单说明。 + ++ [redis-migrate-tool](https://github.com/vipshop/redis-migrate-tool) + + redis-migrate-tool是用c语言开发的Redis数据迁移工具,可以做到在stadalone、sentinel、cluster、rdb(不支持做为target)彼此迁移数据, +服务于唯品会公司数千个Redis节点,从数据迁移的准确性、稳定性、高效性等方面都能满足的生产环境的需求。 +使用文档:[https://github.com/vipshop/redis-migrate-tool](https://github.com/vipshop/redis-migrate-tool) + ++ [redis-shake](https://github.com/alibaba/RedisShake) + + redis-migrate-tool仅支持Redis3-4的迁移,不支持redis5-5/4/3 版本数据同步(rdb文件解析异常),而且已经不在维护,故升级为阿里云开源的redis-shake工具。 + 该工具友好地支持2.8-5.0版本的数据同步/解析、恢复、备份,支持多种部署结构迁移场景,后续会支持断点续传、多活等,且由Redis&MongoDB团队持续优化和维护。 + 使用文档: [https://github.com/alibaba/RedisShake](https://github.com/alibaba/RedisShake) + + +      迁移工具是基于复制的原理,所以是实时迁移的,这点比起redis自带的redis-trib.rb的import 功能要方便很多。 +故CacheCloud选择它们作为数据迁移的基础组件,通过可视化的方式完成参数配置、节点数据迁移、进度查询、日志查询、配置查询和历史记录等一系列功能,使得数据迁移更简单、便捷。 + + + +### 三、CacheCloud数据迁移工具如何部署和使用 + +1. 准备迁移工具机器 + + 初始化机器,在“管理后台-机器管理”页面点击“添加新机器”,请参考相关文档; + + 添加机器时候,选择机器类型为“Redis迁移工具机器”,建议单独使用一台机器做迁移,因为迁移的过程可能会占用机器的很多资源。 +![](../../img/operate/migrate/cc-migrate-machine.png) + +2. 安装部署redis-migrate-tool/redis-shake + + redis-migrate-tool +在“管理后台-系统配置管理”中配置安装目录, +![](../../img/operate/migrate/cc-migrate-redis-migtool-dir.png) + 安装方法可以参考redis-migrate-tool主页或者按照如下安装: + ``` + $ cd /opt/cachecloud/ + $ wget https://github.com/vipshop/redis-migrate-tool/archive/master.zip + $ mv master master.zip + $ unzip master.zip + $ mv redis-migrate-tool-master redis-migrate-tool + $ cd redis-migrate-tool + $ mkdir data + $ autoreconf -fvi + $ ./configure + $ make + $ src/redis-migrate-tool -h + ``` + 最为重要的一步是, 注意这里的cachecloud-open是ssh的用户名 + ``` + $ chown -R ${cachecloud-ssh-username}.${cachecloud-ssh-username} /opt/cachecloud/redis-migrate-tool + ``` + + redis-shake + 在“管理后台-系统配置管理”中配置安装目录: + ![](../../img/operate/migrate/cc-migrate-redis-shake-dir.png) + 安装方法可以参考redis-shake主页或者按照如下安装: + ``` + 1.下载redis-shake安装包,下载地址 https://github.com/alibaba/RedisShake/releases + 2.解压到/opt/cachecloud/redis-shake目录下 + 3.创建文件夹conf(存放配置文件)、 logs(存放日志)和pid(存放进程信息) + 4.下载redis-full-check安装包,下载地址 https://github.com/alibaba/RedisFullCheck/releases + 5.解压到/opt/cachecloud/redis-full-shake目录下 + 6.创建文件夹data(存放比对数据结果) + ``` +3. 添加迁移任务 + a. 点击“迁移数据工具”进入迁移数据记录列表页面。 + + b. 在移数据记录列表中可以查看历史迁移任务信息,任务的配置文件、迁移状态、迁移日志,停止任务,进行数据校验等运维操作。 + + c. 点击“点击添加新的迁移按钮”,选择迁移工具,迁移机器,配置源和目标信息,点击验证按钮,按照通过后就开启了一个迁移的任务,回到迁移列表,就可以观察迁移日志、关闭迁移任务、迁移状态查询等。 + + +4. 数据校验 + - 数据迁移完毕后,点击“数据校验”按钮,跳转到如下界面,说明校验程序已经启动: + + - 可通过点击“校验日志”,查看log(默认log 5s刷新): + + - 查看数据校验结果 + 校验数据报错在校验机器的/opt/cachecloud/redis-full-check/data目录下: + ![](../../img/operate/migrate/cc-migrate-checkres.png) + 查看不一致结果记录到result文件: + ![](../../img/operate/migrate/cc-migrate-checkres2.png) + +### 四、客户端怎么迁移 +1. 当迁移工作基本完成后,我们就需要迁移客户端了,为了方便演示我们假设只有两个客户端。 + +2. 迁移第一个客户端,观察客户端是否出现异常。 + +3. 迁移第二个客户端,继续观察。 + +4. 检查source应用是否还有调用。 + +5. 下线source应用和迁移工具。 + + +### 五、一些建议 +- 尽可能单独找一台机器作为迁移机器,因为迁移的过程可能会占用机器的很多资源; +- redis-migrate-tool/redis-shake很具体的原理问题和细节可以到社区或CacheCloud群里找作者提问; +- redis-shake使用或迁移日志中的问题可以参考: + [https://github.com/alibaba/RedisShake/wiki/FAQ](https://github.com/alibaba/RedisShake/wiki/FAQ) + [https://github.com/alibaba/RedisShake/issues](https://github.com/alibaba/RedisShake/issues) +- 迁移工具页面还有很多要优化的地方,后期会听取大家意见逐渐改善。 + diff --git a/cachecloud-web/src/main/resources/static/wiki/function/operation-resource.md b/cachecloud-web/src/main/resources/static/wiki/function/operation-resource.md new file mode 100644 index 00000000..8fd4a2d6 --- /dev/null +++ b/cachecloud-web/src/main/resources/static/wiki/function/operation-resource.md @@ -0,0 +1,93 @@ +## 系统资源管理 + +##### 目录 + +* [一、目的](#cc1) +* [二、仓库配置](#cc2) +* [三、目录管理](#cc3) +* [四、脚本管理](#cc4) +* [五、Redis资源管理](#cc5) +* [六、迁移工具资源管理](#cc6) + + + + +### 一、目的 + +CacheCloud系统需要依赖一些脚本,Redis资源包,迁移工具,公钥/私钥等资源,由于比较零碎,此项功能用于对资源及其版本同一管理控制,简化运维提升效率。 +主要分以下几大类: + +- 仓库配置:配置资源的仓库地址,可用于推送和下载资源; +- 目录管理:区分各类资源的目录结构,便于管理资源; +- 脚本管理:管理系统依赖资源的脚本; +- Redis资源管理: 管理redis资源包不同版本及推送下载; +- 迁移工具资源管理:管理迁移工具不同资源包版本及推送下载; + + + +### 二、仓库配置 + + + +远程仓库地址:可访问的仓库ip地址;例如:10.%.%.% + +根目录:仓库资源的根目录;例如:/opt/download/software/cachecloud/resource + +资源下载地址:可访问资源地址;例如:http://%.%.sohuno.com/software/cachecloud/resource + + + +### 三、目录管理 + + + +对不同类型资源创建不同目录,需要做推送后及在远程仓库创建对应资源目录(格式:/${dir}); + + + +### 四、脚本管理 + + + +对系统依赖脚本进行版本管理,可在编译内容和推送脚本到仓库上; + + + +### 五、Redis资源管理 + + + +Redis资源管理支持对Redis大小版本做统一管理和版本控制(3.0.x -> 3.2.x -> 4.0.x -> 5.0.x -> 6.0.0-rc)的不断迭代更新,项目也需要用到不同版本和新特性。 + +#### 5.1.新增资源包 + +- 1).Redis版本格式规范,验证格式:redis-主.子.增量版本 +- 2).安装目录规范:/opt/cachecloud/redis-主.子.增量版本 +- 3).是否备份:可直接继承之前版本配置模板,进行增量修改配置 + +#### 5.2.修改配置 + +点击`修改配置`,可新增配置项: + + + +#### 5.3、应用版本升级 + +只支持小版本增量升级(Redis-3.2.x/Redis-4.0.x/Redis-5.0.x): +例如:Redis-5.0.6 升级到 Redis-5.0.7 + + + +点击`版本升级`,可升级当前应用版本: + + + +升级完后,若为升级版本5.0.7为当前管理的最终小版本号,则应用按钮会显示为`最新版本`。 + + + +### 六、迁移工具资源管理 + + + +对系统迁移工具版本管理,方便对资源的可控和管理; diff --git a/cachecloud-web/src/main/resources/static/wiki/function/operation-schedule.md b/cachecloud-web/src/main/resources/static/wiki/function/operation-schedule.md new file mode 100644 index 00000000..30979159 --- /dev/null +++ b/cachecloud-web/src/main/resources/static/wiki/function/operation-schedule.md @@ -0,0 +1,5 @@ +## 调度任务 + +对CacheCloud平台的定时任务进行管理,可开启/暂停/删除某个定时任务。 + + \ No newline at end of file diff --git a/cachecloud-web/src/main/resources/static/wiki/function/operation-systemalert.md b/cachecloud-web/src/main/resources/static/wiki/function/operation-systemalert.md new file mode 100644 index 00000000..80612084 --- /dev/null +++ b/cachecloud-web/src/main/resources/static/wiki/function/operation-systemalert.md @@ -0,0 +1,5 @@ +## 系统配置管理 + +对CacheCloud平台的基本配置信息进行管理。具体配置模板信息如下: + + diff --git a/cachecloud-web/src/main/resources/static/wiki/function/operation-task.md b/cachecloud-web/src/main/resources/static/wiki/function/operation-task.md new file mode 100644 index 00000000..d13a2257 --- /dev/null +++ b/cachecloud-web/src/main/resources/static/wiki/function/operation-task.md @@ -0,0 +1,22 @@ +## Redis任务流管理 + +CacheCloud平台引入任务流机制,将用户操作提交为异步任务,复杂任务拆分为多个子任务,每个子任务又按照步骤依次执行,每个任务/步骤直接是相互独立,可查看任务关系、任务信息、执行状态、运行日志等信息。 + + + +- 下面以“RedisClusterAppDeployTask”任务为例进行使用讲解。 + + + +1. 管理员提交“redis-cluster开通”任务(参考[应用运维-Redis Cluster](#cc2-1-cluster)); +2. 系统自动生成“RedisClusterAppDeployTask”*(部署redis-cluster应用)父任务及多个“RedisServerInstallTask”(在机器上安装redis server)子任务,可查看各任务的基本信息、状态、进度等信息; +3. 点击“执行步骤”可查看任务的具体执行步骤,包括任务的基本信息(各种参数),任务流/步骤列表,任务详细日志(帮助问题排查); +4. 对运行中的任务可以暂停,对暂停/失败的任务亦可提交“重试任务”。 + + + + + + + + diff --git a/cachecloud-web/src/main/resources/static/wiki/function/operation-template.md b/cachecloud-web/src/main/resources/static/wiki/function/operation-template.md new file mode 100644 index 00000000..7dfd28d9 --- /dev/null +++ b/cachecloud-web/src/main/resources/static/wiki/function/operation-template.md @@ -0,0 +1,5 @@ +## Redis配置模板管理 + +此功能是Redis全局配置模板(每次开启应用时用到),并非用于修改线上配置。可对redis的不同版本不同配置项进行增删改查操作,请谨慎修改。 + + \ No newline at end of file diff --git a/cachecloud-web/src/main/resources/static/wiki/function/operation-user.md b/cachecloud-web/src/main/resources/static/wiki/function/operation-user.md new file mode 100644 index 00000000..aaa90ead --- /dev/null +++ b/cachecloud-web/src/main/resources/static/wiki/function/operation-user.md @@ -0,0 +1,5 @@ +## 用户管理 + +管理CacheCloud平台用户信息。 + + \ No newline at end of file diff --git a/cachecloud-web/src/main/resources/static/wiki/function/operations.md b/cachecloud-web/src/main/resources/static/wiki/function/operations.md index f71a1a75..1eb0e34f 100644 --- a/cachecloud-web/src/main/resources/static/wiki/function/operations.md +++ b/cachecloud-web/src/main/resources/static/wiki/function/operations.md @@ -1,264 +1,10 @@ - - - -       CacheCloud除了有面向用户的界面,还有面向管理员的运维界面,帮助管理员做一些如工单处理、日常运维管理等工作。 - - - - -## 一、用户工单运维 - - - -### 1. 工单类型 - -       如下图所示,来自用户的工单有以下几种: - -+ 应用申请:用户需要申请开通redis standalone、redis sentinel和redis cluster; -+ 应用扩容:用户需要对当前的应用进行内存容量扩容; -+ 应用配置修改:用户希望对当前的redis配置做调整; -+ 键值分析:用户申请分析应用BigKey/过期键/键值分布等情况; -+ 注册用户申请:管理员只需要开通或驳回就可以了。 - - - - - -### 2. 开通应用 - (1). 不同类型的redis,开通使用不同的格式。 - (2). 一键开通中唯一需要的就是机器的IP。 - 添加机器时,要综合考虑,用户提交关于客户端的基本信息:QPS、容量、机房、主从等信息,决定选用的什么配置、什么机房的机器。 - -#### (1) redis-standalone开通 -- 如下图,按照步骤填选: - - - -- 点击自动生成“部署预览”,确认部署信息后,点击“开始部署”: - - - -#### (2) redis-sentinel开通 - -步骤同上 - -![](../../img/function/server/server-sentinel.png) - -#### (3) redis-cluster开通 - -步骤同上 - -![](../../img/function/server/server-cluster.png) - -详细操作可参考:[运维手册-应用部署](../../wiki/operate/appDeploy.md)。 - - - -### 3. 扩容申请 -- 垂直扩容 -在“扩容配置”一栏填写扩容后单实例最大内存即可 - - - -- 水平扩容 - -水平扩容相对麻烦且费时一些,在开通时候管理员尽量根据用户提交的信息(QPS,容量等),尽量提前预支一些实例,如果还是抗不住,就可做水平扩容。 - -a. 添加一个redis-cluster节点,格式为masterIp:memSize:slaveIp,并meet到集群中; - -b. 迁移slot: 迁移slot速度较慢,CacheCloud支持slot断点续传的功能。 - - - - - -### 4. 配置修改申请 -填写需要修改的配置项和配置值。 - - - - - -### 5. 键值分析 -点击“分析”,进行键值分析 - - - -跳转到键值分析任务流 - - - - - -### 6. 注册用户申请 -直接点击通过或者驳回即可。 - - - -## 二、管理员运维 - - - - - -### 1. 应用运维 - -在应用运维中可管理应用实例,查看应用机器列表,应用详情和审批列表,修改应用密码,并提供了应用诊断工具。 - -- 管理应用实例 - -**Redis Sentinel** - -a. 一键添加sentinel节点 -b. 一键Failover -c. 上下线实例 -d. 添加slave节点 -e. 查看操作日志 - -![](../../img/function/server/app-operation-sentinel.png) - - - -**Redis Cluster** - -a. 添加Slave -b.一键Failover -c.上下线实例 -d. 查看操作日志 - -![](../../img/function/server/app-operation-cluster.png) - -- 应用机器列表 - -查看该应用分布机器的使用情况,包括内存使用率、已分配内存、cpu使用率等信息。 - -![](../../img/function/server/app-machines.png) - -- 应用详情和审批列表 - -![](../../img/function/server/app-detail.png) - -- 应用密码修改 - -![](../../img/function/server/app-redisPassword.png) - -- 应用诊断工具 - -目前提供对应用拓扑规范检查的功能,提交任务流处理。 - -![](../../img/function/server/app-topologyExam.png) - - - - -### 2. 应用迁移 - -应用一键迁移,对当前应用所有节点进行failover,迁移到其他机器。详细操作可参考:[运维手册-应用迁移](../../wiki/operate/appMigrate.md)。 - -![](../../img/function/server/app-migrate.png) - - - -### 3. 应用下线 - -下线当前应用,销毁所有存活节点。点击“应用下线”,跳转到应用下线任务流,可查看下线情况。 - - - -### 4. Redis版本更新 - -升级应用的redis版本。详细操作可参考:[运维手册-Redis版本管理.应用版本升级](../access/redisVersion.md)。 - - - -## 三、 机器管理 -新机器除了要用脚本进行初始化安装CacheCloud的环境以外,还要统一进行管理,CacheCloud后台提供了机器的增删改查功能,还有一些简单的(cpu,网络,负载)监控功能。 - -CacheCloud之所以没有提供完整的机器监控功能,是因为各个公司一般都有自己专门的机器运维和监控工具,其中或自己开发或使用像ganglia、nagios等软件搭建。 - - - -![](../../img/function/server/server-machine-add.png) - - - -## 四、 用户管理 - -管理CacheCloud平台用户信息。 - - - - - -## 五、Redis版本管理 - -统一管理redis版本,提供查看安装各redis版本的机器信息,修改版本信息,一键安装等功能。 -详细操作可参考:[运维手册-版本管理](../access/redisVersion.md)。 - - - - - -## 六、Redis任务流管理 - -CacheCloud平台引入任务流机制,将用户操作提交为异步任务,复杂任务拆分为多个子任务,每个子任务又按照步骤依次执行,每个任务/步骤直接是相互独立,可查看任务关系、任务信息、执行状态、运行日志等信息。 - - - -- 下面以“RedisClusterAppDeployTask”任务为例进行使用讲解。 - - - -1. 管理员提交“redis-cluster开通”任务(参考[应用运维-Redis Cluster](#cc2-1-cluster)); -2. 系统自动生成“RedisClusterAppDeployTask”*(部署redis-cluster应用)父任务及多个“RedisServerInstallTask”(在机器上安装redis server)子任务,可查看各任务的基本信息、状态、进度等信息; -3. 点击“执行步骤”可查看任务的具体执行步骤,包括任务的基本信息(各种参数),任务流/步骤列表,任务详细日志(帮助问题排查); -4. 对运行中的任务可以暂停,对暂停/失败的任务亦可提交“重试任务”。 - - - - - - - - - - - - -## 七、Redis报警阈值管理 - -管理redis报警阈值,支持对应用整体/单个实例进行报警配置。 - - - - - -![](../../img/function/server/cc-alter-app.png) - - - -## 八、Redis配置模板管理 - -此功能是Redis全局配置模板(每次开启应用时用到),并非用于修改线上配置。可对redis的不同版本不同配置项进行增删改查操作,请谨慎修改。 - - - - - - -## 九、调度任务 - -对CacheCloud平台的定时任务进行管理,可开启/暂停/删除某个定时任务。 - - - - - -## 十、系统配置管理 - -对CacheCloud平台的基本配置信息进行管理。具体配置模板信息如下: - - - ------ +CacheCloud除了有面向用户的界面,还有面向管理员的运维界面,帮助管理员做一些如工单处理、日常运维管理等工作。 +按照功能可分为一下六大块功能: + +- 数据统计 +- 运维功能 +- 配置管理 +- 任务管理 +- 用户管理 +- 系统通知 diff --git a/cachecloud-web/src/main/resources/static/wiki/function/operations.toc.md b/cachecloud-web/src/main/resources/static/wiki/function/operations.toc.md deleted file mode 100644 index 32c2046d..00000000 --- a/cachecloud-web/src/main/resources/static/wiki/function/operations.toc.md +++ /dev/null @@ -1,22 +0,0 @@ -##### 目录 - -* [一、用户工单运维](#cc1) - * [1. 工单类型](#cc1-1) - * [2. 开通应用](#cc1-2) - * [3. 扩容申请](#cc1-3) - * [4. 配置修改申请](#cc1-4) - * [5. 注册用户申请](#cc1-5) - * [5. 键值分析](#cc1-6) -* [二、管理员运维](#cc2) - * [1. 应用运维](#cc2-1) - * [2. 应用迁移](#cc2-2) - * [3. 应用下线](#cc2-3) - * [4. Redis版本更新](#cc2-4) -* [三、机器管理](#cc3) -* [四、用户管理](#cc4) -* [五、Redis版本管理(new)](#cc5) -* [六、Redis任务流管理(new)](#cc6) -* [七、Redis报警阈值管理](#cc7) -* [八、Redis配置模板管理](#cc8) -* [九、调度任务](#cc9) -* [十、系统配置管理](#cc10) \ No newline at end of file diff --git a/cachecloud-web/src/main/resources/static/wiki/function/statistics.md b/cachecloud-web/src/main/resources/static/wiki/function/statistics.md index d501c3ea..4829904e 100644 --- a/cachecloud-web/src/main/resources/static/wiki/function/statistics.md +++ b/cachecloud-web/src/main/resources/static/wiki/function/statistics.md @@ -1,5 +1,5 @@ -## 一、服务端全局统计 +## 服务端全局统计 - 1.资源总览:统计所有在线运行应用数量、实例数量、机器数量、redis版本数量以及机器内存分配和使用情况汇总。 diff --git a/cachecloud-web/src/main/resources/static/wiki/function/system-alert.md b/cachecloud-web/src/main/resources/static/wiki/function/system-alert.md new file mode 100644 index 00000000..a8bcc15f --- /dev/null +++ b/cachecloud-web/src/main/resources/static/wiki/function/system-alert.md @@ -0,0 +1,5 @@ +## 系统通知 + +支持发布CacheCloud平台系统通知。 + + diff --git a/cachecloud-web/src/main/resources/static/wiki/intro/index.md b/cachecloud-web/src/main/resources/static/wiki/intro/index.md index ff704273..0d5b143a 100644 --- a/cachecloud-web/src/main/resources/static/wiki/intro/index.md +++ b/cachecloud-web/src/main/resources/static/wiki/intro/index.md @@ -1,9 +1,9 @@ ### 目录 * [一、CacheCloud是什么](#cc1) -* [二、CacheCloud功能架构](#cc2) -* [三、CacheCloud使用规模](#cc3) -* [四、阿里云ecs试用版本](#cc4) -* [五、FAQ快速接入](#cc5) +* [二、CacheCloud快速接入](#cc2) +* [三、CacheCloud功能架构](#cc3) +* [四、CacheCloud使用规模](#cc4) +* [五、阿里云ecs试用版本](#cc5) * [六、服务用户](#cc6) * [七、图书分享](#cc7) * [八、支持与帮助](#cc8) @@ -11,11 +11,18 @@ ## 一、CacheCloud是什么 -CacheCloud是一个Redis云管理平台:支持Redis多种架构(Standalone、Sentinel、Cluster)高效管理、有效降低大规模redis运维成本,提升资源管控能力和利用率。平台提供快速搭建/迁移,运维管理,弹性伸缩,统计监控,客户端整合接入等功能。 +CacheCloud是一个Redis云管理平台:支持Redis多种架构(Standalone、Sentinel、Cluster)高效管理,有效降低大规模redis运维成本,提升资源管控能力和利用率。平台提供redis快速搭建、迁移,运维管理,弹性伸缩,统计监控,客户端整合接入等功能。 + ![cachecloud云平台](../../img/readme/cachecloud.png) -## 二、CacheCloud功能架构 +## 二、CacheCloud快速接入 + ++ [快速开始](../../wiki/quickstart/index.md) ++ [客户端接入](../../wiki/access/client.md) + + +## 三、CacheCloud功能架构 + Wiki管理:FAQ、系统介绍、系统接入、系统功能、功能手册、常见问题; + Redis搭建:环境初始化、实例部署安装、类型架构支持; @@ -25,27 +32,21 @@ CacheCloud是一个Redis云管理平台:支持Redis多种架构(Standalone、S + 统计监控:指标采集、应用统计、节点统计、机器统计、监控报警、问题诊断; - -## 三、CacheCloud使用规模 + +## 四、CacheCloud使用规模 + 400亿+ commands/day + 15T+ Memory Total + 300+ app Total / 3000+ Instances Total + 200+ Machines Total - -## 四、阿里云ecs试用版本 + +## 五、阿里云ecs试用版本 + CacheCloud后台地址:[地址](http://47.97.112.178:8080/admin/app/list) + 用户名/登录密码:cachecloud_user:cachecloud_user + 开源版本试用时间: 2021-01-18 ,如果大家有空闲公网资源可以贡献,请[联系我们](#cc8) - -## 五、FAQ快速接入 - -+ [快速开始](../../wiki/quickstart/index.md) -+ [客户端接入](../../wiki/access/client.md) - ## 六、服务用户 diff --git a/cachecloud-web/src/main/resources/static/wiki/operate/appUpgrade.md b/cachecloud-web/src/main/resources/static/wiki/operate/appUpgrade.md new file mode 100644 index 00000000..3629807e --- /dev/null +++ b/cachecloud-web/src/main/resources/static/wiki/operate/appUpgrade.md @@ -0,0 +1,39 @@ +## 应用升级 + +应用升级即对应用的redis版本进行升级,小版本和大版本采取不同升级方式。 + +### 1. 应用小版本升级 + +使用“应用迁移工具”,逐步替换节点。 + +#### 1)进入CacheCloud后台 + +在CacheCloud后台的“应用运维”页面,选择要进行迁移的应用,点击“应用迁移”。 这种迁移方式不会更换应用的appId,通过主从节点的failover实现,是对客户端无感知迁移。 + + + + +#### 2)使用迁移工具进行迁移 + +应用迁移整体包含八个主要步骤,具体可参考:[应用迁移文档](../../wiki/function/operation-app#cc2) + ++ (1) 应用信息:查看源应用的实例IP,角色和Redis版本等信息,选择迁移的目标机房和迁移机器; ++ (2) 应用迁移计划:这一步主要查看节点的变更信息,新增实例(Slave)的IP和端口号,点击继续进行新老Salve节点的替换; ++ (3) 新老Salve节点替换: 替换完成之后查看最新实例信息,点击继续,进行主从切换; ++ (4) 主从Failover: 完成主从节点切换,点击继续,添加新的Slave; ++ (5) 添加Slave: 添加新的Slave; ++ (6) 新实例状态检测:检查新实例的连接状态,是否异常,点击继续,下线老的Slave; ++ (7) 下线Slave: 下线老的Slave; ++ (8) 迁移完成: 完成迁移。 + +### 2. 应用大版本升级 + +redis大版本间由于各版本配置不兼容的问题,无法使用“应用迁移工具”进行应用升级,我们推荐使用“应用迁移工具”实现大版本的升级工作。 + +#### 1)创建新的redis应用 ++ 用户申请redis应用,参考[“系统功能-我的申请-创建工单-应用申请”](../../wiki/function/job)。 ++ 管理员审批、开通应用,参考[“开通应用”](../../wiki/function/operation-job#cc1-2)。 + +#### 2)进行新老redis应用数据迁移 ++ 新老应用数据迁移,参考[“数据迁移”](../../wiki/function/operation-migrate)。 ++ 数据迁移完毕,通知客户端更新到新应用。 \ No newline at end of file diff --git a/cachecloud-web/src/main/resources/static/wiki/operate/index.md b/cachecloud-web/src/main/resources/static/wiki/operate/index.md index 1193a67f..0726c5be 100644 --- a/cachecloud-web/src/main/resources/static/wiki/operate/index.md +++ b/cachecloud-web/src/main/resources/static/wiki/operate/index.md @@ -1,16 +1,7 @@ ## CacheCloud运维手册 -本节主要介绍常用运维及系统优化,分以下方面: - -+ 1.[基础概念](baseConcept.md):应用、实例、机器等运维 -+ 2.常用运维 - + 2.1 [基础运维](baseOperate.md) - + 2.2 [应用部署](appDeploy.md) - + 2.3 [迁移工具](migrateTool.md) - + 2.4 [应用迁移](appMigrate.md) - + 2.5 [版本管理](../access/redisVersion.md) - + 2.6 [报警指标](appAlert.md) - + 2.7 [CacheCloud支持密码方案](rediscode.md) -+ 3.[系统运维优化](baseOptimize.md) +本节主要从管理员运维角度,介绍CacheCloud平台的常用运维及系统优化。CacheCloud平台的运维主要分为三大部分:应用运维、机器运维和系统运维,如下图: + +下面的章节我们将对每个部分涉及到的运维功能和技巧展开阐述。 \ No newline at end of file diff --git a/cachecloud-web/src/main/resources/static/wiki/quickstart/index.md b/cachecloud-web/src/main/resources/static/wiki/quickstart/index.md index e6e5d1fe..d24806e3 100644 --- a/cachecloud-web/src/main/resources/static/wiki/quickstart/index.md +++ b/cachecloud-web/src/main/resources/static/wiki/quickstart/index.md @@ -1,3 +1,15 @@ +##### 目录 +* [一、准备war包](#cc1) +* [二、初始化数据](#cc2) +* [三、启动工程](#cc3) +* [四、系统初始配置](#cc4) +* [五、创建一个用户](#cc5) +* [六、准备资源](#cc6) +* [七、创建一个应用](#cc7) + + + + ## 快速接入 quick start ”快速接入“ 帮助你快速启动cachecloud中台,并创建一个应用。 @@ -37,20 +49,16 @@ b.也可以clone源码,自行打包: spring: application: name: cloud.cachecloud-web.open + #配置访问端口,默认8080 + server: + port: 8080 #配置数据库 cachecloud: primary: #mysql数据库 url: ${jdbcUrl} user: ${username} password: ${password} - redis: #配置cachecloud-web需要的redis,用户存储任务流日志,可稍后配置 - main: - host: 127.0.0.1 - port: 6379 - password: - #配置访问端口,默认8080 - server: - port: 8080 + 启动工程 //启动web工程 @@ -139,4 +147,10 @@ b.也可以clone源码,自行打包: (提示:任务流日志是存储在redis中的,即项目启动时配置文件application-open.yml中的cachecloud.redis信息) - \ No newline at end of file + + + + +* 3.导入应用 + + 该功能支持将外部redis实例导入Cachecloud平台进行运维管理。具体操作参考:[应用导入](../../wiki/function/operations#cc1-3.md) \ No newline at end of file diff --git a/cachecloud-web/src/main/resources/static/wiki/quickstart/index.toc.md b/cachecloud-web/src/main/resources/static/wiki/quickstart/index.toc.md index 0fd631ca..904c87ac 100644 --- a/cachecloud-web/src/main/resources/static/wiki/quickstart/index.toc.md +++ b/cachecloud-web/src/main/resources/static/wiki/quickstart/index.toc.md @@ -10,4 +10,5 @@ * [3.添加机器](#cc63) * [七、创建一个应用](#cc7) * [1.申请应用](#cc71) - * [2.部署应用](#cc72) \ No newline at end of file + * [2.部署应用](#cc72) + * [3.导入应用](#cc73) \ No newline at end of file diff --git a/cachecloud-web/src/main/resources/static/wiki/troubleshooting/cachecloud.md b/cachecloud-web/src/main/resources/static/wiki/troubleshooting/cachecloud.md new file mode 100644 index 00000000..dab8e192 --- /dev/null +++ b/cachecloud-web/src/main/resources/static/wiki/troubleshooting/cachecloud.md @@ -0,0 +1,37 @@ +## 关于Cachecloud平台使用问题 + +##### 目录 + +1. [数据库时间问题](#Q1) +2. [平台本身需要的redis问题](#Q2) +3. [用户登录注册问题](#Q3) +4. [现有redis导入问题](#Q4) + + +### 1.数据库时间问题 +CacheCloud版本2 使用mysql-connector-java 8, 与数据库版本不匹配时,时区显示可能有问题。建议在配置jdbcUrl时指定时区serverTimezone=Asia/Shanghai,如下: + + cachecloud: + primary: + url: jdbc:mysql://129.0.0.1:3306/redis_open?useUnicode=true&characterEncoding=UTF8&autoReconnect=true&connectTimeout=3000&socketTimeout=10000&serverTimezone=Asia/Shanghai + + +### 2.平台本身需要的redis问题 +cachecloud-web工程配置文件中可为平台配置一个redis实例信息,用于存储任务流的日志。这一项不是必须的,可在项目启动后再创建配置。 + + cachecloud: + redis: #配置cachecloud-web需要的redis,用户存储任务流log,可稍后配置 + main: + host: ${ip} + port: ${port} + password: ${pwd} + + +### 3.用户登录注册问题 +CacheCloud版本2.2 将支持用户自主提交&修改密码功能。 + + +### 4.现有redis导入问题 +CacheCloud版本1 支持简单的原生redis导入功能,但仅限制于导入redis的应用信息查询,节点监控等功能。 + +CacheCloud版本2.1 新提供“应用导入功能”,实现全方位的redis实例托管,可参考:["应用导入"](../../wiki/function/operation-import)。 \ No newline at end of file diff --git a/cachecloud-web/src/main/resources/static/wiki/troubleshooting/index.md b/cachecloud-web/src/main/resources/static/wiki/troubleshooting/index.md index 9805d32f..ba89adeb 100644 --- a/cachecloud-web/src/main/resources/static/wiki/troubleshooting/index.md +++ b/cachecloud-web/src/main/resources/static/wiki/troubleshooting/index.md @@ -1,9 +1,5 @@ -## 运维常见问题 +## FAQ常见问题 -+ 1.[常见jedis异常类](exception.md) -+ 2.[JedisPool优化](jedispoolconfig.md) -+ 3.[bigkey问题](bigkey.md) -+ 4.[hotkey问题](hotkey.md) -+ 5.[Redis内存优化](memory.md) -+ 6.[activeDefrag引起redis周期性延迟](activefrag.md) +关于CacheCloud的一些问题,分为CacheCloud中台的使用问题和Redis运维的问题。 +我们将大家关心的常见问题,和github issue上提到的问题,会在这里做说明,并持续更新... diff --git a/cachecloud-web/src/main/resources/templates/expAppsDaily.ftl b/cachecloud-web/src/main/resources/templates/expAppsDaily.ftl index 62badb04..65ceee95 100644 --- a/cachecloud-web/src/main/resources/templates/expAppsDaily.ftl +++ b/cachecloud-web/src/main/resources/templates/expAppsDaily.ftl @@ -226,6 +226,134 @@ + + + + + + + + + + + + + <#assign machineEnvs=exceptionMachineEnv["host"]> + <#list machineEnvs as machine> + + + + + + + + + + + +
+ 宿主机ip + + cachecloud_nprocs + + fsync_slow_times + + somaxconn + + redis实例数 + + 文件句柄used/total + + disk used + + 诊断结果 +
+ ${machine.ip} + + ${machine.envs.nproc_threads} + + ${machine.envs.fsync_delay_times} + + ${machine.envs.somaxconn} + + ${machine.envs.instanceNum} + + ${machine.envs.unlimit_used}/${machine.envs.unlimit} + + ${machine.envs.diskUsed} + + 异常 +
+ + + + + + + + + + + + + <#assign machineEnvs=exceptionMachineEnv["container"]> + <#list machineEnvs as container> + + + + + + + + + + +
+ 容器ip + + overcommit_memory + + thp_enabled + + thp_defrag + + swappiness + + nproc + + 诊断结果 +
+ ${container.ip} + + ${container.envs.overcommit_memory} + + ${container.envs.transparent_hugepage_enable} + + ${container.envs.transparent_hugepage_defrag} + + ${container.envs.swappines} + + ${container.envs.nproc} + + 异常 +
+
@@ -36,7 +37,9 @@
-
+ <%--
--%> + <%--
--%> + <%--
--%>
diff --git a/cachecloud-web/src/main/webapp/WEB-INF/jsp/manage/resource/module.jsp b/cachecloud-web/src/main/webapp/WEB-INF/jsp/manage/resource/module.jsp new file mode 100644 index 00000000..9f746fdb --- /dev/null +++ b/cachecloud-web/src/main/webapp/WEB-INF/jsp/manage/resource/module.jsp @@ -0,0 +1,120 @@ +<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %> +<%@include file="/WEB-INF/jsp/manage/commons/taglibs.jsp" %> +<%@include file="/WEB-INF/jsp/manage/include/cache_cloud_main_js_include.jsp" %> +<%@include file="/WEB-INF/jsp/manage/include/cache_cloud_main_css.jsp" %> + + + +
+
+

+ 模块管理 +

+
+ +
+
+
+ +
+
+
+
+ +
+ + + +
+ +
+
+
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
序号模块名说明最后更新时间操作人状态操作
${resource.id} + ${resource.name} + ${resource.name} + + ${resource.intro} + + + + ${resource.username} + + 未推送 + 已推送 + + + + + +
+
+
+
+ +<%@ include file="addModule.jsp" %> + + + + diff --git a/cachecloud-web/src/main/webapp/WEB-INF/jsp/migrate/init.jsp b/cachecloud-web/src/main/webapp/WEB-INF/jsp/migrate/init.jsp index be3c5e68..72d7b672 100644 --- a/cachecloud-web/src/main/webapp/WEB-INF/jsp/migrate/init.jsp +++ b/cachecloud-web/src/main/webapp/WEB-INF/jsp/migrate/init.jsp @@ -24,6 +24,12 @@ $('#redis_shake_config').hide(); } }); + + var targetAppId = document.getElementById('targetAppId').value; + if (targetAppId != '') { + fillAppInstanceList('targetRedisShakeIndex', 'redisTargetPass', 'redisTargetVersion', 'targetServers', 'targetRedisMigrateIndex', 'targetAppName', 'targetAppId'); + } + }); function changeDataType(appIdId, serversId, choose) { @@ -226,18 +232,38 @@ function (data) { var status = data.status; if (status == 1) { + updateForImport(data.migrateId); alert("迁移程序已经启动,请返回迁移列表关注迁移进度!"); - location.href = "/data/migrate/list"; + location.href = "/data/migrate/index?status=-2"; } else { alert("迁移失败,请查看日志分析原因!"); } - var checkButton = document.getElementById("checkButton"); checkButton.disabled = true; } ); } + function updateForImport(migrateId) { + console.log("updateForImport migrateId:" + migrateId); + var importId = document.getElementById("importId"); + if (importId != null && importId.value != '') { + console.log("updateForImport importId:" + importId.value); + $.get( + '/import/app/goOn.json', + { + importId: importId.value, + migrateId: migrateId + }, + function (data) { + var success = data.success; + if (success == 1) { + console.log("updateForImport success"); + } + } + ); + } + } @@ -289,7 +315,7 @@ class="form-control select2_category"> @@ -303,7 +329,9 @@ @@ -365,13 +393,13 @@ - - - @@ -427,6 +455,7 @@
@@ -457,12 +486,13 @@ @@ -514,6 +544,7 @@
@@ -532,6 +563,7 @@
@@ -559,9 +591,12 @@ 源实例详情:
- +
@@ -638,6 +673,8 @@ + + diff --git a/cachecloud-web/src/main/webapp/WEB-INF/jsp/migrate/list.jsp b/cachecloud-web/src/main/webapp/WEB-INF/jsp/migrate/list.jsp deleted file mode 100644 index 14b25866..00000000 --- a/cachecloud-web/src/main/webapp/WEB-INF/jsp/migrate/list.jsp +++ /dev/null @@ -1,311 +0,0 @@ -<%@ page contentType="text/html;charset=UTF-8" language="java" %> -<%@ include file="/WEB-INF/jsp/manage/commons/taglibs.jsp" %> - - - - - 迁移数据记录列表 - - - - -
- -
-
- -
-
- - -
-
-
- -
-
- -
-
- -
-
- -
- -
- -
- - - -
-
- -
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
序号迁移ID迁移工具迁移机器操作人源数据目标数据开始时间结束时间状态查看操作校验数据
${stat.index + 1} - - ${appDataMigrateStatus.migrateId} - - - redis-shake - - - redis-migrate-tool - - - - - - - ${appDataMigrateStatus.migrateMachineIp}:${appDataMigrateStatus.migrateMachinePort} - - - ${appDataMigrateStatus.migrateMachineIp} - - - ${appDataMigrateStatus.userName} - 数据源: - - - 非cachecloud -
- ${appDataMigrateStatus.sourceServers} -
- - cachecloud:${appDataMigrateStatus.sourceAppId} - -
-
- 源类型:redis-${appDataMigrateStatus.sourceMigrateTypeDesc} -
- redis版本:${appDataMigrateStatus.redisSourceVersion} -
- 数据源: - - - 非cachecloud -
- ${appDataMigrateStatus.targetServers} -
- - cachecloud:${appDataMigrateStatus.targetAppId} - -
-
- 目标类型:redis-${appDataMigrateStatus.targetMigrateTypeDesc} -
- redis版本:${appDataMigrateStatus.redisTargetVersion} -
${appDataMigrateStatus.startTimeFormat}${appDataMigrateStatus.endTimeFormat}${appDataMigrateStatus.statusDesc} - 日志

- 配置

- - - - 进度 - - - 进度 - - - - - -
- - - - - 采样校验 - - - 数据校验 -

-  校验日志 -
-
-
-
-
- -
    - -
    -
    -
    -
    -






    - - - - - - - - - diff --git a/cachecloud-web/src/main/webapp/WEB-INF/jsp/user/userRegister.jsp b/cachecloud-web/src/main/webapp/WEB-INF/jsp/user/userRegister.jsp index ec351fb3..ca37bae6 100644 --- a/cachecloud-web/src/main/webapp/WEB-INF/jsp/user/userRegister.jsp +++ b/cachecloud-web/src/main/webapp/WEB-INF/jsp/user/userRegister.jsp @@ -1,204 +1,270 @@ <%@ page contentType="text/html;charset=UTF-8" language="java" %> -<%@ include file="/WEB-INF/jsp/manage/commons/taglibs.jsp"%> +<%@ include file="/WEB-INF/jsp/manage/commons/taglibs.jsp" %> CacheCloud用户申请 -
    - -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    -
    - -
    -
    -
    - -
    - -
    -
    - -
    - -
    - -
    -
    - -
    - -
    - -
    -
    - -
    - -
    - -
    -
    - -
    - -
    - -
    -
    - -
    - -
    - - -
    - - - -
    -
    -
    -
    - -        - -
    -
    -
    -
    -
    - - -
    -
    -
    - -
    -
    -
    - -






    - +
    + +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    + +
    + +
    +
    + +<%--
    --%> +<%-- --%> +<%--
    --%> +<%-- +<%-- class="form-control" onchange="checkPassword()"/>--%> +<%-- 密码中必须包含字母、数字,至少8个字符--%> +<%--
    --%> +<%--
    --%> + +<%--
    --%> +<%-- --%> +<%--
    --%> +<%-- +<%-- class="form-control" onchange="checkConfirmPassword()"/>--%> +<%--
    --%> + +<%--
    --%> + +
    + +
    + +
    +
    + +
    + +
    + +
    +
    + +
    + +
    + +
    +
    + +
    + +
    + +
    +
    + +
    + +
    + +
    +
    + + +

    +
    +
    +
    +
    + +        + +
    +
    +
    +
    +
    +
    + +
    +
    +
    + +
    +
    +
    +
    +






    + diff --git a/cachecloud-web/src/main/webapp/WEB-INF/resources/js/appInit.js b/cachecloud-web/src/main/webapp/WEB-INF/resources/js/appInit.js index 4b4b163a..7e41f8bb 100644 --- a/cachecloud-web/src/main/webapp/WEB-INF/resources/js/appInit.js +++ b/cachecloud-web/src/main/webapp/WEB-INF/resources/js/appInit.js @@ -37,7 +37,7 @@ function saveAppDesc(){ //应用描述 var memSize = document.getElementById("memSize"); if(memSize.value == ""){ - alert("内容容量不能为空"); + alert("内存容量不能为空"); memSize.focus(); return false; } @@ -54,6 +54,17 @@ function saveAppDesc(){ officer.focus(); return false; } + + //redis版本 + var versionId = document.getElementById("versionId"); + if(versionId.value == "-1"){ + var versionName = document.getElementById("versionName"); + if(versionName.value == ""){ + alert("Redis部署版本不能为空"); + officer.focus(); + return false; + } + } //预估QPS var forecaseQps = document.getElementById("forecaseQps"); diff --git a/cachecloud-web/src/main/webapp/WEB-INF/resources/manage/manage/appDeploy.js b/cachecloud-web/src/main/webapp/WEB-INF/resources/manage/manage/appDeploy.js index deaae641..5305a11a 100644 --- a/cachecloud-web/src/main/webapp/WEB-INF/resources/manage/manage/appDeploy.js +++ b/cachecloud-web/src/main/webapp/WEB-INF/resources/manage/manage/appDeploy.js @@ -4,219 +4,218 @@ function generateDeployInfo() { /** - * 1.获取机器信息 - */ + * 1.获取机器信息 + */ var redisMachines = ""; var sentinelMachines = ""; var twemproxyMachines = ""; var pikaMachines = ""; // 1.获取不同类型的机器信息 - if(redisMachines == ''){ - $("#redisMachineList option:selected").each(function(){ - if(this.value != '' ){ - redisMachines += this.value +";"; + if (redisMachines == '') { + $("#redisMachineList option:selected").each(function () { + if (this.value != '') { + redisMachines += this.value + ";"; } }); } - if(sentinelMachines == ''){ - $("#sentinelMachineList option:selected").each(function(){ - if(this.value != ''){ - sentinelMachines += this.value +";"; + if (sentinelMachines == '') { + $("#sentinelMachineList option:selected").each(function () { + if (this.value != '') { + sentinelMachines += this.value + ";"; } }); } - if(twemproxyMachines == ''){ - $("#twemproxyMachineList option:selected").each(function(){ - if(this.value != ''){ - twemproxyMachines += this.value +";"; + if (twemproxyMachines == '') { + $("#twemproxyMachineList option:selected").each(function () { + if (this.value != '') { + twemproxyMachines += this.value + ";"; } }); } - if(pikaMachines == ''){ - $("#pikaMachineList option:selected").each(function(){ - if(this.value != ''){ - pikaMachines += this.value +";"; + if (pikaMachines == '') { + $("#pikaMachineList option:selected").each(function () { + if (this.value != '') { + pikaMachines += this.value + ";"; } }); } /** - * 2.信息验证: - * 2.1 部署机器信息 - * 2.2 资源验证: maxmeory验证 + * 2.信息验证: + * 2.1 部署机器信息 + * 2.2 资源验证: maxmeory验证 */ var maxMemory = $.trim($("#maxMemory").val()); - if(maxMemory == '' || maxMemory== null){ + if (maxMemory == '' || maxMemory == null) { toastr.error("请填写maxMemory内存大小!"); $("#maxMemory").focus(); - return ; - } + return; + } - if($("#appType").val() == '2' || $("#appType").val() == '6'){ //rediscluster /redis standalone - if(redisMachines==''){ + if ($("#appType").val() == '2' || $("#appType").val() == '6') { //rediscluster /redis standalone + if (redisMachines == '') { toastr.error("请选择Redis部署机器!"); $("#redisMachineList").focus(); - return ; + return; } - }else if($("#appType").val() == '5'){ // redis sentinel - if(redisMachines==''){ + } else if ($("#appType").val() == '5') { // redis sentinel + if (redisMachines == '') { toastr.error("请选择Redis部署机器!"); $("#redisMachineList").focus(); - return ; + return; } - if(sentinelMachines==''){ + if (sentinelMachines == '') { toastr.error("请选择Sentinel机器机器!"); $("#sentinelMachineList").focus(); - return ; + return; } - }else if($("#appType").val() == '7'){ // redis twemproxy - if(redisMachines==''){ + } else if ($("#appType").val() == '7') { // redis twemproxy + if (redisMachines == '') { toastr.error("请选择Redis部署机器!"); $("#redisMachineList").focus(); - return ; + return; } - if(sentinelMachines==''){ + if (sentinelMachines == '') { toastr.error("请选择Sentinel机器机器!"); $("#sentinelMachineList").focus(); - return ; + return; } - if(twemproxyMachines==''){ + if (twemproxyMachines == '') { toastr.error("请选择Twemproxy机器机器!"); $("#twemproxyMachineList").focus(); - return ; + return; } - }else if($("#appType").val() == '8'){ // pika sentinel - if(pikaMachines==''){ + } else if ($("#appType").val() == '8') { // pika sentinel + if (pikaMachines == '') { toastr.error("请选择Pika部署机器!"); $("#redisMachineList").focus(); - return ; + return; } - if(sentinelMachines==''){ + if (sentinelMachines == '') { toastr.error("请选择Sentinel机器机器!"); $("#sentinelMachineList").focus(); - return ; + return; } - }else if($("#appType").val() == '9'){ // pika twemproxy - if(pikaMachines==''){ + } else if ($("#appType").val() == '9') { // pika twemproxy + if (pikaMachines == '') { toastr.error("请选择Pika部署机器!"); $("#redisMachineList").focus(); - return ; + return; } - if(sentinelMachines==''){ + if (sentinelMachines == '') { toastr.error("请选择Sentinel机器机器!"); $("#sentinelMachineList").focus(); - return ; + return; } - if(twemproxyMachines==''){ + if (twemproxyMachines == '') { toastr.error("请选择Twemproxy机器机器"); $("#twemproxyMachineList").focus(); - return ; + return; } - } + } - //清除上次记录 - $('#tableList tbody tr').empty(); + //清除上次记录 + $('#tableList tbody tr').empty(); $("#appDeployText").val(""); $("#appDeployInfo").val(""); - $.post( + $.post( '/manage/app/generateDeployInfo.json', - { - type: $("#appType").val(), - hasSalve: 1, + { + type: $("#appType").val(), + hasSalve: 1, maxMemory: maxMemory, - redisNum: $("#redisNum option:selected").val(), - sentinelNum: $("#sentinelNum option:selected").val(), - pikaNum: $("#pikaNum option:selected").val(), - twemproxyNum: $("#twemproxyNum option:selected").val(), + redisNum: $("#redisNum option:selected").val(), + sentinelNum: $("#sentinelNum option:selected").val(), + pikaNum: $("#pikaNum option:selected").val(), + twemproxyNum: $("#twemproxyNum option:selected").val(), redisMachines: redisMachines, sentinelMachines: sentinelMachines, pikaMachines: pikaMachines, twemproxyMachines: twemproxyMachines - }, - function (data) { + }, + function (data) { $("#startDeployInfoLabel").val(""); - if(data.result!='success'){ - toastr.error("实例部署异常:"+data.result); - } - else { + if (data.result != 'success') { + toastr.error("实例部署异常:" + data.result); + } else { /** - * 显示机器部署详情 + * 显示机器部署详情 */ $("#selectMachineId").removeAttr("style"); - $("#clearInfo").attr("style","background:#CCCCCC"); - var resMachines=data.resMachines; - var machineDeployStatMap=data.machineDeployStatMap; + $("#clearInfo").attr("style", "background:#CCCCCC"); + var resMachines = data.resMachines; + var machineDeployStatMap = data.machineDeployStatMap; resMachines.forEach(function (machine) { - var machineDeployStat=machineDeployStatMap[machine.ip]; + var machineDeployStat = machineDeployStatMap[machine.ip]; $("#tableList tbody").prepend( '\n' + - " "+machine.ip+"\n" + - ' '+machine.instanceNum+"  /  "+machine.cpu+'\n' + - ' '+(machine.usedMem/1024/1024/1024).toFixed(1)+'G / '+(machine.mem-(machine.usedMem/1024/1024/1024)).toFixed(1)+'G / '+machine.mem+'G\n' + + " " + machine.ip + "\n" + + ' ' + machine.instanceNum + "  /  " + machine.cpu + '\n' + + ' ' + (machine.usedMem / 1024 / 1024 / 1024).toFixed(1) + 'G / ' + (machine.mem - (machine.usedMem / 1024 / 1024 / 1024)).toFixed(1) + 'G / ' + machine.mem + 'G\n' + ' - G / - G / - G\n' + - ' '+machineDeployStat.masterNum+"  /  "+machineDeployStat.slaveNum+"  /  "+machineDeployStat.sentinelNum+"  /  "+machineDeployStat.twemproxyNum+'\n' + - ' '+machine.realIp+"/  "+machine.rack+'\n' + + ' ' + machineDeployStat.masterNum + "  /  " + machineDeployStat.slaveNum + "  /  " + machineDeployStat.sentinelNum + "  /  " + machineDeployStat.twemproxyNum + '\n' + + ' ' + machine.realIp + "/  " + machine.rack + '\n' + '' ) }) /** - * 展示部署详情 + * 展示部署详情 */ var deployInfos = data.deployInfoList; var appType = $("#appType").val(); - if(appType==2){//Redis-cluster + if (appType == 2) {//Redis-cluster deployInfos.forEach(function (deployInfo) { - var masterIp=deployInfo.masterIp; - var memSize=deployInfo.memSize; - var slaveIp=deployInfo.slaveIp; - appDeployInfo.value=appDeployInfo.value+masterIp+":"+memSize+(slaveIp==''?"\n":":"+slaveIp+"\n"); + var masterIp = deployInfo.masterIp; + var memSize = deployInfo.memSize; + var slaveIp = deployInfo.slaveIp; + appDeployInfo.value = appDeployInfo.value + masterIp + ":" + memSize + (slaveIp == '' ? "\n" : ":" + slaveIp + "\n"); }); - }else if(appType==5){//Redis-sentinel + } else if (appType == 5) {//Redis-sentinel deployInfos.forEach(function (deployInfo) { - if(deployInfo.masterIp!=null&&deployInfo.masterIp!=''){ - var masterIp=deployInfo.masterIp; - var memSize=deployInfo.memSize; - var slaveIp=deployInfo.slaveIp; - appDeployInfo.value=appDeployInfo.value+masterIp+":"+memSize+(slaveIp==''?"\n":":"+slaveIp+"\n"); - }else { - var sentinelIp=deployInfo.sentinelIp; - appDeployInfo.value=appDeployInfo.value+"sentinel:"+sentinelIp+"\n"; + if (deployInfo.masterIp != null && deployInfo.masterIp != '') { + var masterIp = deployInfo.masterIp; + var memSize = deployInfo.memSize; + var slaveIp = deployInfo.slaveIp; + appDeployInfo.value = appDeployInfo.value + masterIp + ":" + memSize + (slaveIp == '' ? "\n" : ":" + slaveIp + "\n"); + } else { + var sentinelIp = deployInfo.sentinelIp; + appDeployInfo.value = appDeployInfo.value + "sentinel:" + sentinelIp + "\n"; } }); - }else if(appType==6){//Redis-standalone + } else if (appType == 6) {//Redis-standalone deployInfos.forEach(function (deployInfo) { - var masterIp=deployInfo.masterIp; - var memSize=deployInfo.memSize; - appDeployInfo.value=masterIp+":"+memSize; + var masterIp = deployInfo.masterIp; + var memSize = deployInfo.memSize; + appDeployInfo.value = masterIp + ":" + memSize; }); - }else if(appType==7){//Redis twemproxy + } else if (appType == 7) {//Redis twemproxy deployInfos.forEach(function (deployInfo) { - if(deployInfo.masterIp!=null&&deployInfo.masterIp!=''){ - var masterIp=deployInfo.masterIp; - var memSize=deployInfo.memSize; - var slaveIp=deployInfo.slaveIp; - appDeployInfo.value=appDeployInfo.value+masterIp+":"+memSize+(slaveIp==''?"\n":":"+slaveIp+"\n"); - }else if(deployInfo.sentinelIp!=null&&deployInfo.sentinelIp!=''){ - var sentinelIp=deployInfo.sentinelIp; - appDeployInfo.value=appDeployInfo.value+"sentinel:"+sentinelIp+"\n"; - }else if(deployInfo.twemproxyIp!=null&&deployInfo.twemproxyIp!=''){ - var twemproxyIp=deployInfo.twemproxyIp; - appDeployInfo.value=appDeployInfo.value+"twemproxy:"+twemproxyIp+"\n"; + if (deployInfo.masterIp != null && deployInfo.masterIp != '') { + var masterIp = deployInfo.masterIp; + var memSize = deployInfo.memSize; + var slaveIp = deployInfo.slaveIp; + appDeployInfo.value = appDeployInfo.value + masterIp + ":" + memSize + (slaveIp == '' ? "\n" : ":" + slaveIp + "\n"); + } else if (deployInfo.sentinelIp != null && deployInfo.sentinelIp != '') { + var sentinelIp = deployInfo.sentinelIp; + appDeployInfo.value = appDeployInfo.value + "sentinel:" + sentinelIp + "\n"; + } else if (deployInfo.twemproxyIp != null && deployInfo.twemproxyIp != '') { + var twemproxyIp = deployInfo.twemproxyIp; + appDeployInfo.value = appDeployInfo.value + "twemproxy:" + twemproxyIp + "\n"; } }); - }else if(appType==8){//pika sentinel + } else if (appType == 8) {//pika sentinel deployInfos.forEach(function (deployInfo) { - if(deployInfo.masterPikaIp!=null&&deployInfo.masterPikaIp!=''){ - var masterPikaIp=deployInfo.masterPikaIp; - var memSize=deployInfo.memSize; - var slavePikaIp=deployInfo.slavePikaIp; - appDeployInfo.value=appDeployInfo.value+masterPikaIp+":"+memSize+(slavePikaIp==''?"\n":":"+slavePikaIp+"\n"); - }else { - var sentinelIp=deployInfo.sentinelIp; - appDeployInfo.value=appDeployInfo.value+sentinelIp+"\n"; + if (deployInfo.masterPikaIp != null && deployInfo.masterPikaIp != '') { + var masterPikaIp = deployInfo.masterPikaIp; + var memSize = deployInfo.memSize; + var slavePikaIp = deployInfo.slavePikaIp; + appDeployInfo.value = appDeployInfo.value + masterPikaIp + ":" + memSize + (slavePikaIp == '' ? "\n" : ":" + slavePikaIp + "\n"); + } else { + var sentinelIp = deployInfo.sentinelIp; + appDeployInfo.value = appDeployInfo.value + sentinelIp + "\n"; } }); - }else if(appType==9){//pika twemproxy + } else if (appType == 9) {//pika twemproxy deployInfos.forEach(function (deployInfo) { if (deployInfo.masterPikaIp != null && deployInfo.masterPikaIp != '') { var masterPikaIp = deployInfo.masterPikaIp; @@ -232,18 +231,18 @@ function generateDeployInfo() { } }); } - } + } } - ); + ); } /** * 置空相关信息 */ -function clearinfo(){ +function clearinfo() { //清除上次记录 $('#tableList tbody tr').empty(); - $("#selectMachineId").attr("style","display:none"); + $("#selectMachineId").attr("style", "display:none"); $("#appDeployText").val(""); $("#appDeployInfo").val(""); } @@ -254,50 +253,50 @@ function clearinfo(){ function addAppDeployTask() { // 是否设置密码 - var isSetPasswd = $("#isSetPasswd").attr("checked")== 'checked' ? 1: 0; + var isSetPasswd = $("#isSetPasswd").attr("checked") == 'checked' ? 1 : 0; var maxMemory = $.trim($("#maxMemory").val()); - if(maxMemory == '' || maxMemory== null){ + if (maxMemory == '' || maxMemory == null) { toastr.error("请填写maxMemory内存大小!"); $("#maxMemory").focus(); - return ; + return; } // 检查部署预览是否为空 // $("#appDeployBtn").attr("disabled",true); - if($("#appDeployInfo").val()==''){ + if ($("#appDeployInfo").val() == '') { toastr.error('请先生成部署预览!'); - return ; - } + return; + } var redisMachines = ""; var sentinelMachines = ""; var twemproxyMachines = ""; var pikaMachines = ""; // 1.获取不同类型的机器信息 - if(redisMachines == ''){ - $("#redisMachineList option:selected").each(function(){ - if(this.value != '' ){ - redisMachines += this.value +";"; + if (redisMachines == '') { + $("#redisMachineList option:selected").each(function () { + if (this.value != '') { + redisMachines += this.value + ";"; } }); } - if(sentinelMachines == ''){ - $("#sentinelMachineList option:selected").each(function(){ - if(this.value != ''){ - sentinelMachines += this.value +";"; + if (sentinelMachines == '') { + $("#sentinelMachineList option:selected").each(function () { + if (this.value != '') { + sentinelMachines += this.value + ";"; } }); } - if(twemproxyMachines == ''){ - $("#twemproxyMachineList option:selected").each(function(){ - if(this.value != ''){ - twemproxyMachines += this.value +";"; + if (twemproxyMachines == '') { + $("#twemproxyMachineList option:selected").each(function () { + if (this.value != '') { + twemproxyMachines += this.value + ";"; } }); } - if(pikaMachines == ''){ - $("#pikaMachineList option:selected").each(function(){ - if(this.value != ''){ - pikaMachines += this.value +";"; + if (pikaMachines == '') { + $("#pikaMachineList option:selected").each(function () { + if (this.value != '') { + pikaMachines += this.value + ";"; } }); } @@ -319,14 +318,16 @@ function addAppDeployTask() { redisMachines: redisMachines, sentinelMachines: sentinelMachines, pikaMachines: pikaMachines, - twemproxyMachines: twemproxyMachines + twemproxyMachines: twemproxyMachines, }, - function(data){ + function (data) { var status = data.status; if (status == 'success') { - $('#appDeployBtn').attr("disabled",true); + $('#appDeployBtn').attr("disabled", true); toastr.success(data.message); - setTimeout("reloadAppStatPage("+data.taskid+");",2000); + console.log("updateForImport"); + updateForImport(data.taskid); + setTimeout("reloadAppStatPage(" + data.taskid + ");", 2000); } else { toastr.error("应用部署失败,请查看系统日志确认相关原因!"); } @@ -335,8 +336,27 @@ function addAppDeployTask() { } //重新加载appDetail页面 -function reloadAppStatPage(taskid){ +function reloadAppStatPage(taskid) { location.href = "/manage/task/flow?taskId=" + taskid; } +function updateForImport(taskId) { + var importId = document.getElementById("importId"); + if (importId != null && importId.value != '') { + $.get( + '/import/app/goOn.json', + { + importId: importId.value, + appBuildTaskId: taskId, + }, + function (data) { + var success = data.success; + if (success == 1) { + console.log("updateForImport success"); + } + } + ); + } +} +