mirror of
https://github.com/didi/KnowStreaming.git
synced 2026-05-18 19:40:24 +08:00
v2.8.0_e初始化
1、测试代码,开源用户尽量不要使用; 2、包含Kafka-HA的相关功能; 3、并非基于2.6.0拉的分支,是基于master分支的 commit-id:462303fca0拉的2.8.0_e的分支。出现这个情况的原因是v2.6.0的代码并不是最新的,2.x最新的代码是462303fca0这个commit对应的代码;
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
package com.xiaojukeji.kafka.manager.service.biz.ha;
|
||||
|
||||
import com.xiaojukeji.kafka.manager.common.entity.Result;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.ha.HaASRelationDO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.vo.ha.HaClusterTopicVO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.vo.normal.topic.HaClusterTopicHaStatusVO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface HaASRelationManager {
|
||||
/**
|
||||
* 获取集群主备信息
|
||||
*/
|
||||
List<HaClusterTopicVO> getHATopics(Long firstClusterPhyId, Long secondClusterPhyId, boolean filterSystemTopics);
|
||||
|
||||
/**
|
||||
* 获取集群Topic的主备状态信息
|
||||
*/
|
||||
Result<List<HaClusterTopicHaStatusVO>> listHaStatusTopics(Long clusterPhyId, Boolean checkMetadata);
|
||||
|
||||
|
||||
/**
|
||||
* 获取获取集群topic高可用关系 0:备topic, 1:主topic, -1非高可用
|
||||
*/
|
||||
Integer getRelation(Long clusterId, String topicName);
|
||||
|
||||
/**
|
||||
* 获取获取集群topic高可用关系
|
||||
*/
|
||||
HaASRelationDO getASRelation(Long clusterId, String topicName);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.xiaojukeji.kafka.manager.service.biz.ha;
|
||||
|
||||
import com.xiaojukeji.kafka.manager.common.entity.Result;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.vo.rd.app.AppRelateTopicsVO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* Ha App管理
|
||||
*/
|
||||
public interface HaAppManager {
|
||||
Result<List<AppRelateTopicsVO>> appRelateTopics(Long clusterPhyId, List<String> filterTopicNameList);
|
||||
|
||||
boolean isContainAllRelateAppTopics(Long clusterPhyId, List<String> filterTopicNameList);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.xiaojukeji.kafka.manager.service.biz.ha;
|
||||
|
||||
import com.xiaojukeji.kafka.manager.common.entity.Result;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.ao.ClusterDetailDTO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.ClusterDO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Ha Cluster管理
|
||||
*/
|
||||
public interface HaClusterManager {
|
||||
List<ClusterDetailDTO> getClusterDetailDTOList(Boolean needDetail);
|
||||
|
||||
Result<Void> addNew(ClusterDO clusterDO, Long activeClusterId, String operator);
|
||||
|
||||
Result<Void> deleteById(Long clusterId, String operator);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.xiaojukeji.kafka.manager.service.biz.ha;
|
||||
|
||||
import com.xiaojukeji.kafka.manager.common.entity.Result;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.TopicOperationResult;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.ao.ha.HaSwitchTopic;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.dto.op.topic.HaTopicRelationDTO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.ha.JobLogDO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* Ha Topic管理
|
||||
*/
|
||||
public interface HaTopicManager {
|
||||
/**
|
||||
* 批量更改主备关系
|
||||
*/
|
||||
Result<List<TopicOperationResult>> batchCreateHaTopic(HaTopicRelationDTO dto, String operator);
|
||||
|
||||
/**
|
||||
* 批量更改主备关系
|
||||
*/
|
||||
Result<List<TopicOperationResult>> batchRemoveHaTopic(HaTopicRelationDTO dto, String operator);
|
||||
|
||||
/**
|
||||
* 可重试的执行主备切换
|
||||
* @param newActiveClusterPhyId 主集群
|
||||
* @param newStandbyClusterPhyId 备集群
|
||||
* @param switchTopicNameList 切换的Topic列表
|
||||
* @param focus 强制切换
|
||||
* @param firstTriggerExecute 第一次触发执行
|
||||
* @param switchLogTemplate 切换日志模版
|
||||
* @param operator 操作人
|
||||
* @return 操作结果
|
||||
*/
|
||||
Result<HaSwitchTopic> switchHaWithCanRetry(Long newActiveClusterPhyId,
|
||||
Long newStandbyClusterPhyId,
|
||||
List<String> switchTopicNameList,
|
||||
boolean focus,
|
||||
boolean firstTriggerExecute,
|
||||
JobLogDO switchLogTemplate,
|
||||
String operator);
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
package com.xiaojukeji.kafka.manager.service.biz.ha.impl;
|
||||
|
||||
import com.xiaojukeji.kafka.manager.common.bizenum.TopicAuthorityEnum;
|
||||
import com.xiaojukeji.kafka.manager.common.bizenum.ha.HaRelationTypeEnum;
|
||||
import com.xiaojukeji.kafka.manager.common.bizenum.ha.HaResTypeEnum;
|
||||
import com.xiaojukeji.kafka.manager.common.constant.KafkaConstant;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.Result;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.ResultStatus;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.ClusterDO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.TopicDO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.gateway.AuthorityDO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.ha.HaASRelationDO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.vo.ha.HaClusterTopicVO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.vo.normal.topic.HaClusterTopicHaStatusVO;
|
||||
import com.xiaojukeji.kafka.manager.common.utils.ValidateUtils;
|
||||
import com.xiaojukeji.kafka.manager.service.biz.ha.HaASRelationManager;
|
||||
import com.xiaojukeji.kafka.manager.service.cache.PhysicalClusterMetadataManager;
|
||||
import com.xiaojukeji.kafka.manager.service.service.TopicManagerService;
|
||||
import com.xiaojukeji.kafka.manager.service.service.gateway.AuthorityService;
|
||||
import com.xiaojukeji.kafka.manager.service.service.ha.HaASRelationService;
|
||||
import com.xiaojukeji.kafka.manager.service.service.ha.HaTopicService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Service
|
||||
public class HaASRelationManagerImpl implements HaASRelationManager {
|
||||
@Autowired
|
||||
private HaASRelationService haASRelationService;
|
||||
|
||||
@Autowired
|
||||
private TopicManagerService topicManagerService;
|
||||
|
||||
@Autowired
|
||||
private HaTopicService haTopicService;
|
||||
|
||||
@Autowired
|
||||
private AuthorityService authorityService;
|
||||
|
||||
@Override
|
||||
public List<HaClusterTopicVO> getHATopics(Long firstClusterPhyId, Long secondClusterPhyId, boolean filterSystemTopics) {
|
||||
List<HaASRelationDO> doList = haASRelationService.listAllHAFromDB(firstClusterPhyId, secondClusterPhyId, HaResTypeEnum.TOPIC);
|
||||
if (ValidateUtils.isEmptyList(doList)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
List<HaClusterTopicVO> voList = new ArrayList<>();
|
||||
for (HaASRelationDO relationDO: doList) {
|
||||
if (filterSystemTopics
|
||||
&& (relationDO.getActiveResName().startsWith("__") || relationDO.getStandbyResName().startsWith("__"))) {
|
||||
// 过滤掉系统Topic && 存在系统Topic,则过滤掉
|
||||
continue;
|
||||
}
|
||||
|
||||
HaClusterTopicVO vo = new HaClusterTopicVO();
|
||||
vo.setClusterId(firstClusterPhyId);
|
||||
if (firstClusterPhyId.equals(relationDO.getActiveClusterPhyId())) {
|
||||
vo.setTopicName(relationDO.getActiveResName());
|
||||
} else {
|
||||
vo.setTopicName(relationDO.getStandbyResName());
|
||||
}
|
||||
|
||||
vo.setProduceAclNum(0);
|
||||
vo.setConsumeAclNum(0);
|
||||
vo.setActiveClusterId(relationDO.getActiveClusterPhyId());
|
||||
vo.setStandbyClusterId(relationDO.getStandbyClusterPhyId());
|
||||
vo.setStatus(relationDO.getStatus());
|
||||
|
||||
// 补充ACL信息
|
||||
List<AuthorityDO> authorityDOList = authorityService.getAuthorityByTopicFromCache(relationDO.getActiveClusterPhyId(), relationDO.getActiveResName());
|
||||
authorityDOList.forEach(elem -> {
|
||||
if ((elem.getAccess() & TopicAuthorityEnum.WRITE.getCode()) > 0) {
|
||||
vo.setProduceAclNum(vo.getProduceAclNum() + 1);
|
||||
}
|
||||
if ((elem.getAccess() & TopicAuthorityEnum.READ.getCode()) > 0) {
|
||||
vo.setConsumeAclNum(vo.getConsumeAclNum() + 1);
|
||||
}
|
||||
});
|
||||
|
||||
voList.add(vo);
|
||||
}
|
||||
|
||||
return voList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<List<HaClusterTopicHaStatusVO>> listHaStatusTopics(Long clusterPhyId, Boolean checkMetadata) {
|
||||
ClusterDO clusterDO = PhysicalClusterMetadataManager.getClusterFromCache(clusterPhyId);
|
||||
if (clusterDO == null){
|
||||
return Result.buildFrom(ResultStatus.CLUSTER_NOT_EXIST);
|
||||
}
|
||||
List<TopicDO> topicDOS = topicManagerService.getByClusterId(clusterPhyId);
|
||||
if (ValidateUtils.isEmptyList(topicDOS)) {
|
||||
return Result.buildSuc(new ArrayList<>());
|
||||
}
|
||||
|
||||
Map<String, Integer> haRelationMap = haTopicService.getRelation(clusterPhyId);
|
||||
List<HaClusterTopicHaStatusVO> statusVOS = new ArrayList<>();
|
||||
topicDOS.stream().filter(topicDO -> !topicDO.getTopicName().startsWith("__"))//过滤引擎自带topic
|
||||
.forEach(topicDO -> {
|
||||
if(checkMetadata && !PhysicalClusterMetadataManager.isTopicExist(clusterPhyId, topicDO.getTopicName())){
|
||||
return;
|
||||
}
|
||||
HaClusterTopicHaStatusVO statusVO = new HaClusterTopicHaStatusVO();
|
||||
statusVO.setClusterId(clusterPhyId);
|
||||
statusVO.setClusterName(clusterDO.getClusterName());
|
||||
statusVO.setTopicName(topicDO.getTopicName());
|
||||
statusVO.setHaRelation(haRelationMap.get(topicDO.getTopicName()));
|
||||
statusVOS.add(statusVO);
|
||||
});
|
||||
|
||||
return Result.buildSuc(statusVOS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getRelation(Long clusterId, String topicName) {
|
||||
HaASRelationDO relationDO = haASRelationService.getHAFromDB(clusterId, topicName, HaResTypeEnum.TOPIC);
|
||||
if (relationDO == null){
|
||||
return HaRelationTypeEnum.UNKNOWN.getCode();
|
||||
}
|
||||
if (topicName.equals(KafkaConstant.COORDINATOR_TOPIC_NAME)){
|
||||
return HaRelationTypeEnum.MUTUAL_BACKUP.getCode();
|
||||
}
|
||||
if (clusterId.equals(relationDO.getActiveClusterPhyId())){
|
||||
return HaRelationTypeEnum.ACTIVE.getCode();
|
||||
}
|
||||
if (clusterId.equals(relationDO.getStandbyClusterPhyId())){
|
||||
return HaRelationTypeEnum.STANDBY.getCode();
|
||||
}
|
||||
return HaRelationTypeEnum.UNKNOWN.getCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HaASRelationDO getASRelation(Long clusterId, String topicName) {
|
||||
return haASRelationService.getHAFromDB(clusterId, topicName, HaResTypeEnum.TOPIC);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package com.xiaojukeji.kafka.manager.service.biz.ha.impl;
|
||||
|
||||
import com.xiaojukeji.kafka.manager.common.bizenum.ha.HaResTypeEnum;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.Result;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.vo.rd.app.AppRelateTopicsVO;
|
||||
import com.xiaojukeji.kafka.manager.service.biz.ha.HaAppManager;
|
||||
import com.xiaojukeji.kafka.manager.service.service.gateway.AuthorityService;
|
||||
import com.xiaojukeji.kafka.manager.service.service.ha.HaASRelationService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
||||
@Service
|
||||
public class HaAppManagerImpl implements HaAppManager {
|
||||
|
||||
@Autowired
|
||||
private AuthorityService authorityService;
|
||||
|
||||
@Autowired
|
||||
private HaASRelationService haASRelationService;
|
||||
|
||||
@Override
|
||||
public Result<List<AppRelateTopicsVO>> appRelateTopics(Long clusterPhyId, List<String> filterTopicNameList) {
|
||||
// 获取关联的Topic列表
|
||||
Map<String, Set<String>> userTopicMap = this.appRelateTopicsMap(clusterPhyId, filterTopicNameList);
|
||||
|
||||
// 获取集群已建立HA的Topic列表
|
||||
Set<String> haTopicNameSet = haASRelationService.listAllHAFromDB(clusterPhyId, HaResTypeEnum.TOPIC)
|
||||
.stream()
|
||||
.map(elem -> elem.getActiveResName())
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
Set<String> filterTopicNameSet = new HashSet<>(filterTopicNameList);
|
||||
|
||||
List<AppRelateTopicsVO> voList = new ArrayList<>();
|
||||
for (Map.Entry<String, Set<String>> entry: userTopicMap.entrySet()) {
|
||||
AppRelateTopicsVO vo = new AppRelateTopicsVO();
|
||||
vo.setClusterPhyId(clusterPhyId);
|
||||
vo.setKafkaUser(entry.getKey());
|
||||
vo.setSelectedTopicNameList(new ArrayList<>());
|
||||
vo.setNotSelectTopicNameList(new ArrayList<>());
|
||||
vo.setNotHaTopicNameList(new ArrayList<>());
|
||||
entry.getValue().forEach(elem -> {
|
||||
if (elem.startsWith("__")) {
|
||||
// ignore
|
||||
return;
|
||||
}
|
||||
|
||||
if (!haTopicNameSet.contains(elem)) {
|
||||
vo.getNotHaTopicNameList().add(elem);
|
||||
} else if (filterTopicNameSet.contains(elem)) {
|
||||
vo.getSelectedTopicNameList().add(elem);
|
||||
} else {
|
||||
vo.getNotSelectTopicNameList().add(elem);
|
||||
}
|
||||
});
|
||||
|
||||
voList.add(vo);
|
||||
}
|
||||
|
||||
return Result.buildSuc(voList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isContainAllRelateAppTopics(Long clusterPhyId, List<String> filterTopicNameList) {
|
||||
Map<String, Set<String>> userTopicMap = this.appRelateTopicsMap(clusterPhyId, filterTopicNameList);
|
||||
|
||||
Set<String> relateTopicSet = new HashSet<>();
|
||||
userTopicMap.values().forEach(elem -> relateTopicSet.addAll(elem));
|
||||
|
||||
return filterTopicNameList.containsAll(relateTopicSet);
|
||||
}
|
||||
|
||||
private Map<String, Set<String>> appRelateTopicsMap(Long clusterPhyId, List<String> filterTopicNameList) {
|
||||
Map<String, Set<String>> userTopicMap = new HashMap<>();
|
||||
for (String topicName: filterTopicNameList) {
|
||||
authorityService.getAuthorityByTopicFromCache(clusterPhyId, topicName)
|
||||
.stream()
|
||||
.map(elem -> elem.getAppId())
|
||||
.filter(item -> !userTopicMap.containsKey(item))
|
||||
.forEach(kafkaUser ->
|
||||
userTopicMap.put(
|
||||
kafkaUser,
|
||||
authorityService.getAuthority(kafkaUser).stream().map(authorityDO -> authorityDO.getTopicName()).collect(Collectors.toSet())
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return userTopicMap;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
package com.xiaojukeji.kafka.manager.service.biz.ha.impl;
|
||||
|
||||
import com.xiaojukeji.kafka.manager.common.bizenum.ClusterModeEnum;
|
||||
import com.xiaojukeji.kafka.manager.common.bizenum.DBStatusEnum;
|
||||
import com.xiaojukeji.kafka.manager.common.bizenum.ha.HaResTypeEnum;
|
||||
import com.xiaojukeji.kafka.manager.common.constant.MsgConstant;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.Result;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.ResultStatus;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.ao.ClusterDetailDTO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.ClusterDO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.LogicalClusterDO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.RegionDO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.ha.HaASRelationDO;
|
||||
import com.xiaojukeji.kafka.manager.common.utils.ListUtils;
|
||||
import com.xiaojukeji.kafka.manager.service.biz.ha.HaClusterManager;
|
||||
import com.xiaojukeji.kafka.manager.service.service.ClusterService;
|
||||
import com.xiaojukeji.kafka.manager.service.service.LogicalClusterService;
|
||||
import com.xiaojukeji.kafka.manager.service.service.RegionService;
|
||||
import com.xiaojukeji.kafka.manager.service.service.ZookeeperService;
|
||||
import com.xiaojukeji.kafka.manager.service.service.ha.HaASRelationService;
|
||||
import com.xiaojukeji.kafka.manager.service.service.ha.HaClusterService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.transaction.interceptor.TransactionAspectSupport;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Component
|
||||
public class HaClusterManagerImpl implements HaClusterManager {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(HaClusterManagerImpl.class);
|
||||
|
||||
@Autowired
|
||||
private ClusterService clusterService;
|
||||
|
||||
@Autowired
|
||||
private HaClusterService haClusterService;
|
||||
|
||||
@Autowired
|
||||
private ZookeeperService zookeeperService;
|
||||
|
||||
@Autowired
|
||||
private LogicalClusterService logicalClusterService;
|
||||
|
||||
@Autowired
|
||||
private RegionService regionService;
|
||||
|
||||
@Autowired
|
||||
private HaASRelationService haASRelationService;
|
||||
|
||||
@Override
|
||||
public List<ClusterDetailDTO> getClusterDetailDTOList(Boolean needDetail) {
|
||||
return clusterService.getClusterDetailDTOList(needDetail);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Result<Void> addNew(ClusterDO clusterDO, Long activeClusterId, String operator) {
|
||||
if (activeClusterId == null) {
|
||||
// 普通集群,直接写入DB
|
||||
Long clusterPhyId = zookeeperService.getClusterIdAndNullIfFailed(clusterDO.getZookeeper());
|
||||
if (clusterPhyId != null && clusterService.getById(clusterPhyId) == null) {
|
||||
// 该集群ID不存在时,则进行设置,如果已经存在了,则忽略
|
||||
clusterDO.setId(clusterPhyId);
|
||||
}
|
||||
|
||||
return Result.buildFrom(clusterService.addNew(clusterDO, operator));
|
||||
}
|
||||
|
||||
//高可用集群
|
||||
ClusterDO activeClusterDO = clusterService.getById(activeClusterId);
|
||||
if (activeClusterDO == null) {
|
||||
// 主集群不存在
|
||||
return Result.buildFromRSAndMsg(ResultStatus.RESOURCE_NOT_EXIST, MsgConstant.getClusterPhyNotExist(activeClusterId));
|
||||
}
|
||||
|
||||
HaASRelationDO oldRelationDO = haClusterService.getHA(activeClusterId);
|
||||
if (oldRelationDO != null){
|
||||
return Result.buildFromRSAndMsg(ResultStatus.RESOURCE_ALREADY_USED,
|
||||
MsgConstant.getActiveClusterDuplicate(activeClusterDO.getId(), activeClusterDO.getClusterName()));
|
||||
}
|
||||
|
||||
Long standbyClusterPhyId = zookeeperService.getClusterIdAndNullIfFailed(clusterDO.getZookeeper());
|
||||
if (standbyClusterPhyId != null && clusterService.getById(standbyClusterPhyId) == null) {
|
||||
// 该集群ID不存在时,则进行设置,如果已经存在了,则忽略
|
||||
clusterDO.setId(standbyClusterPhyId);
|
||||
}
|
||||
|
||||
ResultStatus rs = clusterService.addNew(clusterDO, operator);
|
||||
if (!ResultStatus.SUCCESS.equals(rs)) {
|
||||
return Result.buildFrom(rs);
|
||||
}
|
||||
|
||||
Result<List<Integer>> rli = zookeeperService.getBrokerIds(clusterDO.getZookeeper());
|
||||
if (!rli.hasData()){
|
||||
return Result.buildFrom(ResultStatus.BROKER_NOT_EXIST);
|
||||
}
|
||||
|
||||
// 备集群创建region
|
||||
RegionDO regionDO = new RegionDO(DBStatusEnum.ALIVE.getStatus(), clusterDO.getClusterName(), clusterDO.getId(), ListUtils.intList2String(rli.getData()));
|
||||
rs = regionService.createRegion(regionDO);
|
||||
if (!ResultStatus.SUCCESS.equals(rs)){
|
||||
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
|
||||
|
||||
return Result.buildFrom(rs);
|
||||
}
|
||||
|
||||
// 备集群创建逻辑集群
|
||||
List<LogicalClusterDO> logicalClusterDOS = logicalClusterService.getByPhysicalClusterId(activeClusterId);
|
||||
if (!logicalClusterDOS.isEmpty()) {
|
||||
// 有逻辑集群,则对应创建逻辑集群
|
||||
Integer mode = logicalClusterDOS.get(0).getMode();
|
||||
LogicalClusterDO logicalClusterDO = new LogicalClusterDO(
|
||||
clusterDO.getClusterName(),
|
||||
clusterDO.getClusterName(),
|
||||
ClusterModeEnum.INDEPENDENT_MODE.getCode().equals(mode)?mode:ClusterModeEnum.SHARED_MODE.getCode(),
|
||||
ClusterModeEnum.INDEPENDENT_MODE.getCode().equals(mode)?logicalClusterDOS.get(0).getAppId(): "",
|
||||
clusterDO.getId(),
|
||||
regionDO.getId().toString()
|
||||
);
|
||||
ResultStatus clcRS = logicalClusterService.createLogicalCluster(logicalClusterDO);
|
||||
if (clcRS.getCode() != ResultStatus.SUCCESS.getCode()){
|
||||
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
|
||||
return Result.buildFrom(clcRS);
|
||||
}
|
||||
}
|
||||
|
||||
return haClusterService.createHA(activeClusterId, clusterDO.getId(), operator);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Result<Void> deleteById(Long clusterId, String operator) {
|
||||
HaASRelationDO haRelationDO = haClusterService.getHA(clusterId);
|
||||
if (haRelationDO == null){
|
||||
return clusterService.deleteById(clusterId, operator);
|
||||
}
|
||||
|
||||
Result rv = checkForDelete(haRelationDO, clusterId);
|
||||
if (rv.failed()){
|
||||
return rv;
|
||||
}
|
||||
|
||||
//解除高可用关系
|
||||
Result result = haClusterService.deleteHA(haRelationDO.getActiveClusterPhyId(), haRelationDO.getStandbyClusterPhyId());
|
||||
if (result.failed()){
|
||||
return result;
|
||||
}
|
||||
|
||||
//删除集群
|
||||
result = clusterService.deleteById(clusterId, operator);
|
||||
if (result.failed()){
|
||||
return result;
|
||||
}
|
||||
return Result.buildSuc();
|
||||
}
|
||||
|
||||
private Result<Void> checkForDelete(HaASRelationDO haRelationDO, Long clusterId){
|
||||
List<HaASRelationDO> relationDOS = haASRelationService.listAllHAFromDB(haRelationDO.getActiveClusterPhyId(),
|
||||
haRelationDO.getStandbyClusterPhyId(),
|
||||
HaResTypeEnum.TOPIC);
|
||||
if (relationDOS.stream().filter(relationDO -> !relationDO.getActiveResName().startsWith("__")).count() > 0){
|
||||
return Result.buildFromRSAndMsg(ResultStatus.OPERATION_FORBIDDEN, "集群还存在高可topic");
|
||||
}
|
||||
return Result.buildSuc();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,559 @@
|
||||
package com.xiaojukeji.kafka.manager.service.biz.ha.impl;
|
||||
|
||||
import com.xiaojukeji.kafka.manager.common.bizenum.ha.HaResTypeEnum;
|
||||
import com.xiaojukeji.kafka.manager.common.bizenum.ha.HaStatusEnum;
|
||||
import com.xiaojukeji.kafka.manager.common.constant.MsgConstant;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.Result;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.ResultStatus;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.TopicOperationResult;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.ao.ha.HaSwitchTopic;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.dto.op.topic.HaTopicRelationDTO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.ClusterDO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.TopicDO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.ha.HaASRelationDO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.ha.JobLogDO;
|
||||
import com.xiaojukeji.kafka.manager.common.utils.BackoffUtils;
|
||||
import com.xiaojukeji.kafka.manager.common.utils.ConvertUtil;
|
||||
import com.xiaojukeji.kafka.manager.common.utils.ValidateUtils;
|
||||
import com.xiaojukeji.kafka.manager.service.biz.ha.HaTopicManager;
|
||||
import com.xiaojukeji.kafka.manager.service.cache.PhysicalClusterMetadataManager;
|
||||
import com.xiaojukeji.kafka.manager.service.service.ClusterService;
|
||||
import com.xiaojukeji.kafka.manager.service.service.JobLogService;
|
||||
import com.xiaojukeji.kafka.manager.service.service.TopicManagerService;
|
||||
import com.xiaojukeji.kafka.manager.service.service.gateway.AuthorityService;
|
||||
import com.xiaojukeji.kafka.manager.service.service.ha.HaASRelationService;
|
||||
import com.xiaojukeji.kafka.manager.service.service.ha.HaKafkaUserService;
|
||||
import com.xiaojukeji.kafka.manager.service.service.ha.HaTopicService;
|
||||
import com.xiaojukeji.kafka.manager.service.utils.ConfigUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Component
|
||||
public class HaTopicManagerImpl implements HaTopicManager {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(HaTopicManagerImpl.class);
|
||||
|
||||
@Autowired
|
||||
private ClusterService clusterService;
|
||||
|
||||
@Autowired
|
||||
private AuthorityService authorityService;
|
||||
|
||||
@Autowired
|
||||
private HaTopicService haTopicService;
|
||||
|
||||
@Autowired
|
||||
private HaKafkaUserService haKafkaUserService;
|
||||
|
||||
@Autowired
|
||||
private HaASRelationService haASRelationService;
|
||||
|
||||
@Autowired
|
||||
private TopicManagerService topicManagerService;
|
||||
|
||||
@Autowired
|
||||
private ConfigUtils configUtils;
|
||||
|
||||
@Autowired
|
||||
private JobLogService jobLogService;
|
||||
|
||||
@Override
|
||||
public Result<HaSwitchTopic> switchHaWithCanRetry(Long newActiveClusterPhyId,
|
||||
Long newStandbyClusterPhyId,
|
||||
List<String> switchTopicNameList,
|
||||
boolean focus,
|
||||
boolean firstTriggerExecute,
|
||||
JobLogDO switchLogTemplate,
|
||||
String operator) {
|
||||
LOGGER.info(
|
||||
"method=switchHaWithCanRetry||newActiveClusterPhyId={}||newStandbyClusterPhyId={}||switchTopicNameList={}||focus={}||operator={}",
|
||||
newActiveClusterPhyId, newStandbyClusterPhyId, ConvertUtil.obj2Json(switchTopicNameList), focus, operator
|
||||
);
|
||||
|
||||
// 1、获取集群
|
||||
ClusterDO newActiveClusterPhyDO = clusterService.getById(newActiveClusterPhyId);
|
||||
if (ValidateUtils.isNull(newActiveClusterPhyDO)) {
|
||||
return Result.buildFromRSAndMsg(ResultStatus.RESOURCE_NOT_EXIST, MsgConstant.getClusterPhyNotExist(newActiveClusterPhyId));
|
||||
}
|
||||
|
||||
ClusterDO newStandbyClusterPhyDO = clusterService.getById(newStandbyClusterPhyId);
|
||||
if (ValidateUtils.isNull(newStandbyClusterPhyDO)) {
|
||||
return Result.buildFromRSAndMsg(ResultStatus.RESOURCE_NOT_EXIST, MsgConstant.getClusterPhyNotExist(newStandbyClusterPhyId));
|
||||
}
|
||||
|
||||
// 2、进行参数检查
|
||||
Result<List<HaASRelationDO>> doListResult = this.checkParamAndGetASRelation(newActiveClusterPhyId, newStandbyClusterPhyId, switchTopicNameList);
|
||||
if (doListResult.failed()) {
|
||||
LOGGER.error(
|
||||
"method=switchHaWithCanRetry||newActiveClusterPhyId={}||newStandbyClusterPhyId={}||switchTopicNameList={}||paramErrResult={}||operator={}",
|
||||
newActiveClusterPhyId, newStandbyClusterPhyId, ConvertUtil.obj2Json(switchTopicNameList), doListResult, operator
|
||||
);
|
||||
|
||||
return Result.buildFromIgnoreData(doListResult);
|
||||
}
|
||||
List<HaASRelationDO> doList = doListResult.getData();
|
||||
|
||||
// 3、如果是第一次触发执行,且状态是stable,则修改状态
|
||||
for (HaASRelationDO relationDO: doList) {
|
||||
if (firstTriggerExecute && relationDO.getStatus().equals(HaStatusEnum.STABLE_CODE)) {
|
||||
relationDO.setStatus(HaStatusEnum.SWITCHING_PREPARE_CODE);
|
||||
haASRelationService.updateRelationStatus(relationDO.getId(), HaStatusEnum.SWITCHING_PREPARE_CODE);
|
||||
}
|
||||
}
|
||||
|
||||
// 4、进行切换预处理
|
||||
HaSwitchTopic switchTopic = this.prepareSwitching(newStandbyClusterPhyDO, doList, focus, switchLogTemplate);
|
||||
|
||||
// 5、直接等待10秒,使得相关数据有机会同步完成
|
||||
BackoffUtils.backoff(10000);
|
||||
|
||||
// 6、检查数据同步情况
|
||||
for (HaASRelationDO relationDO: doList) {
|
||||
switchTopic.addHaSwitchTopic(this.checkTopicInSync(newActiveClusterPhyDO, newStandbyClusterPhyDO, relationDO, focus, switchLogTemplate));
|
||||
}
|
||||
|
||||
// 7、删除旧的备Topic的同步配置
|
||||
for (HaASRelationDO relationDO: doList) {
|
||||
switchTopic.addHaSwitchTopic(this.oldStandbyTopicDelFetchConfig(newActiveClusterPhyDO, newStandbyClusterPhyDO, relationDO, focus, switchLogTemplate, operator));
|
||||
}
|
||||
|
||||
// 8、增加新的备Topic的同步配置,
|
||||
switchTopic.addHaSwitchTopic(this.newStandbyTopicAddFetchConfig(newActiveClusterPhyDO, newStandbyClusterPhyDO, doList, focus, switchLogTemplate, operator));
|
||||
|
||||
// 9、进行切换收尾
|
||||
switchTopic.addHaSwitchTopic(this.closeoutSwitching(newActiveClusterPhyDO, newStandbyClusterPhyDO, configUtils.getDKafkaGatewayZK(), doList, focus, switchLogTemplate));
|
||||
|
||||
// 10、状态结果汇总记录
|
||||
doList.forEach(elem -> switchTopic.addActiveTopicStatus(elem.getActiveResName(), elem.getStatus()));
|
||||
|
||||
// 11、日志记录并返回
|
||||
LOGGER.info(
|
||||
"method=switchHaWithCanRetry||newActiveClusterPhyId={}||newStandbyClusterPhyId={}||switchTopicNameList={}||switchResult={}||operator={}",
|
||||
newActiveClusterPhyId, newStandbyClusterPhyId, ConvertUtil.obj2Json(switchTopicNameList), switchTopic, operator
|
||||
);
|
||||
|
||||
return Result.buildSuc(switchTopic);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<List<TopicOperationResult>> batchCreateHaTopic(HaTopicRelationDTO dto, String operator) {
|
||||
List<HaASRelationDO> relationDOS = haASRelationService.listAllHAFromDB(dto.getActiveClusterId(), dto.getStandbyClusterId(), HaResTypeEnum.CLUSTER);
|
||||
if (relationDOS.isEmpty()){
|
||||
return Result.buildFromRSAndMsg(ResultStatus.RESOURCE_NOT_EXIST, "集群高可用关系未建立");
|
||||
}
|
||||
|
||||
//获取主集群已有的高可用topic
|
||||
Map<String, Integer> haRelationMap = haTopicService.getRelation(dto.getActiveClusterId());
|
||||
List<String> topicNames = dto.getTopicNames();
|
||||
if (dto.getAll()){
|
||||
topicNames = topicManagerService.getByClusterId(dto.getActiveClusterId())
|
||||
.stream()
|
||||
.filter(topicDO -> !topicDO.getTopicName().startsWith("__"))//过滤掉kafka自带topic
|
||||
.filter(topicDO -> !haRelationMap.keySet().contains(topicDO.getTopicName()))//过滤调已成为高可用topic的topic
|
||||
.filter(topicDO -> PhysicalClusterMetadataManager.isTopicExist(dto.getActiveClusterId(), topicDO.getTopicName()))
|
||||
.map(TopicDO::getTopicName)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
}
|
||||
|
||||
List<TopicOperationResult> operationResultList = new ArrayList<>();
|
||||
topicNames.forEach(topicName->{
|
||||
Result<Void> rv = haTopicService.createHA(dto.getActiveClusterId(), dto.getStandbyClusterId(),topicName, operator);
|
||||
operationResultList.add(TopicOperationResult.buildFrom(dto.getActiveClusterId(), topicName, rv));
|
||||
});
|
||||
|
||||
return Result.buildSuc(operationResultList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<List<TopicOperationResult>> batchRemoveHaTopic(HaTopicRelationDTO dto, String operator) {
|
||||
List<HaASRelationDO> relationDOS = haASRelationService.listAllHAFromDB(dto.getActiveClusterId(), dto.getStandbyClusterId(), HaResTypeEnum.CLUSTER);
|
||||
if (relationDOS.isEmpty()){
|
||||
return Result.buildFromRSAndMsg(ResultStatus.RESOURCE_NOT_EXIST, "集群高可用关系未建立");
|
||||
}
|
||||
|
||||
List<TopicOperationResult> operationResultList = new ArrayList<>();
|
||||
for(String topicName : dto.getTopicNames()){
|
||||
HaASRelationDO relationDO = haASRelationService.getHAFromDB(
|
||||
dto.getActiveClusterId(),
|
||||
topicName,
|
||||
HaResTypeEnum.TOPIC
|
||||
);
|
||||
if (relationDO == null) {
|
||||
return Result.buildFromRSAndMsg(ResultStatus.RESOURCE_NOT_EXIST, "主备关系不存在");
|
||||
}
|
||||
|
||||
Result<Void> rv = haTopicService.deleteHA(relationDO.getActiveClusterPhyId(), relationDO.getStandbyClusterPhyId(), topicName, operator);
|
||||
operationResultList.add(TopicOperationResult.buildFrom(dto.getActiveClusterId(), topicName, rv));
|
||||
}
|
||||
|
||||
return Result.buildSuc(operationResultList);
|
||||
}
|
||||
|
||||
/**************************************************** private method ****************************************************/
|
||||
|
||||
private void saveLogs(JobLogDO switchLogTemplate, String content) {
|
||||
jobLogService.addLogAndIgnoreException(switchLogTemplate.setAndCopyNew(new Date(), content));
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换预处理
|
||||
* 1、在主集群上,将Topic关联的KafkaUser的active集群设置为None
|
||||
*/
|
||||
private HaSwitchTopic prepareSwitching(ClusterDO oldActiveClusterPhyDO, List<HaASRelationDO> doList, boolean focus, JobLogDO switchLogTemplate) {
|
||||
// 暂停HA的KafkaUser
|
||||
Set<String> stoppedHaKafkaUserSet = new HashSet<>();
|
||||
|
||||
HaSwitchTopic haSwitchTopic = new HaSwitchTopic(true);
|
||||
|
||||
boolean allSuccess = true; // 所有都成功
|
||||
boolean needLog = false; // 需要记录日志
|
||||
for (HaASRelationDO relationDO: doList) {
|
||||
if (!relationDO.getStatus().equals(HaStatusEnum.SWITCHING_PREPARE_CODE)) {
|
||||
// 当前不处于prepare状态
|
||||
haSwitchTopic.setFinished(true);
|
||||
continue;
|
||||
}
|
||||
needLog = true;
|
||||
|
||||
// 获取关联的KafkaUser
|
||||
Set<String> relatedKafkaUserSet = authorityService.getAuthorityByTopic(relationDO.getActiveClusterPhyId(), relationDO.getActiveResName())
|
||||
.stream()
|
||||
.map(elem -> elem.getAppId())
|
||||
.filter(kafkaUser -> !stoppedHaKafkaUserSet.contains(kafkaUser))
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
// 暂停kafkaUser HA
|
||||
for (String kafkaUser: relatedKafkaUserSet) {
|
||||
Result<Void> rv = haKafkaUserService.setNoneHAInKafka(oldActiveClusterPhyDO.getZookeeper(), kafkaUser);
|
||||
if (rv.failed() && !focus) {
|
||||
haSwitchTopic.setFinished(false);
|
||||
|
||||
this.saveLogs(switchLogTemplate, String.format("%s:\t失败,1分钟后再进行重试", HaStatusEnum.SWITCHING_PREPARE.getMsg(oldActiveClusterPhyDO.getClusterName())));
|
||||
return haSwitchTopic;
|
||||
} else if (rv.failed() && focus) {
|
||||
allSuccess = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 记录操作过的user
|
||||
stoppedHaKafkaUserSet.addAll(relatedKafkaUserSet);
|
||||
|
||||
// 修改Topic主备状态
|
||||
relationDO.setStatus(HaStatusEnum.SWITCHING_WAITING_IN_SYNC_CODE);
|
||||
haASRelationService.updateRelationStatus(relationDO.getId(), HaStatusEnum.SWITCHING_WAITING_IN_SYNC_CODE);
|
||||
}
|
||||
|
||||
if (needLog) {
|
||||
this.saveLogs(switchLogTemplate, String.format("%s:\t%s", HaStatusEnum.SWITCHING_PREPARE.getMsg(oldActiveClusterPhyDO.getClusterName()), allSuccess? "成功": "存在失败,但进行强制执行,跳过该操作"));
|
||||
}
|
||||
|
||||
haSwitchTopic.setFinished(true);
|
||||
return haSwitchTopic;
|
||||
}
|
||||
|
||||
/**
|
||||
* 等待主备Topic同步
|
||||
*/
|
||||
private HaSwitchTopic checkTopicInSync(ClusterDO newActiveClusterPhyDO, ClusterDO newStandbyClusterPhyDO, HaASRelationDO relationDO, boolean focus, JobLogDO switchLogTemplate) {
|
||||
HaSwitchTopic haSwitchTopic = new HaSwitchTopic(true);
|
||||
if (!relationDO.getStatus().equals(HaStatusEnum.SWITCHING_WAITING_IN_SYNC_CODE)) {
|
||||
// 状态错误,直接略过
|
||||
haSwitchTopic.setFinished(true);
|
||||
return haSwitchTopic;
|
||||
}
|
||||
|
||||
if (focus) {
|
||||
// 无需等待inSync
|
||||
|
||||
// 修改Topic主备状态
|
||||
relationDO.setStatus(HaStatusEnum.SWITCHING_CLOSE_OLD_STANDBY_TOPIC_FETCH_CODE);
|
||||
haASRelationService.updateRelationStatus(relationDO.getId(), HaStatusEnum.SWITCHING_CLOSE_OLD_STANDBY_TOPIC_FETCH_CODE);
|
||||
|
||||
haSwitchTopic.setFinished(true);
|
||||
this.saveLogs(switchLogTemplate, String.format(
|
||||
"%s:\tTopic:[%s] 强制切换,跳过等待主备同步完成,直接进入下一步",
|
||||
HaStatusEnum.SWITCHING_WAITING_IN_SYNC.getMsg(newActiveClusterPhyDO.getClusterName()),
|
||||
relationDO.getActiveResName()
|
||||
));
|
||||
return haSwitchTopic;
|
||||
}
|
||||
|
||||
Result<Long> lagResult = haTopicService.getStandbyTopicFetchLag(newStandbyClusterPhyDO.getId(), relationDO.getStandbyResName());
|
||||
if (lagResult.failed()) {
|
||||
// 获取Lag信息失败
|
||||
this.saveLogs(switchLogTemplate, String.format(
|
||||
"%s:\tTopic:[%s] 获取同步的Lag信息失败,1分钟后再检查是否主备同步完成",
|
||||
HaStatusEnum.SWITCHING_WAITING_IN_SYNC.getMsg(newActiveClusterPhyDO.getClusterName()),
|
||||
relationDO.getActiveResName()
|
||||
));
|
||||
haSwitchTopic.setFinished(false);
|
||||
return haSwitchTopic;
|
||||
}
|
||||
|
||||
if (lagResult.getData().longValue() > 0) {
|
||||
this.saveLogs(switchLogTemplate, String.format(
|
||||
"%s:\tTopic:[%s] 还存在 %d 条数据未同步完成,1分钟后再检查是否主备同步完成",
|
||||
HaStatusEnum.SWITCHING_WAITING_IN_SYNC.getMsg(newActiveClusterPhyDO.getClusterName()),
|
||||
relationDO.getActiveResName(),
|
||||
lagResult.getData()
|
||||
));
|
||||
|
||||
haSwitchTopic.setFinished(false);
|
||||
return haSwitchTopic;
|
||||
}
|
||||
|
||||
// 修改Topic主备状态
|
||||
relationDO.setStatus(HaStatusEnum.SWITCHING_CLOSE_OLD_STANDBY_TOPIC_FETCH_CODE);
|
||||
haASRelationService.updateRelationStatus(relationDO.getId(), HaStatusEnum.SWITCHING_CLOSE_OLD_STANDBY_TOPIC_FETCH_CODE);
|
||||
|
||||
haSwitchTopic.setFinished(true);
|
||||
this.saveLogs(switchLogTemplate, String.format(
|
||||
"%s:\tTopic:[%s] 主备同步完成",
|
||||
HaStatusEnum.SWITCHING_WAITING_IN_SYNC.getMsg(newActiveClusterPhyDO.getClusterName()),
|
||||
relationDO.getActiveResName()
|
||||
));
|
||||
return haSwitchTopic;
|
||||
}
|
||||
|
||||
/**
|
||||
* 备Topic删除拉取主Topic数据的配置
|
||||
*/
|
||||
private HaSwitchTopic oldStandbyTopicDelFetchConfig(ClusterDO newActiveClusterPhyDO, ClusterDO newStandbyClusterPhyDO, HaASRelationDO relationDO, boolean focus, JobLogDO switchLogTemplate, String operator) {
|
||||
HaSwitchTopic haSwitchTopic = new HaSwitchTopic(true);
|
||||
if (!relationDO.getStatus().equals(HaStatusEnum.SWITCHING_CLOSE_OLD_STANDBY_TOPIC_FETCH_CODE)) {
|
||||
// 状态不对
|
||||
haSwitchTopic.setFinished(true);
|
||||
return haSwitchTopic;
|
||||
}
|
||||
|
||||
Result<Void> rv = haTopicService.stopHAInKafka(
|
||||
newActiveClusterPhyDO, relationDO.getStandbyResName(), // 旧的备
|
||||
operator
|
||||
);
|
||||
if (rv.failed() && !focus) {
|
||||
this.saveLogs(switchLogTemplate, String.format("%s:\tTopic:[%s] 失败,1分钟后再进行重试", HaStatusEnum.SWITCHING_CLOSE_OLD_STANDBY_TOPIC_FETCH.getMsg(newActiveClusterPhyDO.getClusterName()), relationDO.getActiveResName()));
|
||||
haSwitchTopic.setFinished(false);
|
||||
return haSwitchTopic;
|
||||
} else if (rv.failed() && focus) {
|
||||
this.saveLogs(switchLogTemplate, String.format("%s:\tTopic:[%s] 失败,但进行强制执行,跳过该操作", HaStatusEnum.SWITCHING_CLOSE_OLD_STANDBY_TOPIC_FETCH.getMsg(newActiveClusterPhyDO.getClusterName()), relationDO.getActiveResName()));
|
||||
} else {
|
||||
this.saveLogs(switchLogTemplate, String.format("%s:\tTopic:[%s] 成功", HaStatusEnum.SWITCHING_CLOSE_OLD_STANDBY_TOPIC_FETCH.getMsg(newActiveClusterPhyDO.getClusterName()), relationDO.getActiveResName()));
|
||||
}
|
||||
|
||||
// 修改Topic主备状态
|
||||
relationDO.setStatus(HaStatusEnum.SWITCHING_OPEN_NEW_STANDBY_TOPIC_FETCH_CODE);
|
||||
haASRelationService.updateRelationStatus(relationDO.getId(), HaStatusEnum.SWITCHING_OPEN_NEW_STANDBY_TOPIC_FETCH_CODE);
|
||||
|
||||
haSwitchTopic.setFinished(true);
|
||||
return haSwitchTopic;
|
||||
}
|
||||
|
||||
/**
|
||||
* 新的备Topic,创建拉取新主Topic数据的配置
|
||||
*/
|
||||
private HaSwitchTopic newStandbyTopicAddFetchConfig(ClusterDO newActiveClusterPhyDO,
|
||||
ClusterDO newStandbyClusterPhyDO,
|
||||
List<HaASRelationDO> doList,
|
||||
boolean focus,
|
||||
JobLogDO switchLogTemplate,
|
||||
String operator) {
|
||||
boolean forceAndFailed = false;
|
||||
for (HaASRelationDO relationDO: doList) {
|
||||
if (!relationDO.getStatus().equals(HaStatusEnum.SWITCHING_OPEN_NEW_STANDBY_TOPIC_FETCH_CODE)) {
|
||||
// 状态不对
|
||||
continue;
|
||||
}
|
||||
|
||||
Result<Void> rv = null;
|
||||
if (!forceAndFailed) {
|
||||
// 非 强制切换并且失败了
|
||||
rv = haTopicService.activeHAInKafka(
|
||||
newActiveClusterPhyDO, relationDO.getStandbyResName(),
|
||||
newStandbyClusterPhyDO, relationDO.getStandbyResName(),
|
||||
operator
|
||||
);
|
||||
}
|
||||
|
||||
if (forceAndFailed) {
|
||||
// 强制切换并且失败了,记录该日志
|
||||
this.saveLogs(switchLogTemplate, String.format("%s:\tTopic:[%s] 失败,但因为是强制执行且强制执行时依旧出现操作失败,因此直接跳过该操作", HaStatusEnum.SWITCHING_OPEN_NEW_STANDBY_TOPIC_FETCH.getMsg(newStandbyClusterPhyDO.getClusterName()), relationDO.getActiveResName()));
|
||||
|
||||
} else if (rv.failed() && !focus) {
|
||||
// 如果失败了,并且非强制切换,则直接返回
|
||||
this.saveLogs(switchLogTemplate, String.format("%s:\tTopic:[%s] 失败,1分钟后再进行重试", HaStatusEnum.SWITCHING_OPEN_NEW_STANDBY_TOPIC_FETCH.getMsg(newStandbyClusterPhyDO.getClusterName()), relationDO.getActiveResName()));
|
||||
|
||||
return new HaSwitchTopic(false);
|
||||
} else if (rv.failed() && focus) {
|
||||
// 如果失败了,但是是强制切换,则记录日志并继续
|
||||
this.saveLogs(switchLogTemplate, String.format("%s:\tTopic:[%s] 失败,但因为是强制执行,因此跳过该操作", HaStatusEnum.SWITCHING_OPEN_NEW_STANDBY_TOPIC_FETCH.getMsg(newStandbyClusterPhyDO.getClusterName()), relationDO.getActiveResName()));
|
||||
|
||||
forceAndFailed = true;
|
||||
} else {
|
||||
// 记录成功日志
|
||||
this.saveLogs(switchLogTemplate, String.format("%s:\tTopic:[%s] 成功", HaStatusEnum.SWITCHING_OPEN_NEW_STANDBY_TOPIC_FETCH.getMsg(newStandbyClusterPhyDO.getClusterName()), relationDO.getActiveResName()));
|
||||
}
|
||||
|
||||
// 修改Topic主备状态
|
||||
relationDO.setStatus(HaStatusEnum.SWITCHING_CLOSEOUT_CODE);
|
||||
haASRelationService.updateRelationStatus(relationDO.getId(), HaStatusEnum.SWITCHING_CLOSEOUT_CODE);
|
||||
}
|
||||
|
||||
return new HaSwitchTopic(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换收尾
|
||||
* 1、原先的主集群-修改user的active集群,指向新的主集群
|
||||
* 2、原先的备集群-修改user的active集群,指向新的主集群
|
||||
* 3、网关-修改user的active集群,指向新的主集群
|
||||
*/
|
||||
private HaSwitchTopic closeoutSwitching(ClusterDO newActiveClusterPhyDO, ClusterDO newStandbyClusterPhyDO, String gatewayZK, List<HaASRelationDO> doList, boolean focus, JobLogDO switchLogTemplate) {
|
||||
// 暂停HA的KafkaUser
|
||||
Set<String> activeHaKafkaUserSet = new HashSet<>();
|
||||
|
||||
boolean allSuccess = true;
|
||||
boolean needLog = false;
|
||||
boolean forceAndNewStandbyFailed = false; // 强制切换,但是新的备依旧操作失败
|
||||
|
||||
HaSwitchTopic haSwitchTopic = new HaSwitchTopic(true);
|
||||
for (HaASRelationDO relationDO: doList) {
|
||||
if (!relationDO.getStatus().equals(HaStatusEnum.SWITCHING_CLOSEOUT_CODE)) {
|
||||
// 当前不处于closeout状态
|
||||
haSwitchTopic.setFinished(false);
|
||||
continue;
|
||||
}
|
||||
|
||||
needLog = true;
|
||||
|
||||
// 获取关联的KafkaUser
|
||||
Set<String> relatedKafkaUserSet = authorityService.getAuthorityByTopic(relationDO.getActiveClusterPhyId(), relationDO.getActiveResName())
|
||||
.stream()
|
||||
.map(elem -> elem.getAppId())
|
||||
.filter(kafkaUser -> !activeHaKafkaUserSet.contains(kafkaUser))
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
for (String kafkaUser: relatedKafkaUserSet) {
|
||||
// 操作新的主集群
|
||||
Result<Void> rv = haKafkaUserService.activeHAInKafka(newActiveClusterPhyDO.getZookeeper(), newActiveClusterPhyDO.getId(), kafkaUser);
|
||||
if (rv.failed() && !focus) {
|
||||
haSwitchTopic.setFinished(false);
|
||||
this.saveLogs(switchLogTemplate, String.format("%s:\t失败,1分钟后再进行重试", HaStatusEnum.SWITCHING_CLOSEOUT.getMsg(newActiveClusterPhyDO.getClusterName())));
|
||||
return haSwitchTopic;
|
||||
} else if (rv.failed() && focus) {
|
||||
allSuccess = false;
|
||||
}
|
||||
|
||||
// 操作新的备集群,如果出现错误,则下次就不再进行操作ZK。新的备的Topic不是那么重要,因此这里允许出现跳过
|
||||
rv = null;
|
||||
if (!forceAndNewStandbyFailed) {
|
||||
// 如果对备集群的操作过程中,出现了失败,则直接跳过
|
||||
rv = haKafkaUserService.activeHAInKafka(newStandbyClusterPhyDO.getZookeeper(), newActiveClusterPhyDO.getId(), kafkaUser);
|
||||
}
|
||||
|
||||
if (rv != null && rv.failed() && !focus) {
|
||||
haSwitchTopic.setFinished(false);
|
||||
this.saveLogs(switchLogTemplate, String.format("%s:\t失败,1分钟后再进行重试", HaStatusEnum.SWITCHING_CLOSEOUT.getMsg(newActiveClusterPhyDO.getClusterName())));
|
||||
return haSwitchTopic;
|
||||
} else if (rv != null && rv.failed() && focus) {
|
||||
allSuccess = false;
|
||||
forceAndNewStandbyFailed = true;
|
||||
}
|
||||
|
||||
// 操作网关
|
||||
rv = haKafkaUserService.activeHAInKafka(gatewayZK, newActiveClusterPhyDO.getId(), kafkaUser);
|
||||
if (rv.failed() && !focus) {
|
||||
haSwitchTopic.setFinished(false);
|
||||
this.saveLogs(switchLogTemplate, String.format("%s:\t失败,1分钟后再进行重试", HaStatusEnum.SWITCHING_CLOSEOUT.getMsg(newActiveClusterPhyDO.getClusterName())));
|
||||
return haSwitchTopic;
|
||||
} else if (rv.failed() && focus) {
|
||||
allSuccess = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 记录已经激活的User
|
||||
activeHaKafkaUserSet.addAll(relatedKafkaUserSet);
|
||||
|
||||
// 修改Topic主备信息
|
||||
HaASRelationDO newHaASRelationDO = new HaASRelationDO(
|
||||
newActiveClusterPhyDO.getId(), relationDO.getActiveResName(),
|
||||
newStandbyClusterPhyDO.getId(), relationDO.getStandbyResName(),
|
||||
HaResTypeEnum.TOPIC.getCode(),
|
||||
HaStatusEnum.STABLE_CODE
|
||||
);
|
||||
newHaASRelationDO.setId(relationDO.getId());
|
||||
|
||||
haASRelationService.updateById(newHaASRelationDO);
|
||||
}
|
||||
|
||||
if (!needLog) {
|
||||
return haSwitchTopic;
|
||||
}
|
||||
|
||||
this.saveLogs(switchLogTemplate, String.format("%s:\t%s", HaStatusEnum.SWITCHING_CLOSEOUT.getMsg(newActiveClusterPhyDO.getClusterName()), allSuccess? "成功": "存在失败,但进行强制执行,跳过该操作"));
|
||||
return haSwitchTopic;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查参数,并获取主备关系信息
|
||||
*/
|
||||
private Result<List<HaASRelationDO>> checkParamAndGetASRelation(Long activeClusterPhyId, Long standbyClusterPhyId, List<String> switchTopicNameList) {
|
||||
List<HaASRelationDO> doList = new ArrayList<>();
|
||||
for (String topicName: switchTopicNameList) {
|
||||
Result<HaASRelationDO> doResult = this.checkParamAndGetASRelation(activeClusterPhyId, standbyClusterPhyId, topicName);
|
||||
if (doResult.failed()) {
|
||||
return Result.buildFromIgnoreData(doResult);
|
||||
}
|
||||
|
||||
doList.add(doResult.getData());
|
||||
}
|
||||
|
||||
return Result.buildSuc(doList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查参数,并获取主备关系信息
|
||||
*/
|
||||
private Result<HaASRelationDO> checkParamAndGetASRelation(Long activeClusterPhyId, Long standbyClusterPhyId, String topicName) {
|
||||
// newActiveTopic必须存在,新的备Topic可以不存在
|
||||
if (!PhysicalClusterMetadataManager.isTopicExist(activeClusterPhyId, topicName)) {
|
||||
return Result.buildFromRSAndMsg(
|
||||
ResultStatus.RESOURCE_NOT_EXIST,
|
||||
String.format("新的主集群ID:[%d]-Topic:[%s] 不存在", activeClusterPhyId, topicName)
|
||||
);
|
||||
}
|
||||
|
||||
// 查询主备关系是否存在
|
||||
HaASRelationDO relationDO = haASRelationService.getSpecifiedHAFromDB(
|
||||
standbyClusterPhyId,
|
||||
topicName,
|
||||
activeClusterPhyId,
|
||||
topicName,
|
||||
HaResTypeEnum.TOPIC
|
||||
);
|
||||
if (relationDO == null) {
|
||||
// 查询切换后的关系是否存在,如果已经存在,则后续会重新建立一遍
|
||||
relationDO = haASRelationService.getSpecifiedHAFromDB(
|
||||
activeClusterPhyId,
|
||||
topicName,
|
||||
standbyClusterPhyId,
|
||||
topicName,
|
||||
HaResTypeEnum.TOPIC
|
||||
);
|
||||
}
|
||||
|
||||
if (relationDO == null) {
|
||||
// 主备关系不存在
|
||||
return Result.buildFromRSAndMsg(
|
||||
ResultStatus.RESOURCE_NOT_EXIST,
|
||||
String.format("主集群ID:[%d]-Topic:[%s], 备集群ID:[%d] Topic:[%s] 的主备关系不存在,因此无法切换", activeClusterPhyId, topicName, standbyClusterPhyId, topicName)
|
||||
);
|
||||
}
|
||||
|
||||
return Result.buildSuc(relationDO);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.xiaojukeji.kafka.manager.service.biz.job;
|
||||
|
||||
|
||||
import com.xiaojukeji.kafka.manager.common.entity.Result;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.ao.ha.job.HaJobState;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.dto.ha.ASSwitchJobActionDTO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.dto.ha.ASSwitchJobDTO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.vo.ha.job.HaJobDetailVO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
public interface HaASSwitchJobManager {
|
||||
/**
|
||||
* 创建任务
|
||||
*/
|
||||
Result<Long> createJob(ASSwitchJobDTO dto, String operator);
|
||||
|
||||
/**
|
||||
* 执行job
|
||||
* @param jobId 任务ID
|
||||
* @param focus 强制切换
|
||||
* @param firstTriggerExecute 第一次触发执行
|
||||
* @return
|
||||
*/
|
||||
Result<Void> executeJob(Long jobId, boolean focus, boolean firstTriggerExecute);
|
||||
|
||||
Result<HaJobState> jobState(Long jobId);
|
||||
|
||||
/**
|
||||
* 刷新扩展数据
|
||||
*/
|
||||
void flushExtendData(Long jobId);
|
||||
|
||||
/**
|
||||
* 对Job执行操作
|
||||
*/
|
||||
Result<Void> actionJob(Long jobId, ASSwitchJobActionDTO dto);
|
||||
|
||||
Result<List<HaJobDetailVO>> jobDetail(Long jobId);
|
||||
}
|
||||
@@ -0,0 +1,452 @@
|
||||
package com.xiaojukeji.kafka.manager.service.biz.job.impl;
|
||||
|
||||
import com.xiaojukeji.kafka.manager.common.bizenum.JobLogBizTypEnum;
|
||||
import com.xiaojukeji.kafka.manager.common.bizenum.TaskActionEnum;
|
||||
import com.xiaojukeji.kafka.manager.common.bizenum.ha.HaResTypeEnum;
|
||||
import com.xiaojukeji.kafka.manager.common.bizenum.ha.HaStatusEnum;
|
||||
import com.xiaojukeji.kafka.manager.common.bizenum.ha.job.HaJobStatusEnum;
|
||||
import com.xiaojukeji.kafka.manager.common.constant.ConfigConstant;
|
||||
import com.xiaojukeji.kafka.manager.common.constant.Constant;
|
||||
import com.xiaojukeji.kafka.manager.common.constant.MsgConstant;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.Result;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.ResultStatus;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.ao.ha.HaSwitchTopic;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.ao.ha.job.HaJobDetail;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.ao.ha.job.HaJobState;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.ao.ha.job.HaSubJobExtendData;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.dto.ha.ASSwitchJobActionDTO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.dto.ha.ASSwitchJobDTO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.ClusterDO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.ha.HaASRelationDO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.ha.HaASSwitchJobDO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.ha.HaASSwitchSubJobDO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.ha.JobLogDO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.vo.ha.job.HaJobDetailVO;
|
||||
import com.xiaojukeji.kafka.manager.common.utils.BackoffUtils;
|
||||
import com.xiaojukeji.kafka.manager.common.utils.ConvertUtil;
|
||||
import com.xiaojukeji.kafka.manager.common.utils.FutureUtil;
|
||||
import com.xiaojukeji.kafka.manager.common.utils.ValidateUtils;
|
||||
import com.xiaojukeji.kafka.manager.service.biz.ha.HaAppManager;
|
||||
import com.xiaojukeji.kafka.manager.service.biz.ha.HaTopicManager;
|
||||
import com.xiaojukeji.kafka.manager.service.biz.job.HaASSwitchJobManager;
|
||||
import com.xiaojukeji.kafka.manager.service.cache.PhysicalClusterMetadataManager;
|
||||
import com.xiaojukeji.kafka.manager.service.service.ClusterService;
|
||||
import com.xiaojukeji.kafka.manager.service.service.ConfigService;
|
||||
import com.xiaojukeji.kafka.manager.service.service.JobLogService;
|
||||
import com.xiaojukeji.kafka.manager.service.service.ha.HaASSwitchJobService;
|
||||
import com.xiaojukeji.kafka.manager.service.service.ha.HaASRelationService;
|
||||
import com.xiaojukeji.kafka.manager.service.service.ha.HaTopicService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
||||
@Service
|
||||
public class HaASSwitchJobManagerImpl implements HaASSwitchJobManager {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(HaASSwitchJobManagerImpl.class);
|
||||
|
||||
@Autowired
|
||||
private JobLogService jobLogService;
|
||||
|
||||
@Autowired
|
||||
private ClusterService clusterService;
|
||||
|
||||
@Autowired
|
||||
private ConfigService configService;
|
||||
|
||||
@Autowired
|
||||
private HaASRelationService haASRelationService;
|
||||
|
||||
@Autowired
|
||||
private HaASSwitchJobService haASSwitchJobService;
|
||||
|
||||
@Autowired
|
||||
private HaTopicManager haTopicManager;
|
||||
|
||||
@Autowired
|
||||
private HaTopicService haTopicService;
|
||||
|
||||
@Autowired
|
||||
private HaAppManager haAppManager;
|
||||
|
||||
private static final Long BACK_OFF_TIME = 3000L;
|
||||
|
||||
private static final FutureUtil<Void> asyncExecuteJob = FutureUtil.init(
|
||||
"HaASSwitchJobManager",
|
||||
10,
|
||||
10,
|
||||
5000
|
||||
);
|
||||
|
||||
@Override
|
||||
public Result<Long> createJob(ASSwitchJobDTO dto, String operator) {
|
||||
LOGGER.info("method=createJob||activeClusterPhyId={}||switchTopicParam={}||operator={}", dto.getActiveClusterPhyId(), ConvertUtil.obj2Json(dto), operator);
|
||||
|
||||
// 1、检查参数是否合法,并获取需要执行主备切换的Topics
|
||||
Result<Set<String>> haTopicSetResult = this.checkParamLegalAndGetNeedSwitchHaTopics(dto);
|
||||
if (haTopicSetResult.failed()) {
|
||||
// 检查失败,则直接返回
|
||||
return Result.buildFromIgnoreData(haTopicSetResult);
|
||||
}
|
||||
|
||||
LOGGER.info("method=createJob||activeClusterPhyId={}||switchTopics={}||operator={}", dto.getActiveClusterPhyId(), ConvertUtil.obj2Json(haTopicSetResult.getData()), operator);
|
||||
|
||||
// 2、查看是否将KafkaUser关联的Topic都涵盖了
|
||||
if (dto.getMustContainAllKafkaUserTopics() != null
|
||||
&& dto.getMustContainAllKafkaUserTopics()
|
||||
&& (dto.getAll() == null || !dto.getAll())
|
||||
&& !haAppManager.isContainAllRelateAppTopics(dto.getActiveClusterPhyId(), dto.getTopicNameList())) {
|
||||
return Result.buildFromRSAndMsg(ResultStatus.OPERATION_FORBIDDEN, "存在KafkaUser关联的Topic未选中");
|
||||
}
|
||||
|
||||
// 3、创建任务
|
||||
Result<Long> longResult = haASSwitchJobService.createJob(
|
||||
dto.getActiveClusterPhyId(),
|
||||
dto.getStandbyClusterPhyId(),
|
||||
new ArrayList<>(haTopicSetResult.getData()),
|
||||
operator
|
||||
);
|
||||
if (longResult.failed()) {
|
||||
// 创建失败
|
||||
return longResult;
|
||||
}
|
||||
|
||||
LOGGER.info("method=createJob||activeClusterPhyId={}||jobId={}||operator={}||msg=create-job success", dto.getActiveClusterPhyId(), longResult.getData(), operator);
|
||||
|
||||
// 4、为了加快执行效率,这里在创建完成任务之后,会直接异步执行HA切换任务
|
||||
asyncExecuteJob.directSubmitTask(
|
||||
() -> {
|
||||
BackoffUtils.backoff(BACK_OFF_TIME);
|
||||
|
||||
this.executeJob(longResult.getData(), false, true);
|
||||
|
||||
// 更新扩展数据
|
||||
this.flushExtendData(longResult.getData());
|
||||
}
|
||||
);
|
||||
|
||||
// 5、返回结果
|
||||
return longResult;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<Void> executeJob(Long jobId, boolean focus, boolean firstTriggerExecute) {
|
||||
LOGGER.info("method=executeJob||jobId={}||msg=execute job start", jobId);
|
||||
|
||||
// 查询job
|
||||
HaASSwitchJobDO jobDO = haASSwitchJobService.getJobById(jobId);
|
||||
if (jobDO == null) {
|
||||
LOGGER.warn("method=executeJob||jobId={}||msg=job not exist", jobId);
|
||||
return Result.buildFromRSAndMsg(ResultStatus.RESOURCE_NOT_EXIST, String.format("jobId:[%d] 不存在", jobId));
|
||||
}
|
||||
|
||||
// 检查job状态
|
||||
if (!HaJobStatusEnum.isRunning(jobDO.getJobStatus())) {
|
||||
LOGGER.warn("method=executeJob||jobId={}||jobStatus={}||msg=job status illegal", jobId, HaJobStatusEnum.valueOfStatus(jobDO.getJobStatus()));
|
||||
return this.buildActionForbidden(jobId, jobDO.getJobStatus());
|
||||
}
|
||||
|
||||
// 查询子job列表
|
||||
List<HaASSwitchSubJobDO> subJobDOList = haASSwitchJobService.listSubJobsById(jobId);
|
||||
if (ValidateUtils.isEmptyList(subJobDOList)) {
|
||||
// 无子任务,则设置任务状态为成功
|
||||
haASSwitchJobService.updateJobStatus(jobId, HaJobStatusEnum.SUCCESS.getStatus());
|
||||
return Result.buildSuc();
|
||||
}
|
||||
|
||||
Set<Integer> statusSet = new HashSet<>();
|
||||
subJobDOList.forEach(elem -> statusSet.add(elem.getJobStatus()));
|
||||
if (statusSet.size() == 1 && statusSet.contains(HaJobStatusEnum.SUCCESS.getStatus())) {
|
||||
// 无子任务,则设置任务状态为成功
|
||||
haASSwitchJobService.updateJobStatus(jobId, HaJobStatusEnum.SUCCESS.getStatus());
|
||||
return Result.buildSuc();
|
||||
}
|
||||
|
||||
if (firstTriggerExecute) {
|
||||
this.saveLogs(jobDO.getId(), "主备切换开始...");
|
||||
this.saveLogs(jobDO.getId(), "如果主备集群或网关的ZK存在问题,则可能会出现1分钟左右日志不刷新的情况");
|
||||
}
|
||||
|
||||
// 进行主备切换
|
||||
Result<HaSwitchTopic> haSwitchTopicResult = haTopicManager.switchHaWithCanRetry(
|
||||
jobDO.getActiveClusterPhyId(),
|
||||
jobDO.getStandbyClusterPhyId(),
|
||||
subJobDOList.stream().map(elem -> elem.getActiveResName()).collect(Collectors.toList()),
|
||||
focus,
|
||||
firstTriggerExecute,
|
||||
new JobLogDO(JobLogBizTypEnum.HA_SWITCH_JOB_LOG.getCode(), String.valueOf(jobId)),
|
||||
jobDO.getOperator()
|
||||
);
|
||||
|
||||
if (haSwitchTopicResult.failed()) {
|
||||
// 出现错误
|
||||
LOGGER.error("method=executeJob||jobId={}||executeResult={}||msg=execute job failed", jobId, haSwitchTopicResult);
|
||||
return Result.buildFromIgnoreData(haSwitchTopicResult);
|
||||
}
|
||||
|
||||
|
||||
// 执行结果
|
||||
HaSwitchTopic haSwitchTopic = haSwitchTopicResult.getData();
|
||||
Long timeoutUnitSec = this.getTimeoutUnitSecConfig(jobDO.getActiveClusterPhyId());
|
||||
|
||||
// 存储日志
|
||||
if (haSwitchTopic.isFinished()) {
|
||||
this.saveLogs(jobDO.getId(), "主备切换完成.");
|
||||
}
|
||||
|
||||
// 更新状态
|
||||
for (HaASSwitchSubJobDO subJobDO: subJobDOList) {
|
||||
if (haSwitchTopic.isActiveTopicSwitchFinished(subJobDO.getActiveResName()) || haSwitchTopic.isFinished()) {
|
||||
// 执行完成
|
||||
haASSwitchJobService.updateSubJobStatus(subJobDO.getId(), HaJobStatusEnum.SUCCESS.getStatus());
|
||||
} else if (runningInTimeout(subJobDO.getCreateTime().getTime(), timeoutUnitSec)) {
|
||||
// 超时运行中
|
||||
haASSwitchJobService.updateSubJobStatus(subJobDO.getId(), HaJobStatusEnum.RUNNING_IN_TIMEOUT.getStatus());
|
||||
}
|
||||
}
|
||||
|
||||
if (haSwitchTopic.isFinished()) {
|
||||
// 任务执行完成
|
||||
LOGGER.info("method=executeJob||jobId={}||executeResult={}||msg=execute job success", jobId, haSwitchTopicResult);
|
||||
|
||||
// 更新状态
|
||||
haASSwitchJobService.updateJobStatus(jobId, HaJobStatusEnum.SUCCESS.getStatus());
|
||||
} else {
|
||||
LOGGER.info("method=executeJob||jobId={}||executeResult={}||msg=execute job not finished", jobId, haSwitchTopicResult);
|
||||
}
|
||||
|
||||
// 返回结果
|
||||
return Result.buildSuc();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<HaJobState> jobState(Long jobId) {
|
||||
List<HaASSwitchSubJobDO> doList = haASSwitchJobService.listSubJobsById(jobId);
|
||||
if (ValidateUtils.isEmptyList(doList)) {
|
||||
return Result.buildFromRSAndMsg(ResultStatus.RESOURCE_NOT_EXIST, String.format("jobId:[%d] 不存在", jobId));
|
||||
}
|
||||
|
||||
if (System.currentTimeMillis() - doList.get(0).getCreateTime().getTime() <= (BACK_OFF_TIME.longValue() * 2)) {
|
||||
// 进度0
|
||||
return Result.buildSuc(new HaJobState(doList.size(), 0));
|
||||
}
|
||||
|
||||
// 这里会假设主备Topic的名称是一样的
|
||||
Map<String, Integer> progressMap = new HashMap<>();
|
||||
haASRelationService.listAllHAFromDB(doList.get(0).getActiveClusterPhyId(), HaResTypeEnum.TOPIC).stream().forEach(
|
||||
elem -> progressMap.put(elem.getActiveResName(), elem.getStatus())
|
||||
);
|
||||
|
||||
HaJobState haJobState = new HaJobState(
|
||||
doList.stream().map(elem -> elem.getJobStatus()).collect(Collectors.toList()),
|
||||
0
|
||||
);
|
||||
|
||||
// 计算细致的进度信息
|
||||
Integer progress = 0;
|
||||
for (HaASSwitchSubJobDO elem: doList) {
|
||||
if (HaJobStatusEnum.isFinished(elem.getJobStatus())) {
|
||||
progress += 100;
|
||||
continue;
|
||||
}
|
||||
|
||||
progress += HaStatusEnum.calProgress(progressMap.get(elem.getActiveResName()));
|
||||
}
|
||||
haJobState.setProgress(ConvertUtil.double2Int(progress * 1.0 / doList.size()));
|
||||
|
||||
return Result.buildSuc(haJobState);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flushExtendData(Long jobId) {
|
||||
// 因为仅仅是刷新扩展数据,因此不会对jobId等进行严格检查
|
||||
|
||||
// 查询子job列表
|
||||
List<HaASSwitchSubJobDO> subJobDOList = haASSwitchJobService.listSubJobsById(jobId);
|
||||
if (ValidateUtils.isEmptyList(subJobDOList)) {
|
||||
// 无任务,直接返回
|
||||
return;
|
||||
}
|
||||
|
||||
for (HaASSwitchSubJobDO subJobDO: subJobDOList) {
|
||||
try {
|
||||
this.flushExtendData(subJobDO);
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("method=flushExtendData||jobId={}||subJobDO={}||errMsg=exception", jobId, subJobDO, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<Void> actionJob(Long jobId, ASSwitchJobActionDTO dto) {
|
||||
if (!TaskActionEnum.FORCE.getAction().equals(dto.getAction())) {
|
||||
// 不存在,或者不支持
|
||||
return Result.buildFromRSAndMsg(ResultStatus.PARAM_ILLEGAL, "action不存在");
|
||||
}
|
||||
|
||||
// 强制执行,异步执行
|
||||
this.saveLogs(jobId, "开始执行强制切换...");
|
||||
this.saveLogs(jobId, "强制切换过程中,可能出现日志1分钟不刷新情况");
|
||||
this.saveLogs(jobId, "强制切换过程中,因可能与正常切换任务同时执行,因此可能出现日志重复问题");
|
||||
asyncExecuteJob.directSubmitTask(
|
||||
() -> this.executeJob(jobId, true, false)
|
||||
);
|
||||
|
||||
return Result.buildSuc();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<List<HaJobDetailVO>> jobDetail(Long jobId) {
|
||||
// 获取详情
|
||||
Result<List<HaJobDetail>> haResult = haASSwitchJobService.jobDetail(jobId);
|
||||
if (haResult.failed()) {
|
||||
return Result.buildFromIgnoreData(haResult);
|
||||
}
|
||||
|
||||
List<HaJobDetailVO> voList = ConvertUtil.list2List(haResult.getData(), HaJobDetailVO.class);
|
||||
if (voList.isEmpty()) {
|
||||
return Result.buildSuc(voList);
|
||||
}
|
||||
|
||||
ClusterDO activeClusterDO = clusterService.getById(voList.get(0).getActiveClusterPhyId());
|
||||
ClusterDO standbyClusterDO = clusterService.getById(voList.get(0).getStandbyClusterPhyId());
|
||||
|
||||
// 获取超时配置
|
||||
Long timeoutUnitSecConfig = this.getTimeoutUnitSecConfig(voList.get(0).getActiveClusterPhyId());
|
||||
voList.forEach(elem -> {
|
||||
elem.setTimeoutUnitSecConfig(timeoutUnitSecConfig);
|
||||
elem.setActiveClusterPhyName(activeClusterDO != null? activeClusterDO.getClusterName(): "");
|
||||
elem.setStandbyClusterPhyName(standbyClusterDO != null? standbyClusterDO.getClusterName(): "");
|
||||
});
|
||||
|
||||
// 返回结果
|
||||
return Result.buildSuc(voList);
|
||||
}
|
||||
|
||||
/**************************************************** private method ****************************************************/
|
||||
|
||||
/**
|
||||
* 检查参数是否合法并返回需要进行主备切换的Topic
|
||||
*/
|
||||
private Result<Set<String>> checkParamLegalAndGetNeedSwitchHaTopics(ASSwitchJobDTO dto) {
|
||||
// 1、检查主集群是否存在
|
||||
ClusterDO activeClusterDO = clusterService.getById(dto.getActiveClusterPhyId());
|
||||
if (ValidateUtils.isNull(activeClusterDO)) {
|
||||
return Result.buildFromRSAndMsg(ResultStatus.RESOURCE_NOT_EXIST, MsgConstant.getClusterPhyNotExist(dto.getActiveClusterPhyId()));
|
||||
}
|
||||
|
||||
// 2、检查备集群是否存在
|
||||
ClusterDO standbyClusterDO = clusterService.getById(dto.getStandbyClusterPhyId());
|
||||
if (ValidateUtils.isNull(standbyClusterDO)) {
|
||||
return Result.buildFromRSAndMsg(ResultStatus.RESOURCE_NOT_EXIST, MsgConstant.getClusterPhyNotExist(dto.getStandbyClusterPhyId()));
|
||||
}
|
||||
|
||||
// 3、检查集群是否建立了主备关系
|
||||
List<HaASRelationDO> clusterDOList = haASRelationService.listAllHAFromDB(dto.getActiveClusterPhyId(), dto.getStandbyClusterPhyId(), HaResTypeEnum.CLUSTER);
|
||||
if (ValidateUtils.isEmptyList(clusterDOList)) {
|
||||
return Result.buildFromRSAndMsg(ResultStatus.RESOURCE_NOT_EXIST, "集群主备关系未建立");
|
||||
}
|
||||
|
||||
// 4、获取集群当前已经建立主备关系的Topic列表
|
||||
List<HaASRelationDO> topicDOList = haASRelationService.listAllHAFromDB(dto.getActiveClusterPhyId(), dto.getStandbyClusterPhyId(), HaResTypeEnum.TOPIC);
|
||||
|
||||
if (dto.getAll() != null && dto.getAll()) {
|
||||
// 5.1、对集群所有已经建立主备关系的Topic,进行主备切换
|
||||
|
||||
// 过滤掉 __打头的Topic
|
||||
// 过滤掉 当前主集群已经是切换后的主集群的Topic,即这部分Topic已经是切换后的状态了
|
||||
return Result.buildSuc(
|
||||
topicDOList.stream()
|
||||
.filter(elem -> !elem.getActiveResName().startsWith("__"))
|
||||
.filter(elem -> !elem.getActiveClusterPhyId().equals(dto.getActiveClusterPhyId()))
|
||||
.map(elem -> elem.getActiveResName())
|
||||
.collect(Collectors.toSet())
|
||||
);
|
||||
}
|
||||
|
||||
// 5.2、指定Topic进行主备切换
|
||||
|
||||
// 当前已经有主备关系的Topic
|
||||
Set<String> relationTopicNameSet = new HashSet<>();
|
||||
topicDOList.forEach(elem -> relationTopicNameSet.add(elem.getActiveResName()));
|
||||
|
||||
// 逐个检查Topic,此时这里不进行过滤,如果进行过滤之后,会导致一些用户提交的信息丢失。
|
||||
// 比如提交了10个Topic,我过滤成9个,用户就会比较奇怪。
|
||||
// 上一步进行过滤,是减少不必要的Topic的刚扰,PS:也可以考虑增加这些干扰,从而让用户明确知道Topic已进行主备切换
|
||||
for (String topicName: dto.getTopicNameList()) {
|
||||
if (!relationTopicNameSet.contains(topicName)) {
|
||||
return Result.buildFromRSAndMsg(ResultStatus.RESOURCE_NOT_EXIST, String.format("Topic:[%s] 主备关系不存在,需要先建立主备关系", topicName));
|
||||
}
|
||||
|
||||
// 检查新的主Topic是否存在,如果不存在则直接返回错误,不检查新的备Topic是否存在
|
||||
if (!PhysicalClusterMetadataManager.isTopicExist(dto.getActiveClusterPhyId(), topicName)) {
|
||||
return Result.buildFromRSAndMsg(ResultStatus.RESOURCE_NOT_EXIST, MsgConstant.getTopicNotExist(dto.getActiveClusterPhyId(), topicName));
|
||||
}
|
||||
}
|
||||
|
||||
return Result.buildSuc(
|
||||
dto.getTopicNameList().stream().collect(Collectors.toSet())
|
||||
);
|
||||
}
|
||||
|
||||
private void saveLogs(Long jobId, String content) {
|
||||
jobLogService.addLogAndIgnoreException(new JobLogDO(
|
||||
JobLogBizTypEnum.HA_SWITCH_JOB_LOG.getCode(),
|
||||
String.valueOf(jobId),
|
||||
new Date(),
|
||||
content
|
||||
));
|
||||
}
|
||||
|
||||
private void flushExtendData(HaASSwitchSubJobDO subJobDO) {
|
||||
HaSubJobExtendData extendData = new HaSubJobExtendData();
|
||||
Result<Long> sumLagResult = haTopicService.getStandbyTopicFetchLag(subJobDO.getActiveClusterPhyId(), subJobDO.getActiveResName());
|
||||
if (sumLagResult.failed()) {
|
||||
extendData.setSumLag(Constant.INVALID_CODE.longValue());
|
||||
} else {
|
||||
extendData.setSumLag(sumLagResult.getData());
|
||||
}
|
||||
|
||||
haASSwitchJobService.updateSubJobExtendData(subJobDO.getId(), extendData);
|
||||
}
|
||||
|
||||
private Result<Void> buildActionForbidden(Long jobId, Integer jobStatus) {
|
||||
return Result.buildFromRSAndMsg(
|
||||
ResultStatus.OPERATION_FORBIDDEN,
|
||||
String.format("jobId:[%d] 当前 status:[%s], 不允许被执行", jobId, HaJobStatusEnum.valueOfStatus(jobStatus))
|
||||
);
|
||||
}
|
||||
|
||||
private boolean runningInTimeout(Long startTimeUnitMs, Long timeoutUnitSec) {
|
||||
if (timeoutUnitSec == null) {
|
||||
// 配置为空,则返回未超时
|
||||
return false;
|
||||
}
|
||||
|
||||
// 开始时间 + 超时时间 > 当前时间,则为超时
|
||||
return startTimeUnitMs + timeoutUnitSec * 1000 > System.currentTimeMillis();
|
||||
}
|
||||
|
||||
private Long getTimeoutUnitSecConfig(Long activeClusterPhyId) {
|
||||
// 获取该集群配置
|
||||
Long durationUnitSec = configService.getLongValue(
|
||||
ConfigConstant.HA_SWITCH_JOB_TIMEOUT_UNIT_SEC_CONFIG_PREFIX + "_" + activeClusterPhyId,
|
||||
null
|
||||
);
|
||||
|
||||
if (durationUnitSec == null) {
|
||||
// 当前集群配置不存在,则获取默认配置
|
||||
durationUnitSec = configService.getLongValue(
|
||||
ConfigConstant.HA_SWITCH_JOB_TIMEOUT_UNIT_SEC_CONFIG_PREFIX + "_" + Constant.INVALID_CODE,
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
return durationUnitSec;
|
||||
}
|
||||
}
|
||||
@@ -43,7 +43,7 @@ public interface ClusterService {
|
||||
|
||||
ClusterNameDTO getClusterName(Long logicClusterId);
|
||||
|
||||
ResultStatus deleteById(Long clusterId, String operator);
|
||||
Result<Void> deleteById(Long clusterId, String operator);
|
||||
|
||||
/**
|
||||
* 获取优先被选举为controller的broker
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.xiaojukeji.kafka.manager.service.service;
|
||||
|
||||
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.ha.JobLogDO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Job相关的日志
|
||||
*/
|
||||
public interface JobLogService {
|
||||
void addLogAndIgnoreException(JobLogDO jobLogDO);
|
||||
|
||||
List<JobLogDO> listLogs(Integer bizType, String bizKeyword, Long startId);
|
||||
}
|
||||
@@ -2,11 +2,14 @@ package com.xiaojukeji.kafka.manager.service.service;
|
||||
|
||||
import com.xiaojukeji.kafka.manager.common.entity.Result;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.ResultStatus;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.TopicOperationResult;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.ao.RdTopicBasic;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.ao.topic.MineTopicSummary;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.ao.topic.TopicAppData;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.ao.topic.TopicBusinessInfo;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.ao.topic.TopicDTO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.ao.topic.MineTopicSummary;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.dto.op.topic.TopicExpansionDTO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.dto.op.topic.TopicModificationDTO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.TopicDO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.TopicExpiredDO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.TopicStatisticsDO;
|
||||
@@ -130,5 +133,15 @@ public interface TopicManagerService {
|
||||
* @return
|
||||
*/
|
||||
ResultStatus addAuthority(AuthorityDO authorityDO);
|
||||
|
||||
/**
|
||||
* 修改topic
|
||||
*/
|
||||
Result modifyTopic(TopicModificationDTO dto);
|
||||
|
||||
/**
|
||||
* topic扩分区
|
||||
*/
|
||||
TopicOperationResult expandTopic(TopicExpansionDTO dto);
|
||||
}
|
||||
|
||||
|
||||
@@ -65,6 +65,7 @@ public interface TopicService {
|
||||
* 获取Topic的分区的offset
|
||||
*/
|
||||
Map<TopicPartition, Long> getPartitionOffset(ClusterDO clusterDO, String topicName, OffsetPosEnum offsetPosEnum);
|
||||
Map<TopicPartition, Long> getPartitionOffset(Long clusterPhyId, String topicName, OffsetPosEnum offsetPosEnum);
|
||||
|
||||
/**
|
||||
* 获取Topic概览信息
|
||||
|
||||
@@ -42,4 +42,13 @@ public interface ZookeeperService {
|
||||
* @return
|
||||
*/
|
||||
Result deleteControllerPreferredCandidate(Long clusterId, Integer brokerId);
|
||||
|
||||
/**
|
||||
* 获取集群的brokerId
|
||||
* @param zookeeper zookeeper
|
||||
* @return 操作结果
|
||||
*/
|
||||
Result<List<Integer>> getBrokerIds(String zookeeper);
|
||||
|
||||
Long getClusterIdAndNullIfFailed(String zookeeper);
|
||||
}
|
||||
|
||||
@@ -51,6 +51,13 @@ public interface AppService {
|
||||
*/
|
||||
List<AppDO> getByPrincipal(String principal);
|
||||
|
||||
/**
|
||||
* 通过负责人&集群id(排除已被其他集群绑定的app)来查找
|
||||
* @param principal 负责人
|
||||
* @return List<AppDO>
|
||||
*/
|
||||
List<AppDO> getByPrincipalAndClusterId(String principal, Long phyClusterId);
|
||||
|
||||
/**
|
||||
* 通过appId来查,需要check当前登录人是否有权限.
|
||||
* @param appId appId
|
||||
|
||||
@@ -46,6 +46,8 @@ public interface AuthorityService {
|
||||
*/
|
||||
List<AuthorityDO> getAuthorityByTopic(Long clusterId, String topicName);
|
||||
|
||||
List<AuthorityDO> getAuthorityByTopicFromCache(Long clusterId, String topicName);
|
||||
|
||||
List<AuthorityDO> getAuthority(String appId);
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,24 +4,27 @@ import com.alibaba.fastjson.JSONObject;
|
||||
import com.xiaojukeji.kafka.manager.common.bizenum.ModuleEnum;
|
||||
import com.xiaojukeji.kafka.manager.common.bizenum.OperateEnum;
|
||||
import com.xiaojukeji.kafka.manager.common.bizenum.OperationStatusEnum;
|
||||
import com.xiaojukeji.kafka.manager.common.bizenum.ha.HaResTypeEnum;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.ResultStatus;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.ao.AppTopicDTO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.dto.normal.AppDTO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.LogicalClusterDO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.OperateRecordDO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.TopicDO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.gateway.AppDO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.gateway.AuthorityDO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.gateway.KafkaUserDO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.ha.HaASRelationDO;
|
||||
import com.xiaojukeji.kafka.manager.common.utils.ListUtils;
|
||||
import com.xiaojukeji.kafka.manager.common.utils.ValidateUtils;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.LogicalClusterDO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.TopicDO;
|
||||
import com.xiaojukeji.kafka.manager.dao.gateway.AppDao;
|
||||
import com.xiaojukeji.kafka.manager.dao.gateway.KafkaUserDao;
|
||||
import com.xiaojukeji.kafka.manager.service.cache.LogicalClusterMetadataManager;
|
||||
import com.xiaojukeji.kafka.manager.service.service.OperateRecordService;
|
||||
import com.xiaojukeji.kafka.manager.service.service.TopicManagerService;
|
||||
import com.xiaojukeji.kafka.manager.service.service.gateway.AppService;
|
||||
import com.xiaojukeji.kafka.manager.service.service.gateway.AuthorityService;
|
||||
import com.xiaojukeji.kafka.manager.service.service.TopicManagerService;
|
||||
import com.xiaojukeji.kafka.manager.service.service.ha.HaASRelationService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -60,6 +63,9 @@ public class AppServiceImpl implements AppService {
|
||||
@Autowired
|
||||
private OperateRecordService operateRecordService;
|
||||
|
||||
@Autowired
|
||||
private HaASRelationService haASRelationService;
|
||||
|
||||
@Override
|
||||
public ResultStatus addApp(AppDO appDO, String operator) {
|
||||
try {
|
||||
@@ -181,6 +187,52 @@ public class AppServiceImpl implements AppService {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AppDO> getByPrincipalAndClusterId(String principal, Long phyClusterId) {
|
||||
try {
|
||||
List<AppDO> appDOs = appDao.getByPrincipal(principal);
|
||||
if (ValidateUtils.isEmptyList(appDOs)){
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
List<HaASRelationDO> has = haASRelationService.listAllHAFromDB(phyClusterId, HaResTypeEnum.CLUSTER);
|
||||
List<AuthorityDO> authorityDOS;
|
||||
if (has.isEmpty()){
|
||||
authorityDOS = authorityService.listAll().stream()
|
||||
.filter(authorityDO -> !authorityDO.getClusterId().equals(phyClusterId))
|
||||
.collect(Collectors.toList());
|
||||
}else {
|
||||
authorityDOS = authorityService.listAll().stream()
|
||||
.filter(authorityDO -> !(has.get(0).getActiveClusterPhyId().equals(authorityDO.getClusterId())
|
||||
|| has.get(0).getStandbyClusterPhyId().equals(authorityDO.getClusterId())))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
Map<String,List<AuthorityDO>> appClusterIdMap = authorityDOS
|
||||
.stream().filter(authorityDO -> !authorityDO.getClusterId().equals(phyClusterId))
|
||||
.collect(Collectors.groupingBy(AuthorityDO::getAppId));
|
||||
|
||||
//过滤已被其他集群topic使用的app
|
||||
appDOs = appDOs.stream()
|
||||
.filter(appDO -> ListUtils.string2StrList(appDO.getPrincipals()).contains(principal))
|
||||
.filter(appDO -> appClusterIdMap.get(appDO.getAppId()) == null)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
//过滤已被其他集群使用的app
|
||||
List<String> clusterAppIds = logicClusterMetadataManager.getLogicalClusterList()
|
||||
.stream().filter(logicalClusterDO -> !logicalClusterDO.getClusterId().equals(phyClusterId) )
|
||||
.map(LogicalClusterDO::getAppId).collect(Collectors.toList());
|
||||
appDOs = appDOs.stream()
|
||||
.filter(appDO -> !clusterAppIds.contains(appDO.getAppId()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return appDOs;
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("get app list failed, principals:{}.", principal);
|
||||
}
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AppDO getAppByUserAndId(String appId, String curUser) {
|
||||
AppDO appDO = this.getByAppId(appId);
|
||||
|
||||
@@ -4,6 +4,7 @@ import com.xiaojukeji.kafka.manager.common.bizenum.ModuleEnum;
|
||||
import com.xiaojukeji.kafka.manager.common.bizenum.OperateEnum;
|
||||
import com.xiaojukeji.kafka.manager.common.bizenum.OperationStatusEnum;
|
||||
import com.xiaojukeji.kafka.manager.common.bizenum.TopicAuthorityEnum;
|
||||
import com.xiaojukeji.kafka.manager.common.constant.Constant;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.ResultStatus;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.ao.gateway.TopicQuota;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.OperateRecordDO;
|
||||
@@ -75,8 +76,10 @@ public class AuthorityServiceImpl implements AuthorityService {
|
||||
return kafkaAclDao.insert(kafkaAclDO);
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("add authority failed, authorityDO:{}.", authorityDO, e);
|
||||
|
||||
// 返回-1表示出错
|
||||
return Constant.INVALID_CODE;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -124,7 +127,10 @@ public class AuthorityServiceImpl implements AuthorityService {
|
||||
operateRecordService.insert(operateRecordDO);
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("delete authority failed, authorityDO:{}.", authorityDO, e);
|
||||
|
||||
return ResultStatus.MYSQL_ERROR;
|
||||
}
|
||||
|
||||
return ResultStatus.SUCCESS;
|
||||
}
|
||||
|
||||
@@ -152,6 +158,11 @@ public class AuthorityServiceImpl implements AuthorityService {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AuthorityDO> getAuthorityByTopicFromCache(Long clusterId, String topicName) {
|
||||
return authorityDao.getAuthorityByTopicFromCache(clusterId, topicName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AuthorityDO> getAuthority(String appId) {
|
||||
List<AuthorityDO> doList = null;
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
package com.xiaojukeji.kafka.manager.service.service.ha;
|
||||
|
||||
import com.xiaojukeji.kafka.manager.common.bizenum.ha.HaResTypeEnum;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.Result;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.ha.HaASRelationDO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface HaASRelationService {
|
||||
Result<Void> replaceTopicRelationsToDB(Long standbyClusterPhyId, List<HaASRelationDO> topicRelationDOList);
|
||||
|
||||
Result<Void> addHAToDB(HaASRelationDO haASRelationDO);
|
||||
|
||||
Result<Void> deleteById(Long id);
|
||||
|
||||
int updateRelationStatus(Long relationId, Integer newStatus);
|
||||
int updateById(HaASRelationDO haASRelationDO);
|
||||
|
||||
/**
|
||||
* 获取主集群关系
|
||||
*/
|
||||
HaASRelationDO getActiveClusterHAFromDB(Long activeClusterPhyId);
|
||||
|
||||
/**
|
||||
* 获取主备关系
|
||||
*/
|
||||
HaASRelationDO getSpecifiedHAFromDB(Long activeClusterPhyId,
|
||||
String activeResName,
|
||||
Long standbyClusterPhyId,
|
||||
String standbyResName,
|
||||
HaResTypeEnum resTypeEnum);
|
||||
|
||||
/**
|
||||
* 获取主备关系
|
||||
*/
|
||||
HaASRelationDO getHAFromDB(Long firstClusterPhyId,
|
||||
String firstResName,
|
||||
HaResTypeEnum resTypeEnum);
|
||||
|
||||
/**
|
||||
* 获取备集群主备关系
|
||||
*/
|
||||
List<HaASRelationDO> getStandbyHAFromDB(Long standbyClusterPhyId, HaResTypeEnum resTypeEnum);
|
||||
List<HaASRelationDO> getActiveHAFromDB(Long activeClusterPhyId, HaResTypeEnum resTypeEnum);
|
||||
|
||||
/**
|
||||
* 获取主备关系
|
||||
*/
|
||||
List<HaASRelationDO> listAllHAFromDB(HaResTypeEnum resTypeEnum);
|
||||
|
||||
/**
|
||||
* 获取主备关系
|
||||
*/
|
||||
List<HaASRelationDO> listAllHAFromDB(Long firstClusterPhyId, HaResTypeEnum resTypeEnum);
|
||||
|
||||
/**
|
||||
* 获取主备关系
|
||||
*/
|
||||
List<HaASRelationDO> listAllHAFromDB(Long firstClusterPhyId, Long secondClusterPhyId, HaResTypeEnum resTypeEnum);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.xiaojukeji.kafka.manager.service.service.ha;
|
||||
|
||||
|
||||
import com.xiaojukeji.kafka.manager.common.entity.Result;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.ao.ha.job.HaJobDetail;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.ao.ha.job.HaSubJobExtendData;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.ha.HaASSwitchJobDO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.ha.HaASSwitchSubJobDO;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public interface HaASSwitchJobService {
|
||||
/**
|
||||
* 创建任务
|
||||
*/
|
||||
Result<Long> createJob(Long activeClusterPhyId, Long standbyClusterPhyId, List<String> topicNameList, String operator);
|
||||
|
||||
/**
|
||||
* 更新任务状态
|
||||
*/
|
||||
int updateJobStatus(Long jobId, Integer jobStatus);
|
||||
|
||||
/**
|
||||
* 更新子任务状态
|
||||
*/
|
||||
int updateSubJobStatus(Long subJobId, Integer jobStatus);
|
||||
|
||||
/**
|
||||
* 更新子任务扩展数据
|
||||
*/
|
||||
int updateSubJobExtendData(Long subJobId, HaSubJobExtendData extendData);
|
||||
|
||||
/**
|
||||
* 任务详情
|
||||
*/
|
||||
Result<List<HaJobDetail>> jobDetail(Long jobId);
|
||||
|
||||
/**
|
||||
* 正在运行中的job
|
||||
*/
|
||||
List<Long> listRunningJobs(Long ignoreAfterTime);
|
||||
|
||||
/**
|
||||
* 集群近期的任务ID
|
||||
*/
|
||||
Map<Long/*集群ID*/, HaASSwitchJobDO> listClusterLatestJobs();
|
||||
|
||||
HaASSwitchJobDO getJobById(Long jobId);
|
||||
|
||||
List<HaASSwitchSubJobDO> listSubJobsById(Long jobId);
|
||||
|
||||
/**
|
||||
* 获取所有切换任务
|
||||
*/
|
||||
List<HaASSwitchSubJobDO> listAll(Boolean isAsc);
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.xiaojukeji.kafka.manager.service.service.ha;
|
||||
|
||||
import com.xiaojukeji.kafka.manager.common.entity.Result;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.ClusterDO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.ha.HaASRelationDO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.vo.ha.HaClusterVO;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 集群主备关系
|
||||
*/
|
||||
public interface HaClusterService {
|
||||
/**
|
||||
* 创建主备关系
|
||||
*/
|
||||
Result<Void> createHA(Long activeClusterPhyId, Long standbyClusterPhyId, String operator);
|
||||
Result<Void> createHAInKafka(String zookeeper, ClusterDO needWriteToZKClusterDO, String operator);
|
||||
|
||||
/**
|
||||
* 切换主备关系
|
||||
*/
|
||||
Result<Void> switchHA(Long newActiveClusterPhyId, Long newStandbyClusterPhyId);
|
||||
|
||||
/**
|
||||
* 删除主备关系
|
||||
*/
|
||||
Result<Void> deleteHA(Long activeClusterPhyId, Long standbyClusterPhyId);
|
||||
|
||||
/**
|
||||
* 获取主备关系
|
||||
*/
|
||||
HaASRelationDO getHA(Long activeClusterPhyId);
|
||||
|
||||
/**
|
||||
* 获取集群主备关系
|
||||
*/
|
||||
Map<Long, Integer> getClusterHARelation();
|
||||
|
||||
/**
|
||||
* 获取主备关系
|
||||
*/
|
||||
Result<List<HaClusterVO>> listAllHA();
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.xiaojukeji.kafka.manager.service.service.ha;
|
||||
|
||||
import com.xiaojukeji.kafka.manager.common.entity.Result;
|
||||
|
||||
|
||||
/**
|
||||
* Topic主备关系管理
|
||||
* 不包括ACL,Gateway等信息
|
||||
*/
|
||||
public interface HaKafkaUserService {
|
||||
|
||||
Result<Void> setNoneHAInKafka(String zookeeper, String kafkaUser);
|
||||
|
||||
/**
|
||||
* 暂停HA
|
||||
*/
|
||||
Result<Void> stopHAInKafka(String zookeeper, String kafkaUser);
|
||||
|
||||
/**
|
||||
* 激活HA
|
||||
*/
|
||||
Result<Void> activeHAInKafka(String zookeeper, Long activeClusterPhyId, String kafkaUser);
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.xiaojukeji.kafka.manager.service.service.ha;
|
||||
|
||||
import com.xiaojukeji.kafka.manager.common.entity.Result;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.ClusterDO;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Topic主备关系管理
|
||||
* 不包括ACL,Gateway等信息
|
||||
*/
|
||||
public interface HaTopicService {
|
||||
/**
|
||||
* 创建主备关系
|
||||
*/
|
||||
Result<Void> createHA(Long activeClusterPhyId, Long standbyClusterPhyId, String topicName, String operator);
|
||||
Result<Void> activeHAInKafkaNotCheck(ClusterDO activeClusterDO, String activeTopicName, ClusterDO standbyClusterDO, String standbyTopicName, String operator);
|
||||
Result<Void> activeHAInKafka(ClusterDO activeClusterDO, String activeTopicName, ClusterDO standbyClusterDO, String standbyTopicName, String operator);
|
||||
|
||||
/**
|
||||
* 删除主备关系
|
||||
*/
|
||||
Result<Void> deleteHA(Long activeClusterPhyId, Long standbyClusterPhyId, String topicName, String operator);
|
||||
Result<Void> stopHAInKafka(ClusterDO standbyClusterDO, String standbyTopicName, String operator);
|
||||
|
||||
/**
|
||||
* 获取集群topic的主备关系
|
||||
*/
|
||||
Map<String, Integer> getRelation(Long clusterId);
|
||||
|
||||
/**
|
||||
* 获取所有集群的备topic名称
|
||||
*/
|
||||
Map<Long, List<String>> getClusterStandbyTopicMap();
|
||||
|
||||
/**
|
||||
* 激活kafkaUserHA
|
||||
*/
|
||||
Result<Void> activeUserHAInKafka(ClusterDO activeClusterDO, ClusterDO standbyClusterDO, String kafkaUser, String operator);
|
||||
|
||||
Result<Long> getStandbyTopicFetchLag(Long standbyClusterPhyId, String topicName);
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
package com.xiaojukeji.kafka.manager.service.service.ha.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.xiaojukeji.kafka.manager.common.bizenum.ha.HaResTypeEnum;
|
||||
import com.xiaojukeji.kafka.manager.common.bizenum.ha.HaStatusEnum;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.Result;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.ResultStatus;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.ha.HaASRelationDO;
|
||||
import com.xiaojukeji.kafka.manager.common.utils.ValidateUtils;
|
||||
import com.xiaojukeji.kafka.manager.dao.ha.HaASRelationDao;
|
||||
import com.xiaojukeji.kafka.manager.service.service.ha.HaASRelationService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
public class HaASRelationServiceImpl implements HaASRelationService {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(HaASRelationServiceImpl.class);
|
||||
|
||||
@Autowired
|
||||
private HaASRelationDao haASRelationDao;
|
||||
|
||||
@Override
|
||||
public Result<Void> replaceTopicRelationsToDB(Long standbyClusterPhyId, List<HaASRelationDO> topicRelationDOList) {
|
||||
try {
|
||||
LambdaQueryWrapper<HaASRelationDO> lambdaQueryWrapper = new LambdaQueryWrapper<>();
|
||||
lambdaQueryWrapper.eq(HaASRelationDO::getResType, HaResTypeEnum.TOPIC.getCode());
|
||||
lambdaQueryWrapper.eq(HaASRelationDO::getStandbyClusterPhyId, standbyClusterPhyId);
|
||||
|
||||
Map<String, HaASRelationDO> dbRelationMap = haASRelationDao.selectList(lambdaQueryWrapper).stream().collect(Collectors.toMap(HaASRelationDO::getUniqueField, Function.identity()));
|
||||
for (HaASRelationDO relationDO: topicRelationDOList) {
|
||||
HaASRelationDO dbRelationDO = dbRelationMap.remove(relationDO.getUniqueField());
|
||||
if (dbRelationDO == null) {
|
||||
// DB中不存在,则插入新的
|
||||
haASRelationDao.insert(relationDO);
|
||||
}
|
||||
}
|
||||
|
||||
// dbRelationMap 中剩余的,是需要进行删除的
|
||||
for (HaASRelationDO dbRelationDO: dbRelationMap.values()) {
|
||||
if (System.currentTimeMillis() - dbRelationDO.getModifyTime().getTime() >= 5 * 1000L) {
|
||||
// 修改时间超过了5分钟了,则进行删除
|
||||
haASRelationDao.deleteById(dbRelationDO.getId());
|
||||
}
|
||||
}
|
||||
|
||||
return Result.buildSuc();
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("method=replaceTopicRelationsToDB||standbyClusterPhyId={}||errMsg=exception.", standbyClusterPhyId, e);
|
||||
|
||||
return Result.buildFromRSAndMsg(ResultStatus.MYSQL_ERROR, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<Void> addHAToDB(HaASRelationDO haASRelationDO) {
|
||||
try{
|
||||
int count = haASRelationDao.insert(haASRelationDO);
|
||||
if (count < 1){
|
||||
LOGGER.error("add ha to db failed! haASRelationDO:{}" , haASRelationDO);
|
||||
return Result.buildFrom(ResultStatus.MYSQL_ERROR);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("add ha to db failed! haASRelationDO:{}" , haASRelationDO);
|
||||
return Result.buildFrom(ResultStatus.MYSQL_ERROR);
|
||||
}
|
||||
return Result.buildSuc();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<Void> deleteById(Long id) {
|
||||
try {
|
||||
haASRelationDao.deleteById(id);
|
||||
} catch (Exception e){
|
||||
LOGGER.error("class=HaASRelationServiceImpl||method=deleteById||id={}||errMsg=exception", id, e);
|
||||
return Result.buildFrom(ResultStatus.MYSQL_ERROR);
|
||||
}
|
||||
return Result.buildSuc();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int updateRelationStatus(Long relationId, Integer newStatus) {
|
||||
return haASRelationDao.updateById(new HaASRelationDO(relationId, newStatus));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int updateById(HaASRelationDO haASRelationDO) {
|
||||
return haASRelationDao.updateById(haASRelationDO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HaASRelationDO getActiveClusterHAFromDB(Long activeClusterPhyId) {
|
||||
LambdaQueryWrapper<HaASRelationDO> lambdaQueryWrapper = new LambdaQueryWrapper<>();
|
||||
lambdaQueryWrapper.eq(HaASRelationDO::getActiveClusterPhyId, activeClusterPhyId);
|
||||
lambdaQueryWrapper.eq(HaASRelationDO::getResType, HaResTypeEnum.CLUSTER.getCode());
|
||||
|
||||
return haASRelationDao.selectOne(lambdaQueryWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HaASRelationDO getSpecifiedHAFromDB(Long activeClusterPhyId, String activeResName,
|
||||
Long standbyClusterPhyId, String standbyResName,
|
||||
HaResTypeEnum resTypeEnum) {
|
||||
LambdaQueryWrapper<HaASRelationDO> lambdaQueryWrapper = new LambdaQueryWrapper<>();
|
||||
HaASRelationDO relationDO = new HaASRelationDO(
|
||||
activeClusterPhyId,
|
||||
activeResName,
|
||||
standbyClusterPhyId,
|
||||
standbyResName,
|
||||
resTypeEnum.getCode(),
|
||||
HaStatusEnum.UNKNOWN.getCode()
|
||||
);
|
||||
lambdaQueryWrapper.eq(HaASRelationDO::getUniqueField, relationDO.getUniqueField());
|
||||
|
||||
return haASRelationDao.selectOne(lambdaQueryWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HaASRelationDO getHAFromDB(Long firstClusterPhyId, String firstResName, HaResTypeEnum resTypeEnum) {
|
||||
List<HaASRelationDO> haASRelationDOS = listAllHAFromDB(firstClusterPhyId, resTypeEnum);
|
||||
for(HaASRelationDO haASRelationDO : haASRelationDOS){
|
||||
if (haASRelationDO.getActiveResName().equals(firstResName)
|
||||
|| haASRelationDO.getActiveResName().equals(firstResName)){
|
||||
return haASRelationDO;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HaASRelationDO> getStandbyHAFromDB(Long standbyClusterPhyId, HaResTypeEnum resTypeEnum) {
|
||||
LambdaQueryWrapper<HaASRelationDO> lambdaQueryWrapper = new LambdaQueryWrapper<>();
|
||||
lambdaQueryWrapper.eq(HaASRelationDO::getResType, resTypeEnum.getCode());
|
||||
lambdaQueryWrapper.eq(HaASRelationDO::getStandbyClusterPhyId, standbyClusterPhyId);
|
||||
|
||||
return haASRelationDao.selectList(lambdaQueryWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HaASRelationDO> getActiveHAFromDB(Long activeClusterPhyId, HaResTypeEnum resTypeEnum) {
|
||||
LambdaQueryWrapper<HaASRelationDO> lambdaQueryWrapper = new LambdaQueryWrapper<>();
|
||||
lambdaQueryWrapper.eq(HaASRelationDO::getResType, resTypeEnum.getCode());
|
||||
lambdaQueryWrapper.eq(HaASRelationDO::getActiveClusterPhyId, activeClusterPhyId);
|
||||
|
||||
return haASRelationDao.selectList(lambdaQueryWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HaASRelationDO> listAllHAFromDB(HaResTypeEnum resTypeEnum) {
|
||||
LambdaQueryWrapper<HaASRelationDO> lambdaQueryWrapper = new LambdaQueryWrapper<>();
|
||||
lambdaQueryWrapper.eq(HaASRelationDO::getResType, resTypeEnum.getCode());
|
||||
|
||||
return haASRelationDao.selectList(lambdaQueryWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HaASRelationDO> listAllHAFromDB(Long firstClusterPhyId, HaResTypeEnum resTypeEnum) {
|
||||
LambdaQueryWrapper<HaASRelationDO> lambdaQueryWrapper = new LambdaQueryWrapper<>();
|
||||
lambdaQueryWrapper.eq(HaASRelationDO::getResType, resTypeEnum.getCode());
|
||||
lambdaQueryWrapper.and(lambda ->
|
||||
lambda.eq(HaASRelationDO::getActiveClusterPhyId, firstClusterPhyId).or().eq(HaASRelationDO::getStandbyClusterPhyId, firstClusterPhyId)
|
||||
);
|
||||
|
||||
// 查询HA列表
|
||||
List<HaASRelationDO> doList = haASRelationDao.selectList(lambdaQueryWrapper);
|
||||
if (ValidateUtils.isNull(doList)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
return doList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HaASRelationDO> listAllHAFromDB(Long firstClusterPhyId, Long secondClusterPhyId, HaResTypeEnum resTypeEnum) {
|
||||
// 查询HA列表
|
||||
List<HaASRelationDO> doList = this.listAllHAFromDB(firstClusterPhyId, resTypeEnum);
|
||||
if (ValidateUtils.isNull(doList)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
if (secondClusterPhyId == null) {
|
||||
// 如果为null,则直接返回全部
|
||||
return doList;
|
||||
}
|
||||
|
||||
// 手动过滤掉不需要的集群
|
||||
return doList.stream()
|
||||
.filter(elem -> elem.getActiveClusterPhyId().equals(secondClusterPhyId) || elem.getStandbyClusterPhyId().equals(secondClusterPhyId))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
package com.xiaojukeji.kafka.manager.service.service.ha.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.xiaojukeji.kafka.manager.common.bizenum.ha.HaResTypeEnum;
|
||||
import com.xiaojukeji.kafka.manager.common.bizenum.ha.job.HaJobStatusEnum;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.Result;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.ResultStatus;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.ao.ha.job.*;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.ha.HaASSwitchJobDO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.ha.HaASSwitchSubJobDO;
|
||||
import com.xiaojukeji.kafka.manager.common.utils.ConvertUtil;
|
||||
import com.xiaojukeji.kafka.manager.common.utils.ValidateUtils;
|
||||
import com.xiaojukeji.kafka.manager.dao.ha.HaASSwitchJobDao;
|
||||
import com.xiaojukeji.kafka.manager.dao.ha.HaASSwitchSubJobDao;
|
||||
import com.xiaojukeji.kafka.manager.service.service.ha.HaASSwitchJobService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.transaction.interceptor.TransactionAspectSupport;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
public class HaASSwitchJobServiceImpl implements HaASSwitchJobService {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(HaASSwitchJobServiceImpl.class);
|
||||
|
||||
@Autowired
|
||||
private HaASSwitchJobDao haASSwitchJobDao;
|
||||
|
||||
@Autowired
|
||||
private HaASSwitchSubJobDao haASSwitchSubJobDao;
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Result<Long> createJob(Long activeClusterPhyId, Long standbyClusterPhyId, List<String> topicNameList, String operator) {
|
||||
try {
|
||||
// 父任务
|
||||
HaASSwitchJobDO jobDO = new HaASSwitchJobDO(activeClusterPhyId, standbyClusterPhyId, HaJobStatusEnum.RUNNING.getStatus(), operator);
|
||||
haASSwitchJobDao.insert(jobDO);
|
||||
|
||||
// 子任务
|
||||
for (String topicName: topicNameList) {
|
||||
haASSwitchSubJobDao.insert(new HaASSwitchSubJobDO(
|
||||
jobDO.getId(),
|
||||
activeClusterPhyId,
|
||||
topicName,
|
||||
standbyClusterPhyId,
|
||||
topicName,
|
||||
HaResTypeEnum.TOPIC.getCode(),
|
||||
HaJobStatusEnum.RUNNING.getStatus(),
|
||||
""
|
||||
));
|
||||
}
|
||||
|
||||
return Result.buildSuc(jobDO.getId());
|
||||
} catch (Exception e) {
|
||||
LOGGER.error(
|
||||
"method=createJob||activeClusterPhyId={}||standbyClusterPhyId={}||topicNameList={}||operator={}||errMsg=exception",
|
||||
activeClusterPhyId, standbyClusterPhyId, ConvertUtil.obj2Json(topicNameList), operator, e
|
||||
);
|
||||
|
||||
// 如果这一步出错了,则对上一步进行手动回滚
|
||||
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
|
||||
|
||||
return Result.buildFromRSAndMsg(ResultStatus.MYSQL_ERROR, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int updateJobStatus(Long jobId, Integer jobStatus) {
|
||||
HaASSwitchJobDO jobDO = new HaASSwitchJobDO();
|
||||
jobDO.setId(jobId);
|
||||
jobDO.setJobStatus(jobStatus);
|
||||
return haASSwitchJobDao.updateById(jobDO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int updateSubJobStatus(Long subJobId, Integer jobStatus) {
|
||||
HaASSwitchSubJobDO subJobDO = new HaASSwitchSubJobDO();
|
||||
subJobDO.setId(subJobId);
|
||||
subJobDO.setJobStatus(jobStatus);
|
||||
return haASSwitchSubJobDao.updateById(subJobDO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int updateSubJobExtendData(Long subJobId, HaSubJobExtendData extendData) {
|
||||
HaASSwitchSubJobDO subJobDO = new HaASSwitchSubJobDO();
|
||||
subJobDO.setId(subJobId);
|
||||
subJobDO.setExtendData(ConvertUtil.obj2Json(extendData));
|
||||
return haASSwitchSubJobDao.updateById(subJobDO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<List<HaJobDetail>> jobDetail(Long jobId) {
|
||||
LambdaQueryWrapper<HaASSwitchSubJobDO> lambdaQueryWrapper = new LambdaQueryWrapper<>();
|
||||
lambdaQueryWrapper.eq(HaASSwitchSubJobDO::getJobId, jobId);
|
||||
|
||||
List<HaASSwitchSubJobDO> doList = haASSwitchSubJobDao.selectList(lambdaQueryWrapper);
|
||||
if (ValidateUtils.isEmptyList(doList)) {
|
||||
return Result.buildFromRSAndMsg(ResultStatus.RESOURCE_NOT_EXIST, String.format("jobId:[%d] 不存在", jobId));
|
||||
}
|
||||
|
||||
List<HaJobDetail> detailList = new ArrayList<>();
|
||||
doList.stream().forEach(elem -> {
|
||||
HaJobDetail detail = new HaJobDetail();
|
||||
detail.setTopicName(elem.getActiveResName());
|
||||
detail.setActiveClusterPhyId(elem.getActiveClusterPhyId());
|
||||
detail.setStandbyClusterPhyId(elem.getStandbyClusterPhyId());
|
||||
detail.setStatus(elem.getJobStatus());
|
||||
|
||||
// Lag信息
|
||||
HaSubJobExtendData extendData = ConvertUtil.str2ObjByJson(elem.getExtendData(), HaSubJobExtendData.class);
|
||||
detail.setSumLag(extendData != null? extendData.getSumLag(): null);
|
||||
|
||||
detailList.add(detail);
|
||||
});
|
||||
|
||||
return Result.buildSuc(detailList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> listRunningJobs(Long ignoreAfterTime) {
|
||||
return new ArrayList<>(new HashSet<>(
|
||||
this.listAfterTimeRunningJobs(ignoreAfterTime).values()
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Long, HaASSwitchJobDO> listClusterLatestJobs() {
|
||||
List<HaASSwitchJobDO> doList = haASSwitchJobDao.listAllLatest();
|
||||
|
||||
Map<Long, HaASSwitchJobDO> doMap = new HashMap<>();
|
||||
for (HaASSwitchJobDO jobDO: doList) {
|
||||
HaASSwitchJobDO inMapJobDO = doMap.get(jobDO.getActiveClusterPhyId());
|
||||
if (inMapJobDO == null || inMapJobDO.getId() <= jobDO.getId()) {
|
||||
doMap.put(jobDO.getActiveClusterPhyId(), jobDO);
|
||||
}
|
||||
|
||||
inMapJobDO = doMap.get(jobDO.getStandbyClusterPhyId());
|
||||
if (inMapJobDO == null || inMapJobDO.getId() <= jobDO.getId()) {
|
||||
doMap.put(jobDO.getStandbyClusterPhyId(), jobDO);
|
||||
}
|
||||
}
|
||||
|
||||
return doMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HaASSwitchJobDO getJobById(Long jobId) {
|
||||
return haASSwitchJobDao.selectById(jobId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HaASSwitchSubJobDO> listSubJobsById(Long jobId) {
|
||||
LambdaQueryWrapper<HaASSwitchSubJobDO> lambdaQueryWrapper = new LambdaQueryWrapper<>();
|
||||
lambdaQueryWrapper.eq(HaASSwitchSubJobDO::getJobId, jobId);
|
||||
return haASSwitchSubJobDao.selectList(lambdaQueryWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HaASSwitchSubJobDO> listAll(Boolean isAsc) {
|
||||
LambdaQueryWrapper<HaASSwitchSubJobDO> lambdaQueryWrapper = new LambdaQueryWrapper<>();
|
||||
lambdaQueryWrapper.orderBy(isAsc != null, isAsc, HaASSwitchSubJobDO::getId);
|
||||
return haASSwitchSubJobDao.selectList(lambdaQueryWrapper);
|
||||
}
|
||||
|
||||
/**************************************************** private method ****************************************************/
|
||||
|
||||
private Map<Long, Long> listAfterTimeRunningJobs(Long ignoreAfterTime) {
|
||||
LambdaQueryWrapper<HaASSwitchJobDO> jobLambdaQueryWrapper = new LambdaQueryWrapper<>();
|
||||
jobLambdaQueryWrapper.eq(HaASSwitchJobDO::getJobStatus, HaJobStatusEnum.RUNNING.getStatus());
|
||||
List<HaASSwitchJobDO> jobDOList = haASSwitchJobDao.selectList(jobLambdaQueryWrapper);
|
||||
if (jobDOList == null) {
|
||||
return new HashMap<>();
|
||||
}
|
||||
|
||||
// 获取指定时间之前的任务
|
||||
jobDOList = jobDOList.stream().filter(job -> job.getCreateTime().getTime() <= ignoreAfterTime).collect(Collectors.toList());
|
||||
|
||||
Map<Long, Long> clusterPhyIdAndJobIdMap = new HashMap<>();
|
||||
jobDOList.forEach(elem -> {
|
||||
clusterPhyIdAndJobIdMap.put(elem.getActiveClusterPhyId(), elem.getId());
|
||||
clusterPhyIdAndJobIdMap.put(elem.getStandbyClusterPhyId(), elem.getId());
|
||||
});
|
||||
return clusterPhyIdAndJobIdMap;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,389 @@
|
||||
package com.xiaojukeji.kafka.manager.service.service.ha.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.xiaojukeji.kafka.manager.common.bizenum.ha.HaRelationTypeEnum;
|
||||
import com.xiaojukeji.kafka.manager.common.bizenum.ha.HaResTypeEnum;
|
||||
import com.xiaojukeji.kafka.manager.common.bizenum.ha.HaStatusEnum;
|
||||
import com.xiaojukeji.kafka.manager.common.bizenum.ha.job.HaJobStatusEnum;
|
||||
import com.xiaojukeji.kafka.manager.common.constant.KafkaConstant;
|
||||
import com.xiaojukeji.kafka.manager.common.constant.MsgConstant;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.Result;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.ResultStatus;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.ao.ClusterDetailDTO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.ClusterDO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.ha.HaASRelationDO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.ha.HaASSwitchJobDO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.vo.ha.HaClusterVO;
|
||||
import com.xiaojukeji.kafka.manager.common.utils.JsonUtils;
|
||||
import com.xiaojukeji.kafka.manager.dao.ha.HaASRelationDao;
|
||||
import com.xiaojukeji.kafka.manager.service.cache.PhysicalClusterMetadataManager;
|
||||
import com.xiaojukeji.kafka.manager.service.service.ClusterService;
|
||||
import com.xiaojukeji.kafka.manager.service.service.ZookeeperService;
|
||||
import com.xiaojukeji.kafka.manager.service.service.ha.HaASRelationService;
|
||||
import com.xiaojukeji.kafka.manager.service.service.ha.HaASSwitchJobService;
|
||||
import com.xiaojukeji.kafka.manager.service.service.ha.HaClusterService;
|
||||
import com.xiaojukeji.kafka.manager.service.service.ha.HaTopicService;
|
||||
import com.xiaojukeji.kafka.manager.service.utils.ConfigUtils;
|
||||
import com.xiaojukeji.kafka.manager.service.utils.HaClusterCommands;
|
||||
import com.xiaojukeji.kafka.manager.service.utils.HaTopicCommands;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 集群主备关系
|
||||
*/
|
||||
@Service
|
||||
public class HaClusterServiceImpl implements HaClusterService {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(HaClusterServiceImpl.class);
|
||||
|
||||
@Autowired
|
||||
private ClusterService clusterService;
|
||||
|
||||
@Autowired
|
||||
private HaASRelationService haASRelationService;
|
||||
|
||||
@Autowired
|
||||
private HaASRelationDao haActiveStandbyRelationDao;
|
||||
|
||||
@Autowired
|
||||
private HaTopicService haTopicService;
|
||||
|
||||
@Autowired
|
||||
private PhysicalClusterMetadataManager physicalClusterMetadataManager;
|
||||
|
||||
@Autowired
|
||||
private HaASSwitchJobService haASSwitchJobService;
|
||||
|
||||
@Autowired
|
||||
private ConfigUtils configUtils;
|
||||
|
||||
@Autowired
|
||||
private ZookeeperService zookeeperService;
|
||||
|
||||
@Override
|
||||
public Result<Void> createHA(Long activeClusterPhyId, Long standbyClusterPhyId, String operator) {
|
||||
ClusterDO activeClusterDO = clusterService.getById(activeClusterPhyId);
|
||||
if (activeClusterDO == null){
|
||||
return Result.buildFrom(ResultStatus.CLUSTER_NOT_EXIST);
|
||||
}
|
||||
|
||||
ClusterDO standbyClusterDO = clusterService.getById(standbyClusterPhyId);
|
||||
if (standbyClusterDO == null){
|
||||
return Result.buildFrom(ResultStatus.CLUSTER_NOT_EXIST);
|
||||
}
|
||||
|
||||
HaASRelationDO oldRelationDO = getHA(activeClusterPhyId);
|
||||
if (oldRelationDO != null){
|
||||
return Result.buildFromRSAndMsg(ResultStatus.RESOURCE_ALREADY_USED,
|
||||
MsgConstant.getActiveClusterDuplicate(activeClusterDO.getId(), activeClusterDO.getClusterName()));
|
||||
|
||||
}
|
||||
|
||||
//更新集群配置
|
||||
Result<Void> rv = this.modifyHaClusterConfig(activeClusterDO, standbyClusterDO, operator);
|
||||
if (rv.failed()){
|
||||
return rv;
|
||||
}
|
||||
|
||||
//更新__consumer_offsets配置
|
||||
rv = this.modifyHaTopicConfig(activeClusterDO, standbyClusterDO, operator);
|
||||
if (rv.failed()){
|
||||
return rv;
|
||||
}
|
||||
|
||||
//添加db数据
|
||||
return haASRelationService.addHAToDB(
|
||||
new HaASRelationDO(
|
||||
activeClusterPhyId,
|
||||
activeClusterPhyId.toString(),
|
||||
standbyClusterPhyId,
|
||||
standbyClusterPhyId.toString(),
|
||||
HaResTypeEnum.CLUSTER.getCode(),
|
||||
HaStatusEnum.STABLE.getCode()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<Void> createHAInKafka(String zookeeper, ClusterDO needWriteToZKClusterDO, String operator) {
|
||||
Properties props = new Properties();
|
||||
props.putAll(getSecurityProperties(needWriteToZKClusterDO.getSecurityProperties()));
|
||||
props.put(KafkaConstant.BOOTSTRAP_SERVERS, needWriteToZKClusterDO.getBootstrapServers());
|
||||
props.put(KafkaConstant.DIDI_KAFKA_ENABLE, "false");
|
||||
|
||||
Result<List<Integer>> rli = zookeeperService.getBrokerIds(needWriteToZKClusterDO.getZookeeper());
|
||||
if (rli.failed()){
|
||||
return Result.buildFromIgnoreData(rli);
|
||||
}
|
||||
|
||||
String kafkaVersion = physicalClusterMetadataManager.getKafkaVersion(needWriteToZKClusterDO.getId(), rli.getData());
|
||||
if (kafkaVersion != null && kafkaVersion.contains("-d-")){
|
||||
int dVersion = Integer.valueOf(kafkaVersion.split("-")[2]);
|
||||
if (dVersion > 200){
|
||||
props.put(KafkaConstant.DIDI_KAFKA_ENABLE, "true");
|
||||
}
|
||||
}
|
||||
|
||||
ResultStatus rs = HaClusterCommands.modifyHaClusterConfig(zookeeper, needWriteToZKClusterDO.getId(), props);
|
||||
if (!ResultStatus.SUCCESS.equals(rs)) {
|
||||
LOGGER.error("class=HaClusterServiceImpl||method=createHAInKafka||zookeeper={}||firstClusterDO={}||operator={}||msg=add ha-cluster config failed!", zookeeper, needWriteToZKClusterDO, operator);
|
||||
return Result.buildFailure("add ha-cluster config failed");
|
||||
}
|
||||
|
||||
return Result.buildFrom(rs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<Void> switchHA(Long newActiveClusterPhyId, Long newStandbyClusterPhyId) {
|
||||
return Result.buildSuc();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<Void> deleteHA(Long activeClusterPhyId, Long standbyClusterPhyId) {
|
||||
ClusterDO clusterDO = clusterService.getById(activeClusterPhyId);
|
||||
if (clusterDO == null){
|
||||
return Result.buildFrom(ResultStatus.CLUSTER_NOT_EXIST);
|
||||
}
|
||||
ClusterDO standbyClusterDO = clusterService.getById(standbyClusterPhyId);
|
||||
if (standbyClusterDO == null){
|
||||
return Result.buildFrom(ResultStatus.CLUSTER_NOT_EXIST);
|
||||
}
|
||||
|
||||
HaASRelationDO relationDO = getHA(activeClusterPhyId);
|
||||
if (relationDO == null){
|
||||
return Result.buildSuc();
|
||||
}
|
||||
|
||||
//删除配置
|
||||
Result delResult = delClusterHaConfig(clusterDO, standbyClusterDO);
|
||||
if (delResult.failed()){
|
||||
return delResult;
|
||||
}
|
||||
|
||||
//删除db
|
||||
Result delDbResult = delDBHaCluster(activeClusterPhyId, standbyClusterPhyId);
|
||||
if (delDbResult.failed()){
|
||||
return delDbResult;
|
||||
}
|
||||
|
||||
return Result.buildSuc();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HaASRelationDO getHA(Long activeClusterPhyId) {
|
||||
return haASRelationService.getActiveClusterHAFromDB(activeClusterPhyId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Long, Integer> getClusterHARelation() {
|
||||
Map<Long, Integer> relationMap = new HashMap<>();
|
||||
List<HaASRelationDO> haASRelationDOS = haASRelationService.listAllHAFromDB(HaResTypeEnum.CLUSTER);
|
||||
if (haASRelationDOS.isEmpty()){
|
||||
return relationMap;
|
||||
}
|
||||
haASRelationDOS.forEach(haASRelationDO -> {
|
||||
relationMap.put(haASRelationDO.getActiveClusterPhyId(), HaRelationTypeEnum.ACTIVE.getCode());
|
||||
relationMap.put(haASRelationDO.getStandbyClusterPhyId(), HaRelationTypeEnum.STANDBY.getCode());
|
||||
});
|
||||
return relationMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<List<HaClusterVO>> listAllHA() {
|
||||
//高可用集群
|
||||
List<HaASRelationDO> clusterRelationDOS = haASRelationService.listAllHAFromDB(HaResTypeEnum.CLUSTER);
|
||||
Map<Long, HaASRelationDO> activeMap = clusterRelationDOS.stream().collect(Collectors.toMap(HaASRelationDO::getActiveClusterPhyId, Function.identity()));
|
||||
List<Long> standbyList = clusterRelationDOS.stream().map(HaASRelationDO::getStandbyClusterPhyId).collect(Collectors.toList());
|
||||
|
||||
//高可用topic
|
||||
List<HaASRelationDO> topicRelationDOS = haASRelationService.listAllHAFromDB(HaResTypeEnum.TOPIC);
|
||||
//主集群topic数
|
||||
Map<Long,Long> activeTopicCountMap = topicRelationDOS.stream()
|
||||
.filter(haASRelationDO -> !haASRelationDO.getActiveResName().startsWith("__"))
|
||||
.collect(Collectors.groupingBy(HaASRelationDO::getActiveClusterPhyId, Collectors.counting()));
|
||||
Map<Long,Long> standbyTopicCountMap = topicRelationDOS.stream()
|
||||
.filter(haASRelationDO -> !haASRelationDO.getStandbyResName().startsWith("__"))
|
||||
.collect(Collectors.groupingBy(HaASRelationDO::getStandbyClusterPhyId, Collectors.counting()));
|
||||
|
||||
//切换job
|
||||
Map<Long/*集群ID*/, HaASSwitchJobDO> jobDOS = haASSwitchJobService.listClusterLatestJobs();
|
||||
|
||||
List<HaClusterVO> haClusterVOS = new ArrayList<>();
|
||||
Map<Long,ClusterDetailDTO> clusterDetailDTOMap = clusterService.getClusterDetailDTOList(Boolean.TRUE).stream().collect(Collectors.toMap(ClusterDetailDTO::getClusterId, Function.identity()));
|
||||
for (Map.Entry<Long,ClusterDetailDTO> entry : clusterDetailDTOMap.entrySet()){
|
||||
ClusterDetailDTO clusterDetailDTO = entry.getValue();
|
||||
//高可用集群
|
||||
if (activeMap.containsKey(entry.getKey())){
|
||||
//主集群
|
||||
HaASRelationDO relationDO = activeMap.get(clusterDetailDTO.getClusterId());
|
||||
HaClusterVO haClusterVO = new HaClusterVO();
|
||||
BeanUtils.copyProperties(clusterDetailDTO,haClusterVO);
|
||||
haClusterVO.setHaStatus(relationDO.getStatus());
|
||||
haClusterVO.setActiveTopicCount(activeTopicCountMap.get(clusterDetailDTO.getClusterId())==null
|
||||
?0L:activeTopicCountMap.get(clusterDetailDTO.getClusterId()));
|
||||
haClusterVO.setStandbyTopicCount(standbyTopicCountMap.get(clusterDetailDTO.getClusterId())==null
|
||||
?0L:standbyTopicCountMap.get(clusterDetailDTO.getClusterId()));
|
||||
HaASSwitchJobDO jobDO = jobDOS.get(haClusterVO.getClusterId());
|
||||
haClusterVO.setHaStatus(jobDO != null && HaJobStatusEnum.isRunning(jobDO.getJobStatus())
|
||||
?HaStatusEnum.SWITCHING_CODE: HaStatusEnum.STABLE_CODE);
|
||||
ClusterDetailDTO standbyClusterDetail = clusterDetailDTOMap.get(relationDO.getStandbyClusterPhyId());
|
||||
if (standbyClusterDetail != null){
|
||||
//备集群
|
||||
HaClusterVO standbyCluster = new HaClusterVO();
|
||||
BeanUtils.copyProperties(standbyClusterDetail,standbyCluster);
|
||||
standbyCluster.setActiveTopicCount(activeTopicCountMap.get(standbyClusterDetail.getClusterId())==null
|
||||
?0L:activeTopicCountMap.get(standbyClusterDetail.getClusterId()));
|
||||
standbyCluster.setStandbyTopicCount(standbyTopicCountMap.get(standbyClusterDetail.getClusterId())==null
|
||||
?0L:standbyTopicCountMap.get(standbyClusterDetail.getClusterId()));
|
||||
|
||||
standbyCluster.setHaASSwitchJobId(jobDO != null ? jobDO.getId() : null);
|
||||
standbyCluster.setHaStatus(haClusterVO.getHaStatus());
|
||||
haClusterVO.setHaClusterVO(standbyCluster);
|
||||
}
|
||||
haClusterVOS.add(haClusterVO);
|
||||
}else if(!standbyList.contains(clusterDetailDTO.getClusterId())){
|
||||
//普通集群
|
||||
HaClusterVO haClusterVO = new HaClusterVO();
|
||||
BeanUtils.copyProperties(clusterDetailDTO,haClusterVO);
|
||||
haClusterVOS.add(haClusterVO);
|
||||
}
|
||||
}
|
||||
return Result.buildSuc(haClusterVOS);
|
||||
}
|
||||
|
||||
private Result<Void> modifyHaClusterConfig(ClusterDO activeClusterDO, ClusterDO standbyClusterDO, String operator){
|
||||
//更新A集群配置信息
|
||||
Result<Void> activeResult = createHAInKafka(activeClusterDO.getZookeeper(), standbyClusterDO, operator);
|
||||
if (activeResult.failed()){
|
||||
return activeResult;
|
||||
}
|
||||
|
||||
//更新gateway上A集群的配置
|
||||
Result<Void> activeGatewayResult = this.createHAInKafka(configUtils.getDKafkaGatewayZK(), activeClusterDO, operator);
|
||||
if (activeGatewayResult.failed()){
|
||||
return activeGatewayResult;
|
||||
}
|
||||
|
||||
//更新B集群配置信息
|
||||
Result<Void> standbyResult = this.createHAInKafka(standbyClusterDO.getZookeeper(), activeClusterDO, operator);
|
||||
if (standbyResult.failed()){
|
||||
return standbyResult;
|
||||
}
|
||||
//更新gateway上B集群的配置
|
||||
Result<Void> standbyGatewayResult = this.createHAInKafka(configUtils.getDKafkaGatewayZK(), standbyClusterDO, operator);
|
||||
if (standbyGatewayResult.failed()){
|
||||
return activeGatewayResult;
|
||||
}
|
||||
|
||||
return Result.buildSuc();
|
||||
}
|
||||
|
||||
private Result<Void> modifyHaTopicConfig(ClusterDO activeClusterDO, ClusterDO standbyClusterDO, String operator){
|
||||
//添加B集群拉取A集群offsets的配置信息
|
||||
Result aResult = haTopicService.activeHAInKafkaNotCheck(activeClusterDO, KafkaConstant.COORDINATOR_TOPIC_NAME,
|
||||
standbyClusterDO, KafkaConstant.COORDINATOR_TOPIC_NAME, operator);
|
||||
if (aResult.failed()){
|
||||
return aResult;
|
||||
}
|
||||
|
||||
//添加A集群拉取B集群offsets的配置信息
|
||||
return haTopicService.activeHAInKafkaNotCheck(standbyClusterDO, KafkaConstant.COORDINATOR_TOPIC_NAME,
|
||||
activeClusterDO, KafkaConstant.COORDINATOR_TOPIC_NAME, operator);
|
||||
|
||||
}
|
||||
|
||||
private Result<Void> delClusterHaConfig(ClusterDO clusterDO, ClusterDO standbyClusterDO){
|
||||
//删除A集群同步B集群Offset配置
|
||||
ResultStatus resultStatus = HaTopicCommands.deleteHaTopicConfig(
|
||||
clusterDO,
|
||||
KafkaConstant.COORDINATOR_TOPIC_NAME,
|
||||
Arrays.asList(KafkaConstant.DIDI_HA_REMOTE_CLUSTER, KafkaConstant.DIDI_HA_SYNC_TOPIC_CONFIGS_ENABLED)
|
||||
);
|
||||
if (resultStatus.getCode() != 0){
|
||||
LOGGER.error("delete active cluster config failed! clusterId:{} standbyClusterId:{}" , clusterDO.getId(), standbyClusterDO.getId());
|
||||
return Result.buildFailure("删除主集群__consumer_offsets高可用配置失败,请重试!");
|
||||
}
|
||||
|
||||
//删除A集群配置信息
|
||||
resultStatus = HaClusterCommands.coverHaClusterConfig(clusterDO.getZookeeper(), standbyClusterDO.getId(), new Properties());
|
||||
if (resultStatus.getCode() != 0){
|
||||
LOGGER.error("delete cluster config failed! clusterId:{} standbyClusterId:{}" , clusterDO.getId(), standbyClusterDO.getId());
|
||||
return Result.buildFailure("删除主集群高可用配置失败,请重试!");
|
||||
}
|
||||
|
||||
//删除B集群同步A集群Offset配置
|
||||
resultStatus = HaTopicCommands.deleteHaTopicConfig(
|
||||
standbyClusterDO,
|
||||
KafkaConstant.COORDINATOR_TOPIC_NAME,
|
||||
Arrays.asList(KafkaConstant.DIDI_HA_REMOTE_CLUSTER, KafkaConstant.DIDI_HA_SYNC_TOPIC_CONFIGS_ENABLED)
|
||||
);
|
||||
if (resultStatus.getCode() != 0){
|
||||
LOGGER.error("delete standby cluster config failed! clusterId:{} standbyClusterId:{}" , clusterDO.getId(), standbyClusterDO.getId());
|
||||
}
|
||||
|
||||
//删除B集群配置信息
|
||||
resultStatus = HaClusterCommands.coverHaClusterConfig(standbyClusterDO.getZookeeper(), standbyClusterDO.getId(), new Properties());
|
||||
if (resultStatus.getCode() != 0){
|
||||
LOGGER.error("delete standby cluster config failed! clusterId:{} standbyClusterId:{}" , clusterDO.getId(), standbyClusterDO.getId());
|
||||
}
|
||||
|
||||
//更新gateway中备集群配置信息
|
||||
resultStatus = HaClusterCommands.coverHaClusterConfig(configUtils.getDKafkaGatewayZK(), standbyClusterDO.getId(), new Properties());
|
||||
if (resultStatus.getCode() != 0){
|
||||
LOGGER.error("delete spare gateway config failed! clusterId:{} standbyClusterId:{}" , clusterDO.getId(), standbyClusterDO.getId());
|
||||
}
|
||||
|
||||
//删除gateway中A集群配置信息
|
||||
resultStatus = HaClusterCommands.coverHaClusterConfig(configUtils.getDKafkaGatewayZK(), clusterDO.getId(), new Properties());
|
||||
if (resultStatus.getCode() != 0){
|
||||
LOGGER.error("delete host gateway config failed! clusterId:{} standbyClusterId:{}" , clusterDO.getId(), standbyClusterDO.getId());
|
||||
}
|
||||
|
||||
return Result.buildSuc();
|
||||
}
|
||||
|
||||
private Result delDBHaCluster(Long activeClusterPhyId, Long standbyClusterPhyId){
|
||||
LambdaQueryWrapper<HaASRelationDO> topicQueryWrapper = new LambdaQueryWrapper();
|
||||
topicQueryWrapper.eq(HaASRelationDO::getResType, HaResTypeEnum.TOPIC.getCode());
|
||||
topicQueryWrapper.eq(HaASRelationDO::getActiveClusterPhyId, activeClusterPhyId);
|
||||
List<HaASRelationDO> relationDOS = haActiveStandbyRelationDao.selectList(topicQueryWrapper);
|
||||
if (!relationDOS.isEmpty()){
|
||||
return Result.buildFrom(ResultStatus.HA_CLUSTER_DELETE_FORBIDDEN);
|
||||
}
|
||||
|
||||
try {
|
||||
LambdaQueryWrapper<HaASRelationDO> queryWrapper = new LambdaQueryWrapper();
|
||||
queryWrapper.eq(HaASRelationDO::getActiveClusterPhyId, activeClusterPhyId);
|
||||
queryWrapper.eq(HaASRelationDO::getResType, HaResTypeEnum.CLUSTER.getCode());
|
||||
|
||||
int count = haActiveStandbyRelationDao.delete(queryWrapper);
|
||||
if (count < 1){
|
||||
LOGGER.error("delete HA failed! clusterId:{} standbyClusterId:{}" , activeClusterPhyId, standbyClusterPhyId);
|
||||
return Result.buildFrom(ResultStatus.MYSQL_ERROR);
|
||||
}
|
||||
}catch (Exception e){
|
||||
LOGGER.error("delete HA failed! clusterId:{} standbyClusterId:{}" , activeClusterPhyId, standbyClusterPhyId);
|
||||
return Result.buildFrom(ResultStatus.MYSQL_ERROR);
|
||||
}
|
||||
return Result.buildSuc();
|
||||
}
|
||||
|
||||
private Properties getSecurityProperties(String securityPropertiesStr){
|
||||
Properties securityProperties = new Properties();
|
||||
if (StringUtils.isBlank(securityPropertiesStr)){
|
||||
return securityProperties;
|
||||
}
|
||||
securityProperties.putAll(JsonUtils.stringToObj(securityPropertiesStr, Properties.class));
|
||||
securityProperties.put(KafkaConstant.SASL_JAAS_CONFIG, securityProperties.getProperty(KafkaConstant.SASL_JAAS_CONFIG)==null
|
||||
?"":securityProperties.getProperty(KafkaConstant.SASL_JAAS_CONFIG).replaceAll("\"","\\\\\""));
|
||||
return securityProperties;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.xiaojukeji.kafka.manager.service.service.ha.impl;
|
||||
|
||||
import com.xiaojukeji.kafka.manager.common.constant.KafkaConstant;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.Result;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.ResultStatus;
|
||||
import com.xiaojukeji.kafka.manager.service.service.ha.HaKafkaUserService;
|
||||
import com.xiaojukeji.kafka.manager.service.utils.HaKafkaUserCommands;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Properties;
|
||||
|
||||
@Service
|
||||
public class HaKafkaUserServiceImpl implements HaKafkaUserService {
|
||||
|
||||
@Override
|
||||
public Result<Void> setNoneHAInKafka(String zookeeper, String kafkaUser) {
|
||||
Properties props = new Properties();
|
||||
props.put(KafkaConstant.DIDI_HA_ACTIVE_CLUSTER, KafkaConstant.NONE);
|
||||
|
||||
return HaKafkaUserCommands.modifyHaUserConfig(zookeeper, kafkaUser, props)?
|
||||
Result.buildSuc(): // 修改成功
|
||||
Result.buildFrom(ResultStatus.ZOOKEEPER_OPERATE_FAILED); // 修改失败
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<Void> stopHAInKafka(String zookeeper, String kafkaUser) {
|
||||
return HaKafkaUserCommands.deleteHaUserConfig(zookeeper, kafkaUser, Arrays.asList(KafkaConstant.DIDI_HA_ACTIVE_CLUSTER))?
|
||||
Result.buildSuc(): // 修改成功
|
||||
Result.buildFrom(ResultStatus.ZOOKEEPER_OPERATE_FAILED); // 修改失败
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<Void> activeHAInKafka(String zookeeper, Long activeClusterPhyId, String kafkaUser) {
|
||||
Properties props = new Properties();
|
||||
props.put(KafkaConstant.DIDI_HA_ACTIVE_CLUSTER, String.valueOf(activeClusterPhyId));
|
||||
|
||||
return HaKafkaUserCommands.modifyHaUserConfig(zookeeper, kafkaUser, props)?
|
||||
Result.buildSuc(): // 修改成功
|
||||
Result.buildFrom(ResultStatus.ZOOKEEPER_OPERATE_FAILED); // 修改失败
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,469 @@
|
||||
package com.xiaojukeji.kafka.manager.service.service.ha.impl;
|
||||
|
||||
import com.xiaojukeji.kafka.manager.common.bizenum.ha.HaRelationTypeEnum;
|
||||
import com.xiaojukeji.kafka.manager.common.bizenum.ha.HaResTypeEnum;
|
||||
import com.xiaojukeji.kafka.manager.common.bizenum.ha.HaStatusEnum;
|
||||
import com.xiaojukeji.kafka.manager.common.constant.Constant;
|
||||
import com.xiaojukeji.kafka.manager.common.constant.KafkaConstant;
|
||||
import com.xiaojukeji.kafka.manager.common.constant.MsgConstant;
|
||||
import com.xiaojukeji.kafka.manager.common.constant.TopicCreationConstant;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.Result;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.ResultStatus;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.ao.gateway.TopicQuota;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.ClusterDO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.TopicDO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.gateway.AuthorityDO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.ha.HaASRelationDO;
|
||||
import com.xiaojukeji.kafka.manager.common.utils.ValidateUtils;
|
||||
import com.xiaojukeji.kafka.manager.common.utils.jmx.JmxAttributeEnum;
|
||||
import com.xiaojukeji.kafka.manager.common.utils.jmx.JmxConnectorWrap;
|
||||
import com.xiaojukeji.kafka.manager.common.zookeeper.znode.brokers.PartitionState;
|
||||
import com.xiaojukeji.kafka.manager.common.zookeeper.znode.brokers.TopicMetadata;
|
||||
import com.xiaojukeji.kafka.manager.service.cache.PhysicalClusterMetadataManager;
|
||||
import com.xiaojukeji.kafka.manager.service.service.AdminService;
|
||||
import com.xiaojukeji.kafka.manager.service.service.ClusterService;
|
||||
import com.xiaojukeji.kafka.manager.service.service.TopicManagerService;
|
||||
import com.xiaojukeji.kafka.manager.service.service.gateway.AuthorityService;
|
||||
import com.xiaojukeji.kafka.manager.service.service.gateway.QuotaService;
|
||||
import com.xiaojukeji.kafka.manager.service.service.ha.HaASRelationService;
|
||||
import com.xiaojukeji.kafka.manager.service.service.ha.HaKafkaUserService;
|
||||
import com.xiaojukeji.kafka.manager.service.service.ha.HaTopicService;
|
||||
import com.xiaojukeji.kafka.manager.service.utils.ConfigUtils;
|
||||
import com.xiaojukeji.kafka.manager.service.utils.HaTopicCommands;
|
||||
import com.xiaojukeji.kafka.manager.service.utils.KafkaZookeeperUtils;
|
||||
import com.xiaojukeji.kafka.manager.service.utils.TopicCommands;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.transaction.interceptor.TransactionAspectSupport;
|
||||
|
||||
import javax.management.Attribute;
|
||||
import javax.management.ObjectName;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
public class HaTopicServiceImpl implements HaTopicService {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(HaTopicServiceImpl.class);
|
||||
|
||||
@Autowired
|
||||
private ClusterService clusterService;
|
||||
|
||||
@Autowired
|
||||
private QuotaService quotaService;
|
||||
|
||||
@Autowired
|
||||
private AdminService adminService;
|
||||
|
||||
@Autowired
|
||||
private HaASRelationService haASRelationService;
|
||||
|
||||
@Autowired
|
||||
private AuthorityService authorityService;
|
||||
|
||||
@Autowired
|
||||
private HaKafkaUserService haKafkaUserService;
|
||||
|
||||
@Autowired
|
||||
private ConfigUtils configUtils;
|
||||
|
||||
@Autowired
|
||||
private TopicManagerService topicManagerService;
|
||||
|
||||
@Override
|
||||
public Result<Void> createHA(Long activeClusterPhyId, Long standbyClusterPhyId, String topicName, String operator) {
|
||||
ClusterDO activeClusterDO = PhysicalClusterMetadataManager.getClusterFromCache(activeClusterPhyId);
|
||||
if (activeClusterDO == null) {
|
||||
return Result.buildFromRSAndMsg(ResultStatus.CLUSTER_NOT_EXIST, "主集群不存在");
|
||||
}
|
||||
|
||||
ClusterDO standbyClusterDO = PhysicalClusterMetadataManager.getClusterFromCache(standbyClusterPhyId);
|
||||
if (standbyClusterDO == null) {
|
||||
return Result.buildFromRSAndMsg(ResultStatus.CLUSTER_NOT_EXIST, "备集群不存在");
|
||||
}
|
||||
|
||||
// 查询关系是否已经存在
|
||||
HaASRelationDO relationDO = haASRelationService.getSpecifiedHAFromDB(
|
||||
activeClusterPhyId,
|
||||
topicName,
|
||||
standbyClusterPhyId,
|
||||
topicName,
|
||||
HaResTypeEnum.TOPIC
|
||||
);
|
||||
if (relationDO != null) {
|
||||
// 如果已存在该高可用Topic,则直接返回成功
|
||||
return Result.buildSuc();
|
||||
}
|
||||
|
||||
Result<TopicDO> checkResult = this.checkHaTopicAndGetBizInfo(activeClusterPhyId, standbyClusterPhyId, topicName);
|
||||
if (checkResult.failed()){
|
||||
return Result.buildFromIgnoreData(checkResult);
|
||||
}
|
||||
|
||||
//更新高可用Topic配置
|
||||
Result<Void> rv = this.modifyHaConfig(
|
||||
activeClusterDO,
|
||||
topicName,
|
||||
standbyClusterDO,
|
||||
topicName,
|
||||
operator
|
||||
);
|
||||
if (rv.failed()){
|
||||
return rv;
|
||||
}
|
||||
|
||||
// 新增备Topic
|
||||
rv = this.addStandbyTopic(checkResult.getData(), activeClusterDO, standbyClusterDO, operator);
|
||||
if (rv.failed()) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// 备topic添加权限以及quota
|
||||
rv = this.addStandbyTopicAuthorityAndQuota(activeClusterPhyId, standbyClusterPhyId, topicName);
|
||||
if (rv.failed()){
|
||||
return rv;
|
||||
}
|
||||
|
||||
//添加db业务信息
|
||||
return haASRelationService.addHAToDB(
|
||||
new HaASRelationDO(
|
||||
activeClusterPhyId,
|
||||
topicName,
|
||||
standbyClusterPhyId,
|
||||
topicName,
|
||||
HaResTypeEnum.TOPIC.getCode(),
|
||||
HaStatusEnum.STABLE.getCode()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private Result<Void> addStandbyTopic(TopicDO activeTopicDO, ClusterDO activeClusterDO, ClusterDO standbyClusterDO, String operator){
|
||||
// 获取主Topic配置信息
|
||||
Properties activeTopicProps = TopicCommands.fetchTopicConfig(activeClusterDO, activeTopicDO.getTopicName());
|
||||
if (activeTopicProps == null){
|
||||
return Result.buildFromRSAndMsg(ResultStatus.FAIL, "创建备Topic时,获取主Topic配置失败");
|
||||
}
|
||||
|
||||
TopicDO newTopicDO = new TopicDO(
|
||||
activeTopicDO.getAppId(),
|
||||
standbyClusterDO.getId(),
|
||||
activeTopicDO.getTopicName(),
|
||||
activeTopicDO.getDescription(),
|
||||
TopicCreationConstant.DEFAULT_QUOTA
|
||||
);
|
||||
TopicMetadata topicMetadata = PhysicalClusterMetadataManager.getTopicMetadata(activeClusterDO.getId(), activeTopicDO.getTopicName());
|
||||
|
||||
ResultStatus rs = adminService.createTopic(standbyClusterDO,
|
||||
newTopicDO,
|
||||
topicMetadata.getPartitionNum(),
|
||||
topicMetadata.getReplicaNum(),
|
||||
null,
|
||||
PhysicalClusterMetadataManager.getBrokerIdList(standbyClusterDO.getId()),
|
||||
activeTopicProps,
|
||||
operator,
|
||||
operator
|
||||
);
|
||||
|
||||
if (ResultStatus.SUCCESS.equals(rs)) {
|
||||
LOGGER.error(
|
||||
"method=createHA||activeClusterPhyId={}||standbyClusterPhyId={}||activeTopicDO={}||result={}||msg=create haTopic create topic failed.",
|
||||
activeClusterDO.getId(), standbyClusterDO.getId(), activeTopicDO, rs
|
||||
);
|
||||
return Result.buildFromRSAndMsg(rs, String.format("创建备Topic失败,原因:%s", rs.getMessage()));
|
||||
}
|
||||
|
||||
return Result.buildSuc();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<Void> activeHAInKafka(ClusterDO activeClusterDO, String activeTopicName, ClusterDO standbyClusterDO, String standbyTopicName, String operator) {
|
||||
if (!PhysicalClusterMetadataManager.isTopicExist(activeClusterDO.getId(), activeTopicName)) {
|
||||
// 主Topic不存在
|
||||
return Result.buildFrom(ResultStatus.TOPIC_NOT_EXIST);
|
||||
}
|
||||
if (!PhysicalClusterMetadataManager.isTopicExist(standbyClusterDO.getId(), standbyTopicName)) {
|
||||
// 备Topic不存在
|
||||
return Result.buildFrom(ResultStatus.TOPIC_NOT_EXIST);
|
||||
}
|
||||
|
||||
return this.activeTopicHAConfigInKafka(activeClusterDO, activeTopicName, standbyClusterDO, standbyTopicName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<Void> activeHAInKafkaNotCheck(ClusterDO activeClusterDO, String activeTopicName, ClusterDO standbyClusterDO, String standbyTopicName, String operator) {
|
||||
//更新开启topic高可用配置,并将备集群的配置信息指向主集群
|
||||
Result<Void> rv = activeTopicHAConfigInKafka(activeClusterDO, activeTopicName, standbyClusterDO, standbyTopicName);
|
||||
if (rv.failed()){
|
||||
return rv;
|
||||
}
|
||||
return Result.buildSuc();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Result<Void> deleteHA(Long activeClusterPhyId, Long standbyClusterPhyId, String topicName, String operator) {
|
||||
ClusterDO activeClusterDO = clusterService.getById(activeClusterPhyId);
|
||||
if (activeClusterDO == null){
|
||||
return Result.buildFromRSAndMsg(ResultStatus.CLUSTER_NOT_EXIST, "主集群不存在");
|
||||
}
|
||||
|
||||
ClusterDO standbyClusterDO = clusterService.getById(standbyClusterPhyId);
|
||||
if (standbyClusterDO == null){
|
||||
return Result.buildFromRSAndMsg(ResultStatus.CLUSTER_NOT_EXIST, "备集群不存在");
|
||||
}
|
||||
|
||||
HaASRelationDO relationDO = haASRelationService.getHAFromDB(
|
||||
activeClusterPhyId,
|
||||
topicName,
|
||||
HaResTypeEnum.TOPIC
|
||||
);
|
||||
if (relationDO == null) {
|
||||
return Result.buildFromRSAndMsg(ResultStatus.RESOURCE_NOT_EXIST, "主备关系不存在");
|
||||
}
|
||||
if (!relationDO.getStatus().equals(HaStatusEnum.STABLE_CODE)) {
|
||||
return Result.buildFromRSAndMsg(ResultStatus.OPERATION_FORBIDDEN, "主备切换中,不允许解绑");
|
||||
}
|
||||
|
||||
// 删除高可用配置信息
|
||||
Result<Void> rv = this.stopHAInKafka(standbyClusterDO, topicName, operator);
|
||||
if(rv.failed()){
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = haASRelationService.deleteById(relationDO.getId());
|
||||
if(rv.failed()){
|
||||
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
|
||||
return rv;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<Void> stopHAInKafka(ClusterDO standbyClusterDO, String standbyTopicName, String operator) {
|
||||
//删除副集群同步主集群topic配置
|
||||
ResultStatus rs = HaTopicCommands.deleteHaTopicConfig(
|
||||
standbyClusterDO,
|
||||
standbyTopicName,
|
||||
Arrays.asList(KafkaConstant.DIDI_HA_SYNC_TOPIC_CONFIGS_ENABLED, KafkaConstant.DIDI_HA_REMOTE_CLUSTER)
|
||||
);
|
||||
if (!ResultStatus.SUCCESS.equals(rs)) {
|
||||
LOGGER.error(
|
||||
"method=deleteHAInKafka||standbyClusterId={}||standbyTopicName={}||rs={}||msg=delete topic ha failed.",
|
||||
standbyClusterDO.getId(), standbyTopicName, rs
|
||||
);
|
||||
return Result.buildFromRSAndMsg(rs, "delete topic ha failed");
|
||||
}
|
||||
|
||||
return Result.buildSuc();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Integer> getRelation(Long clusterId) {
|
||||
Map<String, Integer> relationMap = new HashMap<>();
|
||||
List<HaASRelationDO> relationDOS = haASRelationService.listAllHAFromDB(clusterId, HaResTypeEnum.TOPIC);
|
||||
if (relationDOS.isEmpty()){
|
||||
return relationMap;
|
||||
}
|
||||
|
||||
//主topic
|
||||
List<String> activeTopics = relationDOS.stream().filter(haASRelationDO -> haASRelationDO.getActiveClusterPhyId().equals(clusterId)).map(HaASRelationDO::getActiveResName).collect(Collectors.toList());
|
||||
activeTopics.stream().forEach(topicName -> relationMap.put(topicName, HaRelationTypeEnum.ACTIVE.getCode()));
|
||||
|
||||
//备topic
|
||||
List<String> standbyTopics = relationDOS.stream().filter(haASRelationDO -> haASRelationDO.getStandbyClusterPhyId().equals(clusterId)).map(HaASRelationDO::getStandbyResName).collect(Collectors.toList());
|
||||
standbyTopics.stream().forEach(topicName -> relationMap.put(topicName, HaRelationTypeEnum.STANDBY.getCode()));
|
||||
|
||||
//互备
|
||||
relationMap.put(KafkaConstant.COORDINATOR_TOPIC_NAME, HaRelationTypeEnum.MUTUAL_BACKUP.getCode());
|
||||
|
||||
return relationMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Long, List<String>> getClusterStandbyTopicMap() {
|
||||
Map<Long, List<String>> clusterStandbyTopicMap = new HashMap<>();
|
||||
List<HaASRelationDO> relationDOS = haASRelationService.listAllHAFromDB(HaResTypeEnum.TOPIC);
|
||||
if (relationDOS.isEmpty()){
|
||||
return clusterStandbyTopicMap;
|
||||
}
|
||||
return relationDOS.stream().collect(Collectors.groupingBy(HaASRelationDO::getStandbyClusterPhyId, Collectors.mapping(HaASRelationDO::getStandbyResName, Collectors.toList())));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<Void> activeUserHAInKafka(ClusterDO activeClusterDO, ClusterDO standbyClusterDO, String kafkaUser, String operator) {
|
||||
Result<Void> rv;
|
||||
rv = haKafkaUserService.activeHAInKafka(activeClusterDO.getZookeeper(), activeClusterDO.getId(), kafkaUser);
|
||||
if (rv.failed()) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = haKafkaUserService.activeHAInKafka(standbyClusterDO.getZookeeper(), activeClusterDO.getId(), kafkaUser);
|
||||
if (rv.failed()) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = haKafkaUserService.activeHAInKafka(configUtils.getDKafkaGatewayZK(), activeClusterDO.getId(), kafkaUser);
|
||||
if (rv.failed()) {
|
||||
return rv;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<Long> getStandbyTopicFetchLag(Long standbyClusterPhyId, String topicName) {
|
||||
TopicMetadata metadata = PhysicalClusterMetadataManager.getTopicMetadata(standbyClusterPhyId, topicName);
|
||||
if (metadata == null) {
|
||||
return Result.buildFromRSAndMsg(ResultStatus.RESOURCE_NOT_EXIST, MsgConstant.getTopicNotExist(standbyClusterPhyId, topicName));
|
||||
}
|
||||
|
||||
List<Integer> partitionIdList = new ArrayList<>(metadata.getPartitionMap().getPartitions().keySet());
|
||||
|
||||
List<PartitionState> partitionStateList = KafkaZookeeperUtils.getTopicPartitionState(
|
||||
PhysicalClusterMetadataManager.getZKConfig(standbyClusterPhyId),
|
||||
topicName,
|
||||
partitionIdList
|
||||
);
|
||||
|
||||
if (partitionStateList.size() != partitionIdList.size()) {
|
||||
return Result.buildFromRSAndMsg(ResultStatus.ZOOKEEPER_READ_FAILED, "读取ZK的分区元信息失败");
|
||||
}
|
||||
|
||||
Long sumLag = 0L;
|
||||
for (Integer leaderBrokerId: partitionStateList.stream().map(elem -> elem.getLeader()).collect(Collectors.toSet())) {
|
||||
JmxConnectorWrap jmxConnectorWrap = PhysicalClusterMetadataManager.getJmxConnectorWrap(standbyClusterPhyId, leaderBrokerId);
|
||||
if (jmxConnectorWrap == null || !jmxConnectorWrap.checkJmxConnectionAndInitIfNeed()) {
|
||||
return Result.buildFromRSAndMsg(ResultStatus.OPERATION_FAILED, String.format("获取BrokerId=%d的jmx客户端失败", leaderBrokerId));
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
ObjectName objectName = new ObjectName(
|
||||
"kafka.server:type=FetcherLagMetrics,name=ConsumerLag,clientId=MirrorFetcherThread-*" + "-" + standbyClusterPhyId + "*" + ",topic=" + topicName + ",partition=*"
|
||||
);
|
||||
|
||||
Set<ObjectName> objectNameSet = jmxConnectorWrap.queryNames(objectName, null);
|
||||
for (ObjectName name: objectNameSet) {
|
||||
List<Attribute> attributeList = jmxConnectorWrap.getAttributes(name, JmxAttributeEnum.VALUE_ATTRIBUTE.getAttribute()).asList();
|
||||
for (Attribute attribute: attributeList) {
|
||||
sumLag += Long.valueOf(attribute.getValue().toString());
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOGGER.error(
|
||||
"class=HaTopicServiceImpl||method=getStandbyTopicFetchLag||standbyClusterPhyId={}||topicName={}||leaderBrokerId={}||errMsg=exception.",
|
||||
standbyClusterPhyId, topicName, leaderBrokerId, e
|
||||
);
|
||||
|
||||
return Result.buildFromRSAndMsg(ResultStatus.OPERATION_FAILED, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
return Result.buildSuc(sumLag);
|
||||
}
|
||||
|
||||
/**************************************************** private method ****************************************************/
|
||||
|
||||
private Result<Void> activeTopicHAConfigInKafka(ClusterDO activeClusterDO, String activeTopicName, ClusterDO standbyClusterDO, String standbyTopicName) {
|
||||
//更新ha-topic配置
|
||||
Properties standbyTopicProps = new Properties();
|
||||
standbyTopicProps.put(KafkaConstant.DIDI_HA_SYNC_TOPIC_CONFIGS_ENABLED, Boolean.TRUE.toString());
|
||||
standbyTopicProps.put(KafkaConstant.DIDI_HA_REMOTE_CLUSTER, activeClusterDO.getId().toString());
|
||||
if (!activeTopicName.equals(standbyTopicName)) {
|
||||
standbyTopicProps.put(KafkaConstant.DIDI_HA_REMOTE_TOPIC, activeTopicName);
|
||||
}
|
||||
ResultStatus rs = HaTopicCommands.modifyHaTopicConfig(standbyClusterDO, standbyTopicName, standbyTopicProps);
|
||||
if (!ResultStatus.SUCCESS.equals(rs)) {
|
||||
LOGGER.error(
|
||||
"method=createHAInKafka||activeClusterId={}||activeTopicName={}||standbyClusterId={}||standbyTopicName={}||rs={}||msg=create topic ha failed.",
|
||||
activeClusterDO.getId(), activeTopicName, standbyClusterDO.getId(), standbyTopicName, rs
|
||||
);
|
||||
return Result.buildFromRSAndMsg(rs, "modify ha topic config failed");
|
||||
}
|
||||
|
||||
return Result.buildSuc();
|
||||
}
|
||||
|
||||
public Result<Void> addStandbyTopicAuthorityAndQuota(Long activeClusterPhyId, Long standbyClusterPhyId, String topicName) {
|
||||
List<AuthorityDO> authorityDOS = authorityService.getAuthorityByTopic(activeClusterPhyId, topicName);
|
||||
try {
|
||||
for (AuthorityDO authorityDO : authorityDOS) {
|
||||
//权限
|
||||
AuthorityDO newAuthorityDO = new AuthorityDO();
|
||||
newAuthorityDO.setAppId(authorityDO.getAppId());
|
||||
newAuthorityDO.setClusterId(standbyClusterPhyId);
|
||||
newAuthorityDO.setTopicName(topicName);
|
||||
newAuthorityDO.setAccess(authorityDO.getAccess());
|
||||
|
||||
//quota
|
||||
TopicQuota activeTopicQuotaDO = quotaService.getQuotaFromZk(
|
||||
activeClusterPhyId,
|
||||
topicName,
|
||||
authorityDO.getAppId()
|
||||
);
|
||||
|
||||
TopicQuota standbyTopicQuotaDO = new TopicQuota();
|
||||
standbyTopicQuotaDO.setTopicName(topicName);
|
||||
standbyTopicQuotaDO.setAppId(activeTopicQuotaDO.getAppId());
|
||||
standbyTopicQuotaDO.setClusterId(standbyClusterPhyId);
|
||||
standbyTopicQuotaDO.setConsumeQuota(activeTopicQuotaDO.getConsumeQuota());
|
||||
standbyTopicQuotaDO.setProduceQuota(activeTopicQuotaDO.getProduceQuota());
|
||||
|
||||
int result = authorityService.addAuthorityAndQuota(newAuthorityDO, standbyTopicQuotaDO);
|
||||
if (Constant.INVALID_CODE == result){
|
||||
return Result.buildFrom(ResultStatus.OPERATION_FAILED);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOGGER.error(
|
||||
"method=addStandbyTopicAuthorityAndQuota||activeClusterPhyId={}||standbyClusterPhyId={}||topicName={}||errMsg=exception.",
|
||||
activeClusterPhyId, standbyClusterPhyId, topicName, e
|
||||
);
|
||||
|
||||
return Result.buildFailure("备Topic复制主Topic权限及配额失败");
|
||||
}
|
||||
|
||||
return Result.buildSuc();
|
||||
}
|
||||
|
||||
private Result<TopicDO> checkHaTopicAndGetBizInfo(Long activeClusterPhyId, Long standbyClusterPhyId, String topicName){
|
||||
if (PhysicalClusterMetadataManager.isTopicExist(standbyClusterPhyId, topicName)) {
|
||||
return Result.buildFromRSAndMsg(ResultStatus.TOPIC_ALREADY_EXIST, "备集群已存在该Topic,请先删除,再行绑定!");
|
||||
}
|
||||
|
||||
if (!PhysicalClusterMetadataManager.isTopicExist(activeClusterPhyId, topicName)) {
|
||||
return Result.buildFromRSAndMsg(ResultStatus.TOPIC_NOT_EXIST, "主集群不存在该Topic");
|
||||
}
|
||||
|
||||
TopicDO topicDO = topicManagerService.getByTopicName(activeClusterPhyId, topicName);
|
||||
if (ValidateUtils.isNull(topicDO)) {
|
||||
return Result.buildFromRSAndMsg(ResultStatus.RESOURCE_NOT_EXIST, "主集群Topic所属KafkaUser信息不存在");
|
||||
}
|
||||
|
||||
return Result.buildSuc(topicDO);
|
||||
}
|
||||
|
||||
private Result<Void> modifyHaConfig(ClusterDO activeClusterDO, String activeTopic, ClusterDO standbyClusterDO, String standbyTopic, String operator){
|
||||
//更新副集群同步主集群topic配置
|
||||
Result<Void> rv = activeHAInKafkaNotCheck(activeClusterDO, activeTopic, standbyClusterDO, standbyTopic, operator);
|
||||
if (rv.failed()){
|
||||
LOGGER.error("method=createHA||activeTopic:{} standbyTopic:{}||msg=create haTopic modify standby topic config failed!.", activeTopic, standbyTopic);
|
||||
return Result.buildFailure("modify standby topic config failed,please try again");
|
||||
}
|
||||
|
||||
//更新user配置,通知用户指向主集群
|
||||
Set<String> relatedKafkaUserSet = authorityService.getAuthorityByTopic(activeClusterDO.getId(), activeTopic)
|
||||
.stream()
|
||||
.map(elem -> elem.getAppId())
|
||||
.collect(Collectors.toSet());
|
||||
for(String kafkaUser: relatedKafkaUserSet) {
|
||||
rv = this.activeUserHAInKafka(activeClusterDO, standbyClusterDO, kafkaUser, operator);
|
||||
if (rv.failed()) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
return Result.buildSuc();
|
||||
}
|
||||
}
|
||||
@@ -2,21 +2,27 @@ package com.xiaojukeji.kafka.manager.service.service.impl;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.xiaojukeji.kafka.manager.common.bizenum.*;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.OperateRecordDO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.gateway.AuthorityDO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.ao.gateway.TopicQuota;
|
||||
import com.xiaojukeji.kafka.manager.common.bizenum.ModuleEnum;
|
||||
import com.xiaojukeji.kafka.manager.common.bizenum.OperateEnum;
|
||||
import com.xiaojukeji.kafka.manager.common.bizenum.TaskStatusEnum;
|
||||
import com.xiaojukeji.kafka.manager.common.bizenum.TopicAuthorityEnum;
|
||||
import com.xiaojukeji.kafka.manager.common.constant.Constant;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.ResultStatus;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.ao.gateway.TopicQuota;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.ClusterDO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.OperateRecordDO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.TopicDO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.gateway.AuthorityDO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.ha.HaASRelationDO;
|
||||
import com.xiaojukeji.kafka.manager.common.utils.ValidateUtils;
|
||||
import com.xiaojukeji.kafka.manager.common.zookeeper.ZkConfigImpl;
|
||||
import com.xiaojukeji.kafka.manager.common.zookeeper.znode.brokers.BrokerMetadata;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.ClusterDO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.TopicDO;
|
||||
import com.xiaojukeji.kafka.manager.common.zookeeper.znode.brokers.TopicMetadata;
|
||||
import com.xiaojukeji.kafka.manager.service.biz.ha.HaASRelationManager;
|
||||
import com.xiaojukeji.kafka.manager.service.cache.PhysicalClusterMetadataManager;
|
||||
import com.xiaojukeji.kafka.manager.service.service.*;
|
||||
import com.xiaojukeji.kafka.manager.service.service.gateway.AuthorityService;
|
||||
import com.xiaojukeji.kafka.manager.service.service.ha.HaTopicService;
|
||||
import com.xiaojukeji.kafka.manager.service.utils.KafkaZookeeperUtils;
|
||||
import com.xiaojukeji.kafka.manager.service.utils.TopicCommands;
|
||||
import kafka.admin.AdminOperationException;
|
||||
@@ -55,6 +61,12 @@ public class AdminServiceImpl implements AdminService {
|
||||
@Autowired
|
||||
private AuthorityService authorityService;
|
||||
|
||||
@Autowired
|
||||
private HaTopicService haTopicService;
|
||||
|
||||
@Autowired
|
||||
private HaASRelationManager haASRelationManager;
|
||||
|
||||
@Autowired
|
||||
private OperateRecordService operateRecordService;
|
||||
|
||||
@@ -123,15 +135,22 @@ public class AdminServiceImpl implements AdminService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultStatus deleteTopic(ClusterDO clusterDO,
|
||||
String topicName,
|
||||
String operator) {
|
||||
// 1. 集群中删除topic
|
||||
public ResultStatus deleteTopic(ClusterDO clusterDO, String topicName, String operator) {
|
||||
// 1. 若存在高可用topic,先解除高可用关系才能删除topic
|
||||
HaASRelationDO haASRelationDO = haASRelationManager.getASRelation(clusterDO.getId(), topicName);
|
||||
if (haASRelationDO != null){
|
||||
//高可用topic不允许删除
|
||||
if (haASRelationDO.getStandbyClusterPhyId().equals(clusterDO.getId())){
|
||||
return ResultStatus.HA_TOPIC_DELETE_FORBIDDEN;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 集群中删除topic
|
||||
ResultStatus rs = TopicCommands.deleteTopic(clusterDO, topicName);
|
||||
if (!ResultStatus.SUCCESS.equals(rs)) {
|
||||
return rs;
|
||||
}
|
||||
// 2. 记录操作
|
||||
// 3. 记录操作
|
||||
Map<String, Object> content = new HashMap<>(2);
|
||||
content.put("clusterId", clusterDO.getId());
|
||||
content.put("topicName", topicName);
|
||||
@@ -144,12 +163,13 @@ public class AdminServiceImpl implements AdminService {
|
||||
operateRecordDO.setOperator(operator);
|
||||
operateRecordService.insert(operateRecordDO);
|
||||
|
||||
// 3. 数据库中删除topic
|
||||
// 4. 数据库中删除topic
|
||||
topicManagerService.deleteByTopicName(clusterDO.getId(), topicName);
|
||||
topicExpiredService.deleteByTopicName(clusterDO.getId(), topicName);
|
||||
|
||||
// 4. 数据库中删除authority
|
||||
// 5. 数据库中删除authority
|
||||
authorityService.deleteAuthorityByTopic(clusterDO.getId(), topicName);
|
||||
|
||||
return rs;
|
||||
}
|
||||
|
||||
@@ -346,7 +366,6 @@ public class AdminServiceImpl implements AdminService {
|
||||
|
||||
@Override
|
||||
public ResultStatus modifyTopicConfig(ClusterDO clusterDO, String topicName, Properties properties, String operator) {
|
||||
ResultStatus rs = TopicCommands.modifyTopicConfig(clusterDO, topicName, properties);
|
||||
return rs;
|
||||
return TopicCommands.modifyTopicConfig(clusterDO, topicName, properties);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,13 +3,16 @@ package com.xiaojukeji.kafka.manager.service.service.impl;
|
||||
import com.xiaojukeji.kafka.manager.common.bizenum.DBStatusEnum;
|
||||
import com.xiaojukeji.kafka.manager.common.bizenum.ModuleEnum;
|
||||
import com.xiaojukeji.kafka.manager.common.bizenum.OperateEnum;
|
||||
import com.xiaojukeji.kafka.manager.common.bizenum.ha.HaRelationTypeEnum;
|
||||
import com.xiaojukeji.kafka.manager.common.bizenum.ha.HaResTypeEnum;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.Result;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.ResultStatus;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.ao.ClusterDetailDTO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.ao.cluster.ControllerPreferredCandidate;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.*;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.ha.HaASRelationDO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.vo.normal.cluster.ClusterNameDTO;
|
||||
import com.xiaojukeji.kafka.manager.common.utils.ListUtils;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.*;
|
||||
import com.xiaojukeji.kafka.manager.common.utils.ValidateUtils;
|
||||
import com.xiaojukeji.kafka.manager.common.zookeeper.znode.brokers.BrokerMetadata;
|
||||
import com.xiaojukeji.kafka.manager.dao.ClusterDao;
|
||||
@@ -18,15 +21,16 @@ import com.xiaojukeji.kafka.manager.dao.ControllerDao;
|
||||
import com.xiaojukeji.kafka.manager.service.cache.LogicalClusterMetadataManager;
|
||||
import com.xiaojukeji.kafka.manager.service.cache.PhysicalClusterMetadataManager;
|
||||
import com.xiaojukeji.kafka.manager.service.service.*;
|
||||
import com.xiaojukeji.kafka.manager.service.service.ha.HaASRelationService;
|
||||
import com.xiaojukeji.kafka.manager.service.service.ha.HaClusterService;
|
||||
import com.xiaojukeji.kafka.manager.service.utils.ConfigUtils;
|
||||
import org.apache.zookeeper.WatchedEvent;
|
||||
import org.apache.zookeeper.Watcher;
|
||||
import org.apache.zookeeper.ZooKeeper;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.dao.DuplicateKeyException;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@@ -42,6 +46,9 @@ public class ClusterServiceImpl implements ClusterService {
|
||||
@Autowired
|
||||
private ClusterDao clusterDao;
|
||||
|
||||
@Autowired
|
||||
private HaClusterService haClusterService;
|
||||
|
||||
@Autowired
|
||||
private ClusterMetricsDao clusterMetricsDao;
|
||||
|
||||
@@ -69,6 +76,9 @@ public class ClusterServiceImpl implements ClusterService {
|
||||
@Autowired
|
||||
private OperateRecordService operateRecordService;
|
||||
|
||||
@Autowired
|
||||
private HaASRelationService haASRelationService;
|
||||
|
||||
@Override
|
||||
public ResultStatus addNew(ClusterDO clusterDO, String operator) {
|
||||
if (ValidateUtils.isNull(clusterDO) || ValidateUtils.isNull(operator)) {
|
||||
@@ -96,6 +106,7 @@ public class ClusterServiceImpl implements ClusterService {
|
||||
LOGGER.error("add new cluster failed, operate mysql failed, clusterDO:{}.", clusterDO, e);
|
||||
return ResultStatus.MYSQL_ERROR;
|
||||
}
|
||||
physicalClusterMetadataManager.addNew(clusterDO);
|
||||
return ResultStatus.SUCCESS;
|
||||
}
|
||||
|
||||
@@ -253,9 +264,11 @@ public class ClusterServiceImpl implements ClusterService {
|
||||
Map<Long, Integer> consumerGroupNumMap =
|
||||
needDetail? consumerService.getConsumerGroupNumMap(doList): new HashMap<>(0);
|
||||
|
||||
Map<Long, Integer> haRelationMap = haClusterService.getClusterHARelation();
|
||||
List<ClusterDetailDTO> dtoList = new ArrayList<>();
|
||||
for (ClusterDO clusterDO: doList) {
|
||||
ClusterDetailDTO dto = getClusterDetailDTO(clusterDO, needDetail);
|
||||
dto.setHaRelation(haRelationMap.get(clusterDO.getId()));
|
||||
dto.setConsumerGroupNum(consumerGroupNumMap.get(clusterDO.getId()));
|
||||
dto.setRegionNum(regionNumMap.get(clusterDO.getId()));
|
||||
dtoList.add(dto);
|
||||
@@ -281,10 +294,11 @@ public class ClusterServiceImpl implements ClusterService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultStatus deleteById(Long clusterId, String operator) {
|
||||
@Transactional
|
||||
public Result<Void> deleteById(Long clusterId, String operator) {
|
||||
List<RegionDO> regionDOList = regionService.getByClusterId(clusterId);
|
||||
if (!ValidateUtils.isEmptyList(regionDOList)) {
|
||||
return ResultStatus.OPERATION_FORBIDDEN;
|
||||
return Result.buildFrom(ResultStatus.OPERATION_FORBIDDEN);
|
||||
}
|
||||
try {
|
||||
Map<String, String> content = new HashMap<>();
|
||||
@@ -292,13 +306,14 @@ public class ClusterServiceImpl implements ClusterService {
|
||||
operateRecordService.insert(operator, ModuleEnum.CLUSTER, String.valueOf(clusterId), OperateEnum.DELETE, content);
|
||||
if (clusterDao.deleteById(clusterId) <= 0) {
|
||||
LOGGER.error("delete cluster failed, clusterId:{}.", clusterId);
|
||||
return ResultStatus.MYSQL_ERROR;
|
||||
return Result.buildFrom(ResultStatus.MYSQL_ERROR);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("delete cluster failed, clusterId:{}.", clusterId, e);
|
||||
return ResultStatus.MYSQL_ERROR;
|
||||
return Result.buildFrom(ResultStatus.MYSQL_ERROR);
|
||||
}
|
||||
return ResultStatus.SUCCESS;
|
||||
|
||||
return Result.buildSuc();
|
||||
}
|
||||
|
||||
private ClusterDetailDTO getClusterDetailDTO(ClusterDO clusterDO, Boolean needDetail) {
|
||||
@@ -318,6 +333,21 @@ public class ClusterServiceImpl implements ClusterService {
|
||||
dto.setStatus(clusterDO.getStatus());
|
||||
dto.setGmtCreate(clusterDO.getGmtCreate());
|
||||
dto.setGmtModify(clusterDO.getGmtModify());
|
||||
|
||||
List<HaASRelationDO> haASRelationDOS = haASRelationService
|
||||
.listAllHAFromDB(clusterDO.getId(), HaResTypeEnum.CLUSTER);
|
||||
if (!haASRelationDOS.isEmpty()){
|
||||
ClusterDO mbCluster;
|
||||
if (haASRelationDOS.get(0).getActiveClusterPhyId().equals(clusterDO.getId())){
|
||||
dto.setHaRelation(HaRelationTypeEnum.ACTIVE.getCode());
|
||||
mbCluster = PhysicalClusterMetadataManager.getClusterFromCache(haASRelationDOS.get(0).getStandbyClusterPhyId());
|
||||
}else {
|
||||
dto.setHaRelation(HaRelationTypeEnum.STANDBY.getCode());
|
||||
mbCluster = PhysicalClusterMetadataManager.getClusterFromCache(haASRelationDOS.get(0).getActiveClusterPhyId());
|
||||
}
|
||||
dto.setMutualBackupClusterName(mbCluster != null ? mbCluster.getClusterName() : null);
|
||||
}
|
||||
|
||||
if (ValidateUtils.isNull(needDetail) || !needDetail) {
|
||||
return dto;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.xiaojukeji.kafka.manager.service.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.ha.JobLogDO;
|
||||
import com.xiaojukeji.kafka.manager.dao.ha.JobLogDao;
|
||||
import com.xiaojukeji.kafka.manager.service.service.JobLogService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@Service
|
||||
public class JobLogServiceImpl implements JobLogService {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(JobLogServiceImpl.class);
|
||||
|
||||
@Autowired
|
||||
private JobLogDao jobLogDao;
|
||||
|
||||
@Override
|
||||
public void addLogAndIgnoreException(JobLogDO jobLogDO) {
|
||||
try {
|
||||
jobLogDao.insert(jobLogDO);
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("method=addLogAndIgnoreException||jobLogDO={}||errMsg=exception", jobLogDO);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<JobLogDO> listLogs(Integer bizType, String bizKeyword, Long startId) {
|
||||
LambdaQueryWrapper<JobLogDO> lambdaQueryWrapper = new LambdaQueryWrapper<>();
|
||||
lambdaQueryWrapper.eq(JobLogDO::getBizType, bizType);
|
||||
lambdaQueryWrapper.eq(JobLogDO::getBizKeyword, bizKeyword);
|
||||
if (startId != null) {
|
||||
lambdaQueryWrapper.ge(JobLogDO::getId, startId);
|
||||
}
|
||||
|
||||
return jobLogDao.selectList(lambdaQueryWrapper);
|
||||
}
|
||||
}
|
||||
@@ -5,19 +5,20 @@ import com.xiaojukeji.kafka.manager.common.entity.ResultStatus;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.ao.cluster.LogicalCluster;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.ao.cluster.LogicalClusterMetrics;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.metrics.BrokerMetrics;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.BrokerMetricsDO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.LogicalClusterDO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.gateway.AppDO;
|
||||
import com.xiaojukeji.kafka.manager.common.utils.ListUtils;
|
||||
import com.xiaojukeji.kafka.manager.common.utils.ValidateUtils;
|
||||
import com.xiaojukeji.kafka.manager.common.zookeeper.znode.brokers.BrokerMetadata;
|
||||
import com.xiaojukeji.kafka.manager.common.zookeeper.znode.brokers.TopicMetadata;
|
||||
import com.xiaojukeji.kafka.manager.dao.LogicalClusterDao;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.BrokerMetricsDO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.LogicalClusterDO;
|
||||
import com.xiaojukeji.kafka.manager.service.cache.LogicalClusterMetadataManager;
|
||||
import com.xiaojukeji.kafka.manager.service.cache.PhysicalClusterMetadataManager;
|
||||
import com.xiaojukeji.kafka.manager.service.service.gateway.AppService;
|
||||
import com.xiaojukeji.kafka.manager.service.service.BrokerService;
|
||||
import com.xiaojukeji.kafka.manager.service.service.LogicalClusterService;
|
||||
import com.xiaojukeji.kafka.manager.service.service.gateway.AppService;
|
||||
import com.xiaojukeji.kafka.manager.service.service.ha.HaClusterService;
|
||||
import com.xiaojukeji.kafka.manager.service.utils.MetricsConvertUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -45,6 +46,9 @@ public class LogicalClusterServiceImpl implements LogicalClusterService {
|
||||
@Autowired
|
||||
private AppService appService;
|
||||
|
||||
@Autowired
|
||||
private HaClusterService haClusterService;
|
||||
|
||||
@Autowired
|
||||
private LogicalClusterMetadataManager logicClusterMetadataManager;
|
||||
|
||||
|
||||
@@ -4,38 +4,41 @@ import com.xiaojukeji.kafka.manager.common.bizenum.KafkaClientEnum;
|
||||
import com.xiaojukeji.kafka.manager.common.bizenum.ModuleEnum;
|
||||
import com.xiaojukeji.kafka.manager.common.bizenum.OperateEnum;
|
||||
import com.xiaojukeji.kafka.manager.common.bizenum.TopicAuthorityEnum;
|
||||
import com.xiaojukeji.kafka.manager.common.bizenum.ha.HaRelationTypeEnum;
|
||||
import com.xiaojukeji.kafka.manager.common.constant.KafkaConstant;
|
||||
import com.xiaojukeji.kafka.manager.common.constant.KafkaMetricsCollections;
|
||||
import com.xiaojukeji.kafka.manager.common.constant.TopicCreationConstant;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.Result;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.ResultStatus;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.TopicOperationResult;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.ao.RdTopicBasic;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.ao.topic.MineTopicSummary;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.ao.topic.TopicAppData;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.ao.topic.TopicBusinessInfo;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.ao.topic.TopicDTO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.dto.op.topic.TopicExpansionDTO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.dto.op.topic.TopicModificationDTO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.metrics.TopicMetrics;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.metrics.TopicThrottledMetrics;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.*;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.gateway.AppDO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.gateway.AuthorityDO;
|
||||
import com.xiaojukeji.kafka.manager.common.utils.DateUtils;
|
||||
import com.xiaojukeji.kafka.manager.common.utils.JsonUtils;
|
||||
import com.xiaojukeji.kafka.manager.common.utils.NumberUtils;
|
||||
import com.xiaojukeji.kafka.manager.common.utils.SpringTool;
|
||||
import com.xiaojukeji.kafka.manager.common.utils.ValidateUtils;
|
||||
import com.xiaojukeji.kafka.manager.common.utils.*;
|
||||
import com.xiaojukeji.kafka.manager.common.zookeeper.znode.brokers.TopicMetadata;
|
||||
import com.xiaojukeji.kafka.manager.common.zookeeper.znode.config.TopicQuotaData;
|
||||
import com.xiaojukeji.kafka.manager.dao.TopicDao;
|
||||
import com.xiaojukeji.kafka.manager.dao.TopicExpiredDao;
|
||||
import com.xiaojukeji.kafka.manager.dao.TopicStatisticsDao;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.metrics.TopicThrottledMetrics;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.*;
|
||||
import com.xiaojukeji.kafka.manager.service.biz.ha.HaASRelationManager;
|
||||
import com.xiaojukeji.kafka.manager.service.cache.KafkaMetricsCache;
|
||||
import com.xiaojukeji.kafka.manager.service.cache.LogicalClusterMetadataManager;
|
||||
import com.xiaojukeji.kafka.manager.service.cache.PhysicalClusterMetadataManager;
|
||||
import com.xiaojukeji.kafka.manager.service.service.*;
|
||||
import com.xiaojukeji.kafka.manager.service.service.gateway.AppService;
|
||||
import com.xiaojukeji.kafka.manager.service.service.gateway.AuthorityService;
|
||||
import com.xiaojukeji.kafka.manager.service.service.ha.HaTopicService;
|
||||
import com.xiaojukeji.kafka.manager.service.utils.KafkaZookeeperUtils;
|
||||
import com.xiaojukeji.kafka.manager.service.utils.TopicCommands;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -87,6 +90,15 @@ public class TopicManagerServiceImpl implements TopicManagerService {
|
||||
@Autowired
|
||||
private OperateRecordService operateRecordService;
|
||||
|
||||
@Autowired
|
||||
private HaTopicService haTopicService;
|
||||
|
||||
@Autowired
|
||||
private AdminService adminService;
|
||||
|
||||
@Autowired
|
||||
private HaASRelationManager haASRelationManager;
|
||||
|
||||
@Override
|
||||
public List<TopicDO> listAll() {
|
||||
try {
|
||||
@@ -188,6 +200,7 @@ public class TopicManagerServiceImpl implements TopicManagerService {
|
||||
Map<String, Map<Long, Map<String, AuthorityDO>>> appMap = authorityService.getAllAuthority();
|
||||
// 增加权限信息和App信息
|
||||
List<MineTopicSummary> summaryList = new ArrayList<>();
|
||||
Map<Long, List<String>> clusterStandbyTopicMap = haTopicService.getClusterStandbyTopicMap();
|
||||
for (AppDO appDO : appDOList) {
|
||||
// 查权限
|
||||
for (Map<String, AuthorityDO> subMap : appMap.getOrDefault(appDO.getAppId(), Collections.emptyMap()).values()) {
|
||||
@@ -196,6 +209,11 @@ public class TopicManagerServiceImpl implements TopicManagerService {
|
||||
|| TopicAuthorityEnum.DENY.getCode().equals(authorityDO.getAccess())) {
|
||||
continue;
|
||||
}
|
||||
//过滤备topic
|
||||
List<String> standbyTopics = clusterStandbyTopicMap.get(authorityDO.getClusterId());
|
||||
if (standbyTopics != null && standbyTopics.contains(authorityDO.getTopicName())){
|
||||
continue;
|
||||
}
|
||||
|
||||
MineTopicSummary mineTopicSummary = convert2MineTopicSummary(
|
||||
appDO,
|
||||
@@ -224,6 +242,7 @@ public class TopicManagerServiceImpl implements TopicManagerService {
|
||||
TopicDO topicDO = topicDao.getByTopicName(mineTopicSummary.getPhysicalClusterId(), mineTopicSummary.getTopicName());
|
||||
mineTopicSummary.setDescription(topicDO.getDescription());
|
||||
}
|
||||
|
||||
return summaryList;
|
||||
}
|
||||
|
||||
@@ -302,8 +321,9 @@ public class TopicManagerServiceImpl implements TopicManagerService {
|
||||
}
|
||||
|
||||
List<TopicDTO> dtoList = new ArrayList<>();
|
||||
Map<Long, List<String>> clusterStandbyTopicMap = haTopicService.getClusterStandbyTopicMap();
|
||||
for (ClusterDO clusterDO: clusterDOList) {
|
||||
dtoList.addAll(getTopics(clusterDO, appMap, topicMap.getOrDefault(clusterDO.getId(), new HashMap<>())));
|
||||
dtoList.addAll(getTopics(clusterDO, appMap, topicMap.getOrDefault(clusterDO.getId(), new HashMap<>()),clusterStandbyTopicMap.get(clusterDO.getId())));
|
||||
}
|
||||
return dtoList;
|
||||
}
|
||||
@@ -311,13 +331,18 @@ public class TopicManagerServiceImpl implements TopicManagerService {
|
||||
|
||||
private List<TopicDTO> getTopics(ClusterDO clusterDO,
|
||||
Map<String, AppDO> appMap,
|
||||
Map<String, TopicDO> topicMap) {
|
||||
Map<String, TopicDO> topicMap,
|
||||
List<String> standbyTopicNames) {
|
||||
List<TopicDTO> dtoList = new ArrayList<>();
|
||||
|
||||
for (String topicName: PhysicalClusterMetadataManager.getTopicNameList(clusterDO.getId())) {
|
||||
if (topicName.equals(KafkaConstant.COORDINATOR_TOPIC_NAME) || topicName.equals(KafkaConstant.TRANSACTION_TOPIC_NAME)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//过滤备topic
|
||||
if (standbyTopicNames != null && standbyTopicNames.contains(topicName)){
|
||||
continue;
|
||||
}
|
||||
LogicalClusterDO logicalClusterDO = logicalClusterMetadataManager.getTopicLogicalCluster(
|
||||
clusterDO.getId(),
|
||||
topicName
|
||||
@@ -590,12 +615,12 @@ public class TopicManagerServiceImpl implements TopicManagerService {
|
||||
|
||||
TopicDO topicDO = getByTopicName(physicalClusterId, topicName);
|
||||
if (ValidateUtils.isNull(topicDO)) {
|
||||
return new Result<>(convert2RdTopicBasic(clusterDO, topicName, null, null, regionNameList, properties));
|
||||
return new Result<>(convert2RdTopicBasic(clusterDO, topicName, null, null, regionNameList, properties, HaRelationTypeEnum.UNKNOWN.getCode()));
|
||||
}
|
||||
AppDO appDO = appService.getByAppId(topicDO.getAppId());
|
||||
|
||||
|
||||
return new Result<>(convert2RdTopicBasic(clusterDO, topicName, topicDO, appDO, regionNameList, properties));
|
||||
Integer haRelation = haASRelationManager.getRelation(physicalClusterId, topicName);
|
||||
return new Result<>(convert2RdTopicBasic(clusterDO, topicName, topicDO, appDO, regionNameList, properties, haRelation));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -656,12 +681,56 @@ public class TopicManagerServiceImpl implements TopicManagerService {
|
||||
return ResultStatus.MYSQL_ERROR;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result modifyTopic(TopicModificationDTO dto) {
|
||||
ClusterDO clusterDO = clusterService.getById(dto.getClusterId());
|
||||
if (ValidateUtils.isNull(clusterDO)) {
|
||||
return Result.buildFrom(ResultStatus.CLUSTER_NOT_EXIST);
|
||||
}
|
||||
|
||||
// 获取属性
|
||||
Properties properties = dto.getProperties();
|
||||
if (ValidateUtils.isNull(properties)) {
|
||||
properties = new Properties();
|
||||
}
|
||||
properties.put(KafkaConstant.RETENTION_MS_KEY, String.valueOf(dto.getRetentionTime()));
|
||||
|
||||
// 操作修改
|
||||
String operator = SpringTool.getUserName();
|
||||
ResultStatus rs = TopicCommands.modifyTopicConfig(clusterDO, dto.getTopicName(), properties);
|
||||
if (!ResultStatus.SUCCESS.equals(rs)) {
|
||||
return Result.buildFrom(rs);
|
||||
}
|
||||
modifyTopicByOp(dto.getClusterId(), dto.getTopicName(), dto.getAppId(), dto.getDescription(), operator);
|
||||
return Result.buildSuc();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TopicOperationResult expandTopic(TopicExpansionDTO dto) {
|
||||
ClusterDO clusterDO = clusterService.getById(dto.getClusterId());
|
||||
if (ValidateUtils.isNull(clusterDO)) {
|
||||
return TopicOperationResult.buildFrom(dto.getClusterId(), dto.getTopicName(), ResultStatus.CLUSTER_NOT_EXIST);
|
||||
}
|
||||
|
||||
// 参数检查合法, 开始对Topic进行扩分区
|
||||
ResultStatus statusEnum = adminService.expandPartitions(
|
||||
clusterDO,
|
||||
dto.getTopicName(),
|
||||
dto.getPartitionNum(),
|
||||
dto.getRegionId(),
|
||||
dto.getBrokerIdList(),
|
||||
SpringTool.getUserName()
|
||||
);
|
||||
return TopicOperationResult.buildFrom(dto.getClusterId(), dto.getTopicName(), statusEnum);
|
||||
}
|
||||
|
||||
private RdTopicBasic convert2RdTopicBasic(ClusterDO clusterDO,
|
||||
String topicName,
|
||||
TopicDO topicDO,
|
||||
AppDO appDO,
|
||||
List<String> regionNameList,
|
||||
Properties properties) {
|
||||
Properties properties,
|
||||
Integer haRelation) {
|
||||
RdTopicBasic rdTopicBasic = new RdTopicBasic();
|
||||
rdTopicBasic.setClusterId(clusterDO.getId());
|
||||
rdTopicBasic.setClusterName(clusterDO.getClusterName());
|
||||
@@ -676,6 +745,7 @@ public class TopicManagerServiceImpl implements TopicManagerService {
|
||||
rdTopicBasic.setRegionNameList(regionNameList);
|
||||
rdTopicBasic.setProperties(properties);
|
||||
rdTopicBasic.setRetentionTime(KafkaZookeeperUtils.getTopicRetentionTime(properties));
|
||||
rdTopicBasic.setHaRelation(haRelation);
|
||||
return rdTopicBasic;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
package com.xiaojukeji.kafka.manager.service.service.impl;
|
||||
|
||||
import com.xiaojukeji.kafka.manager.common.bizenum.TopicOffsetChangedEnum;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.Result;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.ResultStatus;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.gateway.AppDO;
|
||||
import com.xiaojukeji.kafka.manager.common.bizenum.OffsetPosEnum;
|
||||
import com.xiaojukeji.kafka.manager.common.bizenum.TopicOffsetChangedEnum;
|
||||
import com.xiaojukeji.kafka.manager.common.constant.Constant;
|
||||
import com.xiaojukeji.kafka.manager.common.constant.KafkaMetricsCollections;
|
||||
import com.xiaojukeji.kafka.manager.common.constant.TopicSampleConstant;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.Result;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.ResultStatus;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.ao.PartitionAttributeDTO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.ao.PartitionOffsetDTO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.ao.topic.*;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.dto.normal.TopicDataSampleDTO;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.metrics.TopicMetrics;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.*;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.gateway.AppDO;
|
||||
import com.xiaojukeji.kafka.manager.common.utils.ValidateUtils;
|
||||
import com.xiaojukeji.kafka.manager.common.utils.jmx.JmxConstant;
|
||||
import com.xiaojukeji.kafka.manager.common.zookeeper.znode.brokers.BrokerMetadata;
|
||||
@@ -22,13 +23,14 @@ import com.xiaojukeji.kafka.manager.common.zookeeper.znode.brokers.TopicMetadata
|
||||
import com.xiaojukeji.kafka.manager.dao.TopicAppMetricsDao;
|
||||
import com.xiaojukeji.kafka.manager.dao.TopicMetricsDao;
|
||||
import com.xiaojukeji.kafka.manager.dao.TopicRequestMetricsDao;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.*;
|
||||
import com.xiaojukeji.kafka.manager.service.biz.ha.HaASRelationManager;
|
||||
import com.xiaojukeji.kafka.manager.service.cache.KafkaClientPool;
|
||||
import com.xiaojukeji.kafka.manager.service.cache.KafkaMetricsCache;
|
||||
import com.xiaojukeji.kafka.manager.service.cache.LogicalClusterMetadataManager;
|
||||
import com.xiaojukeji.kafka.manager.service.cache.PhysicalClusterMetadataManager;
|
||||
import com.xiaojukeji.kafka.manager.service.service.*;
|
||||
import com.xiaojukeji.kafka.manager.service.service.gateway.AppService;
|
||||
import com.xiaojukeji.kafka.manager.service.service.ha.HaTopicService;
|
||||
import com.xiaojukeji.kafka.manager.service.strategy.AbstractHealthScoreStrategy;
|
||||
import com.xiaojukeji.kafka.manager.service.utils.KafkaZookeeperUtils;
|
||||
import com.xiaojukeji.kafka.manager.service.utils.MetricsConvertUtils;
|
||||
@@ -90,6 +92,12 @@ public class TopicServiceImpl implements TopicService {
|
||||
@Autowired
|
||||
private KafkaClientPool kafkaClientPool;
|
||||
|
||||
@Autowired
|
||||
private HaTopicService haTopicService;
|
||||
|
||||
@Autowired
|
||||
private HaASRelationManager haASRelationManager;
|
||||
|
||||
@Override
|
||||
public List<TopicMetricsDO> getTopicMetricsFromDB(Long clusterId, String topicName, Date startTime, Date endTime) {
|
||||
try {
|
||||
@@ -244,6 +252,9 @@ public class TopicServiceImpl implements TopicService {
|
||||
|
||||
basicDTO.setTopicCodeC(jmxService.getTopicCodeCValue(clusterId, topicName));
|
||||
basicDTO.setScore(healthScoreStrategy.calTopicHealthScore(clusterId, topicName));
|
||||
|
||||
basicDTO.setHaRelation(haASRelationManager.getRelation(clusterId, topicName));
|
||||
|
||||
return basicDTO;
|
||||
}
|
||||
|
||||
@@ -325,6 +336,11 @@ public class TopicServiceImpl implements TopicService {
|
||||
return jmxService.getTopicMetrics(clusterId, topicName, metricsCode, byAdd);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<TopicPartition, Long> getPartitionOffset(Long clusterPhyId, String topicName, OffsetPosEnum offsetPosEnum) {
|
||||
return this.getPartitionOffset(clusterService.getById(clusterPhyId), topicName, offsetPosEnum);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<TopicPartition, Long> getPartitionOffset(ClusterDO clusterDO,
|
||||
String topicName,
|
||||
@@ -403,6 +419,7 @@ public class TopicServiceImpl implements TopicService {
|
||||
appDOMap.put(appDO.getAppId(), appDO);
|
||||
}
|
||||
|
||||
Map<String, Integer> haRelationMap = haTopicService.getRelation(clusterId);
|
||||
List<TopicOverview> dtoList = new ArrayList<>();
|
||||
for (String topicName : topicNameList) {
|
||||
TopicMetadata topicMetadata = PhysicalClusterMetadataManager.getTopicMetadata(clusterId, topicName);
|
||||
@@ -417,7 +434,8 @@ public class TopicServiceImpl implements TopicService {
|
||||
logicalClusterMetadataManager.getTopicLogicalCluster(clusterId, topicName),
|
||||
topicMetadata,
|
||||
topicDO,
|
||||
appDO
|
||||
appDO,
|
||||
haRelationMap.get(topicName)
|
||||
);
|
||||
dtoList.add(overview);
|
||||
}
|
||||
@@ -429,13 +447,15 @@ public class TopicServiceImpl implements TopicService {
|
||||
LogicalClusterDO logicalClusterDO,
|
||||
TopicMetadata topicMetadata,
|
||||
TopicDO topicDO,
|
||||
AppDO appDO) {
|
||||
AppDO appDO,
|
||||
Integer haRelation) {
|
||||
TopicOverview overview = new TopicOverview();
|
||||
overview.setClusterId(physicalClusterId);
|
||||
overview.setTopicName(topicMetadata.getTopic());
|
||||
overview.setPartitionNum(topicMetadata.getPartitionNum());
|
||||
overview.setReplicaNum(topicMetadata.getReplicaNum());
|
||||
overview.setUpdateTime(topicMetadata.getModifyTime());
|
||||
overview.setHaRelation(haRelation);
|
||||
overview.setRetentionTime(
|
||||
PhysicalClusterMetadataManager.getTopicRetentionTime(physicalClusterId, topicMetadata.getTopic())
|
||||
);
|
||||
|
||||
@@ -17,6 +17,7 @@ import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* @author zengqiao
|
||||
@@ -124,4 +125,43 @@ public class ZookeeperServiceImpl implements ZookeeperService {
|
||||
}
|
||||
return Result.buildFrom(ResultStatus.ZOOKEEPER_DELETE_FAILED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<List<Integer>> getBrokerIds(String zookeeper) {
|
||||
if (ValidateUtils.isNull(zookeeper)) {
|
||||
return Result.buildFrom(ResultStatus.PARAM_ILLEGAL);
|
||||
}
|
||||
ZkConfigImpl zkConfig = new ZkConfigImpl(zookeeper);
|
||||
if (ValidateUtils.isNull(zkConfig)) {
|
||||
return Result.buildFrom(ResultStatus.ZOOKEEPER_CONNECT_FAILED);
|
||||
}
|
||||
|
||||
try {
|
||||
if (!zkConfig.checkPathExists(ZkPathUtil.BROKER_IDS_ROOT)) {
|
||||
return Result.buildSuc(new ArrayList<>());
|
||||
}
|
||||
List<String> brokerIdList = zkConfig.getChildren(ZkPathUtil.BROKER_IDS_ROOT);
|
||||
if (ValidateUtils.isEmptyList(brokerIdList)) {
|
||||
return Result.buildSuc(new ArrayList<>());
|
||||
}
|
||||
return Result.buildSuc(ListUtils.string2IntList(ListUtils.strList2String(brokerIdList)));
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("class=ZookeeperServiceImpl||method=getBrokerIds||zookeeper={}||errMsg={}", zookeeper, e.getMessage());
|
||||
}
|
||||
return Result.buildFrom(ResultStatus.ZOOKEEPER_READ_FAILED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getClusterIdAndNullIfFailed(String zookeeper) {
|
||||
try {
|
||||
ZkConfigImpl zkConfig = new ZkConfigImpl(zookeeper);
|
||||
Properties props = zkConfig.get(ZkPathUtil.CLUSTER_ID_NODE, Properties.class);
|
||||
|
||||
return Long.valueOf(props.getProperty("id"));
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("class=ZookeeperServiceImpl||method=getClusterIdAndNullIfFailed||zookeeper={}||errMsg=exception", zookeeper, e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -20,4 +20,7 @@ public class ConfigUtils {
|
||||
|
||||
@Value(value = "${spring.profiles.active:dev}")
|
||||
private String kafkaManagerEnv;
|
||||
|
||||
@Value(value = "${d-kafka.gateway-zk:}")
|
||||
private String dKafkaGatewayZK;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
package com.xiaojukeji.kafka.manager.service.utils;
|
||||
|
||||
import com.xiaojukeji.kafka.manager.common.constant.Constant;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.ResultStatus;
|
||||
import kafka.admin.AdminUtils;
|
||||
import kafka.admin.AdminUtils$;
|
||||
import kafka.utils.ZkUtils;
|
||||
import org.apache.kafka.common.security.JaasUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
|
||||
/**
|
||||
* @author fengqiongfeng
|
||||
* @date 21/4/11
|
||||
*/
|
||||
public class HaClusterCommands {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(HaClusterCommands.class);
|
||||
|
||||
private static final String HA_CLUSTERS = "ha-clusters";
|
||||
|
||||
/**
|
||||
* 修改HA集群配置
|
||||
*/
|
||||
public static ResultStatus modifyHaClusterConfig(String zookeeper, Long clusterPhyId, Properties modifiedProps) {
|
||||
ZkUtils zkUtils = null;
|
||||
try {
|
||||
zkUtils = ZkUtils.apply(
|
||||
zookeeper,
|
||||
Constant.DEFAULT_SESSION_TIMEOUT_UNIT_MS,
|
||||
Constant.DEFAULT_SESSION_TIMEOUT_UNIT_MS,
|
||||
JaasUtils.isZkSecurityEnabled()
|
||||
);
|
||||
|
||||
// 获取当前配置
|
||||
Properties props = AdminUtils.fetchEntityConfig(zkUtils, HA_CLUSTERS, clusterPhyId.toString());
|
||||
|
||||
// 补充变更的配置
|
||||
props.putAll(modifiedProps);
|
||||
|
||||
AdminUtils$.MODULE$.kafka$admin$AdminUtils$$changeEntityConfig(zkUtils, HA_CLUSTERS, clusterPhyId.toString(), props);
|
||||
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("method=modifyHaClusterConfig||zookeeper={}||clusterPhyId={}||modifiedProps={}||errMsg=exception", zookeeper, clusterPhyId, modifiedProps, e);
|
||||
|
||||
return ResultStatus.ZOOKEEPER_OPERATE_FAILED;
|
||||
} finally {
|
||||
if (null != zkUtils) {
|
||||
zkUtils.close();
|
||||
}
|
||||
}
|
||||
return ResultStatus.SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取集群高可用配置
|
||||
*/
|
||||
public static Properties fetchHaClusterConfig(String zookeeper, Long clusterPhyId) {
|
||||
ZkUtils zkUtils = null;
|
||||
try {
|
||||
zkUtils = ZkUtils.apply(
|
||||
zookeeper,
|
||||
Constant.DEFAULT_SESSION_TIMEOUT_UNIT_MS,
|
||||
Constant.DEFAULT_SESSION_TIMEOUT_UNIT_MS,
|
||||
JaasUtils.isZkSecurityEnabled()
|
||||
);
|
||||
|
||||
// 获取配置
|
||||
return AdminUtils.fetchEntityConfig(zkUtils, HA_CLUSTERS, clusterPhyId.toString());
|
||||
}catch (Exception e){
|
||||
LOGGER.error("method=fetchHaClusterConfig||zookeeper={}||clusterPhyId={}||errMsg=exception", zookeeper, clusterPhyId, e);
|
||||
|
||||
return null;
|
||||
} finally {
|
||||
if (null != zkUtils) {
|
||||
zkUtils.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除 高可用集群的动态配置
|
||||
*/
|
||||
public static ResultStatus coverHaClusterConfig(String zookeeper, Long clusterPhyId, Properties properties){
|
||||
ZkUtils zkUtils = null;
|
||||
try {
|
||||
zkUtils = ZkUtils.apply(
|
||||
zookeeper,
|
||||
Constant.DEFAULT_SESSION_TIMEOUT_UNIT_MS,
|
||||
Constant.DEFAULT_SESSION_TIMEOUT_UNIT_MS,
|
||||
JaasUtils.isZkSecurityEnabled()
|
||||
);
|
||||
|
||||
AdminUtils$.MODULE$.kafka$admin$AdminUtils$$changeEntityConfig(zkUtils, HA_CLUSTERS, clusterPhyId.toString(), properties);
|
||||
|
||||
return ResultStatus.SUCCESS;
|
||||
}catch (Exception e){
|
||||
LOGGER.error("method=deleteHaClusterConfig||zookeeper={}||clusterPhyId={}||delProps={}||errMsg=exception", zookeeper, clusterPhyId, properties, e);
|
||||
|
||||
return ResultStatus.FAIL;
|
||||
} finally {
|
||||
if (null != zkUtils) {
|
||||
zkUtils.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private HaClusterCommands() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package com.xiaojukeji.kafka.manager.service.utils;
|
||||
|
||||
import com.xiaojukeji.kafka.manager.common.constant.Constant;
|
||||
import com.xiaojukeji.kafka.manager.common.utils.ListUtils;
|
||||
import kafka.admin.AdminUtils;
|
||||
import kafka.admin.AdminUtils$;
|
||||
import kafka.server.ConfigType;
|
||||
import kafka.utils.ZkUtils;
|
||||
import org.apache.kafka.common.security.JaasUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
|
||||
|
||||
/**
|
||||
* @author fengqiongfeng
|
||||
* @date 21/4/11
|
||||
*/
|
||||
public class HaKafkaUserCommands {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(HaKafkaUserCommands.class);
|
||||
|
||||
/**
|
||||
* 修改User配置
|
||||
*/
|
||||
public static boolean modifyHaUserConfig(String zookeeper, String kafkaUser, Properties modifiedProps) {
|
||||
ZkUtils zkUtils = null;
|
||||
try {
|
||||
zkUtils = ZkUtils.apply(
|
||||
zookeeper,
|
||||
Constant.DEFAULT_SESSION_TIMEOUT_UNIT_MS,
|
||||
Constant.DEFAULT_SESSION_TIMEOUT_UNIT_MS,
|
||||
JaasUtils.isZkSecurityEnabled()
|
||||
);
|
||||
// 获取当前配置
|
||||
Properties props = AdminUtils.fetchEntityConfig(zkUtils, ConfigType.User(), kafkaUser);
|
||||
|
||||
// 补充变更的配置
|
||||
props.putAll(modifiedProps);
|
||||
|
||||
// 修改配置, 这里不使用changeUserOrUserClientIdConfig方法的原因是changeUserOrUserClientIdConfig这个方法会进行参数检查
|
||||
AdminUtils$.MODULE$.kafka$admin$AdminUtils$$changeEntityConfig(zkUtils, ConfigType.User(), kafkaUser, props);
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("method=changeHaUserConfig||zookeeper={}||kafkaUser={}||modifiedProps={}||errMsg=exception", zookeeper, kafkaUser, modifiedProps, e);
|
||||
return false;
|
||||
} finally {
|
||||
if (null != zkUtils) {
|
||||
zkUtils.close();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除 高可用集群的动态配置
|
||||
*/
|
||||
public static boolean deleteHaUserConfig(String zookeeper, String kafkaUser, List<String> needDeleteConfigNameList){
|
||||
ZkUtils zkUtils = null;
|
||||
try {
|
||||
zkUtils = ZkUtils.apply(
|
||||
zookeeper,
|
||||
Constant.DEFAULT_SESSION_TIMEOUT_UNIT_MS,
|
||||
Constant.DEFAULT_SESSION_TIMEOUT_UNIT_MS,
|
||||
JaasUtils.isZkSecurityEnabled()
|
||||
);
|
||||
|
||||
Properties presentProps = AdminUtils.fetchEntityConfig(zkUtils, ConfigType.User(), kafkaUser);
|
||||
|
||||
//删除需要删除的的配置
|
||||
for (String configName : needDeleteConfigNameList) {
|
||||
presentProps.remove(configName);
|
||||
}
|
||||
|
||||
// 修改配置, 这里不使用changeUserOrUserClientIdConfig方法的原因是changeUserOrUserClientIdConfig这个方法会进行参数检查
|
||||
AdminUtils$.MODULE$.kafka$admin$AdminUtils$$changeEntityConfig(zkUtils, ConfigType.User(), kafkaUser, presentProps);
|
||||
|
||||
return true;
|
||||
}catch (Exception e){
|
||||
LOGGER.error("method=deleteHaUserConfig||zookeeper={}||kafkaUser={}||delProps={}||errMsg=exception", zookeeper, kafkaUser, ListUtils.strList2String(needDeleteConfigNameList), e);
|
||||
|
||||
} finally {
|
||||
if (null != zkUtils) {
|
||||
zkUtils.close();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private HaKafkaUserCommands() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
package com.xiaojukeji.kafka.manager.service.utils;
|
||||
|
||||
import com.xiaojukeji.kafka.manager.common.constant.Constant;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.ResultStatus;
|
||||
import com.xiaojukeji.kafka.manager.common.entity.pojo.ClusterDO;
|
||||
import com.xiaojukeji.kafka.manager.common.utils.ListUtils;
|
||||
import kafka.admin.AdminOperationException;
|
||||
import kafka.admin.AdminUtils;
|
||||
import kafka.admin.AdminUtils$;
|
||||
import kafka.utils.ZkUtils;
|
||||
import org.apache.kafka.common.errors.*;
|
||||
import org.apache.kafka.common.security.JaasUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import scala.collection.JavaConversions;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* HA-Topic Commands
|
||||
*/
|
||||
public class HaTopicCommands {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(HaTopicCommands.class);
|
||||
|
||||
private static final String HA_TOPICS = "ha-topics";
|
||||
|
||||
/**
|
||||
* 修改HA配置
|
||||
*/
|
||||
public static ResultStatus modifyHaTopicConfig(ClusterDO clusterDO, String topicName, Properties props) {
|
||||
ZkUtils zkUtils = null;
|
||||
try {
|
||||
zkUtils = ZkUtils.apply(
|
||||
clusterDO.getZookeeper(),
|
||||
Constant.DEFAULT_SESSION_TIMEOUT_UNIT_MS,
|
||||
Constant.DEFAULT_SESSION_TIMEOUT_UNIT_MS,
|
||||
JaasUtils.isZkSecurityEnabled()
|
||||
);
|
||||
|
||||
AdminUtils$.MODULE$.kafka$admin$AdminUtils$$changeEntityConfig(zkUtils, HA_TOPICS, topicName, props);
|
||||
} catch (AdminOperationException aoe) {
|
||||
LOGGER.error("method=modifyHaTopicConfig||clusterPhyId={}||topicName={}||props={}||errMsg=exception", clusterDO.getId(), topicName, props, aoe);
|
||||
return ResultStatus.TOPIC_OPERATION_UNKNOWN_TOPIC_PARTITION;
|
||||
} catch (InvalidConfigurationException ice) {
|
||||
LOGGER.error("method=modifyHaTopicConfig||clusterPhyId={}||topicName={}||props={}||errMsg=exception", clusterDO.getId(), topicName, props, ice);
|
||||
return ResultStatus.TOPIC_OPERATION_TOPIC_CONFIG_ILLEGAL;
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("method=modifyHaTopicConfig||clusterPhyId={}||topicName={}||props={}||errMsg=exception", clusterDO.getId(), topicName, props, e);
|
||||
return ResultStatus.TOPIC_OPERATION_UNKNOWN_ERROR;
|
||||
} finally {
|
||||
if (zkUtils != null) {
|
||||
zkUtils.close();
|
||||
}
|
||||
}
|
||||
|
||||
return ResultStatus.SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除指定HA配置
|
||||
*/
|
||||
public static ResultStatus deleteHaTopicConfig(ClusterDO clusterDO, String topicName, List<String> neeDeleteConfigNameList){
|
||||
ZkUtils zkUtils = null;
|
||||
try {
|
||||
zkUtils = ZkUtils.apply(
|
||||
clusterDO.getZookeeper(),
|
||||
Constant.DEFAULT_SESSION_TIMEOUT_UNIT_MS,
|
||||
Constant.DEFAULT_SESSION_TIMEOUT_UNIT_MS,
|
||||
JaasUtils.isZkSecurityEnabled()
|
||||
);
|
||||
|
||||
// 当前配置
|
||||
Properties presentProps = AdminUtils.fetchEntityConfig(zkUtils, HA_TOPICS, topicName);
|
||||
|
||||
//删除需要删除的的配置
|
||||
for (String configName : neeDeleteConfigNameList) {
|
||||
presentProps.remove(configName);
|
||||
}
|
||||
|
||||
AdminUtils$.MODULE$.kafka$admin$AdminUtils$$changeEntityConfig(zkUtils, HA_TOPICS, topicName, presentProps);
|
||||
} catch (Exception e){
|
||||
LOGGER.error("method=deleteHaTopicConfig||clusterPhyId={}||topicName={}||delProps={}||errMsg=exception", clusterDO.getId(), topicName, ListUtils.strList2String(neeDeleteConfigNameList), e);
|
||||
return ResultStatus.FAIL;
|
||||
} finally {
|
||||
if (null != zkUtils) {
|
||||
zkUtils.close();
|
||||
}
|
||||
}
|
||||
return ResultStatus.SUCCESS;
|
||||
}
|
||||
|
||||
public static Properties fetchHaTopicConfig(ClusterDO clusterDO, String topicName){
|
||||
ZkUtils zkUtils = null;
|
||||
try {
|
||||
zkUtils = ZkUtils.apply(
|
||||
clusterDO.getZookeeper(),
|
||||
Constant.DEFAULT_SESSION_TIMEOUT_UNIT_MS,
|
||||
Constant.DEFAULT_SESSION_TIMEOUT_UNIT_MS,
|
||||
JaasUtils.isZkSecurityEnabled()
|
||||
);
|
||||
|
||||
return AdminUtils.fetchEntityConfig(zkUtils, HA_TOPICS, topicName);
|
||||
} catch (Exception e){
|
||||
LOGGER.error("method=fetchHaTopicConfig||clusterPhyId={}||topicName={}||errMsg=exception", clusterDO.getId(), topicName, e);
|
||||
return null;
|
||||
} finally {
|
||||
if (null != zkUtils) {
|
||||
zkUtils.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Map<String, Properties> fetchAllHaTopicConfig(ClusterDO clusterDO) {
|
||||
ZkUtils zkUtils = null;
|
||||
try {
|
||||
zkUtils = ZkUtils.apply(
|
||||
clusterDO.getZookeeper(),
|
||||
Constant.DEFAULT_SESSION_TIMEOUT_UNIT_MS,
|
||||
Constant.DEFAULT_SESSION_TIMEOUT_UNIT_MS,
|
||||
JaasUtils.isZkSecurityEnabled()
|
||||
);
|
||||
|
||||
return JavaConversions.asJavaMap(AdminUtils.fetchAllEntityConfigs(zkUtils, HA_TOPICS));
|
||||
} catch (Exception e){
|
||||
LOGGER.error("method=fetchAllHaTopicConfig||clusterPhyId={}||errMsg=exception", clusterDO.getId(), e);
|
||||
return null;
|
||||
} finally {
|
||||
if (null != zkUtils) {
|
||||
zkUtils.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private HaTopicCommands() {
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import kafka.admin.AdminOperationException;
|
||||
import kafka.admin.AdminUtils;
|
||||
import kafka.admin.BrokerMetadata;
|
||||
import kafka.common.TopicAndPartition;
|
||||
import kafka.server.ConfigType;
|
||||
import kafka.utils.ZkUtils;
|
||||
import org.I0Itec.zkclient.exception.ZkNodeExistsException;
|
||||
import org.apache.kafka.common.errors.*;
|
||||
@@ -27,6 +28,8 @@ import java.util.*;
|
||||
public class TopicCommands {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(TopicCommands.class);
|
||||
|
||||
private TopicCommands() {
|
||||
}
|
||||
|
||||
public static ResultStatus createTopic(ClusterDO clusterDO,
|
||||
String topicName,
|
||||
@@ -51,7 +54,7 @@ public class TopicCommands {
|
||||
replicaNum,
|
||||
randomFixedStartIndex(),
|
||||
-1
|
||||
);
|
||||
);
|
||||
|
||||
// 写ZK
|
||||
AdminUtils.createOrUpdateTopicPartitionAssignmentPathInZK(
|
||||
@@ -129,6 +132,11 @@ public class TopicCommands {
|
||||
Constant.DEFAULT_SESSION_TIMEOUT_UNIT_MS,
|
||||
JaasUtils.isZkSecurityEnabled()
|
||||
);
|
||||
|
||||
if(!zkUtils.pathExists(zkUtils.getTopicPath(topicName))){
|
||||
return ResultStatus.TOPIC_NOT_EXIST;
|
||||
}
|
||||
|
||||
AdminUtils.changeTopicConfig(zkUtils, topicName, config);
|
||||
} catch (AdminOperationException e) {
|
||||
LOGGER.error("class=TopicCommands||method=modifyTopicConfig||errMsg={}||clusterDO={}||topicName={}||config={}", e.getMessage(), clusterDO, topicName,config, e);
|
||||
@@ -209,6 +217,31 @@ public class TopicCommands {
|
||||
return ResultStatus.SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Topic的动态配置
|
||||
*/
|
||||
public static Properties fetchTopicConfig(ClusterDO clusterDO, String topicName){
|
||||
ZkUtils zkUtils = null;
|
||||
try {
|
||||
zkUtils = ZkUtils.apply(
|
||||
clusterDO.getZookeeper(),
|
||||
Constant.DEFAULT_SESSION_TIMEOUT_UNIT_MS,
|
||||
Constant.DEFAULT_SESSION_TIMEOUT_UNIT_MS,
|
||||
JaasUtils.isZkSecurityEnabled()
|
||||
);
|
||||
|
||||
return AdminUtils.fetchEntityConfig(zkUtils, ConfigType.Topic(), topicName);
|
||||
} catch (Exception e){
|
||||
LOGGER.error("get topic config failed, zk:{},topic:{} .err:{}", clusterDO.getZookeeper(), topicName, e);
|
||||
} finally {
|
||||
if (null != zkUtils) {
|
||||
zkUtils.close();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Seq<BrokerMetadata> convert2BrokerMetadataSeq(List<Integer> brokerIdList) {
|
||||
List<BrokerMetadata> brokerMetadataList = new ArrayList<>();
|
||||
for (Integer brokerId: brokerIdList) {
|
||||
|
||||
@@ -22,12 +22,10 @@ import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.dao.DuplicateKeyException;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.BeforeMethod;
|
||||
import org.testng.annotations.DataProvider;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static org.mockito.Mockito.reset;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
@@ -327,8 +325,8 @@ public class ClusterServiceTest extends BaseTest {
|
||||
@Test(description = "测试删除集群时,该集群下还有region,禁止删除")
|
||||
public void deleteById2OperationForbiddenTest() {
|
||||
when(regionService.getByClusterId(Mockito.anyLong())).thenReturn(Arrays.asList(new RegionDO()));
|
||||
ResultStatus resultStatus = clusterService.deleteById(1L, "admin");
|
||||
Assert.assertEquals(resultStatus.getCode(), ResultStatus.OPERATION_FORBIDDEN.getCode());
|
||||
Result result = clusterService.deleteById(1L, "admin");
|
||||
Assert.assertEquals(result.successful(), ResultStatus.OPERATION_FORBIDDEN.getCode());
|
||||
}
|
||||
|
||||
@Test(description = "测试删除集群成功")
|
||||
@@ -337,18 +335,18 @@ public class ClusterServiceTest extends BaseTest {
|
||||
when(regionService.getByClusterId(Mockito.anyLong())).thenReturn(Collections.emptyList());
|
||||
Mockito.when(operateRecordService.insert(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any())).thenReturn(1);
|
||||
Mockito.when(clusterDao.deleteById(Mockito.any())).thenReturn(1);
|
||||
ResultStatus resultStatus = clusterService.deleteById(clusterDO.getId(), "admin");
|
||||
Assert.assertEquals(resultStatus.getCode(), ResultStatus.SUCCESS.getCode());
|
||||
Result result = clusterService.deleteById(clusterDO.getId(), "admin");
|
||||
Assert.assertEquals(result.successful(), ResultStatus.SUCCESS.getCode());
|
||||
|
||||
}
|
||||
|
||||
@Test(description = "测试MYSQL_ERROR")
|
||||
public void deleteById2MysqlErrorTest() {
|
||||
when(regionService.getByClusterId(Mockito.anyLong())).thenReturn(Collections.emptyList());
|
||||
ResultStatus resultStatus = clusterService.deleteById(100L, "admin");
|
||||
Result result = clusterService.deleteById(100L, "admin");
|
||||
Mockito.when(operateRecordService.insert(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any())).thenReturn(1);
|
||||
Mockito.when(clusterDao.deleteById(Mockito.any())).thenReturn(-1);
|
||||
Assert.assertEquals(resultStatus.getCode(), ResultStatus.MYSQL_ERROR.getCode());
|
||||
Assert.assertEquals(result.successful(), ResultStatus.MYSQL_ERROR.getCode());
|
||||
}
|
||||
|
||||
@Test(description = "测试从zk中获取被选举的broker")
|
||||
|
||||
@@ -371,7 +371,7 @@ public class TopicServiceTest extends BaseTest {
|
||||
private void getPartitionOffset2EmptyTest() {
|
||||
ClusterDO clusterDO = getClusterDO();
|
||||
Map<TopicPartition, Long> partitionOffset = topicService.getPartitionOffset(
|
||||
null, null, OffsetPosEnum.BEGINNING);
|
||||
clusterDO, null, OffsetPosEnum.BEGINNING);
|
||||
Assert.assertTrue(partitionOffset.isEmpty());
|
||||
|
||||
Map<TopicPartition, Long> partitionOffset2 = topicService.getPartitionOffset(
|
||||
|
||||
Reference in New Issue
Block a user