package com.sohu.cache.web.controller;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import com.sohu.cache.constant.CommandResult;
import com.sohu.cache.constant.MachineInfoEnum;
import com.sohu.cache.constant.AppDataMigrateEnum;
import com.sohu.cache.constant.AppDataMigrateResult;
import com.sohu.cache.constant.RedisMigrateToolConstant;
import com.sohu.cache.entity.AppDataMigrateSearch;
import com.sohu.cache.entity.AppDataMigrateStatus;
import com.sohu.cache.entity.AppDesc;
import com.sohu.cache.entity.AppUser;
import com.sohu.cache.entity.InstanceInfo;
import com.sohu.cache.entity.MachineInfo;
import com.sohu.cache.machine.MachineCenter;
import com.sohu.cache.redis.RedisCenter;
import com.sohu.cache.stats.app.AppDataMigrateCenter;
import com.sohu.cache.util.ConstUtils;
import com.sohu.cache.util.TypeUtil;
import com.sohu.cache.web.service.AppService;
/**
* 应用数据迁移入口
*
* @author leifu
* @Date 2016-6-8
* @Time 下午11:10:34
*/
@Controller
@RequestMapping("/data/migrate")
public class AppDataMigrateController extends BaseController {
@Resource(name = "appDataMigrateCenter")
private AppDataMigrateCenter appDataMigrateCenter;
@Resource(name = "appService")
private AppService appService;
@Resource(name = "machineCenter")
private MachineCenter machineCenter;
@Resource(name = "redisCenter")
private RedisCenter redisCenter;
private static Set<String> MIGRATE_SAMPLE_USEFUL_LINES = new HashSet<String>();
static {
MIGRATE_SAMPLE_USEFUL_LINES.add("Checked keys");
MIGRATE_SAMPLE_USEFUL_LINES.add("Inconsistent value keys");
MIGRATE_SAMPLE_USEFUL_LINES.add("Inconsistent expire keys");
MIGRATE_SAMPLE_USEFUL_LINES.add("Other check error keys");
MIGRATE_SAMPLE_USEFUL_LINES.add("Checked OK keys");
}
/**
* 初始化界面
* @return
*/
@RequestMapping(value = "/init")
public ModelAndView init(HttpServletRequest request, HttpServletResponse response, Model model) {
List<MachineInfo> machineInfoList = machineCenter.getMachineInfoByType(MachineInfoEnum.TypeEnum.REDIS_MIGRATE_TOOL);
model.addAttribute("machineInfoList", machineInfoList);
return new ModelAndView("migrate/init");
}
/**
* 检查配置
* @return
*/
@RequestMapping(value = "/check")
public ModelAndView check(HttpServletRequest request, HttpServletResponse response, Model model) {
//相关参数
String migrateMachineIp = request.getParameter("migrateMachineIp");
String sourceRedisMigrateIndex = request.getParameter("sourceRedisMigrateIndex");
AppDataMigrateEnum sourceRedisMigrateEnum = AppDataMigrateEnum.getByIndex(NumberUtils.toInt(sourceRedisMigrateIndex, -1));
String sourceServers = request.getParameter("sourceServers");
String targetRedisMigrateIndex = request.getParameter("targetRedisMigrateIndex");
AppDataMigrateEnum targetRedisMigrateEnum = AppDataMigrateEnum.getByIndex(NumberUtils.toInt(targetRedisMigrateIndex, -1));
String targetServers = request.getParameter("targetServers");
String redisSourcePass = request.getParameter("redisSourcePass");
String redisTargetPass = request.getParameter("redisTargetPass");
//检查返回结果
AppDataMigrateResult redisMigrateResult = appDataMigrateCenter.check(migrateMachineIp, sourceRedisMigrateEnum, sourceServers, targetRedisMigrateEnum, targetServers, redisSourcePass, redisTargetPass);
model.addAttribute("status", redisMigrateResult.getStatus());
model.addAttribute("message", redisMigrateResult.getMessage());
return new ModelAndView("");
}
/**
* 开始迁移
* @return
*/
@RequestMapping(value = "/start")
public ModelAndView start(HttpServletRequest request, HttpServletResponse response, Model model) {
//相关参数
String migrateMachineIp = request.getParameter("migrateMachineIp");
String sourceRedisMigrateIndex = request.getParameter("sourceRedisMigrateIndex");
AppDataMigrateEnum sourceRedisMigrateEnum = AppDataMigrateEnum.getByIndex(NumberUtils.toInt(sourceRedisMigrateIndex, -1));
String sourceServers = request.getParameter("sourceServers");
String targetRedisMigrateIndex = request.getParameter("targetRedisMigrateIndex");
AppDataMigrateEnum targetRedisMigrateEnum = AppDataMigrateEnum.getByIndex(NumberUtils.toInt(targetRedisMigrateIndex, -1));
String targetServers = request.getParameter("targetServers");
long sourceAppId = NumberUtils.toLong(request.getParameter("sourceAppId"));
long targetAppId = NumberUtils.toLong(request.getParameter("targetAppId"));
String redisSourcePass = request.getParameter("redisSourcePass");
String redisTargetPass = request.getParameter("redisTargetPass");
AppUser appUser = getUserInfo(request);
long userId = appUser == null ? 0 : appUser.getId();
// 不需要对格式进行检验,check已经做过了,开始迁移
boolean isSuccess = appDataMigrateCenter.migrate(migrateMachineIp, sourceRedisMigrateEnum, sourceServers,
targetRedisMigrateEnum, targetServers, sourceAppId, targetAppId, redisSourcePass, redisTargetPass, userId);
model.addAttribute("status", isSuccess ? 1 : 0);
return new ModelAndView("");
}
/**
* 停掉迁移任务
* @return
*/
@RequestMapping(value = "/stop")
public ModelAndView stop(HttpServletRequest request, HttpServletResponse response, Model model) {
//任务id:查到任务相关信息
long id = NumberUtils.toLong(request.getParameter("id"));
AppDataMigrateResult stopMigrateResult = appDataMigrateCenter.stopMigrate(id);
model.addAttribute("status", stopMigrateResult.getStatus());
model.addAttribute("message", stopMigrateResult.getMessage());
return new ModelAndView("");
}
/**
* 查看迁移日志
* @return
*/
@RequestMapping(value = "/log")
public ModelAndView log(HttpServletRequest request, HttpServletResponse response, Model model) {
//任务id:查到任务相关信息
long id = NumberUtils.toLong(request.getParameter("id"));
int pageSize = NumberUtils.toInt(request.getParameter("pageSize"), 0);
if (pageSize == 0) {
pageSize = 100;
}
String log = appDataMigrateCenter.showDataMigrateLog(id, pageSize);
model.addAttribute("logList", Arrays.asList(log.split(ConstUtils.NEXT_LINE)));
return new ModelAndView("migrate/log");
}
/**
* 查看迁移日志
* @return
*/
@RequestMapping(value = "/config")
public ModelAndView config(HttpServletRequest request, HttpServletResponse response, Model model) {
//任务id:查到任务相关信息
long id = NumberUtils.toLong(request.getParameter("id"));
String config = appDataMigrateCenter.showDataMigrateConf(id);
model.addAttribute("configList", Arrays.asList(config.split(ConstUtils.NEXT_LINE)));
return new ModelAndView("migrate/config");
}
/**
* 查看迁移进度
* @return
*/
@RequestMapping(value = "/process")
public ModelAndView showProcess(HttpServletRequest request, HttpServletResponse response, Model model) {
long id = NumberUtils.toLong(request.getParameter("id"));
Map<RedisMigrateToolConstant, Map<String, Object>> migrateToolStatMap = appDataMigrateCenter.showMiragteToolProcess(id);
model.addAttribute("migrateToolStatMap", migrateToolStatMap);
return new ModelAndView("migrate/process");
}
/**
* 查看迁移进度
* @return
*/
@RequestMapping(value = "/checkData")
public ModelAndView checkData(HttpServletRequest request, HttpServletResponse response, Model model) {
long id = NumberUtils.toLong(request.getParameter("id"));
int nums = 1000 + new Random().nextInt(2000);
//为了方便,直接传入命令
CommandResult commandResult = appDataMigrateCenter.sampleCheckData(id, nums);
String message = commandResult.getResult();
List<String> checkDataResultList = new ArrayList<String>();
checkDataResultList.add("一共随机检验了" + nums + "个key" + ",检查结果如下:");
String[] lineArr = message.split(ConstUtils.NEXT_LINE);
for (String line : lineArr) {
if (StringUtils.isBlank(line)) {
continue;
}
// 行数太多显示会有问题
if (lineArr.length > 100 && !isUsefulLine(line)) {
continue;
}
//message格式显示有点问题
line = line.replace("[0m", "");
line = line.replace("[31m", "");
line = line.replace("[33m", "");
checkDataResultList.add(line.trim());
}
model.addAttribute("checkDataResultList", checkDataResultList);
model.addAttribute("checkDataCommand", commandResult.getCommand());
return new ModelAndView("migrate/checkData");
}
private boolean isUsefulLine(String line) {
for (String usefulLine : MIGRATE_SAMPLE_USEFUL_LINES) {
if (line.contains(usefulLine)) {
return true;
}
}
return false;
}
/**
* 查看迁移列表(包含历史)
* @return
*/
@RequestMapping(value = "/list")
public ModelAndView list(HttpServletRequest request, HttpServletResponse response, Model model, AppDataMigrateSearch appDataMigrateSearch) {
List<AppDataMigrateStatus> appDataMigrateStatusList = appDataMigrateCenter.search(appDataMigrateSearch);
model.addAttribute("appDataMigrateStatusList", appDataMigrateStatusList);
model.addAttribute("appDataMigrateSearch", appDataMigrateSearch);
return new ModelAndView("migrate/list");
}
/**
* 通过应用id获取可用的Redis实例信息
* @return
*/
@RequestMapping(value = "/appInstanceList")
public ModelAndView appInstanceList(HttpServletRequest request, HttpServletResponse response, Model model) {
String appIdStr = request.getParameter("appId");
long appId = NumberUtils.toLong(appIdStr);
AppDesc appDesc = appService.getByAppId(appId);
StringBuffer instances = new StringBuffer();
List<InstanceInfo> instanceList = appService.getAppInstanceInfo(appId);
if (CollectionUtils.isNotEmpty(instanceList)) {
for (int i = 0; i < instanceList.size(); i++) {
InstanceInfo instanceInfo = instanceList.get(i);
if (instanceInfo == null) {
continue;
}
if (instanceInfo.isOffline()) {
continue;
}
// 如果是sentinel类型的应用只出master
if (TypeUtil.isRedisSentinel(appDesc.getType())) {
if (TypeUtil.isRedisSentinel(instanceInfo.getType())) {
continue;
}
if (!redisCenter.isMaster(appId, instanceInfo.getIp(), instanceInfo.getPort())) {
continue;
}
}
instances.append(instanceInfo.getIp() + ":" + instanceInfo.getPort());
if (i != instanceList.size() - 1) {
instances.append(ConstUtils.NEXT_LINE);
}
}
}
model.addAttribute("instances", instances.toString());
model.addAttribute("appType", appDesc == null ? -1 : appDesc.getType());
return new ModelAndView("");
}
}