From 89b58dd64eff7d0161ece5472d9bc5e761353adf Mon Sep 17 00:00:00 2001 From: zengqiao Date: Tue, 2 Feb 2021 16:42:20 +0800 Subject: [PATCH 01/33] storage support s3 --- .../manager/common/utils/ValidateUtils.java | 15 +++ .../kafka-manager-kcm/pom.xml | 5 + .../kafka/manager/kcm/KafkaFileService.java | 3 +- .../storage/AbstractStorageService.java | 11 +- .../kcm/component/storage/local/Local.java | 33 ----- .../kcm/component/storage/s3/S3Service.java | 125 ++++++++++++++++++ .../kcm/impl/KafkaFileServiceImpl.java | 7 +- .../versionone/rd/RdKafkaFileController.java | 51 +++++-- .../src/main/resources/application.yml | 7 +- pom.xml | 6 + 10 files changed, 213 insertions(+), 50 deletions(-) delete mode 100644 kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/component/storage/local/Local.java create mode 100644 kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/component/storage/s3/S3Service.java diff --git a/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/utils/ValidateUtils.java b/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/utils/ValidateUtils.java index 1ece8f9f..6bd0c55c 100644 --- a/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/utils/ValidateUtils.java +++ b/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/utils/ValidateUtils.java @@ -2,6 +2,7 @@ package com.xiaojukeji.kafka.manager.common.utils; import org.apache.commons.lang.StringUtils; +import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Set; @@ -11,6 +12,20 @@ import java.util.Set; * @date 20/4/16 */ public class ValidateUtils { + /** + * 任意一个为空, 则返回true + */ + public static boolean anyNull(Object... objects) { + return Arrays.stream(objects).anyMatch(ValidateUtils::isNull); + } + + /** + * 是空字符串或者空 + */ + public static boolean anyBlank(String... strings) { + return Arrays.stream(strings).anyMatch(StringUtils::isBlank); + } + /** * 为空 */ diff --git a/kafka-manager-extends/kafka-manager-kcm/pom.xml b/kafka-manager-extends/kafka-manager-kcm/pom.xml index 741f0f12..7ffd00e3 100644 --- a/kafka-manager-extends/kafka-manager-kcm/pom.xml +++ b/kafka-manager-extends/kafka-manager-kcm/pom.xml @@ -68,5 +68,10 @@ spring-test ${spring-version} + + + io.minio + minio + \ No newline at end of file diff --git a/kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/KafkaFileService.java b/kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/KafkaFileService.java index b2de3a32..babfeb15 100644 --- a/kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/KafkaFileService.java +++ b/kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/KafkaFileService.java @@ -4,6 +4,7 @@ import com.xiaojukeji.kafka.manager.common.entity.Result; import com.xiaojukeji.kafka.manager.common.entity.ResultStatus; import com.xiaojukeji.kafka.manager.common.entity.dto.normal.KafkaFileDTO; import com.xiaojukeji.kafka.manager.common.entity.pojo.KafkaFileDO; +import org.springframework.web.multipart.MultipartFile; import java.util.List; @@ -24,7 +25,7 @@ public interface KafkaFileService { KafkaFileDO getFileByFileName(String fileName); - Result downloadKafkaConfigFile(Long fileId); + Result downloadKafkaFile(Long fileId); String getDownloadBaseUrl(); } diff --git a/kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/component/storage/AbstractStorageService.java b/kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/component/storage/AbstractStorageService.java index 90192b0b..34c209ac 100644 --- a/kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/component/storage/AbstractStorageService.java +++ b/kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/component/storage/AbstractStorageService.java @@ -10,13 +10,20 @@ import org.springframework.web.multipart.MultipartFile; public abstract class AbstractStorageService { /** * 上传 + * @param fileName 文件名 + * @param fileMd5 文件md5 + * @param uploadFile 文件 + * @return 上传结果 */ public abstract boolean upload(String fileName, String fileMd5, MultipartFile uploadFile); /** - * 下载 + * 下载文件 + * @param fileName 文件名 + * @param fileMd5 文件md5 + * @return 文件 */ - public abstract Result download(String fileName, String fileMd5); + public abstract Result download(String fileName, String fileMd5); /** * 下载base地址 diff --git a/kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/component/storage/local/Local.java b/kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/component/storage/local/Local.java deleted file mode 100644 index 992c09e4..00000000 --- a/kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/component/storage/local/Local.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.xiaojukeji.kafka.manager.kcm.component.storage.local; - -import com.xiaojukeji.kafka.manager.common.entity.Result; -import com.xiaojukeji.kafka.manager.common.entity.ResultStatus; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Service; -import com.xiaojukeji.kafka.manager.kcm.component.storage.AbstractStorageService; -import org.springframework.web.multipart.MultipartFile; - -/** - * @author zengqiao - * @date 20/9/17 - */ -@Service("storageService") -public class Local extends AbstractStorageService { - @Value("${kcm.storage.base-url}") - private String baseUrl; - - @Override - public boolean upload(String fileName, String fileMd5, MultipartFile uploadFile) { - return false; - } - - @Override - public Result download(String fileName, String fileMd5) { - return Result.buildFrom(ResultStatus.STORAGE_DOWNLOAD_FILE_FAILED); - } - - @Override - public String getDownloadBaseUrl() { - return baseUrl; - } -} \ No newline at end of file diff --git a/kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/component/storage/s3/S3Service.java b/kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/component/storage/s3/S3Service.java new file mode 100644 index 00000000..419e66e0 --- /dev/null +++ b/kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/component/storage/s3/S3Service.java @@ -0,0 +1,125 @@ +package com.xiaojukeji.kafka.manager.kcm.component.storage.s3; + +import com.xiaojukeji.kafka.manager.common.entity.Result; +import com.xiaojukeji.kafka.manager.common.entity.ResultStatus; +import com.xiaojukeji.kafka.manager.common.utils.ValidateUtils; +import com.xiaojukeji.kafka.manager.kcm.component.storage.AbstractStorageService; +import io.minio.*; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.annotation.PostConstruct; +import java.io.IOException; +import java.io.InputStream; + + +@Service("storageService") +public class S3Service extends AbstractStorageService { + private final static Logger LOGGER = LoggerFactory.getLogger(S3Service.class); + + @Value("${kcm.s3.endpoint:}") + private String endpoint; + + @Value("${kcm.s3.access-key:}") + private String accessKey; + + @Value("${kcm.s3.secret-key:}") + private String secretKey; + + @Value("${kcm.s3.bucket:}") + private String bucket; + + private MinioClient minioClient; + + @PostConstruct + public void init() { + try { + if (ValidateUtils.anyBlank(this.endpoint, this.accessKey, this.secretKey, this.bucket)) { + // without config s3 + return; + } + minioClient = new MinioClient(endpoint, accessKey, secretKey); + } catch (Exception e) { + LOGGER.error("class=S3Service||method=init||fields={}||errMsg={}", this.toString(), e.getMessage()); + } + } + + @Override + public boolean upload(String fileName, String fileMd5, MultipartFile uploadFile) { + InputStream inputStream = null; + try { + if (!createBucketIfNotExist()) { + return false; + } + + inputStream = uploadFile.getInputStream(); + minioClient.putObject(PutObjectArgs.builder() + .bucket(this.bucket) + .object(fileName) + .stream(inputStream, inputStream.available(), -1) + .build() + ); + return true; + } catch (Exception e) { + LOGGER.error("class=S3Service||method=upload||fileName={}||errMsg={}||msg=upload failed", fileName, e.getMessage()); + } finally { + if (inputStream != null) { + try { + inputStream.close(); + } catch (IOException e) { + ; // ignore + } + } + } + return false; + } + + @Override + public Result download(String fileName, String fileMd5) { + try { + final ObjectStat stat = minioClient.statObject(this.bucket, fileName); + + InputStream is = minioClient.getObject(this.bucket, fileName); + + return Result.buildSuc(new MockMultipartFile(fileName, fileName, stat.contentType(), is)); + } catch (Exception e) { + LOGGER.error("class=S3Service||method=download||fileName={}||errMsg={}||msg=download failed", fileName, e.getMessage()); + } + return Result.buildFrom(ResultStatus.STORAGE_DOWNLOAD_FILE_FAILED); + } + + @Override + public String getDownloadBaseUrl() { + return this.endpoint + "/" + this.bucket; + } + + private boolean createBucketIfNotExist() { + try { + boolean found = minioClient.bucketExists(BucketExistsArgs.builder().bucket(this.bucket).build()); + if (!found) { + minioClient.makeBucket(MakeBucketArgs.builder().bucket(this.bucket).build()); + } + + LOGGER.info("class=S3Service||method=createBucketIfNotExist||bucket={}||msg=check and create bucket success", this.bucket); + return true; + } catch (Exception e) { + LOGGER.error("class=S3Service||method=createBucketIfNotExist||bucket={}||errMsg={}||msg=create bucket failed", this.bucket, e.getMessage()); + } + return false; + } + + @Override + public String toString() { + return "S3Service{" + + "endpoint='" + endpoint + '\'' + + ", accessKey='" + accessKey + '\'' + + ", secretKey='" + secretKey + '\'' + + ", bucket='" + bucket + '\'' + + '}'; + } +} diff --git a/kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/impl/KafkaFileServiceImpl.java b/kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/impl/KafkaFileServiceImpl.java index 307c486c..37f8753a 100644 --- a/kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/impl/KafkaFileServiceImpl.java +++ b/kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/impl/KafkaFileServiceImpl.java @@ -4,17 +4,18 @@ import com.xiaojukeji.kafka.manager.common.bizenum.KafkaFileEnum; import com.xiaojukeji.kafka.manager.common.entity.Result; import com.xiaojukeji.kafka.manager.common.entity.ResultStatus; import com.xiaojukeji.kafka.manager.common.entity.dto.normal.KafkaFileDTO; +import com.xiaojukeji.kafka.manager.common.entity.pojo.KafkaFileDO; import com.xiaojukeji.kafka.manager.common.utils.CopyUtils; import com.xiaojukeji.kafka.manager.common.utils.ValidateUtils; import com.xiaojukeji.kafka.manager.dao.KafkaFileDao; -import com.xiaojukeji.kafka.manager.common.entity.pojo.KafkaFileDO; -import com.xiaojukeji.kafka.manager.kcm.component.storage.AbstractStorageService; import com.xiaojukeji.kafka.manager.kcm.KafkaFileService; +import com.xiaojukeji.kafka.manager.kcm.component.storage.AbstractStorageService; 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.web.multipart.MultipartFile; import java.util.ArrayList; import java.util.List; @@ -163,7 +164,7 @@ public class KafkaFileServiceImpl implements KafkaFileService { } @Override - public Result downloadKafkaConfigFile(Long fileId) { + public Result downloadKafkaFile(Long fileId) { KafkaFileDO kafkaFileDO = kafkaFileDao.getById(fileId); if (ValidateUtils.isNull(kafkaFileDO)) { return Result.buildFrom(ResultStatus.RESOURCE_NOT_EXIST); diff --git a/kafka-manager-web/src/main/java/com/xiaojukeji/kafka/manager/web/api/versionone/rd/RdKafkaFileController.java b/kafka-manager-web/src/main/java/com/xiaojukeji/kafka/manager/web/api/versionone/rd/RdKafkaFileController.java index 823bbe70..009d540a 100644 --- a/kafka-manager-web/src/main/java/com/xiaojukeji/kafka/manager/web/api/versionone/rd/RdKafkaFileController.java +++ b/kafka-manager-web/src/main/java/com/xiaojukeji/kafka/manager/web/api/versionone/rd/RdKafkaFileController.java @@ -1,23 +1,30 @@ package com.xiaojukeji.kafka.manager.web.api.versionone.rd; import com.xiaojukeji.kafka.manager.common.bizenum.KafkaFileEnum; +import com.xiaojukeji.kafka.manager.common.constant.ApiPrefix; import com.xiaojukeji.kafka.manager.common.entity.Result; import com.xiaojukeji.kafka.manager.common.entity.dto.normal.KafkaFileDTO; -import com.xiaojukeji.kafka.manager.common.entity.vo.rd.KafkaFileVO; -import com.xiaojukeji.kafka.manager.common.utils.ValidateUtils; -import com.xiaojukeji.kafka.manager.kcm.component.storage.common.StorageEnum; import com.xiaojukeji.kafka.manager.common.entity.pojo.KafkaFileDO; -import com.xiaojukeji.kafka.manager.service.service.ClusterService; -import com.xiaojukeji.kafka.manager.kcm.KafkaFileService; +import com.xiaojukeji.kafka.manager.common.entity.vo.rd.KafkaFileVO; import com.xiaojukeji.kafka.manager.common.utils.JsonUtils; import com.xiaojukeji.kafka.manager.common.utils.SpringTool; -import com.xiaojukeji.kafka.manager.common.constant.ApiPrefix; +import com.xiaojukeji.kafka.manager.common.utils.ValidateUtils; +import com.xiaojukeji.kafka.manager.kcm.KafkaFileService; +import com.xiaojukeji.kafka.manager.kcm.component.storage.common.StorageEnum; +import com.xiaojukeji.kafka.manager.service.service.ClusterService; import com.xiaojukeji.kafka.manager.web.converters.KafkaFileConverter; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; +import org.apache.tomcat.util.http.fileupload.IOUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; +import javax.servlet.http.HttpServletResponse; +import java.io.InputStream; +import java.net.URLEncoder; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -30,6 +37,8 @@ import java.util.Map; @RestController @RequestMapping(ApiPrefix.API_V1_RD_PREFIX) public class RdKafkaFileController { + private final static Logger LOGGER = LoggerFactory.getLogger(RdKafkaFileController.class); + @Autowired private ClusterService clusterService; @@ -71,9 +80,33 @@ public class RdKafkaFileController { return new Result<>(KafkaFileConverter.convertKafkaFileVOList(kafkaFileDOList, clusterService)); } - @ApiOperation(value = "文件预览", notes = "") + @Deprecated + @ApiOperation(value = "文件下载", notes = "") @RequestMapping(value = "kafka-files/{fileId}/config-files", method = RequestMethod.GET) - public Result previewKafkaFile(@PathVariable("fileId") Long fileId) { - return kafkaFileService.downloadKafkaConfigFile(fileId); + public Result downloadKafkaFile(@PathVariable("fileId") Long fileId, HttpServletResponse response) { + Result multipartFileResult = kafkaFileService.downloadKafkaFile(fileId); + + if (multipartFileResult.failed() || ValidateUtils.isNull(multipartFileResult.getData())) { + return multipartFileResult; + } + + InputStream is = null; + try { + response.setContentType(multipartFileResult.getData().getContentType()); + response.setCharacterEncoding("UTF-8"); + response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(multipartFileResult.getData().getOriginalFilename(), "UTF-8")); + is = multipartFileResult.getData().getInputStream(); + IOUtils.copy(is, response.getOutputStream()); + } catch (Exception e) { + LOGGER.error("class=RdKafkaFileController||method=downloadKafkaFile||fileId={}||errMsg={}||msg=modify response failed", fileId, e.getMessage()); + } finally { + try { + if (is != null) { + is.close(); + } + } catch (Exception e) { + } + } + return Result.buildSuc(); } } \ No newline at end of file diff --git a/kafka-manager-web/src/main/resources/application.yml b/kafka-manager-web/src/main/resources/application.yml index 8c137da8..5b01d321 100644 --- a/kafka-manager-web/src/main/resources/application.yml +++ b/kafka-manager-web/src/main/resources/application.yml @@ -52,8 +52,11 @@ account: kcm: enabled: false - storage: - base-url: http://127.0.0.1 + s3: + endpoint: 127.0.0.1 + access-key: 1234567890 + secret-key: 0987654321 + bucket: logi-kafka n9e: base-url: http://127.0.0.1:8004 user-token: 12345678 diff --git a/pom.xml b/pom.xml index 7165880e..6588d335 100644 --- a/pom.xml +++ b/pom.xml @@ -223,6 +223,12 @@ curator-recipes 2.10.0 + + + io.minio + minio + 7.1.0 + \ No newline at end of file From 63d291cb470a54d9b74c291711b2973964697849 Mon Sep 17 00:00:00 2001 From: 17hao Date: Wed, 3 Feb 2021 15:50:33 +0800 Subject: [PATCH 02/33] Remove __consumer_offsets from topic list --- .../manager/service/service/impl/TopicManagerServiceImpl.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/impl/TopicManagerServiceImpl.java b/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/impl/TopicManagerServiceImpl.java index 0b42d068..9e381587 100644 --- a/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/impl/TopicManagerServiceImpl.java +++ b/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/impl/TopicManagerServiceImpl.java @@ -50,6 +50,8 @@ import java.util.stream.Collectors; public class TopicManagerServiceImpl implements TopicManagerService { private static final Logger LOGGER = LoggerFactory.getLogger(TopicManagerServiceImpl.class); + private static final String CONSUMER_OFFSETS_TOPIC = "__consumer_offsets"; + @Autowired private TopicDao topicDao; @@ -275,6 +277,8 @@ public class TopicManagerServiceImpl implements TopicManagerService { } Map> topicMap = new HashMap<>(appList.size()); for (TopicDO topicDO: topicList) { + if (topicDO.getTopicName().equals(CONSUMER_OFFSETS_TOPIC)) + continue; Map subTopicMap = topicMap.getOrDefault(topicDO.getClusterId(), new HashMap<>()); subTopicMap.put(topicDO.getTopicName(), topicDO); topicMap.put(topicDO.getClusterId(), subTopicMap); From 712851a8a56725a097bf89ab479af332c0957e7f Mon Sep 17 00:00:00 2001 From: 17hao Date: Wed, 3 Feb 2021 16:06:16 +0800 Subject: [PATCH 03/33] Add braces --- .../manager/service/service/impl/TopicManagerServiceImpl.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/impl/TopicManagerServiceImpl.java b/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/impl/TopicManagerServiceImpl.java index 9e381587..96913e50 100644 --- a/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/impl/TopicManagerServiceImpl.java +++ b/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/impl/TopicManagerServiceImpl.java @@ -277,8 +277,9 @@ public class TopicManagerServiceImpl implements TopicManagerService { } Map> topicMap = new HashMap<>(appList.size()); for (TopicDO topicDO: topicList) { - if (topicDO.getTopicName().equals(CONSUMER_OFFSETS_TOPIC)) + if (topicDO.getTopicName().equals(CONSUMER_OFFSETS_TOPIC)) { continue; + } Map subTopicMap = topicMap.getOrDefault(topicDO.getClusterId(), new HashMap<>()); subTopicMap.put(topicDO.getTopicName(), topicDO); topicMap.put(topicDO.getClusterId(), subTopicMap); From 5b7d7ad65daedd84de4cc1a7db82fa2668ea7e34 Mon Sep 17 00:00:00 2001 From: ZHAOYINRUI <51046167+ZHAOYINRUI@users.noreply.github.com> Date: Wed, 3 Feb 2021 18:01:42 +0800 Subject: [PATCH 04/33] Create resource_apply.md --- docs/user_guide/resource_apply.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 docs/user_guide/resource_apply.md diff --git a/docs/user_guide/resource_apply.md b/docs/user_guide/resource_apply.md new file mode 100644 index 00000000..ba811b05 --- /dev/null +++ b/docs/user_guide/resource_apply.md @@ -0,0 +1,31 @@ +--- + +![kafka-manager-logo](../assets/images/common/logo_name.png) + +**一站式`Apache Kafka`集群指标监控与运维管控平台** + +--- + + +# 资源申请文档 + +## 主要名词解释 + +- 应用(App):作为Kafka中的账户,使用AppID+password作为身份标识 +- 集群:可使用平台提供的共享集群,也可为某一应用申请单独的集群 +- Topic:可申请创建Topic或申请其他Topic的生产/消费权限。进行生产/消费时通过Topic+AppID进行身份鉴权 +![production_consumption_flow](../assets/images/resource_apply/production_consumption_flow.png) + +## 应用申请 +应用(App)作为Kafka中的账户,使用AppID+password作为身份标识。对Topic进行生产/消费时通过Topic+AppID进行身份鉴权。 + +用户申请应用,经由运维人员审批,审批通过后获得AppID和密钥 + +## 集群申请 +可使用平台提供的共享集群,若对隔离性、稳定性、生产消费速率有更高的需求,可对某一应用申请单独的集群 + +## Topic申请 +- 用户可根据已申请的应用创建Topic。创建后,应用负责人默认拥有该Topic的生产/消费权限和管理权限 +- 也可申请其他Topic的生产、消费权限。经由Topic所属应用的负责人审批后,即可拥有相应权限。 + + From 6471efed5f12593e5d587caf1ca8dbaf0f390853 Mon Sep 17 00:00:00 2001 From: ZHAOYINRUI <51046167+ZHAOYINRUI@users.noreply.github.com> Date: Wed, 3 Feb 2021 18:04:40 +0800 Subject: [PATCH 05/33] Add files via upload --- .../production_consumption_flow.png | Bin 0 -> 121691 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/user_guide/assets/resource_apply/production_consumption_flow.png diff --git a/docs/user_guide/assets/resource_apply/production_consumption_flow.png b/docs/user_guide/assets/resource_apply/production_consumption_flow.png new file mode 100644 index 0000000000000000000000000000000000000000..36187c83dbcc3a6a6fdaec5c57f931e3a9c4e0b3 GIT binary patch literal 121691 zcmY(r1z1$y_dQHAfFKRh-Q6wS-Q7qI-6bvE-Q68SDh^T3i(Z0!kbF`U!voe^TZh!3I7+x~NKtLex$Y zoPdAvw9uCMpr8Oj2YwHLfPut=fPFay{1Sr1|KEE_NE!&}zn?=vKzy=>fO$Pf5qy04 zNd>=NzVq)0oe%kXHu$rAsQ=D}($0te-}|30Uju5hO@j{zj?y|V5D+-%FE2>Tyjk$D zg&}0bMbteZ&%5E%35VRMys4%jaQCxaOyPRvgUD#nq6{ zUb}dLzN8Q={w6T5V^~SeYb-a`F-??a#RxPOrE*pb=7IPSfbjjX?9^53$A!-kilZfq z%XeDijw;0*m(%&J4UHpB4J`~E7MM4XVm+v@2O3%^jg5_sF`Yf5RP`!a_W$$8d^R-C z(#p!yrWXAlJCChxY^?RV_)uX1q{aZ;mxHn@%%X^%n~X*#+yA@X%Vk~a&x!u;UGnDv z%K*&^G)}+&^W`f*da%Y-YXIcy)m{!Il05{~s^m7m{@)1mchOkjt9BhAq5n5z78u!> z0AB49$@$xN{~M0*0J$-W=D-#d-2cXB3`oXm(2Qmu(jdv(gcCx>=m7(MxQ?A^{^9L169BNTrx5jxiWhhJZmeeS0!zeLP(-f@Dnl&xnDAs6DL!VzbHb zm;*q0i%&hD{eB;pq#2ejE~;_({&d9Sb;iugdT@Z@nwyd|NtXs}#BrrxNh3^Y;b9bN3U@tqN;9sCt0?p18 zG5RVsM<%)L#c7F+A5U&!;z^WsrB z%%x6ygERFeU#)2VaNgu8yw#XS?$sEuvN)^I z7n%YazOeXW^uQ=#@MK3pIMhe>wXe20k#|2$@^hH>13GrX&a6-8ttB+52_|}3-on+o zVvf>Y5;)5`y12)~e?XJJ;bFvambcmsS3>kL0~ygEd}e)+g)y+9Oa^QfM2x|J*P9t)hjnAU9Tj;tO zzMY42^M6Y={w;WMUX!Yo-liG(>qwHPZh+*m>!~|c)$s^x-oxWkNFRUlGh*22)PnM$ zcJ%7P`*2(L>(iQ$aDS8xZ9z6R{vm(n3LJr`zHAsirlr1GJX#paJJGSUEd{++`!qM7 zSR1KVFZKm5sFb%EY(-z&SH67wV%N>$v@4#;wNcZ4?MJu7^$oNk3|D5Bc;AhZVk|DZ zZ$?SPNu5s7M2~$@u`g+m_<(>RuEe%=az4X_dvSsBP@GsEPth*DcD=I`c)G%cNo<_N zQAo1}As>qyKCp^?d3a1~jL)?3_<*#3@Cm$l=Nin0@<)RHg(_hpnDO3GX!-Vp9b@2f zeF6?^+%Bh9wA_+7%?2l38QnAV|E=}idB@1o_ z&h?2>zmMxNQa$CBv5%E2t>OpfhX>YzAdxDP ziX~FGWy=tl??V;Q*_>M|aDHZk(IuyqHhiInSBvs0Bssia<$aYjdEB4AR_L7H`{qRi2@#fwh&Q}SJ5n}!mnPpzc*jL@~u1`PaVyAKLc;_JZQL=v5!luEZP=txim#0XLWV8 zIraa{fMh+JFs)@G6UWjw{nJzQm4!orpQtlDi9DF{(dO}cJQPS>iP5-ShG~kH7A_+F zN5${DxOkm%=~vhzAPe;E@PCGK0|V%0AM?9N==6DbbhRauQ!si9e-NybQ3fw@b$@cE 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

kojzpaj(72nUK{n;67+$OBl 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 z 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 Date: Wed, 3 Feb 2021 18:07:34 +0800 Subject: [PATCH 06/33] Update resource_apply.md --- docs/user_guide/resource_apply.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/user_guide/resource_apply.md b/docs/user_guide/resource_apply.md index ba811b05..61c3c5e8 100644 --- a/docs/user_guide/resource_apply.md +++ b/docs/user_guide/resource_apply.md @@ -1,3 +1,4 @@ + --- ![kafka-manager-logo](../assets/images/common/logo_name.png) From 276c15cc23c11592a020ddd85eccee2486530e76 Mon Sep 17 00:00:00 2001 From: ZHAOYINRUI <51046167+ZHAOYINRUI@users.noreply.github.com> Date: Wed, 3 Feb 2021 18:08:15 +0800 Subject: [PATCH 07/33] Delete docs/user_guide/resource_apply directory --- .../resource_apply/resource_apply.md | 32 ------------------- 1 file changed, 32 deletions(-) delete mode 100644 docs/user_guide/resource_apply/resource_apply.md diff --git a/docs/user_guide/resource_apply/resource_apply.md b/docs/user_guide/resource_apply/resource_apply.md deleted file mode 100644 index 427d8b14..00000000 --- a/docs/user_guide/resource_apply/resource_apply.md +++ /dev/null @@ -1,32 +0,0 @@ - ---- - -![kafka-manager-logo](../assets/images/common/logo_name.png) - -**一站式`Apache Kafka`集群指标监控与运维管控平台** - ---- - - -# 资源申请文档 - -## 主要名词解释 - -- 应用(App):作为Kafka中的账户,使用AppID+password作为身份标识 -- 集群:可使用平台提供的共享集群,也可为某一应用申请单独的集群 -- Topic:可申请创建Topic或申请其他Topic的生产/消费权限。进行生产/消费时通过Topic+AppID进行身份鉴权 -![production_consumption_flow](/production_consumption_flow.png) - -## 应用申请 -应用(App)作为Kafka中的账户,使用AppID+password作为身份标识。对Topic进行生产/消费时通过Topic+AppID进行身份鉴权。 - -用户申请应用,经由运维人员审批,审批通过后获得AppID和密钥 - -## 集群申请 -可使用平台提供的共享集群,若对隔离性、稳定性、生产消费速率有更高的需求,可对某一应用申请单独的集群 - -## Topic申请 -- 用户可根据已申请的应用创建Topic。创建后,应用负责人默认拥有该Topic的生产/消费权限和管理权限 -- 也可申请其他Topic的生产、消费权限。经由Topic所属应用的负责人审批后,即可拥有相应权限。 - - From 10ba0cf976fd6c31b3b3488bf30f19df48e7b2fa Mon Sep 17 00:00:00 2001 From: ZHAOYINRUI <51046167+ZHAOYINRUI@users.noreply.github.com> Date: Wed, 3 Feb 2021 18:18:02 +0800 Subject: [PATCH 08/33] Update resource_apply.md --- docs/user_guide/resource_apply.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/user_guide/resource_apply.md b/docs/user_guide/resource_apply.md index 61c3c5e8..46b956dc 100644 --- a/docs/user_guide/resource_apply.md +++ b/docs/user_guide/resource_apply.md @@ -14,8 +14,8 @@ - 应用(App):作为Kafka中的账户,使用AppID+password作为身份标识 - 集群:可使用平台提供的共享集群,也可为某一应用申请单独的集群 -- Topic:可申请创建Topic或申请其他Topic的生产/消费权限。进行生产/消费时通过Topic+AppID进行身份鉴权 -![production_consumption_flow](../assets/images/resource_apply/production_consumption_flow.png) +- Topic:可申请创建Topic或申请其他Topic的生产/消费权限。进行生产/消费时通过Topic+AppID进行身份鉴权 +![production_consumption_flow](../assets/resource_apply/production_consumption_flow.png) ## 应用申请 应用(App)作为Kafka中的账户,使用AppID+password作为身份标识。对Topic进行生产/消费时通过Topic+AppID进行身份鉴权。 From 3dd0f7f2c3448f0365bda49ef5dc98423cccef44 Mon Sep 17 00:00:00 2001 From: ZHAOYINRUI <51046167+ZHAOYINRUI@users.noreply.github.com> Date: Wed, 3 Feb 2021 19:41:33 +0800 Subject: [PATCH 09/33] Update add_cluster.md --- docs/user_guide/add_cluster/add_cluster.md | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/docs/user_guide/add_cluster/add_cluster.md b/docs/user_guide/add_cluster/add_cluster.md index 14c1d907..99d2c205 100644 --- a/docs/user_guide/add_cluster/add_cluster.md +++ b/docs/user_guide/add_cluster/add_cluster.md @@ -5,16 +5,26 @@ **一站式`Apache Kafka`集群指标监控与运维管控平台** + --- # 集群接入 -集群的接入总共需要三个步骤,分别是: -1. 接入物理集群 -2. 创建Region -3. 创建逻辑集群 +## 主要概念讲解 +面对大规模集群、业务场景复杂的情况,引入Region、逻辑集群的概念 +- Region:划分部分Broker作为一个 Region,用Region定义资源划分的单位,提高扩展性和隔离性。如果部分Topic异常也不会影响大面积的Broker +- 逻辑集群:逻辑集群由部分Region组成,便于对大规模集群按照业务划分、保障能力进行管理 -备注:接入集群需要2、3两步是因为普通用户的视角下,看到的都是逻辑集群,如果没有2、3两步,那么普通用户看不到任何信息。 + +集群的接入总共需要三个步骤,分别是: +1. 接入物理集群:填写机器地址、安全协议等配置信息,接入真实的物理集群 +2. 创建Region:将部分Broker划分为一个Region +3. 创建逻辑集群:逻辑集群由部分Region组成,可根据业务划分、保障等级来创建相应的逻辑集群 + +![op_cluster_flow](assets/op_cluster_flow.png) + + +**备注:接入集群需要2、3两步是因为普通用户的视角下,看到的都是逻辑集群,如果没有2、3两步,那么普通用户看不到任何信息。** ## 1、接入物理集群 @@ -36,4 +46,4 @@ ![op_add_logical_cluster](assets/op_add_logical_cluster.jpg) -如上图所示,填写逻辑集群信息,然后点击确定,即可完成逻辑集群的创建。 \ No newline at end of file +如上图所示,填写逻辑集群信息,然后点击确定,即可完成逻辑集群的创建。 From 96ca17d26c747b81bf4ea9f2e2c41bc5d28f97f5 Mon Sep 17 00:00:00 2001 From: ZHAOYINRUI <51046167+ZHAOYINRUI@users.noreply.github.com> Date: Wed, 3 Feb 2021 19:43:03 +0800 Subject: [PATCH 10/33] Add files via upload --- .../add_cluster/assets/op_cluster_flow.png | Bin 0 -> 107895 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/user_guide/add_cluster/assets/op_cluster_flow.png diff --git a/docs/user_guide/add_cluster/assets/op_cluster_flow.png b/docs/user_guide/add_cluster/assets/op_cluster_flow.png new file mode 100644 index 0000000000000000000000000000000000000000..283f2676d61925e086d102f14df2670428454e9d GIT binary patch literal 107895 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 Date: Wed, 3 Feb 2021 20:52:44 +0800 Subject: [PATCH 11/33] Update resource_apply.md --- docs/user_guide/resource_apply.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/user_guide/resource_apply.md b/docs/user_guide/resource_apply.md index 46b956dc..87537f95 100644 --- a/docs/user_guide/resource_apply.md +++ b/docs/user_guide/resource_apply.md @@ -15,7 +15,7 @@ - 应用(App):作为Kafka中的账户,使用AppID+password作为身份标识 - 集群:可使用平台提供的共享集群,也可为某一应用申请单独的集群 - Topic:可申请创建Topic或申请其他Topic的生产/消费权限。进行生产/消费时通过Topic+AppID进行身份鉴权 -![production_consumption_flow](../assets/resource_apply/production_consumption_flow.png) +![production_consumption_flow](assets/resource_apply/production_consumption_flow.png) ## 应用申请 应用(App)作为Kafka中的账户,使用AppID+password作为身份标识。对Topic进行生产/消费时通过Topic+AppID进行身份鉴权。 From dfb625377b85afae67f9e17d6e7a263a11be9aea Mon Sep 17 00:00:00 2001 From: 17hao Date: Wed, 3 Feb 2021 22:30:38 +0800 Subject: [PATCH 12/33] Using existing topic name constant --- .../manager/service/service/impl/TopicManagerServiceImpl.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/impl/TopicManagerServiceImpl.java b/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/impl/TopicManagerServiceImpl.java index 96913e50..fc5f678d 100644 --- a/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/impl/TopicManagerServiceImpl.java +++ b/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/impl/TopicManagerServiceImpl.java @@ -50,8 +50,6 @@ import java.util.stream.Collectors; public class TopicManagerServiceImpl implements TopicManagerService { private static final Logger LOGGER = LoggerFactory.getLogger(TopicManagerServiceImpl.class); - private static final String CONSUMER_OFFSETS_TOPIC = "__consumer_offsets"; - @Autowired private TopicDao topicDao; @@ -277,7 +275,7 @@ public class TopicManagerServiceImpl implements TopicManagerService { } Map> topicMap = new HashMap<>(appList.size()); for (TopicDO topicDO: topicList) { - if (topicDO.getTopicName().equals(CONSUMER_OFFSETS_TOPIC)) { + if (topicDO.getTopicName().equals(KafkaConstant.COORDINATOR_TOPIC_NAME)) { continue; } Map subTopicMap = topicMap.getOrDefault(topicDO.getClusterId(), new HashMap<>()); From e8de40328690b729a9d6572a0bfa1f808e6842e7 Mon Sep 17 00:00:00 2001 From: 17hao Date: Thu, 4 Feb 2021 12:14:44 +0800 Subject: [PATCH 13/33] Hide __transaction_state in topic list && fix logic error --- .../service/impl/TopicManagerServiceImpl.java | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/impl/TopicManagerServiceImpl.java b/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/impl/TopicManagerServiceImpl.java index fc5f678d..e903b2e7 100644 --- a/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/impl/TopicManagerServiceImpl.java +++ b/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/impl/TopicManagerServiceImpl.java @@ -2,6 +2,7 @@ package com.xiaojukeji.kafka.manager.service.service.impl; import com.xiaojukeji.kafka.manager.common.bizenum.KafkaClientEnum; import com.xiaojukeji.kafka.manager.common.bizenum.TopicAuthorityEnum; +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; @@ -275,11 +276,13 @@ public class TopicManagerServiceImpl implements TopicManagerService { } Map> topicMap = new HashMap<>(appList.size()); for (TopicDO topicDO: topicList) { - if (topicDO.getTopicName().equals(KafkaConstant.COORDINATOR_TOPIC_NAME)) { + String topicName = topicDO.getTopicName(); + if (topicName.equals(KafkaConstant.COORDINATOR_TOPIC_NAME) || + topicName.equals(KafkaConstant.TRANSACTION_TOPIC_NAME)) { continue; } Map subTopicMap = topicMap.getOrDefault(topicDO.getClusterId(), new HashMap<>()); - subTopicMap.put(topicDO.getTopicName(), topicDO); + subTopicMap.put(topicName, topicDO); topicMap.put(topicDO.getClusterId(), subTopicMap); } @@ -304,6 +307,11 @@ public class TopicManagerServiceImpl implements TopicManagerService { continue; } + TopicDO topicDO = topicMap.get(topicName); + if (ValidateUtils.isNull(topicDO)) { + continue; + } + TopicDTO dto = new TopicDTO(); dtoList.add(dto); @@ -311,11 +319,6 @@ public class TopicManagerServiceImpl implements TopicManagerService { dto.setLogicalClusterName(logicalClusterDO.getName()); dto.setTopicName(topicName); dto.setNeedAuth(Boolean.TRUE); - - TopicDO topicDO = topicMap.get(topicName); - if (ValidateUtils.isNull(topicDO)) { - continue; - } dto.setDescription(topicDO.getDescription()); dto.setAppId(topicDO.getAppId()); From 9491418f3b5d7c38d32442b63b46b9e09c7faf8f Mon Sep 17 00:00:00 2001 From: 17hao Date: Thu, 4 Feb 2021 12:32:28 +0800 Subject: [PATCH 14/33] Update if statements --- .../service/service/impl/TopicManagerServiceImpl.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/impl/TopicManagerServiceImpl.java b/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/impl/TopicManagerServiceImpl.java index e903b2e7..f86d022a 100644 --- a/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/impl/TopicManagerServiceImpl.java +++ b/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/impl/TopicManagerServiceImpl.java @@ -303,12 +303,10 @@ public class TopicManagerServiceImpl implements TopicManagerService { clusterDO.getId(), topicName ); - if (ValidateUtils.isNull(logicalClusterDO)) { - continue; - } TopicDO topicDO = topicMap.get(topicName); - if (ValidateUtils.isNull(topicDO)) { + + if (ValidateUtils.isNull(topicDO) || ValidateUtils.isNull(logicalClusterDO)) { continue; } From c7919210a2cf3d90f58b3578539b96e6bfcd9578 Mon Sep 17 00:00:00 2001 From: 17hao Date: Thu, 4 Feb 2021 16:30:23 +0800 Subject: [PATCH 15/33] Fix topic list filter condition --- .../service/impl/TopicManagerServiceImpl.java | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/impl/TopicManagerServiceImpl.java b/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/impl/TopicManagerServiceImpl.java index f86d022a..a2d5aa92 100644 --- a/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/impl/TopicManagerServiceImpl.java +++ b/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/impl/TopicManagerServiceImpl.java @@ -276,13 +276,8 @@ public class TopicManagerServiceImpl implements TopicManagerService { } Map> topicMap = new HashMap<>(appList.size()); for (TopicDO topicDO: topicList) { - String topicName = topicDO.getTopicName(); - if (topicName.equals(KafkaConstant.COORDINATOR_TOPIC_NAME) || - topicName.equals(KafkaConstant.TRANSACTION_TOPIC_NAME)) { - continue; - } Map subTopicMap = topicMap.getOrDefault(topicDO.getClusterId(), new HashMap<>()); - subTopicMap.put(topicName, topicDO); + subTopicMap.put(topicDO.getTopicName(), topicDO); topicMap.put(topicDO.getClusterId(), subTopicMap); } @@ -299,14 +294,15 @@ public class TopicManagerServiceImpl implements TopicManagerService { Map topicMap) { List dtoList = new ArrayList<>(); for (String topicName: PhysicalClusterMetadataManager.getTopicNameList(clusterDO.getId())) { + if (topicName.equals(KafkaConstant.COORDINATOR_TOPIC_NAME) || topicName.equals(KafkaConstant.TRANSACTION_TOPIC_NAME)) { + continue; + } + LogicalClusterDO logicalClusterDO = logicalClusterMetadataManager.getTopicLogicalCluster( clusterDO.getId(), topicName ); - - TopicDO topicDO = topicMap.get(topicName); - - if (ValidateUtils.isNull(topicDO) || ValidateUtils.isNull(logicalClusterDO)) { + if (ValidateUtils.isNull(logicalClusterDO)) { continue; } @@ -317,6 +313,11 @@ public class TopicManagerServiceImpl implements TopicManagerService { dto.setLogicalClusterName(logicalClusterDO.getName()); dto.setTopicName(topicName); dto.setNeedAuth(Boolean.TRUE); + + TopicDO topicDO = topicMap.get(topicName); + if (ValidateUtils.isNull(topicDO)) { + continue; + } dto.setDescription(topicDO.getDescription()); dto.setAppId(topicDO.getAppId()); From ed93c50fefb62d91d083a85440ebed4a6674d441 Mon Sep 17 00:00:00 2001 From: EricZeng Date: Thu, 4 Feb 2021 16:54:42 +0800 Subject: [PATCH 16/33] modify without logical cluster desc --- docs/user_guide/faq.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/docs/user_guide/faq.md b/docs/user_guide/faq.md index 28138a3a..8ab9781a 100644 --- a/docs/user_guide/faq.md +++ b/docs/user_guide/faq.md @@ -10,7 +10,7 @@ # FAQ - 0、Github图裂问题解决 -- 1、Topic申请时没有可选择的集群? +- 1、Topic申请、新建监控告警等操作时没有可选择的集群? - 2、逻辑集群 & Region的用途? - 3、登录失败? - 4、页面流量信息等无数据? @@ -37,9 +37,13 @@ --- -### 1、Topic申请时没有可选择的集群? +### 1、Topic申请、新建监控告警等操作时没有可选择的集群? -- 参看 [kafka-manager 接入集群](docs/user_guide/add_cluster/add_cluster.md) 手册,这里的Region和逻辑集群都必须添加。 +缺少逻辑集群导致的,在Topic管理、监控告警、集群管理这三个Tab下面都是普通用户视角,普通用户看到的集群都是逻辑集群,因此在这三个Tab下进行操作时,都需要有逻辑集群。 + +逻辑集群的创建参看: + +- [kafka-manager 接入集群](docs/user_guide/add_cluster/add_cluster.md) 手册,这里的Region和逻辑集群都必须添加。 --- From 31ce3b9c08652a1885e78824370918d70bf93246 Mon Sep 17 00:00:00 2001 From: ZHAOYINRUI <51046167+ZHAOYINRUI@users.noreply.github.com> Date: Thu, 4 Feb 2021 19:08:28 +0800 Subject: [PATCH 17/33] Update add_cluster.md --- docs/user_guide/add_cluster/add_cluster.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/user_guide/add_cluster/add_cluster.md b/docs/user_guide/add_cluster/add_cluster.md index 99d2c205..1774a9be 100644 --- a/docs/user_guide/add_cluster/add_cluster.md +++ b/docs/user_guide/add_cluster/add_cluster.md @@ -13,8 +13,8 @@ ## 主要概念讲解 面对大规模集群、业务场景复杂的情况,引入Region、逻辑集群的概念 - Region:划分部分Broker作为一个 Region,用Region定义资源划分的单位,提高扩展性和隔离性。如果部分Topic异常也不会影响大面积的Broker -- 逻辑集群:逻辑集群由部分Region组成,便于对大规模集群按照业务划分、保障能力进行管理 - +- 逻辑集群:逻辑集群由部分Region组成,便于对大规模集群按照业务划分、保障能力进行管理 +![op_cluster_arch](assets/op_cluster_arch.png) 集群的接入总共需要三个步骤,分别是: 1. 接入物理集群:填写机器地址、安全协议等配置信息,接入真实的物理集群 From 75e7e81c05d8630bd9882f44fde5fb6d6ff55ef6 Mon Sep 17 00:00:00 2001 From: ZHAOYINRUI <51046167+ZHAOYINRUI@users.noreply.github.com> Date: Thu, 4 Feb 2021 19:09:02 +0800 Subject: [PATCH 18/33] Add files via upload --- .../add_cluster/assets/op_cluster_arch.png | Bin 0 -> 127189 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/user_guide/add_cluster/assets/op_cluster_arch.png diff --git a/docs/user_guide/add_cluster/assets/op_cluster_arch.png b/docs/user_guide/add_cluster/assets/op_cluster_arch.png new file mode 100644 index 0000000000000000000000000000000000000000..aa972d9e8bbfc6c89d6a3c8ff344d7f5051b22cc GIT binary patch literal 127189 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 =} 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 L#?Jq!TmP@-biy0e#j#cal(nRcD5NH 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 Date: Thu, 4 Feb 2021 19:10:11 +0800 Subject: [PATCH 19/33] Update README.md --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index d0887cac..aaa7e1d8 100644 --- a/README.md +++ b/README.md @@ -53,11 +53,20 @@ ## 2 相关文档 +### 2.1 产品文档 - [滴滴Logi-KafkaManager 安装手册](docs/install_guide/install_guide_cn.md) - [滴滴Logi-KafkaManager 接入集群](docs/user_guide/add_cluster/add_cluster.md) - [滴滴Logi-KafkaManager 用户使用手册](docs/user_guide/user_guide_cn.md) - [滴滴Logi-KafkaManager FAQ](docs/user_guide/faq.md) +### 2.2 社区文章 +- [滴滴云官网产品介绍](https://www.didiyun.com/production/logi-KafkaManager.html) +- [7年沉淀之作--滴滴Logi日志服务套件](https://mp.weixin.qq.com/s/-KQp-Qo3WKEOc9wIR2iFnw) +- [滴滴Logi-KafkaManager 一站式Kafka监控与管控平台](https://mp.weixin.qq.com/s/9qSZIkqCnU6u9nLMvOOjIQ) +- [滴滴Logi-KafkaManager 开源之路](https://xie.infoq.cn/article/0223091a99e697412073c0d64) +- [滴滴Logi-KafkaManager 系列视频教程](https://mp.weixin.qq.com/s/9X7gH0tptHPtfjPPSdGO8g) +- [kafka实践(十五):滴滴开源Kafka管控平台 Logi-KafkaManager研究--A叶子叶来](https://blog.csdn.net/yezonggang/article/details/113106244) + ## 3 滴滴Logi开源用户钉钉交流群 ![dingding_group](./docs/assets/images/common/dingding_group.jpg) From 1fdb85234c06bae25da1fdd4b4714e00885d1951 Mon Sep 17 00:00:00 2001 From: 17hao Date: Fri, 5 Feb 2021 12:18:50 +0800 Subject: [PATCH 20/33] Record editting topic --- .../service/impl/TopicManagerServiceImpl.java | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/impl/TopicManagerServiceImpl.java b/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/impl/TopicManagerServiceImpl.java index a2d5aa92..5709fffc 100644 --- a/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/impl/TopicManagerServiceImpl.java +++ b/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/impl/TopicManagerServiceImpl.java @@ -1,6 +1,9 @@ package com.xiaojukeji.kafka.manager.service.service.impl; +import com.alibaba.fastjson.JSONObject; 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.constant.KafkaConstant; import com.xiaojukeji.kafka.manager.common.constant.KafkaMetricsCollections; @@ -81,6 +84,9 @@ public class TopicManagerServiceImpl implements TopicManagerService { @Autowired private RegionService regionService; + @Autowired + private OperateRecordService operateRecordService; + @Override public List listAll() { try { @@ -341,6 +347,12 @@ public class TopicManagerServiceImpl implements TopicManagerService { if (ValidateUtils.isNull(topicDO)) { return ResultStatus.TOPIC_NOT_EXIST; } + + Map content = new HashMap<>(2); + content.put("clusterId", clusterId); + content.put("topicName", topicName); + recordOperation(content, topicName, operator); + topicDO.setDescription(description); if (topicDao.updateByName(topicDO) > 0) { return ResultStatus.SUCCESS; @@ -364,6 +376,12 @@ public class TopicManagerServiceImpl implements TopicManagerService { return ResultStatus.APP_NOT_EXIST; } + Map content = new HashMap<>(4); + content.put("clusterId", clusterId); + content.put("topicName", topicName); + content.put("appId", appId); + recordOperation(content, topicName, operator); + TopicDO topicDO = topicDao.getByTopicName(clusterId, topicName); if (ValidateUtils.isNull(topicDO)) { // 不存在, 则需要插入 @@ -394,6 +412,16 @@ public class TopicManagerServiceImpl implements TopicManagerService { return ResultStatus.MYSQL_ERROR; } + private void recordOperation(Map content, String topicName, String operator) { + OperateRecordDO operateRecordDO = new OperateRecordDO(); + operateRecordDO.setModuleId(ModuleEnum.TOPIC.getCode()); + operateRecordDO.setOperateId(OperateEnum.EDIT.getCode()); + operateRecordDO.setResource(topicName); + operateRecordDO.setContent(JSONObject.toJSONString(content)); + operateRecordDO.setOperator(operator); + operateRecordService.insert(operateRecordDO); + } + @Override public int deleteByTopicName(Long clusterId, String topicName) { try { From 02d6463faa0dc7d9fb11ddb94828389db1d60df3 Mon Sep 17 00:00:00 2001 From: 17hao Date: Sat, 6 Feb 2021 18:43:36 +0800 Subject: [PATCH 21/33] Using JsonUtils instead of fastjson --- kafka-manager-common/pom.xml | 7 +++++++ .../manager/common/utils/JsonUtilsTest.java | 18 ++++++++++++++++++ .../service/impl/TopicManagerServiceImpl.java | 3 +-- 3 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 kafka-manager-common/src/test/java/com/xiaojukeji/kafka/manager/common/utils/JsonUtilsTest.java diff --git a/kafka-manager-common/pom.xml b/kafka-manager-common/pom.xml index 67fbcdbd..7316d5d0 100644 --- a/kafka-manager-common/pom.xml +++ b/kafka-manager-common/pom.xml @@ -104,5 +104,12 @@ javax.servlet javax.servlet-api + + + junit + junit + 4.12 + test + \ No newline at end of file diff --git a/kafka-manager-common/src/test/java/com/xiaojukeji/kafka/manager/common/utils/JsonUtilsTest.java b/kafka-manager-common/src/test/java/com/xiaojukeji/kafka/manager/common/utils/JsonUtilsTest.java new file mode 100644 index 00000000..1d338015 --- /dev/null +++ b/kafka-manager-common/src/test/java/com/xiaojukeji/kafka/manager/common/utils/JsonUtilsTest.java @@ -0,0 +1,18 @@ +package com.xiaojukeji.kafka.manager.common.utils; + +import org.junit.Assert; +import org.junit.Test; + +import java.util.HashMap; +import java.util.Map; + +public class JsonUtilsTest { + @Test + public void testMapToJsonString() { + Map map = new HashMap<>(); + map.put("key", "value"); + map.put("int", 1); + String expectRes = "{\"key\":\"value\",\"int\":1}"; + Assert.assertEquals(expectRes, JsonUtils.toJSONString(map)); + } +} diff --git a/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/impl/TopicManagerServiceImpl.java b/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/impl/TopicManagerServiceImpl.java index 5709fffc..6ee9a499 100644 --- a/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/impl/TopicManagerServiceImpl.java +++ b/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/impl/TopicManagerServiceImpl.java @@ -1,6 +1,5 @@ package com.xiaojukeji.kafka.manager.service.service.impl; -import com.alibaba.fastjson.JSONObject; import com.xiaojukeji.kafka.manager.common.bizenum.KafkaClientEnum; import com.xiaojukeji.kafka.manager.common.bizenum.ModuleEnum; import com.xiaojukeji.kafka.manager.common.bizenum.OperateEnum; @@ -417,7 +416,7 @@ public class TopicManagerServiceImpl implements TopicManagerService { operateRecordDO.setModuleId(ModuleEnum.TOPIC.getCode()); operateRecordDO.setOperateId(OperateEnum.EDIT.getCode()); operateRecordDO.setResource(topicName); - operateRecordDO.setContent(JSONObject.toJSONString(content)); + operateRecordDO.setContent(JsonUtils.toJSONString(content)); operateRecordDO.setOperator(operator); operateRecordService.insert(operateRecordDO); } From 0668debec649132420d2af1c5a74fed00b9ec5ef Mon Sep 17 00:00:00 2001 From: 17hao Date: Sat, 6 Feb 2021 18:46:47 +0800 Subject: [PATCH 22/33] Update pom.xml --- kafka-manager-common/pom.xml | 2 -- 1 file changed, 2 deletions(-) diff --git a/kafka-manager-common/pom.xml b/kafka-manager-common/pom.xml index 7316d5d0..6a8ff0cb 100644 --- a/kafka-manager-common/pom.xml +++ b/kafka-manager-common/pom.xml @@ -108,8 +108,6 @@ junit junit - 4.12 - test \ No newline at end of file From 70c237da72791ac39b39052826d70cf64a24c87e Mon Sep 17 00:00:00 2001 From: 17hao Date: Sun, 7 Feb 2021 13:23:22 +0800 Subject: [PATCH 23/33] Tracking changes applied to Kafka cluster --- .../service/service/ClusterService.java | 2 +- .../service/impl/ClusterServiceImpl.java | 20 ++++++++++- .../service/utils/ChangeTrackingUtils.java | 35 +++++++++++++++++++ .../versionone/op/OpClusterController.java | 2 +- 4 files changed, 56 insertions(+), 3 deletions(-) create mode 100644 kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/utils/ChangeTrackingUtils.java diff --git a/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/ClusterService.java b/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/ClusterService.java index 004a3f51..b2c5f7b2 100644 --- a/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/ClusterService.java +++ b/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/ClusterService.java @@ -43,7 +43,7 @@ public interface ClusterService { ClusterNameDTO getClusterName(Long logicClusterId); - ResultStatus deleteById(Long clusterId); + ResultStatus deleteById(Long clusterId, String operator); /** * 获取优先被选举为controller的broker diff --git a/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/impl/ClusterServiceImpl.java b/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/impl/ClusterServiceImpl.java index fd28308d..74640e2a 100644 --- a/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/impl/ClusterServiceImpl.java +++ b/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/impl/ClusterServiceImpl.java @@ -1,6 +1,8 @@ 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.entity.Result; import com.xiaojukeji.kafka.manager.common.entity.ResultStatus; import com.xiaojukeji.kafka.manager.common.entity.ao.ClusterDetailDTO; @@ -19,6 +21,7 @@ import com.xiaojukeji.kafka.manager.service.service.ClusterService; import com.xiaojukeji.kafka.manager.service.service.ConsumerService; import com.xiaojukeji.kafka.manager.service.service.RegionService; import com.xiaojukeji.kafka.manager.service.service.ZookeeperService; +import com.xiaojukeji.kafka.manager.service.utils.ChangeTrackingUtils; import com.xiaojukeji.kafka.manager.service.utils.ConfigUtils; import org.apache.zookeeper.ZooKeeper; import org.slf4j.Logger; @@ -65,6 +68,9 @@ public class ClusterServiceImpl implements ClusterService { @Autowired private ZookeeperService zookeeperService; + @Autowired + private ChangeTrackingUtils changeTrackingUtils; + @Override public ResultStatus addNew(ClusterDO clusterDO, String operator) { if (ValidateUtils.isNull(clusterDO) || ValidateUtils.isNull(operator)) { @@ -74,6 +80,11 @@ public class ClusterServiceImpl implements ClusterService { return ResultStatus.ZOOKEEPER_CONNECT_FAILED; } try { + Map content = new HashMap<>(); + content.put("zk address", clusterDO.getZookeeper()); + content.put("bootstrap servers", clusterDO.getBootstrapServers()); + content.put("security properties", clusterDO.getSecurityProperties()); + changeTrackingUtils.saveOperateRecord(operator, ModuleEnum.CLUSTER, clusterDO.getClusterName(), OperateEnum.ADD, content); if (clusterDao.insert(clusterDO) <= 0) { LOGGER.error("add new cluster failed, clusterDO:{}.", clusterDO); return ResultStatus.MYSQL_ERROR; @@ -104,6 +115,10 @@ public class ClusterServiceImpl implements ClusterService { return ResultStatus.CHANGE_ZOOKEEPER_FORBIDDEN; } clusterDO.setStatus(originClusterDO.getStatus()); + Map content = new HashMap<>(); + content.put("cluster id", clusterDO.getId().toString()); + content.put("security properties", clusterDO.getSecurityProperties()); + changeTrackingUtils.saveOperateRecord(operator, ModuleEnum.CLUSTER, clusterDO.getClusterName(), OperateEnum.EDIT, content); return updateById(clusterDO); } @@ -254,12 +269,15 @@ public class ClusterServiceImpl implements ClusterService { } @Override - public ResultStatus deleteById(Long clusterId) { + public ResultStatus deleteById(Long clusterId, String operator) { List regionDOList = regionService.getByClusterId(clusterId); if (!ValidateUtils.isEmptyList(regionDOList)) { return ResultStatus.OPERATION_FORBIDDEN; } try { + Map content = new HashMap<>(); + content.put("cluster id", clusterId.toString()); + changeTrackingUtils.saveOperateRecord(operator, ModuleEnum.CLUSTER, getClusterName(clusterId).getPhysicalClusterName(), OperateEnum.DELETE, content); if (clusterDao.deleteById(clusterId) <= 0) { LOGGER.error("delete cluster failed, clusterId:{}.", clusterId); return ResultStatus.MYSQL_ERROR; diff --git a/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/utils/ChangeTrackingUtils.java b/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/utils/ChangeTrackingUtils.java new file mode 100644 index 00000000..3fac4ffc --- /dev/null +++ b/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/utils/ChangeTrackingUtils.java @@ -0,0 +1,35 @@ +package com.xiaojukeji.kafka.manager.service.utils; + +import com.xiaojukeji.kafka.manager.common.bizenum.ModuleEnum; +import com.xiaojukeji.kafka.manager.common.bizenum.OperateEnum; +import com.xiaojukeji.kafka.manager.common.entity.pojo.OperateRecordDO; +import com.xiaojukeji.kafka.manager.common.utils.JsonUtils; +import com.xiaojukeji.kafka.manager.service.service.OperateRecordService; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.Map; + +/** + * Track changes applied to Kafka. + */ +public class ChangeTrackingUtils { + private final OperateRecordService operateRecordService; + + @Autowired + public ChangeTrackingUtils(OperateRecordService operateRecordService) { + this.operateRecordService = operateRecordService; + } + + /** + * Saving operate record to database. + */ + public void saveOperateRecord(String operator, ModuleEnum module, String resourceName, OperateEnum operate, Map content) { + OperateRecordDO operateRecordDO = new OperateRecordDO(); + operateRecordDO.setOperator(operator); + operateRecordDO.setModuleId(module.getCode()); + operateRecordDO.setResource(resourceName); + operateRecordDO.setOperateId(operate.getCode()); + operateRecordDO.setContent(JsonUtils.toJSONString(content)); + operateRecordService.insert(operateRecordDO); + } +} diff --git a/kafka-manager-web/src/main/java/com/xiaojukeji/kafka/manager/web/api/versionone/op/OpClusterController.java b/kafka-manager-web/src/main/java/com/xiaojukeji/kafka/manager/web/api/versionone/op/OpClusterController.java index 21547aa9..07b7dbc4 100644 --- a/kafka-manager-web/src/main/java/com/xiaojukeji/kafka/manager/web/api/versionone/op/OpClusterController.java +++ b/kafka-manager-web/src/main/java/com/xiaojukeji/kafka/manager/web/api/versionone/op/OpClusterController.java @@ -43,7 +43,7 @@ public class OpClusterController { @RequestMapping(value = "clusters", method = RequestMethod.DELETE) @ResponseBody public Result delete(@RequestParam(value = "clusterId") Long clusterId) { - return Result.buildFrom(clusterService.deleteById(clusterId)); + return Result.buildFrom(clusterService.deleteById(clusterId, SpringTool.getUserName())); } @ApiOperation(value = "修改集群信息") From 832320abc616cd12122142150c073079d80d0211 Mon Sep 17 00:00:00 2001 From: 17hao Date: Sun, 7 Feb 2021 14:20:57 +0800 Subject: [PATCH 24/33] Improve code's cohesion && save jmx properties --- .../service/service/OperateRecordService.java | 5 +++ .../service/impl/ClusterServiceImpl.java | 16 ++++----- .../impl/OperateRecordServiceImpl.java | 15 ++++++++ .../service/utils/ChangeTrackingUtils.java | 35 ------------------- 4 files changed, 27 insertions(+), 44 deletions(-) delete mode 100644 kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/utils/ChangeTrackingUtils.java diff --git a/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/OperateRecordService.java b/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/OperateRecordService.java index c5007ac6..5b2909ca 100644 --- a/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/OperateRecordService.java +++ b/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/OperateRecordService.java @@ -1,9 +1,12 @@ package com.xiaojukeji.kafka.manager.service.service; +import com.xiaojukeji.kafka.manager.common.bizenum.ModuleEnum; +import com.xiaojukeji.kafka.manager.common.bizenum.OperateEnum; import com.xiaojukeji.kafka.manager.common.entity.dto.rd.OperateRecordDTO; import com.xiaojukeji.kafka.manager.common.entity.pojo.OperateRecordDO; import java.util.List; +import java.util.Map; /** * @author zhongyuankai @@ -12,5 +15,7 @@ import java.util.List; public interface OperateRecordService { int insert(OperateRecordDO operateRecordDO); + int insert(String operator, ModuleEnum module, String resourceName, OperateEnum operate, Map content); + List queryByCondt(OperateRecordDTO dto); } diff --git a/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/impl/ClusterServiceImpl.java b/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/impl/ClusterServiceImpl.java index 74640e2a..609c8cf9 100644 --- a/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/impl/ClusterServiceImpl.java +++ b/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/impl/ClusterServiceImpl.java @@ -17,11 +17,7 @@ import com.xiaojukeji.kafka.manager.dao.ClusterMetricsDao; 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.ClusterService; -import com.xiaojukeji.kafka.manager.service.service.ConsumerService; -import com.xiaojukeji.kafka.manager.service.service.RegionService; -import com.xiaojukeji.kafka.manager.service.service.ZookeeperService; -import com.xiaojukeji.kafka.manager.service.utils.ChangeTrackingUtils; +import com.xiaojukeji.kafka.manager.service.service.*; import com.xiaojukeji.kafka.manager.service.utils.ConfigUtils; import org.apache.zookeeper.ZooKeeper; import org.slf4j.Logger; @@ -69,7 +65,7 @@ public class ClusterServiceImpl implements ClusterService { private ZookeeperService zookeeperService; @Autowired - private ChangeTrackingUtils changeTrackingUtils; + private OperateRecordService operateRecordService; @Override public ResultStatus addNew(ClusterDO clusterDO, String operator) { @@ -84,7 +80,8 @@ public class ClusterServiceImpl implements ClusterService { content.put("zk address", clusterDO.getZookeeper()); content.put("bootstrap servers", clusterDO.getBootstrapServers()); content.put("security properties", clusterDO.getSecurityProperties()); - changeTrackingUtils.saveOperateRecord(operator, ModuleEnum.CLUSTER, clusterDO.getClusterName(), OperateEnum.ADD, content); + content.put("jmx properties", clusterDO.getJmxProperties()); + operateRecordService.insert(operator, ModuleEnum.CLUSTER, clusterDO.getClusterName(), OperateEnum.ADD, content); if (clusterDao.insert(clusterDO) <= 0) { LOGGER.error("add new cluster failed, clusterDO:{}.", clusterDO); return ResultStatus.MYSQL_ERROR; @@ -118,7 +115,8 @@ public class ClusterServiceImpl implements ClusterService { Map content = new HashMap<>(); content.put("cluster id", clusterDO.getId().toString()); content.put("security properties", clusterDO.getSecurityProperties()); - changeTrackingUtils.saveOperateRecord(operator, ModuleEnum.CLUSTER, clusterDO.getClusterName(), OperateEnum.EDIT, content); + content.put("jmx properties", clusterDO.getJmxProperties()); + operateRecordService.insert(operator, ModuleEnum.CLUSTER, clusterDO.getClusterName(), OperateEnum.EDIT, content); return updateById(clusterDO); } @@ -277,7 +275,7 @@ public class ClusterServiceImpl implements ClusterService { try { Map content = new HashMap<>(); content.put("cluster id", clusterId.toString()); - changeTrackingUtils.saveOperateRecord(operator, ModuleEnum.CLUSTER, getClusterName(clusterId).getPhysicalClusterName(), OperateEnum.DELETE, content); + operateRecordService.insert(operator, ModuleEnum.CLUSTER, getClusterName(clusterId).getPhysicalClusterName(), OperateEnum.DELETE, content); if (clusterDao.deleteById(clusterId) <= 0) { LOGGER.error("delete cluster failed, clusterId:{}.", clusterId); return ResultStatus.MYSQL_ERROR; diff --git a/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/impl/OperateRecordServiceImpl.java b/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/impl/OperateRecordServiceImpl.java index 47702eaa..290bbae5 100644 --- a/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/impl/OperateRecordServiceImpl.java +++ b/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/impl/OperateRecordServiceImpl.java @@ -1,7 +1,10 @@ package com.xiaojukeji.kafka.manager.service.service.impl; +import com.xiaojukeji.kafka.manager.common.bizenum.ModuleEnum; +import com.xiaojukeji.kafka.manager.common.bizenum.OperateEnum; import com.xiaojukeji.kafka.manager.common.entity.dto.rd.OperateRecordDTO; import com.xiaojukeji.kafka.manager.common.entity.pojo.OperateRecordDO; +import com.xiaojukeji.kafka.manager.common.utils.JsonUtils; import com.xiaojukeji.kafka.manager.common.utils.ValidateUtils; import com.xiaojukeji.kafka.manager.dao.OperateRecordDao; import com.xiaojukeji.kafka.manager.service.service.OperateRecordService; @@ -10,6 +13,7 @@ import org.springframework.stereotype.Service; import java.util.Date; import java.util.List; +import java.util.Map; /** * @author zhongyuankai @@ -25,6 +29,17 @@ public class OperateRecordServiceImpl implements OperateRecordService { return operateRecordDao.insert(operateRecordDO); } + @Override + public int insert(String operator, ModuleEnum module, String resourceName, OperateEnum operate, Map content) { + OperateRecordDO operateRecordDO = new OperateRecordDO(); + operateRecordDO.setOperator(operator); + operateRecordDO.setModuleId(module.getCode()); + operateRecordDO.setResource(resourceName); + operateRecordDO.setOperateId(operate.getCode()); + operateRecordDO.setContent(JsonUtils.toJSONString(content)); + return insert(operateRecordDO); + } + @Override public List queryByCondt(OperateRecordDTO dto) { return operateRecordDao.queryByCondt( diff --git a/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/utils/ChangeTrackingUtils.java b/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/utils/ChangeTrackingUtils.java deleted file mode 100644 index 3fac4ffc..00000000 --- a/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/utils/ChangeTrackingUtils.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.xiaojukeji.kafka.manager.service.utils; - -import com.xiaojukeji.kafka.manager.common.bizenum.ModuleEnum; -import com.xiaojukeji.kafka.manager.common.bizenum.OperateEnum; -import com.xiaojukeji.kafka.manager.common.entity.pojo.OperateRecordDO; -import com.xiaojukeji.kafka.manager.common.utils.JsonUtils; -import com.xiaojukeji.kafka.manager.service.service.OperateRecordService; -import org.springframework.beans.factory.annotation.Autowired; - -import java.util.Map; - -/** - * Track changes applied to Kafka. - */ -public class ChangeTrackingUtils { - private final OperateRecordService operateRecordService; - - @Autowired - public ChangeTrackingUtils(OperateRecordService operateRecordService) { - this.operateRecordService = operateRecordService; - } - - /** - * Saving operate record to database. - */ - public void saveOperateRecord(String operator, ModuleEnum module, String resourceName, OperateEnum operate, Map content) { - OperateRecordDO operateRecordDO = new OperateRecordDO(); - operateRecordDO.setOperator(operator); - operateRecordDO.setModuleId(module.getCode()); - operateRecordDO.setResource(resourceName); - operateRecordDO.setOperateId(operate.getCode()); - operateRecordDO.setContent(JsonUtils.toJSONString(content)); - operateRecordService.insert(operateRecordDO); - } -} From c1afc07955fab9d6bf267c44dae743136ec8302e Mon Sep 17 00:00:00 2001 From: 17hao Date: Sun, 7 Feb 2021 15:16:26 +0800 Subject: [PATCH 25/33] Tracking changes applied to app --- .../service/service/gateway/AppService.java | 2 +- .../service/gateway/impl/AppServiceImpl.java | 19 ++++++++++++++++--- .../manager/bpm/order/impl/ApplyAppOrder.java | 2 +- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/gateway/AppService.java b/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/gateway/AppService.java index c78946b6..82aa5513 100644 --- a/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/gateway/AppService.java +++ b/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/gateway/AppService.java @@ -17,7 +17,7 @@ public interface AppService { * @param appDO appDO * @return int */ - ResultStatus addApp(AppDO appDO); + ResultStatus addApp(AppDO appDO, String operator); /** * 删除数据 diff --git a/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/gateway/impl/AppServiceImpl.java b/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/gateway/impl/AppServiceImpl.java index 09b4a071..200b3cf4 100644 --- a/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/gateway/impl/AppServiceImpl.java +++ b/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/gateway/impl/AppServiceImpl.java @@ -60,10 +60,8 @@ public class AppServiceImpl implements AppService { @Autowired private OperateRecordService operateRecordService; - - @Override - public ResultStatus addApp(AppDO appDO) { + public ResultStatus addApp(AppDO appDO, String operator) { try { if (appDao.insert(appDO) < 1) { LOGGER.warn("class=AppServiceImpl||method=addApp||AppDO={}||msg=add fail,{}",appDO,ResultStatus.MYSQL_ERROR.getMessage()); @@ -75,6 +73,15 @@ public class AppServiceImpl implements AppService { kafkaUserDO.setOperation(OperationStatusEnum.CREATE.getCode()); kafkaUserDO.setUserType(0); kafkaUserDao.insert(kafkaUserDO); + + Map content = new HashMap<>(); + content.put("appId", appDO.getAppId()); + content.put("name", appDO.getName()); + content.put("applicant", appDO.getApplicant()); + content.put("password", appDO.getPassword()); + content.put("principals", appDO.getPrincipals()); + content.put("description", appDO.getDescription()); + operateRecordService.insert(operator, ModuleEnum.APP, appDO.getName(), OperateEnum.ADD, content); } catch (DuplicateKeyException e) { LOGGER.error("class=AppServiceImpl||method=addApp||errMsg={}||appDO={}|", e.getMessage(), appDO, e); return ResultStatus.RESOURCE_ALREADY_EXISTED; @@ -141,6 +148,12 @@ public class AppServiceImpl implements AppService { appDO.setDescription(dto.getDescription()); if (appDao.updateById(appDO) > 0) { + Map content = new HashMap<>(); + content.put("appId", appDO.getAppId()); + content.put("name", appDO.getName()); + content.put("principals", appDO.getPrincipals()); + content.put("description", appDO.getDescription()); + operateRecordService.insert(operator, ModuleEnum.APP, appDO.getName(), OperateEnum.EDIT, content); return ResultStatus.SUCCESS; } } catch (DuplicateKeyException e) { diff --git a/kafka-manager-extends/kafka-manager-bpm/src/main/java/com/xiaojukeji/kafka/manager/bpm/order/impl/ApplyAppOrder.java b/kafka-manager-extends/kafka-manager-bpm/src/main/java/com/xiaojukeji/kafka/manager/bpm/order/impl/ApplyAppOrder.java index d902abed..1528ada8 100644 --- a/kafka-manager-extends/kafka-manager-bpm/src/main/java/com/xiaojukeji/kafka/manager/bpm/order/impl/ApplyAppOrder.java +++ b/kafka-manager-extends/kafka-manager-bpm/src/main/java/com/xiaojukeji/kafka/manager/bpm/order/impl/ApplyAppOrder.java @@ -87,6 +87,6 @@ public class ApplyAppOrder extends AbstractAppOrder { appDO.setDescription(orderDO.getDescription()); appDO.generateAppIdAndPassword(orderDO.getId(), configUtils.getIdc()); appDO.setType(0); - return appService.addApp(appDO); + return appService.addApp(appDO, userName); } } From 8e5f93be1c20704c8fd6c893fec5e5450c234d33 Mon Sep 17 00:00:00 2001 From: 17hao Date: Sun, 7 Feb 2021 15:54:41 +0800 Subject: [PATCH 26/33] Tracking delete account --- .../kafka/manager/account/AccountService.java | 2 +- .../manager/account/impl/AccountServiceImpl.java | 11 ++++++++++- .../web/api/versionone/rd/RdAccountController.java | 3 ++- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/kafka-manager-extends/kafka-manager-account/src/main/java/com/xiaojukeji/kafka/manager/account/AccountService.java b/kafka-manager-extends/kafka-manager-account/src/main/java/com/xiaojukeji/kafka/manager/account/AccountService.java index 8b208385..7f4974ea 100644 --- a/kafka-manager-extends/kafka-manager-account/src/main/java/com/xiaojukeji/kafka/manager/account/AccountService.java +++ b/kafka-manager-extends/kafka-manager-account/src/main/java/com/xiaojukeji/kafka/manager/account/AccountService.java @@ -33,7 +33,7 @@ public interface AccountService { * @param username 用户名 * @return */ - ResultStatus deleteByName(String username); + ResultStatus deleteByName(String username, String operator); /** * 更新账号 diff --git a/kafka-manager-extends/kafka-manager-account/src/main/java/com/xiaojukeji/kafka/manager/account/impl/AccountServiceImpl.java b/kafka-manager-extends/kafka-manager-account/src/main/java/com/xiaojukeji/kafka/manager/account/impl/AccountServiceImpl.java index 39d773ed..e4d03c23 100644 --- a/kafka-manager-extends/kafka-manager-account/src/main/java/com/xiaojukeji/kafka/manager/account/impl/AccountServiceImpl.java +++ b/kafka-manager-extends/kafka-manager-account/src/main/java/com/xiaojukeji/kafka/manager/account/impl/AccountServiceImpl.java @@ -6,6 +6,8 @@ import com.xiaojukeji.kafka.manager.account.AccountService; import com.xiaojukeji.kafka.manager.account.common.EnterpriseStaff; import com.xiaojukeji.kafka.manager.account.component.AbstractEnterpriseStaffService; import com.xiaojukeji.kafka.manager.common.bizenum.AccountRoleEnum; +import com.xiaojukeji.kafka.manager.common.bizenum.ModuleEnum; +import com.xiaojukeji.kafka.manager.common.bizenum.OperateEnum; import com.xiaojukeji.kafka.manager.common.constant.Constant; import com.xiaojukeji.kafka.manager.common.entity.Result; import com.xiaojukeji.kafka.manager.common.entity.ResultStatus; @@ -15,6 +17,7 @@ import com.xiaojukeji.kafka.manager.common.utils.EncryptUtil; import com.xiaojukeji.kafka.manager.common.utils.ValidateUtils; import com.xiaojukeji.kafka.manager.dao.AccountDao; import com.xiaojukeji.kafka.manager.service.service.ConfigService; +import com.xiaojukeji.kafka.manager.service.service.OperateRecordService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -48,6 +51,9 @@ public class AccountServiceImpl implements AccountService { @Autowired private AbstractEnterpriseStaffService enterpriseStaffService; + @Autowired + private OperateRecordService operateRecordService; + /** * 用户组织信息 * @@ -82,9 +88,12 @@ public class AccountServiceImpl implements AccountService { } @Override - public ResultStatus deleteByName(String username) { + public ResultStatus deleteByName(String username, String operator) { try { if (accountDao.deleteByName(username) > 0) { + Map content = new HashMap<>(); + content.put("username", username); + operateRecordService.insert(operator, ModuleEnum.AUTHORITY, username, OperateEnum.DELETE, content); return ResultStatus.SUCCESS; } } catch (Exception e) { diff --git a/kafka-manager-web/src/main/java/com/xiaojukeji/kafka/manager/web/api/versionone/rd/RdAccountController.java b/kafka-manager-web/src/main/java/com/xiaojukeji/kafka/manager/web/api/versionone/rd/RdAccountController.java index 5efbbfce..2ca29082 100644 --- a/kafka-manager-web/src/main/java/com/xiaojukeji/kafka/manager/web/api/versionone/rd/RdAccountController.java +++ b/kafka-manager-web/src/main/java/com/xiaojukeji/kafka/manager/web/api/versionone/rd/RdAccountController.java @@ -2,6 +2,7 @@ package com.xiaojukeji.kafka.manager.web.api.versionone.rd; import com.xiaojukeji.kafka.manager.common.entity.ResultStatus; import com.xiaojukeji.kafka.manager.common.entity.vo.common.AccountVO; +import com.xiaojukeji.kafka.manager.common.utils.SpringTool; import com.xiaojukeji.kafka.manager.common.utils.ValidateUtils; import com.xiaojukeji.kafka.manager.common.constant.ApiPrefix; import com.xiaojukeji.kafka.manager.web.converters.AccountConverter; @@ -46,7 +47,7 @@ public class RdAccountController { @RequestMapping(value = "accounts", method = RequestMethod.DELETE) @ResponseBody public Result deleteAccount(@RequestParam("username") String username) { - ResultStatus rs = accountService.deleteByName(username); + ResultStatus rs = accountService.deleteByName(username, SpringTool.getUserName()); return Result.buildFrom(rs); } From 5efd4241726e9c46a338578c112b4fa88d9adbcc Mon Sep 17 00:00:00 2001 From: zengqiao Date: Tue, 9 Feb 2021 11:20:56 +0800 Subject: [PATCH 27/33] version 2.3.0 --- .../bizenum/TopicExpiredStatusEnum.java | 32 +++ .../kafka/manager/common/entity/Result.java | 10 +- .../manager/common/entity/ResultStatus.java | 4 + .../common/entity/ao/ClusterDetailDTO.java | 13 +- .../SinkTopicRequestTimeMetricsConfig.java | 57 ----- .../op/ControllerPreferredCandidateDTO.java | 45 ++++ .../common/entity/dto/rd/ClusterDTO.java | 5 +- .../entity/pojo/gateway/GatewayConfigDO.java | 11 + .../entity/vo/op/expert/ExpiredTopicVO.java | 2 +- .../common/entity/vo/rd/GatewayConfigVO.java | 12 + .../kafka/manager/common/utils/JsonUtils.java | 7 + .../common/utils/jmx/JmxConnectorWrap.java | 2 +- .../manager/common/zookeeper/ZkPathUtil.java | 10 +- .../cache/PhysicalClusterMetadataManager.java | 14 +- .../service/service/ClusterService.java | 17 ++ .../service/service/ZookeeperService.java | 16 ++ .../impl/GatewayConfigServiceImpl.java | 15 +- .../service/impl/ClusterServiceImpl.java | 46 +++- .../service/impl/ConsumerServiceImpl.java | 2 +- .../service/impl/ZookeeperServiceImpl.java | 54 +++++ .../manager/service/utils/TopicCommands.java | 4 +- .../service/zookeeper/TopicStateListener.java | 22 +- .../kafka/manager/dao/TopicDao.java | 2 - .../kafka/manager/dao/gateway/AppDao.java | 4 - .../manager/dao/gateway/AuthorityDao.java | 4 - .../manager/dao/gateway/impl/AppDaoImpl.java | 26 +- .../dao/gateway/impl/AuthorityDaoImpl.java | 46 ++-- .../kafka/manager/dao/impl/TopicDaoImpl.java | 30 ++- .../kafka/manager/task/Constant.java | 5 + .../kafka/manager/task/DaoBackgroundTask.java | 41 ++++ .../resources/mapper/GatewayConfigDao.xml | 8 +- .../OrderExtensionAddGatewayConfigDTO.java | 12 + .../OrderExtensionModifyGatewayConfigDTO.java | 12 + .../kafka/manager/kcm/common/Constant.java | 18 ++ .../common/bizenum/ClusterTaskActionEnum.java | 39 +-- .../kcm/common/entry/ao/ClusterTaskLog.java | 24 ++ .../kcm/common/entry/ao/CreationTaskData.java | 4 +- .../kcm/component/agent/AbstractAgent.java | 75 +++++- .../manager/kcm/component/agent/n9e/N9e.java | 222 +++++++----------- .../agent/n9e/entry/N9eCreationTask.java | 151 ++++++++++++ ...eTaskResultDTO.java => N9eTaskResult.java} | 2 +- .../agent/n9e/entry/N9eTaskStdoutLog.java | 35 +++ .../n9e/entry/bizenum/N9eTaskStatusEnum.java | 59 +++++ .../kcm/component/storage/s3/S3Service.java | 7 +- .../kcm/impl/ClusterTaskServiceImpl.java | 108 +++++---- .../kcm/impl/KafkaFileServiceImpl.java | 4 +- .../gateway/GatewayHeartbeatController.java | 2 +- .../GatewayServiceDiscoveryController.java | 13 +- .../versionone/op/OpClusterController.java | 38 +-- .../op/OpGatewayConfigController.java | 17 +- .../rd/RdGatewayConfigController.java | 4 +- .../versionone/rd/RdKafkaFileController.java | 12 +- .../rd/RdOperateRecordController.java | 3 +- .../web/converters/GatewayModelConverter.java | 17 +- .../web/inteceptor/WebMetricsInterceptor.java | 2 +- .../src/main/resources/application.yml | 4 +- 56 files changed, 1018 insertions(+), 432 deletions(-) create mode 100644 kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/bizenum/TopicExpiredStatusEnum.java delete mode 100644 kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/entity/ao/config/SinkTopicRequestTimeMetricsConfig.java create mode 100644 kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/entity/dto/op/ControllerPreferredCandidateDTO.java create mode 100644 kafka-manager-dao/src/main/java/com/xiaojukeji/kafka/manager/task/Constant.java create mode 100644 kafka-manager-dao/src/main/java/com/xiaojukeji/kafka/manager/task/DaoBackgroundTask.java create mode 100644 kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/common/Constant.java create mode 100644 kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/common/entry/ao/ClusterTaskLog.java create mode 100644 kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/component/agent/n9e/entry/N9eCreationTask.java rename kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/component/agent/n9e/entry/{N9eTaskResultDTO.java => N9eTaskResult.java} (99%) create mode 100644 kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/component/agent/n9e/entry/N9eTaskStdoutLog.java create mode 100644 kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/component/agent/n9e/entry/bizenum/N9eTaskStatusEnum.java diff --git a/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/bizenum/TopicExpiredStatusEnum.java b/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/bizenum/TopicExpiredStatusEnum.java new file mode 100644 index 00000000..bac44235 --- /dev/null +++ b/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/bizenum/TopicExpiredStatusEnum.java @@ -0,0 +1,32 @@ +package com.xiaojukeji.kafka.manager.common.bizenum; + +/** + * 过期Topic状态 + * @author zengqiao + * @date 21/01/25 + */ +public enum TopicExpiredStatusEnum { + ALREADY_NOTIFIED_AND_DELETED(-2, "已通知, 已下线"), + ALREADY_NOTIFIED_AND_CAN_DELETE(-1, "已通知, 可下线"), + ALREADY_EXPIRED_AND_WAIT_NOTIFY(0, "已过期, 待通知"), + ALREADY_NOTIFIED_AND_WAIT_RESPONSE(1, "已通知, 待反馈"), + + ; + + private int status; + + private String message; + + TopicExpiredStatusEnum(int status, String message) { + this.status = status; + this.message = message; + } + + public int getStatus() { + return status; + } + + public String getMessage() { + return message; + } +} diff --git a/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/entity/Result.java b/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/entity/Result.java index 0fb38302..323e9ec9 100644 --- a/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/entity/Result.java +++ b/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/entity/Result.java @@ -97,7 +97,7 @@ public class Result implements Serializable { return result; } - public static Result buildFailure(String message) { + public static Result buildGatewayFailure(String message) { Result result = new Result(); result.setCode(ResultStatus.GATEWAY_INVALID_REQUEST.getCode()); result.setMessage(message); @@ -105,6 +105,14 @@ public class Result implements Serializable { return result; } + public static Result buildFailure(String message) { + Result result = new Result(); + result.setCode(ResultStatus.FAIL.getCode()); + result.setMessage(message); + result.setData(null); + return result; + } + public static Result buildFrom(ResultStatus resultStatus) { Result result = new Result(); result.setCode(resultStatus.getCode()); diff --git a/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/entity/ResultStatus.java b/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/entity/ResultStatus.java index 76e3aca8..94acb56d 100644 --- a/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/entity/ResultStatus.java +++ b/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/entity/ResultStatus.java @@ -12,6 +12,8 @@ public enum ResultStatus { SUCCESS(Constant.SUCCESS, "success"), + FAIL(1, "操作失败"), + /** * 操作错误[1000, 2000) * ------------------------------------------------------------------------------------------ @@ -91,6 +93,8 @@ public enum ResultStatus { ZOOKEEPER_CONNECT_FAILED(8020, "zookeeper connect failed"), ZOOKEEPER_READ_FAILED(8021, "zookeeper read failed"), + ZOOKEEPER_WRITE_FAILED(8022, "zookeeper write failed"), + ZOOKEEPER_DELETE_FAILED(8023, "zookeeper delete failed"), // 调用集群任务里面的agent失败 CALL_CLUSTER_TASK_AGENT_FAILED(8030, " call cluster task agent failed"), diff --git a/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/entity/ao/ClusterDetailDTO.java b/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/entity/ao/ClusterDetailDTO.java index 937d9cf8..2e903485 100644 --- a/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/entity/ao/ClusterDetailDTO.java +++ b/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/entity/ao/ClusterDetailDTO.java @@ -23,6 +23,8 @@ public class ClusterDetailDTO { private String securityProperties; + private String jmxProperties; + private Integer status; private Date gmtCreate; @@ -103,6 +105,14 @@ public class ClusterDetailDTO { this.securityProperties = securityProperties; } + public String getJmxProperties() { + return jmxProperties; + } + + public void setJmxProperties(String jmxProperties) { + this.jmxProperties = jmxProperties; + } + public Integer getStatus() { return status; } @@ -176,8 +186,9 @@ public class ClusterDetailDTO { ", bootstrapServers='" + bootstrapServers + '\'' + ", kafkaVersion='" + kafkaVersion + '\'' + ", idc='" + idc + '\'' + - ", mode='" + mode + '\'' + + ", mode=" + mode + ", securityProperties='" + securityProperties + '\'' + + ", jmxProperties='" + jmxProperties + '\'' + ", status=" + status + ", gmtCreate=" + gmtCreate + ", gmtModify=" + gmtModify + diff --git a/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/entity/ao/config/SinkTopicRequestTimeMetricsConfig.java b/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/entity/ao/config/SinkTopicRequestTimeMetricsConfig.java deleted file mode 100644 index 91faaba1..00000000 --- a/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/entity/ao/config/SinkTopicRequestTimeMetricsConfig.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.xiaojukeji.kafka.manager.common.entity.ao.config; - -/** - * @author zengqiao - * @date 20/9/7 - */ -public class SinkTopicRequestTimeMetricsConfig { - private Long clusterId; - - private String topicName; - - private Long startId; - - private Long step; - - public Long getClusterId() { - return clusterId; - } - - public void setClusterId(Long clusterId) { - this.clusterId = clusterId; - } - - public String getTopicName() { - return topicName; - } - - public void setTopicName(String topicName) { - this.topicName = topicName; - } - - public Long getStartId() { - return startId; - } - - public void setStartId(Long startId) { - this.startId = startId; - } - - public Long getStep() { - return step; - } - - public void setStep(Long step) { - this.step = step; - } - - @Override - public String toString() { - return "SinkTopicRequestTimeMetricsConfig{" + - "clusterId=" + clusterId + - ", topicName='" + topicName + '\'' + - ", startId=" + startId + - ", step=" + step + - '}'; - } -} \ No newline at end of file diff --git a/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/entity/dto/op/ControllerPreferredCandidateDTO.java b/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/entity/dto/op/ControllerPreferredCandidateDTO.java new file mode 100644 index 00000000..1b4c95b9 --- /dev/null +++ b/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/entity/dto/op/ControllerPreferredCandidateDTO.java @@ -0,0 +1,45 @@ +package com.xiaojukeji.kafka.manager.common.entity.dto.op; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +import java.util.List; + +/** + * @author zengqiao + * @date 21/01/24 + */ +@JsonIgnoreProperties(ignoreUnknown = true) +@ApiModel(description="优选为Controller的候选者") +public class ControllerPreferredCandidateDTO { + @ApiModelProperty(value="集群ID") + private Long clusterId; + + @ApiModelProperty(value="优选为controller的BrokerId") + private List brokerIdList; + + public Long getClusterId() { + return clusterId; + } + + public void setClusterId(Long clusterId) { + this.clusterId = clusterId; + } + + public List getBrokerIdList() { + return brokerIdList; + } + + public void setBrokerIdList(List brokerIdList) { + this.brokerIdList = brokerIdList; + } + + @Override + public String toString() { + return "ControllerPreferredCandidateDTO{" + + "clusterId=" + clusterId + + ", brokerIdList=" + brokerIdList + + '}'; + } +} diff --git a/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/entity/dto/rd/ClusterDTO.java b/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/entity/dto/rd/ClusterDTO.java index 0b6fcebb..7afc09c6 100644 --- a/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/entity/dto/rd/ClusterDTO.java +++ b/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/entity/dto/rd/ClusterDTO.java @@ -102,12 +102,11 @@ public class ClusterDTO { '}'; } - public Boolean legal() { + public boolean legal() { if (ValidateUtils.isNull(clusterName) || ValidateUtils.isNull(zookeeper) || ValidateUtils.isNull(idc) - || ValidateUtils.isNull(bootstrapServers) - ) { + || ValidateUtils.isNull(bootstrapServers)) { return false; } return true; diff --git a/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/entity/pojo/gateway/GatewayConfigDO.java b/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/entity/pojo/gateway/GatewayConfigDO.java index c0e96000..fa29c7cf 100644 --- a/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/entity/pojo/gateway/GatewayConfigDO.java +++ b/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/entity/pojo/gateway/GatewayConfigDO.java @@ -17,6 +17,8 @@ public class GatewayConfigDO { private Long version; + private String description; + private Date createTime; private Date modifyTime; @@ -61,6 +63,14 @@ public class GatewayConfigDO { this.version = version; } + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + public Date getCreateTime() { return createTime; } @@ -85,6 +95,7 @@ public class GatewayConfigDO { ", name='" + name + '\'' + ", value='" + value + '\'' + ", version=" + version + + ", description='" + description + '\'' + ", createTime=" + createTime + ", modifyTime=" + modifyTime + '}'; diff --git a/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/entity/vo/op/expert/ExpiredTopicVO.java b/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/entity/vo/op/expert/ExpiredTopicVO.java index 46c7a3a2..c4921259 100644 --- a/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/entity/vo/op/expert/ExpiredTopicVO.java +++ b/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/entity/vo/op/expert/ExpiredTopicVO.java @@ -28,7 +28,7 @@ public class ExpiredTopicVO { @ApiModelProperty(value = "负责人") private String principals; - @ApiModelProperty(value = "状态, -1:可下线, 0:过期待通知, 1+:已通知待反馈") + @ApiModelProperty(value = "状态, -1:已通知可下线, 0:过期待通知, 1+:已通知待反馈") private Integer status; public Long getClusterId() { diff --git a/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/entity/vo/rd/GatewayConfigVO.java b/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/entity/vo/rd/GatewayConfigVO.java index a0b402af..72314c31 100644 --- a/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/entity/vo/rd/GatewayConfigVO.java +++ b/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/entity/vo/rd/GatewayConfigVO.java @@ -26,6 +26,9 @@ public class GatewayConfigVO { @ApiModelProperty(value="版本") private Long version; + @ApiModelProperty(value="描述说明") + private String description; + @ApiModelProperty(value="创建时间") private Date createTime; @@ -72,6 +75,14 @@ public class GatewayConfigVO { this.version = version; } + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + public Date getCreateTime() { return createTime; } @@ -96,6 +107,7 @@ public class GatewayConfigVO { ", name='" + name + '\'' + ", value='" + value + '\'' + ", version=" + version + + ", description='" + description + '\'' + ", createTime=" + createTime + ", modifyTime=" + modifyTime + '}'; diff --git a/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/utils/JsonUtils.java b/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/utils/JsonUtils.java index 46d177ad..283d59c5 100644 --- a/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/utils/JsonUtils.java +++ b/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/utils/JsonUtils.java @@ -60,6 +60,13 @@ public class JsonUtils { return JSON.parseObject(src, clazz); } + public static List stringToArrObj(String src, Class clazz) { + if (ValidateUtils.isBlank(src)) { + return null; + } + return JSON.parseArray(src, clazz); + } + public static List parseTopicConnections(Long clusterId, JSONObject jsonObject, long postTime) { List connectionDOList = new ArrayList<>(); for (String clientType: jsonObject.keySet()) { diff --git a/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/utils/jmx/JmxConnectorWrap.java b/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/utils/jmx/JmxConnectorWrap.java index fc70c6b2..c7c69ca3 100644 --- a/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/utils/jmx/JmxConnectorWrap.java +++ b/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/utils/jmx/JmxConnectorWrap.java @@ -79,7 +79,7 @@ public class JmxConnectorWrap { try { Map environment = new HashMap(); if (!ValidateUtils.isBlank(this.jmxConfig.getUsername()) && !ValidateUtils.isBlank(this.jmxConfig.getPassword())) { - environment.put(javax.management.remote.JMXConnector.CREDENTIALS, Arrays.asList(this.jmxConfig.getUsername(), this.jmxConfig.getPassword())); + environment.put(JMXConnector.CREDENTIALS, Arrays.asList(this.jmxConfig.getUsername(), this.jmxConfig.getPassword())); } if (jmxConfig.isOpenSSL() != null && this.jmxConfig.isOpenSSL()) { environment.put(Context.SECURITY_PROTOCOL, "ssl"); diff --git a/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/zookeeper/ZkPathUtil.java b/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/zookeeper/ZkPathUtil.java index e0a5632a..6705f435 100644 --- a/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/zookeeper/ZkPathUtil.java +++ b/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/zookeeper/ZkPathUtil.java @@ -33,7 +33,9 @@ public class ZkPathUtil { private static final String D_METRICS_CONFIG_ROOT_NODE = CONFIG_ROOT_NODE + ZOOKEEPER_SEPARATOR + "KafkaExMetrics"; - public static final String D_CONTROLLER_CANDIDATES = CONFIG_ROOT_NODE + ZOOKEEPER_SEPARATOR + "extension/candidates"; + public static final String D_CONFIG_EXTENSION_ROOT_NODE = CONFIG_ROOT_NODE + ZOOKEEPER_SEPARATOR + "extension"; + + public static final String D_CONTROLLER_CANDIDATES = D_CONFIG_EXTENSION_ROOT_NODE + ZOOKEEPER_SEPARATOR + "candidates"; public static String getBrokerIdNodePath(Integer brokerId) { return BROKER_IDS_ROOT + ZOOKEEPER_SEPARATOR + String.valueOf(brokerId); @@ -111,6 +113,10 @@ public class ZkPathUtil { } public static String getKafkaExtraMetricsPath(Integer brokerId) { - return D_METRICS_CONFIG_ROOT_NODE + ZOOKEEPER_SEPARATOR + String.valueOf(brokerId); + return D_METRICS_CONFIG_ROOT_NODE + ZOOKEEPER_SEPARATOR + brokerId; + } + + public static String getControllerCandidatePath(Integer brokerId) { + return D_CONTROLLER_CANDIDATES + ZOOKEEPER_SEPARATOR + brokerId; } } diff --git a/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/cache/PhysicalClusterMetadataManager.java b/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/cache/PhysicalClusterMetadataManager.java index 59453919..e3b8f23f 100644 --- a/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/cache/PhysicalClusterMetadataManager.java +++ b/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/cache/PhysicalClusterMetadataManager.java @@ -15,10 +15,7 @@ import com.xiaojukeji.kafka.manager.common.zookeeper.znode.brokers.TopicMetadata import com.xiaojukeji.kafka.manager.common.zookeeper.ZkConfigImpl; import com.xiaojukeji.kafka.manager.dao.ControllerDao; import com.xiaojukeji.kafka.manager.common.utils.jmx.JmxConnectorWrap; -import com.xiaojukeji.kafka.manager.dao.TopicDao; -import com.xiaojukeji.kafka.manager.dao.gateway.AuthorityDao; import com.xiaojukeji.kafka.manager.service.service.JmxService; -import com.xiaojukeji.kafka.manager.service.utils.ConfigUtils; import com.xiaojukeji.kafka.manager.service.zookeeper.*; import com.xiaojukeji.kafka.manager.service.service.ClusterService; import com.xiaojukeji.kafka.manager.common.zookeeper.ZkPathUtil; @@ -49,15 +46,6 @@ public class PhysicalClusterMetadataManager { @Autowired private ClusterService clusterService; - @Autowired - private ConfigUtils configUtils; - - @Autowired - private TopicDao topicDao; - - @Autowired - private AuthorityDao authorityDao; - private final static Map CLUSTER_MAP = new ConcurrentHashMap<>(); private final static Map CONTROLLER_DATA_MAP = new ConcurrentHashMap<>(); @@ -133,7 +121,7 @@ public class PhysicalClusterMetadataManager { zkConfig.watchChildren(ZkPathUtil.BROKER_IDS_ROOT, brokerListener); //增加Topic监控 - TopicStateListener topicListener = new TopicStateListener(clusterDO.getId(), zkConfig, topicDao, authorityDao); + TopicStateListener topicListener = new TopicStateListener(clusterDO.getId(), zkConfig); topicListener.init(); zkConfig.watchChildren(ZkPathUtil.BROKER_TOPICS_ROOT, topicListener); diff --git a/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/ClusterService.java b/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/ClusterService.java index b2c5f7b2..2feb321b 100644 --- a/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/ClusterService.java +++ b/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/ClusterService.java @@ -4,6 +4,7 @@ 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.dto.op.ControllerPreferredCandidateDTO; import com.xiaojukeji.kafka.manager.common.entity.vo.normal.cluster.ClusterNameDTO; import com.xiaojukeji.kafka.manager.common.entity.pojo.ClusterDO; import com.xiaojukeji.kafka.manager.common.entity.pojo.ClusterMetricsDO; @@ -51,4 +52,20 @@ public interface ClusterService { * @return void */ Result> getControllerPreferredCandidates(Long clusterId); + + /** + * 增加优先被选举为controller的broker + * @param clusterId 集群ID + * @param brokerIdList brokerId列表 + * @return + */ + Result addControllerPreferredCandidates(Long clusterId, List brokerIdList); + + /** + * 减少优先被选举为controller的broker + * @param clusterId 集群ID + * @param brokerIdList brokerId列表 + * @return + */ + Result deleteControllerPreferredCandidates(Long clusterId, List brokerIdList); } diff --git a/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/ZookeeperService.java b/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/ZookeeperService.java index d24b2d24..d52d3bc7 100644 --- a/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/ZookeeperService.java +++ b/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/ZookeeperService.java @@ -26,4 +26,20 @@ public interface ZookeeperService { * @return 操作结果 */ Result> getControllerPreferredCandidates(Long clusterId); + + /** + * 增加优先被选举为controller的broker + * @param clusterId 集群ID + * @param brokerId brokerId + * @return + */ + Result addControllerPreferredCandidate(Long clusterId, Integer brokerId); + + /** + * 减少优先被选举为controller的broker + * @param clusterId 集群ID + * @param brokerId brokerId + * @return + */ + Result deleteControllerPreferredCandidate(Long clusterId, Integer brokerId); } diff --git a/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/gateway/impl/GatewayConfigServiceImpl.java b/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/gateway/impl/GatewayConfigServiceImpl.java index fce7b605..18ee0a0d 100644 --- a/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/gateway/impl/GatewayConfigServiceImpl.java +++ b/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/gateway/impl/GatewayConfigServiceImpl.java @@ -221,13 +221,24 @@ public class GatewayConfigServiceImpl implements GatewayConfigService { if (ValidateUtils.isNull(oldGatewayConfigDO)) { return Result.buildFrom(ResultStatus.RESOURCE_NOT_EXIST); } + if (!oldGatewayConfigDO.getName().equals(newGatewayConfigDO.getName()) || !oldGatewayConfigDO.getType().equals(newGatewayConfigDO.getType()) || ValidateUtils.isBlank(newGatewayConfigDO.getValue())) { return Result.buildFrom(ResultStatus.PARAM_ILLEGAL); } - newGatewayConfigDO.setVersion(oldGatewayConfigDO.getVersion() + 1); - if (gatewayConfigDao.updateById(oldGatewayConfigDO) > 0) { + + // 获取当前同类配置, 插入之后需要增大这个version + List gatewayConfigDOList = gatewayConfigDao.getByConfigType(newGatewayConfigDO.getType()); + Long version = 1L; + for (GatewayConfigDO elem: gatewayConfigDOList) { + if (elem.getVersion() > version) { + version = elem.getVersion() + 1L; + } + } + + newGatewayConfigDO.setVersion(version); + if (gatewayConfigDao.updateById(newGatewayConfigDO) > 0) { return Result.buildSuc(); } return Result.buildFrom(ResultStatus.MYSQL_ERROR); diff --git a/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/impl/ClusterServiceImpl.java b/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/impl/ClusterServiceImpl.java index 609c8cf9..e1a619a8 100644 --- a/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/impl/ClusterServiceImpl.java +++ b/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/impl/ClusterServiceImpl.java @@ -111,12 +111,13 @@ public class ClusterServiceImpl implements ClusterService { // 不允许修改zk地址 return ResultStatus.CHANGE_ZOOKEEPER_FORBIDDEN; } - clusterDO.setStatus(originClusterDO.getStatus()); Map content = new HashMap<>(); content.put("cluster id", clusterDO.getId().toString()); content.put("security properties", clusterDO.getSecurityProperties()); content.put("jmx properties", clusterDO.getJmxProperties()); operateRecordService.insert(operator, ModuleEnum.CLUSTER, clusterDO.getClusterName(), OperateEnum.EDIT, content); + + clusterDO.setStatus(originClusterDO.getStatus()); return updateById(clusterDO); } @@ -214,7 +215,7 @@ public class ClusterServiceImpl implements ClusterService { if (zk != null) { zk.close(); } - } catch (Throwable t) { + } catch (Exception e) { return false; } } @@ -275,7 +276,7 @@ public class ClusterServiceImpl implements ClusterService { try { Map content = new HashMap<>(); content.put("cluster id", clusterId.toString()); - operateRecordService.insert(operator, ModuleEnum.CLUSTER, getClusterName(clusterId).getPhysicalClusterName(), OperateEnum.DELETE, content); + 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; @@ -289,8 +290,9 @@ public class ClusterServiceImpl implements ClusterService { private ClusterDetailDTO getClusterDetailDTO(ClusterDO clusterDO, Boolean needDetail) { if (ValidateUtils.isNull(clusterDO)) { - return null; + return new ClusterDetailDTO(); } + ClusterDetailDTO dto = new ClusterDetailDTO(); dto.setClusterId(clusterDO.getId()); dto.setClusterName(clusterDO.getClusterName()); @@ -299,6 +301,7 @@ public class ClusterServiceImpl implements ClusterService { dto.setKafkaVersion(physicalClusterMetadataManager.getKafkaVersionFromCache(clusterDO.getId())); dto.setIdc(configUtils.getIdc()); dto.setSecurityProperties(clusterDO.getSecurityProperties()); + dto.setJmxProperties(clusterDO.getJmxProperties()); dto.setStatus(clusterDO.getStatus()); dto.setGmtCreate(clusterDO.getGmtCreate()); dto.setGmtModify(clusterDO.getGmtModify()); @@ -337,4 +340,39 @@ public class ClusterServiceImpl implements ClusterService { } return Result.buildSuc(controllerPreferredCandidateList); } + + @Override + public Result addControllerPreferredCandidates(Long clusterId, List brokerIdList) { + if (ValidateUtils.isNull(clusterId) || ValidateUtils.isEmptyList(brokerIdList)) { + return Result.buildFrom(ResultStatus.PARAM_ILLEGAL); + } + + // 增加的BrokerId需要判断是否存活 + for (Integer brokerId: brokerIdList) { + if (!PhysicalClusterMetadataManager.isBrokerAlive(clusterId, brokerId)) { + return Result.buildFrom(ResultStatus.BROKER_NOT_EXIST); + } + + Result result = zookeeperService.addControllerPreferredCandidate(clusterId, brokerId); + if (result.failed()) { + return result; + } + } + return Result.buildSuc(); + } + + @Override + public Result deleteControllerPreferredCandidates(Long clusterId, List brokerIdList) { + if (ValidateUtils.isNull(clusterId) || ValidateUtils.isEmptyList(brokerIdList)) { + return Result.buildFrom(ResultStatus.PARAM_ILLEGAL); + } + + for (Integer brokerId: brokerIdList) { + Result result = zookeeperService.deleteControllerPreferredCandidate(clusterId, brokerId); + if (result.failed()) { + return result; + } + } + return Result.buildSuc(); + } } diff --git a/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/impl/ConsumerServiceImpl.java b/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/impl/ConsumerServiceImpl.java index e228d36c..0d60d828 100644 --- a/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/impl/ConsumerServiceImpl.java +++ b/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/impl/ConsumerServiceImpl.java @@ -129,7 +129,7 @@ public class ConsumerServiceImpl implements ConsumerService { } summary.setState(consumerGroupSummary.state()); - java.util.Iterator> it = JavaConversions.asJavaIterator(consumerGroupSummary.consumers().iterator()); + Iterator> it = JavaConversions.asJavaIterator(consumerGroupSummary.consumers().iterator()); while (it.hasNext()) { List consumerSummaryList = JavaConversions.asJavaList(it.next()); for (AdminClient.ConsumerSummary consumerSummary: consumerSummaryList) { diff --git a/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/impl/ZookeeperServiceImpl.java b/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/impl/ZookeeperServiceImpl.java index aa31ed33..c4c89513 100644 --- a/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/impl/ZookeeperServiceImpl.java +++ b/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/service/impl/ZookeeperServiceImpl.java @@ -70,4 +70,58 @@ public class ZookeeperServiceImpl implements ZookeeperService { } return Result.buildFrom(ResultStatus.ZOOKEEPER_READ_FAILED); } + + @Override + public Result addControllerPreferredCandidate(Long clusterId, Integer brokerId) { + if (ValidateUtils.isNull(clusterId)) { + return Result.buildFrom(ResultStatus.PARAM_ILLEGAL); + } + ZkConfigImpl zkConfig = PhysicalClusterMetadataManager.getZKConfig(clusterId); + if (ValidateUtils.isNull(zkConfig)) { + return Result.buildFrom(ResultStatus.ZOOKEEPER_CONNECT_FAILED); + } + + try { + if (zkConfig.checkPathExists(ZkPathUtil.getControllerCandidatePath(brokerId))) { + // 节点已经存在, 则直接忽略 + return Result.buildSuc(); + } + + if (!zkConfig.checkPathExists(ZkPathUtil.D_CONFIG_EXTENSION_ROOT_NODE)) { + zkConfig.setOrCreatePersistentNodeStat(ZkPathUtil.D_CONFIG_EXTENSION_ROOT_NODE, ""); + } + + if (!zkConfig.checkPathExists(ZkPathUtil.D_CONTROLLER_CANDIDATES)) { + zkConfig.setOrCreatePersistentNodeStat(ZkPathUtil.D_CONTROLLER_CANDIDATES, ""); + } + + zkConfig.setOrCreatePersistentNodeStat(ZkPathUtil.getControllerCandidatePath(brokerId), ""); + return Result.buildSuc(); + } catch (Exception e) { + LOGGER.error("class=ZookeeperServiceImpl||method=addControllerPreferredCandidate||clusterId={}||brokerId={}||errMsg={}||", clusterId, brokerId, e.getMessage()); + } + return Result.buildFrom(ResultStatus.ZOOKEEPER_WRITE_FAILED); + } + + @Override + public Result deleteControllerPreferredCandidate(Long clusterId, Integer brokerId) { + if (ValidateUtils.isNull(clusterId)) { + return Result.buildFrom(ResultStatus.PARAM_ILLEGAL); + } + ZkConfigImpl zkConfig = PhysicalClusterMetadataManager.getZKConfig(clusterId); + if (ValidateUtils.isNull(zkConfig)) { + return Result.buildFrom(ResultStatus.ZOOKEEPER_CONNECT_FAILED); + } + + try { + if (!zkConfig.checkPathExists(ZkPathUtil.getControllerCandidatePath(brokerId))) { + return Result.buildSuc(); + } + zkConfig.delete(ZkPathUtil.getControllerCandidatePath(brokerId)); + return Result.buildSuc(); + } catch (Exception e) { + LOGGER.error("class=ZookeeperServiceImpl||method=deleteControllerPreferredCandidate||clusterId={}||brokerId={}||errMsg={}||", clusterId, brokerId, e.getMessage()); + } + return Result.buildFrom(ResultStatus.ZOOKEEPER_DELETE_FAILED); + } } \ No newline at end of file diff --git a/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/utils/TopicCommands.java b/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/utils/TopicCommands.java index 58e5d98b..6995eb97 100644 --- a/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/utils/TopicCommands.java +++ b/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/utils/TopicCommands.java @@ -44,7 +44,7 @@ public class TopicCommands { ); // 生成分配策略 - scala.collection.Map> replicaAssignment = + scala.collection.Map> replicaAssignment = AdminUtils.assignReplicasToBrokers( convert2BrokerMetadataSeq(brokerIdList), partitionNum, @@ -177,7 +177,7 @@ public class TopicCommands { ) ); - Map> existingAssignJavaMap = + Map> existingAssignJavaMap = JavaConversions.asJavaMap(existingAssignScalaMap); // 新增分区的分配策略和旧的分配策略合并 Map> targetMap = new HashMap<>(); diff --git a/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/zookeeper/TopicStateListener.java b/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/zookeeper/TopicStateListener.java index f808b976..4314a101 100644 --- a/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/zookeeper/TopicStateListener.java +++ b/kafka-manager-core/src/main/java/com/xiaojukeji/kafka/manager/service/zookeeper/TopicStateListener.java @@ -5,8 +5,6 @@ import com.xiaojukeji.kafka.manager.common.zookeeper.znode.brokers.TopicMetadata import com.xiaojukeji.kafka.manager.common.zookeeper.StateChangeListener; import com.xiaojukeji.kafka.manager.common.zookeeper.ZkConfigImpl; import com.xiaojukeji.kafka.manager.common.zookeeper.ZkPathUtil; -import com.xiaojukeji.kafka.manager.dao.TopicDao; -import com.xiaojukeji.kafka.manager.dao.gateway.AuthorityDao; import com.xiaojukeji.kafka.manager.service.cache.PhysicalClusterMetadataManager; import com.xiaojukeji.kafka.manager.service.cache.ThreadPool; import org.apache.zookeeper.data.Stat; @@ -24,28 +22,17 @@ import java.util.concurrent.*; * @date 20/5/14 */ public class TopicStateListener implements StateChangeListener { - private final static Logger LOGGER = LoggerFactory.getLogger(TopicStateListener.class); + private static final Logger LOGGER = LoggerFactory.getLogger(TopicStateListener.class); private Long clusterId; private ZkConfigImpl zkConfig; - private TopicDao topicDao; - - private AuthorityDao authorityDao; - public TopicStateListener(Long clusterId, ZkConfigImpl zkConfig) { this.clusterId = clusterId; this.zkConfig = zkConfig; } - public TopicStateListener(Long clusterId, ZkConfigImpl zkConfig, TopicDao topicDao, AuthorityDao authorityDao) { - this.clusterId = clusterId; - this.zkConfig = zkConfig; - this.topicDao = topicDao; - this.authorityDao = authorityDao; - } - @Override public void init() { try { @@ -53,7 +40,7 @@ public class TopicStateListener implements StateChangeListener { FutureTask[] taskList = new FutureTask[topicNameList.size()]; for (int i = 0; i < topicNameList.size(); i++) { String topicName = topicNameList.get(i); - taskList[i] = new FutureTask(new Callable() { + taskList[i] = new FutureTask(new Callable() { @Override public Object call() throws Exception { processTopicAdded(topicName); @@ -65,7 +52,6 @@ public class TopicStateListener implements StateChangeListener { } catch (Exception e) { LOGGER.error("init topics metadata failed, clusterId:{}.", clusterId, e); } - return; } @Override @@ -92,8 +78,6 @@ public class TopicStateListener implements StateChangeListener { private void processTopicDelete(String topicName) { LOGGER.warn("delete topic, clusterId:{} topicName:{}.", clusterId, topicName); PhysicalClusterMetadataManager.removeTopicMetadata(clusterId, topicName); - topicDao.removeTopicInCache(clusterId, topicName); - authorityDao.removeAuthorityInCache(clusterId, topicName); } private void processTopicAdded(String topicName) { @@ -122,4 +106,4 @@ public class TopicStateListener implements StateChangeListener { LOGGER.error("add topic failed, clusterId:{} topicMetadata:{}.", clusterId, topicMetadata, e); } } -} \ No newline at end of file +} diff --git a/kafka-manager-dao/src/main/java/com/xiaojukeji/kafka/manager/dao/TopicDao.java b/kafka-manager-dao/src/main/java/com/xiaojukeji/kafka/manager/dao/TopicDao.java index 3d3f5410..64e089a6 100644 --- a/kafka-manager-dao/src/main/java/com/xiaojukeji/kafka/manager/dao/TopicDao.java +++ b/kafka-manager-dao/src/main/java/com/xiaojukeji/kafka/manager/dao/TopicDao.java @@ -22,6 +22,4 @@ public interface TopicDao { List listAll(); TopicDO getTopic(Long clusterId, String topicName, String appId); - - TopicDO removeTopicInCache(Long clusterId, String topicName); } \ No newline at end of file diff --git a/kafka-manager-dao/src/main/java/com/xiaojukeji/kafka/manager/dao/gateway/AppDao.java b/kafka-manager-dao/src/main/java/com/xiaojukeji/kafka/manager/dao/gateway/AppDao.java index 218c8656..7802005a 100644 --- a/kafka-manager-dao/src/main/java/com/xiaojukeji/kafka/manager/dao/gateway/AppDao.java +++ b/kafka-manager-dao/src/main/java/com/xiaojukeji/kafka/manager/dao/gateway/AppDao.java @@ -16,8 +16,6 @@ public interface AppDao { */ int insert(AppDO appDO); - int insertIgnoreGatewayDB(AppDO appDO); - /** * 删除appId * @param appName App名称 @@ -60,6 +58,4 @@ public interface AppDao { * @return int */ int updateById(AppDO appDO); - - List listNewAll(); } \ No newline at end of file diff --git a/kafka-manager-dao/src/main/java/com/xiaojukeji/kafka/manager/dao/gateway/AuthorityDao.java b/kafka-manager-dao/src/main/java/com/xiaojukeji/kafka/manager/dao/gateway/AuthorityDao.java index a7a8affe..655218e9 100644 --- a/kafka-manager-dao/src/main/java/com/xiaojukeji/kafka/manager/dao/gateway/AuthorityDao.java +++ b/kafka-manager-dao/src/main/java/com/xiaojukeji/kafka/manager/dao/gateway/AuthorityDao.java @@ -15,8 +15,6 @@ public interface AuthorityDao { */ int insert(AuthorityDO authorityDO); - int replaceIgnoreGatewayDB(AuthorityDO authorityDO); - /** * 获取权限 * @param clusterId 集群id @@ -38,7 +36,5 @@ public interface AuthorityDao { Map>> getAllAuthority(); - void removeAuthorityInCache(Long clusterId, String topicName); - int deleteAuthorityByTopic(Long clusterId, String topicName); } diff --git a/kafka-manager-dao/src/main/java/com/xiaojukeji/kafka/manager/dao/gateway/impl/AppDaoImpl.java b/kafka-manager-dao/src/main/java/com/xiaojukeji/kafka/manager/dao/gateway/impl/AppDaoImpl.java index aa08c1b4..62475b9b 100644 --- a/kafka-manager-dao/src/main/java/com/xiaojukeji/kafka/manager/dao/gateway/impl/AppDaoImpl.java +++ b/kafka-manager-dao/src/main/java/com/xiaojukeji/kafka/manager/dao/gateway/impl/AppDaoImpl.java @@ -2,6 +2,7 @@ package com.xiaojukeji.kafka.manager.dao.gateway.impl; import com.xiaojukeji.kafka.manager.common.entity.pojo.gateway.AppDO; import com.xiaojukeji.kafka.manager.dao.gateway.AppDao; +import com.xiaojukeji.kafka.manager.task.Constant; import org.mybatis.spring.SqlSessionTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; @@ -21,7 +22,7 @@ public class AppDaoImpl implements AppDao { /** * APP最近的一次更新时间, 更新之后的缓存 */ - private static Long APP_CACHE_LATEST_UPDATE_TIME = 0L; + private static volatile long APP_CACHE_LATEST_UPDATE_TIME = Constant.START_TIMESTAMP; private static final Map APP_MAP = new ConcurrentHashMap<>(); @Override @@ -29,11 +30,6 @@ public class AppDaoImpl implements AppDao { return sqlSession.insert("AppDao.insert", appDO); } - @Override - public int insertIgnoreGatewayDB(AppDO appDO) { - return sqlSession.insert("AppDao.insert", appDO); - } - @Override public int deleteByName(String appName) { return sqlSession.delete("AppDao.deleteByName", appName); @@ -66,7 +62,12 @@ public class AppDaoImpl implements AppDao { } private void updateTopicCache() { - Long timestamp = System.currentTimeMillis(); + long timestamp = System.currentTimeMillis(); + + if (timestamp + 1000 <= APP_CACHE_LATEST_UPDATE_TIME) { + // 近一秒内的请求不走db + return; + } Date afterTime = new Date(APP_CACHE_LATEST_UPDATE_TIME); List doList = sqlSession.selectList("AppDao.listAfterTime", afterTime); @@ -76,19 +77,22 @@ public class AppDaoImpl implements AppDao { /** * 更新APP缓存 */ - synchronized private void updateTopicCache(List doList, Long timestamp) { + private synchronized void updateTopicCache(List doList, long timestamp) { if (doList == null || doList.isEmpty() || APP_CACHE_LATEST_UPDATE_TIME >= timestamp) { // 本次无数据更新, 或者本次更新过时 时, 忽略本次更新 return; } + if (APP_CACHE_LATEST_UPDATE_TIME == Constant.START_TIMESTAMP) { + APP_MAP.clear(); + } + for (AppDO elem: doList) { APP_MAP.put(elem.getAppId(), elem); } APP_CACHE_LATEST_UPDATE_TIME = timestamp; } - @Override - public List listNewAll() { - return sqlSession.selectList("AppDao.listNewAll"); + public static void resetCache() { + APP_CACHE_LATEST_UPDATE_TIME = Constant.START_TIMESTAMP; } } \ No newline at end of file diff --git a/kafka-manager-dao/src/main/java/com/xiaojukeji/kafka/manager/dao/gateway/impl/AuthorityDaoImpl.java b/kafka-manager-dao/src/main/java/com/xiaojukeji/kafka/manager/dao/gateway/impl/AuthorityDaoImpl.java index 74a7cab0..1b5df873 100644 --- a/kafka-manager-dao/src/main/java/com/xiaojukeji/kafka/manager/dao/gateway/impl/AuthorityDaoImpl.java +++ b/kafka-manager-dao/src/main/java/com/xiaojukeji/kafka/manager/dao/gateway/impl/AuthorityDaoImpl.java @@ -1,8 +1,8 @@ package com.xiaojukeji.kafka.manager.dao.gateway.impl; import com.xiaojukeji.kafka.manager.common.entity.pojo.gateway.AuthorityDO; -import com.xiaojukeji.kafka.manager.common.utils.ValidateUtils; import com.xiaojukeji.kafka.manager.dao.gateway.AuthorityDao; +import com.xiaojukeji.kafka.manager.task.Constant; import org.mybatis.spring.SqlSessionTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; @@ -23,7 +23,8 @@ public class AuthorityDaoImpl implements AuthorityDao { * Authority最近的一次更新时间, 更新之后的缓存 * >> */ - private static Long AUTHORITY_CACHE_LATEST_UPDATE_TIME = 0L; + private static volatile long AUTHORITY_CACHE_LATEST_UPDATE_TIME = Constant.START_TIMESTAMP; + private static final Map>> AUTHORITY_MAP = new ConcurrentHashMap<>(); @Override @@ -31,11 +32,6 @@ public class AuthorityDaoImpl implements AuthorityDao { return sqlSession.insert("AuthorityDao.replace", authorityDO); } - @Override - public int replaceIgnoreGatewayDB(AuthorityDO authorityDO) { - return sqlSession.insert("AuthorityDao.replace", authorityDO); - } - @Override public List getAuthority(Long clusterId, String topicName, String appId) { Map params = new HashMap<>(3); @@ -62,8 +58,8 @@ public class AuthorityDaoImpl implements AuthorityDao { } List authorityDOList = new ArrayList<>(); - for (Long clusterId: doMap.keySet()) { - authorityDOList.addAll(doMap.get(clusterId).values()); + for (Map.Entry> entry: doMap.entrySet()) { + authorityDOList.addAll(entry.getValue().values()); } return authorityDOList; } @@ -87,23 +83,6 @@ public class AuthorityDaoImpl implements AuthorityDao { return AUTHORITY_MAP; } - @Override - public void removeAuthorityInCache(Long clusterId, String topicName) { - AUTHORITY_MAP.forEach((appId, map) -> { - map.forEach((id, subMap) -> { - if (id.equals(clusterId)) { - subMap.remove(topicName); - if (subMap.isEmpty()) { - map.remove(id); - } - } - }); - if (map.isEmpty()) { - AUTHORITY_MAP.remove(appId); - } - }); - } - @Override public int deleteAuthorityByTopic(Long clusterId, String topicName) { Map params = new HashMap<>(2); @@ -116,6 +95,11 @@ public class AuthorityDaoImpl implements AuthorityDao { private void updateAuthorityCache() { Long timestamp = System.currentTimeMillis(); + if (timestamp + 1000 <= AUTHORITY_CACHE_LATEST_UPDATE_TIME) { + // 近一秒内的请求不走db + return; + } + Date afterTime = new Date(AUTHORITY_CACHE_LATEST_UPDATE_TIME); List doList = sqlSession.selectList("AuthorityDao.listAfterTime", afterTime); updateAuthorityCache(doList, timestamp); @@ -124,11 +108,15 @@ public class AuthorityDaoImpl implements AuthorityDao { /** * 更新Topic缓存 */ - synchronized private void updateAuthorityCache(List doList, Long timestamp) { + private synchronized void updateAuthorityCache(List doList, Long timestamp) { if (doList == null || doList.isEmpty() || AUTHORITY_CACHE_LATEST_UPDATE_TIME >= timestamp) { // 本次无数据更新, 或者本次更新过时 时, 忽略本次更新 return; } + if (AUTHORITY_CACHE_LATEST_UPDATE_TIME == Constant.START_TIMESTAMP) { + AUTHORITY_MAP.clear(); + } + for (AuthorityDO elem: doList) { Map> doMap = AUTHORITY_MAP.getOrDefault(elem.getAppId(), new ConcurrentHashMap<>()); @@ -139,4 +127,8 @@ public class AuthorityDaoImpl implements AuthorityDao { } AUTHORITY_CACHE_LATEST_UPDATE_TIME = timestamp; } + + public static void resetCache() { + AUTHORITY_CACHE_LATEST_UPDATE_TIME = Constant.START_TIMESTAMP; + } } diff --git a/kafka-manager-dao/src/main/java/com/xiaojukeji/kafka/manager/dao/impl/TopicDaoImpl.java b/kafka-manager-dao/src/main/java/com/xiaojukeji/kafka/manager/dao/impl/TopicDaoImpl.java index ba4468df..3c1ba335 100644 --- a/kafka-manager-dao/src/main/java/com/xiaojukeji/kafka/manager/dao/impl/TopicDaoImpl.java +++ b/kafka-manager-dao/src/main/java/com/xiaojukeji/kafka/manager/dao/impl/TopicDaoImpl.java @@ -2,6 +2,7 @@ package com.xiaojukeji.kafka.manager.dao.impl; import com.xiaojukeji.kafka.manager.common.entity.pojo.TopicDO; import com.xiaojukeji.kafka.manager.dao.TopicDao; +import com.xiaojukeji.kafka.manager.task.Constant; import org.mybatis.spring.SqlSessionTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; @@ -18,7 +19,8 @@ public class TopicDaoImpl implements TopicDao { /** * Topic最近的一次更新时间, 更新之后的缓存 */ - private static Long TOPIC_CACHE_LATEST_UPDATE_TIME = 0L; + private static volatile long TOPIC_CACHE_LATEST_UPDATE_TIME = Constant.START_TIMESTAMP; + private static final Map> TOPIC_MAP = new ConcurrentHashMap<>(); @Autowired @@ -62,7 +64,7 @@ public class TopicDaoImpl implements TopicDao { @Override public List getByClusterId(Long clusterId) { updateTopicCache(); - return new ArrayList<>(TOPIC_MAP.getOrDefault(clusterId, new ConcurrentHashMap<>(0)).values()); + return new ArrayList<>(TOPIC_MAP.getOrDefault(clusterId, Collections.emptyMap()).values()); } @Override @@ -75,28 +77,28 @@ public class TopicDaoImpl implements TopicDao { updateTopicCache(); List doList = new ArrayList<>(); for (Long clusterId: TOPIC_MAP.keySet()) { - doList.addAll(TOPIC_MAP.getOrDefault(clusterId, new ConcurrentHashMap<>(0)).values()); + doList.addAll(TOPIC_MAP.getOrDefault(clusterId, Collections.emptyMap()).values()); } return doList; } @Override public TopicDO getTopic(Long clusterId, String topicName, String appId) { - Map params = new HashMap<>(2); + Map params = new HashMap<>(3); params.put("clusterId", clusterId); params.put("topicName", topicName); params.put("appId", appId); return sqlSession.selectOne("TopicDao.getTopic", params); } - @Override - public TopicDO removeTopicInCache(Long clusterId, String topicName) { - return TOPIC_MAP.getOrDefault(clusterId, new HashMap<>(0)).remove(topicName); - } - private void updateTopicCache() { Long timestamp = System.currentTimeMillis(); + if (timestamp + 1000 <= TOPIC_CACHE_LATEST_UPDATE_TIME) { + // 近一秒内的请求不走db + return; + } + Date afterTime = new Date(TOPIC_CACHE_LATEST_UPDATE_TIME); List doList = sqlSession.selectList("TopicDao.listAfterTime", afterTime); updateTopicCache(doList, timestamp); @@ -105,11 +107,15 @@ public class TopicDaoImpl implements TopicDao { /** * 更新Topic缓存 */ - synchronized private void updateTopicCache(List doList, Long timestamp) { + private synchronized void updateTopicCache(List doList, Long timestamp) { if (doList == null || doList.isEmpty() || TOPIC_CACHE_LATEST_UPDATE_TIME >= timestamp) { // 本次无数据更新, 或者本次更新过时 时, 忽略本次更新 return; } + if (TOPIC_CACHE_LATEST_UPDATE_TIME == Constant.START_TIMESTAMP) { + TOPIC_MAP.clear(); + } + for (TopicDO elem: doList) { Map doMap = TOPIC_MAP.getOrDefault(elem.getClusterId(), new ConcurrentHashMap<>()); doMap.put(elem.getTopicName(), elem); @@ -117,4 +123,8 @@ public class TopicDaoImpl implements TopicDao { } TOPIC_CACHE_LATEST_UPDATE_TIME = timestamp; } + + public static void resetCache() { + TOPIC_CACHE_LATEST_UPDATE_TIME = Constant.START_TIMESTAMP; + } } \ No newline at end of file diff --git a/kafka-manager-dao/src/main/java/com/xiaojukeji/kafka/manager/task/Constant.java b/kafka-manager-dao/src/main/java/com/xiaojukeji/kafka/manager/task/Constant.java new file mode 100644 index 00000000..3a50d7c1 --- /dev/null +++ b/kafka-manager-dao/src/main/java/com/xiaojukeji/kafka/manager/task/Constant.java @@ -0,0 +1,5 @@ +package com.xiaojukeji.kafka.manager.task; + +public class Constant { + public static final long START_TIMESTAMP = 0; +} diff --git a/kafka-manager-dao/src/main/java/com/xiaojukeji/kafka/manager/task/DaoBackgroundTask.java b/kafka-manager-dao/src/main/java/com/xiaojukeji/kafka/manager/task/DaoBackgroundTask.java new file mode 100644 index 00000000..a750aff8 --- /dev/null +++ b/kafka-manager-dao/src/main/java/com/xiaojukeji/kafka/manager/task/DaoBackgroundTask.java @@ -0,0 +1,41 @@ +package com.xiaojukeji.kafka.manager.task; + +import com.xiaojukeji.kafka.manager.common.utils.factory.DefaultThreadFactory; +import com.xiaojukeji.kafka.manager.dao.gateway.impl.AppDaoImpl; +import com.xiaojukeji.kafka.manager.dao.gateway.impl.AuthorityDaoImpl; +import com.xiaojukeji.kafka.manager.dao.impl.TopicDaoImpl; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +import javax.annotation.PostConstruct; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +/** + * 后台任务线程 + * @author zengqiao + * @date 21/02/02 + */ +@Service +public class DaoBackgroundTask { + private static final Logger LOGGER = LoggerFactory.getLogger(DaoBackgroundTask.class); + + private static final ScheduledExecutorService SYNC_CACHE_THREAD_POOL = Executors.newSingleThreadScheduledExecutor(new DefaultThreadFactory("syncCacheTask")); + + @PostConstruct + public void init() { + SYNC_CACHE_THREAD_POOL.scheduleAtFixedRate(() -> { + LOGGER.info("class=DaoBackgroundTask||method=init||msg=sync cache start"); + + TopicDaoImpl.resetCache(); + + AppDaoImpl.resetCache(); + + AuthorityDaoImpl.resetCache(); + + LOGGER.info("class=DaoBackgroundTask||method=init||msg=sync cache finished"); + }, 1, 10, TimeUnit.MINUTES); + } +} diff --git a/kafka-manager-dao/src/main/resources/mapper/GatewayConfigDao.xml b/kafka-manager-dao/src/main/resources/mapper/GatewayConfigDao.xml index 8aa91925..ac003836 100644 --- a/kafka-manager-dao/src/main/resources/mapper/GatewayConfigDao.xml +++ b/kafka-manager-dao/src/main/resources/mapper/GatewayConfigDao.xml @@ -8,6 +8,7 @@ + @@ -27,9 +28,9 @@ @@ -45,7 +46,8 @@ `type`=#{type}, `name`=#{name}, `value`=#{value}, - `version`=#{version} + `version`=#{version}, + `description`=#{description} WHERE id=#{id} ]]> diff --git a/kafka-manager-extends/kafka-manager-bpm/src/main/java/com/xiaojukeji/kafka/manager/bpm/common/entry/apply/gateway/OrderExtensionAddGatewayConfigDTO.java b/kafka-manager-extends/kafka-manager-bpm/src/main/java/com/xiaojukeji/kafka/manager/bpm/common/entry/apply/gateway/OrderExtensionAddGatewayConfigDTO.java index 0045bfe2..6a2c0bb4 100644 --- a/kafka-manager-extends/kafka-manager-bpm/src/main/java/com/xiaojukeji/kafka/manager/bpm/common/entry/apply/gateway/OrderExtensionAddGatewayConfigDTO.java +++ b/kafka-manager-extends/kafka-manager-bpm/src/main/java/com/xiaojukeji/kafka/manager/bpm/common/entry/apply/gateway/OrderExtensionAddGatewayConfigDTO.java @@ -18,6 +18,9 @@ public class OrderExtensionAddGatewayConfigDTO { @ApiModelProperty(value = "值") private String value; + @ApiModelProperty(value = "描述说明") + private String description; + public String getType() { return type; } @@ -42,12 +45,21 @@ public class OrderExtensionAddGatewayConfigDTO { this.value = value; } + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + @Override public String toString() { return "OrderExtensionAddGatewayConfigDTO{" + "type='" + type + '\'' + ", name='" + name + '\'' + ", value='" + value + '\'' + + ", description='" + description + '\'' + '}'; } diff --git a/kafka-manager-extends/kafka-manager-bpm/src/main/java/com/xiaojukeji/kafka/manager/bpm/common/entry/apply/gateway/OrderExtensionModifyGatewayConfigDTO.java b/kafka-manager-extends/kafka-manager-bpm/src/main/java/com/xiaojukeji/kafka/manager/bpm/common/entry/apply/gateway/OrderExtensionModifyGatewayConfigDTO.java index f5212f8c..3f749ea7 100644 --- a/kafka-manager-extends/kafka-manager-bpm/src/main/java/com/xiaojukeji/kafka/manager/bpm/common/entry/apply/gateway/OrderExtensionModifyGatewayConfigDTO.java +++ b/kafka-manager-extends/kafka-manager-bpm/src/main/java/com/xiaojukeji/kafka/manager/bpm/common/entry/apply/gateway/OrderExtensionModifyGatewayConfigDTO.java @@ -23,6 +23,9 @@ public class OrderExtensionModifyGatewayConfigDTO { @ApiModelProperty(value = "值") private String value; + @ApiModelProperty(value = "描述说明") + private String description; + public Long getId() { return id; } @@ -55,6 +58,14 @@ public class OrderExtensionModifyGatewayConfigDTO { this.value = value; } + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + @Override public String toString() { return "OrderExtensionModifyGatewayConfigDTO{" + @@ -62,6 +73,7 @@ public class OrderExtensionModifyGatewayConfigDTO { ", type='" + type + '\'' + ", name='" + name + '\'' + ", value='" + value + '\'' + + ", description='" + description + '\'' + '}'; } diff --git a/kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/common/Constant.java b/kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/common/Constant.java new file mode 100644 index 00000000..f73c3fd6 --- /dev/null +++ b/kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/common/Constant.java @@ -0,0 +1,18 @@ +package com.xiaojukeji.kafka.manager.kcm.common; + +public class Constant { + /** + * + */ + public static final String TASK_TITLE_PREFIX = "Logi-Kafka"; + + /** + * 并发度,顺序执行 + */ + public static final Integer AGENT_TASK_BATCH = 1; + + /** + * 失败的容忍度为0 + */ + public static final Integer AGENT_TASK_TOLERANCE = 0; +} diff --git a/kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/common/bizenum/ClusterTaskActionEnum.java b/kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/common/bizenum/ClusterTaskActionEnum.java index 556acab8..a51e2c68 100644 --- a/kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/common/bizenum/ClusterTaskActionEnum.java +++ b/kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/common/bizenum/ClusterTaskActionEnum.java @@ -6,34 +6,35 @@ package com.xiaojukeji.kafka.manager.kcm.common.bizenum; * @date 20/4/26 */ public enum ClusterTaskActionEnum { - START(0, "start"), - PAUSE(1, "pause"), - IGNORE(2, "ignore"), - CANCEL(3, "cancel"), - ROLLBACK(4, "rollback"), + UNKNOWN("unknown"), + + START("start"), + PAUSE("pause"), + + IGNORE("ignore"), + CANCEL("cancel"), + + REDO("redo"), + KILL("kill"), + + ROLLBACK("rollback"), + ; - private Integer code; - private String message; + private String action; - ClusterTaskActionEnum(Integer code, String message) { - this.code = code; - this.message = message; + ClusterTaskActionEnum(String action) { + this.action = action; } - public Integer getCode() { - return code; - } - - public String getMessage() { - return message; + public String getAction() { + return action; } @Override public String toString() { - return "TaskActionEnum{" + - "code=" + code + - ", message='" + message + '\'' + + return "ClusterTaskActionEnum{" + + "action='" + action + '\'' + '}'; } } diff --git a/kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/common/entry/ao/ClusterTaskLog.java b/kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/common/entry/ao/ClusterTaskLog.java new file mode 100644 index 00000000..ff89fa99 --- /dev/null +++ b/kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/common/entry/ao/ClusterTaskLog.java @@ -0,0 +1,24 @@ +package com.xiaojukeji.kafka.manager.kcm.common.entry.ao; + +public class ClusterTaskLog { + private String stdout; + + public ClusterTaskLog(String stdout) { + this.stdout = stdout; + } + + public String getStdout() { + return stdout; + } + + public void setStdout(String stdout) { + this.stdout = stdout; + } + + @Override + public String toString() { + return "AgentOperationTaskLog{" + + "stdout='" + stdout + '\'' + + '}'; + } +} diff --git a/kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/common/entry/ao/CreationTaskData.java b/kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/common/entry/ao/CreationTaskData.java index bc025d5c..8c2cd1ec 100644 --- a/kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/common/entry/ao/CreationTaskData.java +++ b/kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/common/entry/ao/CreationTaskData.java @@ -1,5 +1,7 @@ package com.xiaojukeji.kafka.manager.kcm.common.entry.ao; +import com.xiaojukeji.kafka.manager.common.entity.Result; + import java.util.List; /** @@ -119,7 +121,7 @@ public class CreationTaskData { @Override public String toString() { - return "CreationTaskDTO{" + + return "CreationTaskData{" + "uuid='" + uuid + '\'' + ", clusterId=" + clusterId + ", hostList=" + hostList + diff --git a/kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/component/agent/AbstractAgent.java b/kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/component/agent/AbstractAgent.java index 88872868..70ce5902 100644 --- a/kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/component/agent/AbstractAgent.java +++ b/kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/component/agent/AbstractAgent.java @@ -1,9 +1,18 @@ package com.xiaojukeji.kafka.manager.kcm.component.agent; +import com.xiaojukeji.kafka.manager.common.entity.Result; +import com.xiaojukeji.kafka.manager.kcm.common.bizenum.ClusterTaskActionEnum; import com.xiaojukeji.kafka.manager.kcm.common.bizenum.ClusterTaskStateEnum; import com.xiaojukeji.kafka.manager.kcm.common.bizenum.ClusterTaskSubStateEnum; +import com.xiaojukeji.kafka.manager.kcm.common.entry.ao.ClusterTaskLog; import com.xiaojukeji.kafka.manager.kcm.common.entry.ao.CreationTaskData; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; import java.util.Map; @@ -13,33 +22,79 @@ import java.util.Map; * @date 20/4/26 */ public abstract class AbstractAgent { + private static final Logger LOGGER = LoggerFactory.getLogger(AbstractAgent.class); + /** * 创建任务 + * @param creationTaskData 创建任务参数 + * @return 任务ID */ - public abstract Long createTask(CreationTaskData dto); + public abstract Result createTask(CreationTaskData creationTaskData); /** - * 任务动作 + * 执行任务 + * @param taskId 任务ID + * @param actionEnum 执行动作 + * @return true:触发成功, false:触发失败 */ - public abstract Boolean actionTask(Long taskId, String action); + public abstract boolean actionTask(Long taskId, ClusterTaskActionEnum actionEnum); /** - * 任务动作 + * 执行任务 + * @param taskId 任务ID + * @param actionEnum 执行动作 + * @param hostname 具体主机 + * @return true:触发成功, false:触发失败 */ - public abstract Boolean actionHostTask(Long taskId, String action, String hostname); + public abstract boolean actionHostTask(Long taskId, ClusterTaskActionEnum actionEnum, String hostname); /** - * 获取任务状态 + * 获取任务运行的状态[阻塞, 执行中, 完成等] + * @param taskId 任务ID + * @return 任务状态 */ - public abstract ClusterTaskStateEnum getTaskState(Long agentTaskId); + public abstract Result getTaskExecuteState(Long taskId); /** * 获取任务结果 + * @param taskId 任务ID + * @return 任务结果 */ - public abstract Map getTaskResult(Long taskId); + public abstract Result> getTaskResult(Long taskId); /** - * 获取任务日志 + * 获取任务执行日志 + * @param taskId 任务ID + * @param hostname 具体主机 + * @return 机器运行日志 */ - public abstract String getTaskLog(Long agentTaskId, String hostname); + public abstract Result getTaskLog(Long taskId, String hostname); + + protected static String readScriptInJarFile(String fileName) { + InputStream inputStream = AbstractAgent.class.getClassLoader().getResourceAsStream(fileName); + if (inputStream == null) { + LOGGER.error("class=AbstractAgent||method=readScriptInJarFile||fileName={}||msg=read script failed", fileName); + return ""; + } + + try { + BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); + String line = null; + + StringBuilder sb = new StringBuilder(); + while ((line = bufferedReader.readLine()) != null) { + sb.append(line).append("\n"); + } + return sb.toString(); + } catch (Exception e) { + LOGGER.error("class=AbstractAgent||method=readScriptInJarFile||fileName={}||errMsg={}||msg=read script failed", fileName, e.getMessage()); + } finally { + try { + inputStream.close(); + } catch (IOException e) { + LOGGER.error("class=AbstractAgent||method=readScriptInJarFile||fileName={}||errMsg={}||msg=close reading script failed", fileName, e.getMessage()); + } + } + return ""; + } } \ No newline at end of file diff --git a/kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/component/agent/n9e/N9e.java b/kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/component/agent/n9e/N9e.java index f1f4b586..6e3fa677 100644 --- a/kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/component/agent/n9e/N9e.java +++ b/kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/component/agent/n9e/N9e.java @@ -1,8 +1,11 @@ package com.xiaojukeji.kafka.manager.kcm.component.agent.n9e; -import com.alibaba.fastjson.JSON; import com.xiaojukeji.kafka.manager.common.bizenum.KafkaFileEnum; +import com.xiaojukeji.kafka.manager.common.entity.Result; +import com.xiaojukeji.kafka.manager.kcm.common.Constant; +import com.xiaojukeji.kafka.manager.kcm.common.bizenum.ClusterTaskActionEnum; import com.xiaojukeji.kafka.manager.kcm.common.bizenum.ClusterTaskTypeEnum; +import com.xiaojukeji.kafka.manager.kcm.common.entry.ao.ClusterTaskLog; import com.xiaojukeji.kafka.manager.kcm.common.entry.ao.CreationTaskData; import com.xiaojukeji.kafka.manager.common.utils.HttpUtils; import com.xiaojukeji.kafka.manager.common.utils.JsonUtils; @@ -11,20 +14,17 @@ import com.xiaojukeji.kafka.manager.common.utils.ValidateUtils; import com.xiaojukeji.kafka.manager.kcm.common.bizenum.ClusterTaskStateEnum; import com.xiaojukeji.kafka.manager.kcm.common.bizenum.ClusterTaskSubStateEnum; import com.xiaojukeji.kafka.manager.kcm.component.agent.AbstractAgent; +import com.xiaojukeji.kafka.manager.kcm.component.agent.n9e.entry.N9eCreationTask; import com.xiaojukeji.kafka.manager.kcm.component.agent.n9e.entry.N9eResult; -import com.xiaojukeji.kafka.manager.kcm.component.agent.n9e.entry.N9eTaskResultDTO; -import com.xiaojukeji.kafka.manager.kcm.component.agent.n9e.entry.N9eTaskStatusEnum; -import com.xiaojukeji.kafka.manager.kcm.component.agent.n9e.entry.N9eTaskStdoutDTO; +import com.xiaojukeji.kafka.manager.kcm.component.agent.n9e.entry.N9eTaskResult; +import com.xiaojukeji.kafka.manager.kcm.component.agent.n9e.entry.N9eTaskStdoutLog; +import com.xiaojukeji.kafka.manager.kcm.component.agent.n9e.entry.bizenum.N9eTaskStatusEnum; import org.springframework.beans.factory.annotation.Value; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import javax.annotation.PostConstruct; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -54,16 +54,6 @@ public class N9e extends AbstractAgent { private String script; - /** - * 并发度,顺序执行 - */ - private static final Integer BATCH = 1; - - /** - * 失败的容忍度为0 - */ - private static final Integer TOLERANCE = 0; - private static final String CREATE_TASK_URI = "/api/job-ce/tasks"; private static final String ACTION_TASK_URI = "/api/job-ce/task/{taskId}/action"; @@ -82,143 +72,134 @@ public class N9e extends AbstractAgent { } @Override - public Long createTask(CreationTaskData creationTaskData) { - Map param = buildCreateTaskParam(creationTaskData); + public Result createTask(CreationTaskData creationTaskData) { + String content = JsonUtils.toJSONString(buildCreateTaskParam(creationTaskData)); String response = null; try { - response = HttpUtils.postForString( - baseUrl + CREATE_TASK_URI, - JsonUtils.toJSONString(param), - buildHeader() - ); - N9eResult zr = JSON.parseObject(response, N9eResult.class); - if (!ValidateUtils.isBlank(zr.getErr())) { - LOGGER.warn("class=N9e||method=createTask||param={}||errMsg={}||msg=call create task fail", JsonUtils.toJSONString(param),zr.getErr()); - return null; + response = HttpUtils.postForString(baseUrl + CREATE_TASK_URI, content, buildHeader()); + N9eResult nr = JsonUtils.stringToObj(response, N9eResult.class); + if (!ValidateUtils.isBlank(nr.getErr())) { + LOGGER.error("class=N9e||method=createTask||param={}||response={}||msg=call create task failed", content, response); + return Result.buildFailure(nr.getErr()); } - return Long.valueOf(zr.getDat().toString()); + return Result.buildSuc(Long.valueOf(nr.getDat().toString())); } catch (Exception e) { - LOGGER.error("create task failed, req:{}.", creationTaskData, e); + LOGGER.error("class=N9e||method=createTask||param={}||response={}||errMsg={}||msg=call create task failed", content, response, e.getMessage()); } - return null; + return Result.buildFailure("create n9e task failed"); } @Override - public Boolean actionTask(Long taskId, String action) { + public boolean actionTask(Long taskId, ClusterTaskActionEnum actionEnum) { Map param = new HashMap<>(1); - param.put("action", action); + param.put("action", actionEnum.getAction()); String response = null; try { - response = HttpUtils.putForString( - baseUrl + ACTION_TASK_URI.replace("{taskId}", taskId.toString()), - JSON.toJSONString(param), - buildHeader() - ); - N9eResult zr = JSON.parseObject(response, N9eResult.class); - if (ValidateUtils.isBlank(zr.getErr())) { + response = HttpUtils.putForString(baseUrl + ACTION_TASK_URI.replace("{taskId}", String.valueOf(taskId)), JsonUtils.toJSONString(param), buildHeader()); + N9eResult nr = JsonUtils.stringToObj(response, N9eResult.class); + if (ValidateUtils.isBlank(nr.getErr())) { return true; } - LOGGER.warn("class=N9e||method=actionTask||param={}||errMsg={}||msg=call action task fail", JSON.toJSONString(param),zr.getErr()); + + LOGGER.error("class=N9e||method=actionTask||param={}||response={}||msg=call action task fail", JsonUtils.toJSONString(param), response); return false; } catch (Exception e) { - LOGGER.error("action task failed, taskId:{}, action:{}.", taskId, action, e); + LOGGER.error("class=N9e||method=actionTask||param={}||response={}||errMsg={}||msg=call action task fail", JsonUtils.toJSONString(param), response, e.getMessage()); } return false; } @Override - public Boolean actionHostTask(Long taskId, String action, String hostname) { - Map param = new HashMap<>(2); - param.put("action", action); - param.put("hostname", hostname); + public boolean actionHostTask(Long taskId, ClusterTaskActionEnum actionEnum, String hostname) { + Map params = new HashMap<>(2); + params.put("action", actionEnum.getAction()); + params.put("hostname", hostname); String response = null; try { - response = HttpUtils.putForString( - baseUrl + ACTION_HOST_TASK_URI.replace("{taskId}", taskId.toString()), - JSON.toJSONString(param), - buildHeader() - ); - N9eResult zr = JSON.parseObject(response, N9eResult.class); - if (ValidateUtils.isBlank(zr.getErr())) { + response = HttpUtils.putForString(baseUrl + ACTION_HOST_TASK_URI.replace("{taskId}", String.valueOf(taskId)), JsonUtils.toJSONString(params), buildHeader()); + N9eResult nr = JsonUtils.stringToObj(response, N9eResult.class); + if (ValidateUtils.isBlank(nr.getErr())) { return true; } - LOGGER.warn("class=N9e||method=actionHostTask||param={}||errMsg={}||msg=call action host task fail", JSON.toJSONString(param),zr.getErr()); + + LOGGER.error("class=N9e||method=actionHostTask||params={}||response={}||msg=call action host task fail", JsonUtils.toJSONString(params), response); return false; } catch (Exception e) { - LOGGER.error("action task failed, taskId:{} action:{} hostname:{}.", taskId, action, hostname, e); + LOGGER.error("class=N9e||method=actionHostTask||params={}||response={}||errMsg={}||msg=call action host task fail", JsonUtils.toJSONString(params), response, e.getMessage()); } return false; } @Override - public ClusterTaskStateEnum getTaskState(Long agentTaskId) { + public Result getTaskExecuteState(Long taskId) { String response = null; try { // 获取任务的state - response = HttpUtils.get( - baseUrl + TASK_STATE_URI.replace("{taskId}", agentTaskId.toString()), null - ); - N9eResult n9eResult = JSON.parseObject(response, N9eResult.class); - if (!ValidateUtils.isBlank(n9eResult.getErr())) { - LOGGER.error("get response result failed, agentTaskId:{} response:{}.", agentTaskId, response); - return null; + response = HttpUtils.get(baseUrl + TASK_STATE_URI.replace("{taskId}", String.valueOf(taskId)), null); + N9eResult nr = JsonUtils.stringToObj(response, N9eResult.class); + if (!ValidateUtils.isBlank(nr.getErr())) { + return Result.buildFailure(nr.getErr()); } - String state = JSON.parseObject(JSON.toJSONString(n9eResult.getDat()), String.class); + + String state = JsonUtils.stringToObj(JsonUtils.toJSONString(nr.getDat()), String.class); + N9eTaskStatusEnum n9eTaskStatusEnum = N9eTaskStatusEnum.getByMessage(state); if (ValidateUtils.isNull(n9eTaskStatusEnum)) { - LOGGER.error("get task status failed, agentTaskId:{} state:{}.", agentTaskId, state); - return null; + LOGGER.error("class=N9e||method=getTaskExecuteState||taskId={}||response={}||msg=get task state failed", taskId, response); + return Result.buildFailure("unknown state, state:" + state); } - return n9eTaskStatusEnum.getStatus(); + return Result.buildSuc(n9eTaskStatusEnum.getStatus()); } catch (Exception e) { - LOGGER.error("get task status failed, agentTaskId:{} response:{}.", agentTaskId, response, e); + LOGGER.error("class=N9e||method=getTaskExecuteState||taskId={}||response={}||errMsg={}||msg=get task state failed", taskId, response, e.getMessage()); } - return null; + return Result.buildFailure("get task state failed"); } @Override - public Map getTaskResult(Long agentTaskId) { + public Result> getTaskResult(Long taskId) { String response = null; try { // 获取子任务的state - response = HttpUtils.get(baseUrl + TASK_SUB_STATE_URI.replace("{taskId}", agentTaskId.toString()), null); - N9eResult n9eResult = JSON.parseObject(response, N9eResult.class); + response = HttpUtils.get(baseUrl + TASK_SUB_STATE_URI.replace("{taskId}", String.valueOf(taskId)), null); + N9eResult nr = JsonUtils.stringToObj(response, N9eResult.class); + if (!ValidateUtils.isBlank(nr.getErr())) { + LOGGER.error("class=N9e||method=getTaskResult||taskId={}||response={}||msg=get task result failed", taskId, response); + return Result.buildFailure(nr.getErr()); + } - N9eTaskResultDTO n9eTaskResultDTO = - JSON.parseObject(JSON.toJSONString(n9eResult.getDat()), N9eTaskResultDTO.class); - return n9eTaskResultDTO.convert2HostnameStatusMap(); + return Result.buildSuc(JsonUtils.stringToObj(JsonUtils.toJSONString(nr.getDat()), N9eTaskResult.class).convert2HostnameStatusMap()); } catch (Exception e) { - LOGGER.error("get task result failed, agentTaskId:{} response:{}.", agentTaskId, response, e); + LOGGER.error("class=N9e||method=getTaskResult||taskId={}||response={}||errMsg={}||msg=get task result failed", taskId, response, e.getMessage()); } - return null; + return Result.buildFailure("get task result failed"); } @Override - public String getTaskLog(Long agentTaskId, String hostname) { + public Result getTaskLog(Long taskId, String hostname) { + Map params = new HashMap<>(1); + params.put("hostname", hostname); + String response = null; try { - Map params = new HashMap<>(1); - params.put("hostname", hostname); + response = HttpUtils.get(baseUrl + TASK_STD_LOG_URI.replace("{taskId}", String.valueOf(taskId)), params); + N9eResult nr = JsonUtils.stringToObj(response, N9eResult.class); + if (!ValidateUtils.isBlank(nr.getErr())) { + LOGGER.error("class=N9e||method=getTaskLog||taskId={}||response={}||msg=get task log failed", taskId, response); + return Result.buildFailure(nr.getErr()); + } - response = HttpUtils.get(baseUrl + TASK_STD_LOG_URI.replace("{taskId}", agentTaskId.toString()), params); - N9eResult n9eResult = JSON.parseObject(response, N9eResult.class); - if (!ValidateUtils.isBlank(n9eResult.getErr())) { - LOGGER.error("get task log failed, agentTaskId:{} response:{}.", agentTaskId, response); - return null; - } - List dtoList = - JSON.parseArray(JSON.toJSONString(n9eResult.getDat()), N9eTaskStdoutDTO.class); + List dtoList = JsonUtils.stringToArrObj(JsonUtils.toJSONString(nr.getDat()), N9eTaskStdoutLog.class); if (ValidateUtils.isEmptyList(dtoList)) { - return ""; + return Result.buildSuc(new ClusterTaskLog("")); } - return dtoList.get(0).getStdout(); + return Result.buildSuc(new ClusterTaskLog(dtoList.get(0).getStdout())); } catch (Exception e) { - LOGGER.error("get task log failed, agentTaskId:{}.", agentTaskId, e); + LOGGER.error("class=N9e||method=getTaskLog||taskId={}||response={}||errMsg={}||msg=get task log failed", taskId, response, e.getMessage()); } - return null; + return Result.buildFailure("get task log failed"); } private Map buildHeader() { @@ -228,7 +209,7 @@ public class N9e extends AbstractAgent { return headers; } - private Map buildCreateTaskParam(CreationTaskData creationTaskData) { + private N9eCreationTask buildCreateTaskParam(CreationTaskData creationTaskData) { StringBuilder sb = new StringBuilder(); sb.append(creationTaskData.getUuid()).append(",,"); sb.append(creationTaskData.getClusterId()).append(",,"); @@ -240,46 +221,17 @@ public class N9e extends AbstractAgent { sb.append(creationTaskData.getServerPropertiesMd5()).append(",,"); sb.append(creationTaskData.getServerPropertiesUrl()); - Map params = new HashMap<>(10); - params.put("title", String.format("集群ID=%d-升级部署", creationTaskData.getClusterId())); - params.put("batch", BATCH); - params.put("tolerance", TOLERANCE); - params.put("timeout", timeout); - params.put("pause", ListUtils.strList2String(creationTaskData.getPauseList())); - params.put("script", this.script); - params.put("args", sb.toString()); - params.put("account", account); - params.put("action", "pause"); - params.put("hosts", creationTaskData.getHostList()); - return params; - } - - private static String readScriptInJarFile(String fileName) { - InputStream inputStream = N9e.class.getClassLoader().getResourceAsStream(fileName); - if (inputStream == null) { - LOGGER.error("read kcm script failed, filename:{}", fileName); - return ""; - } - - try { - BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); - String line = null; - StringBuilder stringBuilder = new StringBuilder(""); - - while ((line = bufferedReader.readLine()) != null) { - stringBuilder.append(line); - stringBuilder.append("\n"); - } - return stringBuilder.toString(); - } catch (IOException e) { - LOGGER.error("read kcm script failed, filename:{}", fileName, e); - return ""; - } finally { - try { - inputStream.close(); - } catch (IOException e) { - LOGGER.error("close reading kcm script failed, filename:{}", fileName, e); - } - } + N9eCreationTask n9eCreationTask = new N9eCreationTask(); + n9eCreationTask.setTitle(Constant.TASK_TITLE_PREFIX + "-集群ID:" + creationTaskData.getClusterId()); + n9eCreationTask.setBatch(Constant.AGENT_TASK_BATCH); + n9eCreationTask.setTolerance(Constant.AGENT_TASK_TOLERANCE); + n9eCreationTask.setTimeout(this.timeout); + n9eCreationTask.setPause(ListUtils.strList2String(creationTaskData.getPauseList())); + n9eCreationTask.setScript(this.script); + n9eCreationTask.setArgs(sb.toString()); + n9eCreationTask.setAccount(this.account); + n9eCreationTask.setAction(ClusterTaskActionEnum.PAUSE.getAction()); + n9eCreationTask.setHosts(creationTaskData.getHostList()); + return n9eCreationTask; } } \ No newline at end of file diff --git a/kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/component/agent/n9e/entry/N9eCreationTask.java b/kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/component/agent/n9e/entry/N9eCreationTask.java new file mode 100644 index 00000000..6ca4c85c --- /dev/null +++ b/kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/component/agent/n9e/entry/N9eCreationTask.java @@ -0,0 +1,151 @@ +package com.xiaojukeji.kafka.manager.kcm.component.agent.n9e.entry; + +import java.util.List; + +public class N9eCreationTask { + /** + * 任务标题 + */ + private String title; + + /** + * 并发度, =2则表示两台并发执行 + */ + private Integer batch; + + /** + * 错误容忍度, 达到容忍度之上时, 任务会被暂停并不可以继续执行 + */ + private Integer tolerance; + + /** + * 单台任务的超时时间(秒) + */ + private Integer timeout; + + /** + * 暂停点, 格式: host1,host2,host3 + */ + private String pause; + + /** + * 任务执行对应的脚本 + */ + private String script; + + /** + * 任务参数 + */ + private String args; + + /** + * 使用的账号 + */ + private String account; + + /** + * 动作 + */ + private String action; + + /** + * 操作的主机列表 + */ + private List hosts; + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public Integer getBatch() { + return batch; + } + + public void setBatch(Integer batch) { + this.batch = batch; + } + + public Integer getTolerance() { + return tolerance; + } + + public void setTolerance(Integer tolerance) { + this.tolerance = tolerance; + } + + public Integer getTimeout() { + return timeout; + } + + public void setTimeout(Integer timeout) { + this.timeout = timeout; + } + + public String getPause() { + return pause; + } + + public void setPause(String pause) { + this.pause = pause; + } + + public String getScript() { + return script; + } + + public void setScript(String script) { + this.script = script; + } + + public String getArgs() { + return args; + } + + public void setArgs(String args) { + this.args = args; + } + + public String getAccount() { + return account; + } + + public void setAccount(String account) { + this.account = account; + } + + public String getAction() { + return action; + } + + public void setAction(String action) { + this.action = action; + } + + public List getHosts() { + return hosts; + } + + public void setHosts(List hosts) { + this.hosts = hosts; + } + + @Override + public String toString() { + return "N9eCreationTask{" + + "title='" + title + '\'' + + ", batch=" + batch + + ", tolerance=" + tolerance + + ", timeout=" + timeout + + ", pause='" + pause + '\'' + + ", script='" + script + '\'' + + ", args='" + args + '\'' + + ", account='" + account + '\'' + + ", action='" + action + '\'' + + ", hosts=" + hosts + + '}'; + } +} diff --git a/kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/component/agent/n9e/entry/N9eTaskResultDTO.java b/kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/component/agent/n9e/entry/N9eTaskResult.java similarity index 99% rename from kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/component/agent/n9e/entry/N9eTaskResultDTO.java rename to kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/component/agent/n9e/entry/N9eTaskResult.java index b787f016..e0e67b0e 100644 --- a/kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/component/agent/n9e/entry/N9eTaskResultDTO.java +++ b/kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/component/agent/n9e/entry/N9eTaskResult.java @@ -12,7 +12,7 @@ import java.util.Map; * @author zengqiao * @date 20/9/7 */ -public class N9eTaskResultDTO { +public class N9eTaskResult { private List waiting; private List running; diff --git a/kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/component/agent/n9e/entry/N9eTaskStdoutLog.java b/kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/component/agent/n9e/entry/N9eTaskStdoutLog.java new file mode 100644 index 00000000..622aaa3e --- /dev/null +++ b/kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/component/agent/n9e/entry/N9eTaskStdoutLog.java @@ -0,0 +1,35 @@ +package com.xiaojukeji.kafka.manager.kcm.component.agent.n9e.entry; + +/** + * @author zengqiao + * @date 20/9/7 + */ +public class N9eTaskStdoutLog { + private String host; + + private String stdout; + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + public String getStdout() { + return stdout; + } + + public void setStdout(String stdout) { + this.stdout = stdout; + } + + @Override + public String toString() { + return "N9eTaskStdoutDTO{" + + "host='" + host + '\'' + + ", stdout='" + stdout + '\'' + + '}'; + } +} \ No newline at end of file diff --git a/kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/component/agent/n9e/entry/bizenum/N9eTaskStatusEnum.java b/kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/component/agent/n9e/entry/bizenum/N9eTaskStatusEnum.java new file mode 100644 index 00000000..4453e703 --- /dev/null +++ b/kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/component/agent/n9e/entry/bizenum/N9eTaskStatusEnum.java @@ -0,0 +1,59 @@ +package com.xiaojukeji.kafka.manager.kcm.component.agent.n9e.entry.bizenum; + +import com.xiaojukeji.kafka.manager.kcm.common.bizenum.ClusterTaskStateEnum; + +/** + * @author zengqiao + * @date 20/9/3 + */ +public enum N9eTaskStatusEnum { + DONE(0, "done", ClusterTaskStateEnum.FINISHED), + PAUSE(1, "pause", ClusterTaskStateEnum.BLOCKED), + START(2, "start", ClusterTaskStateEnum.RUNNING), + ; + + private Integer code; + + private String message; + + private ClusterTaskStateEnum status; + + N9eTaskStatusEnum(Integer code, String message, ClusterTaskStateEnum status) { + this.code = code; + this.message = message; + this.status = status; + } + + public Integer getCode() { + return code; + } + + public void setCode(Integer code) { + this.code = code; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public ClusterTaskStateEnum getStatus() { + return status; + } + + public void setStatus(ClusterTaskStateEnum status) { + this.status = status; + } + + public static N9eTaskStatusEnum getByMessage(String message) { + for (N9eTaskStatusEnum elem: N9eTaskStatusEnum.values()) { + if (elem.message.equals(message)) { + return elem; + } + } + return null; + } +} \ No newline at end of file diff --git a/kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/component/storage/s3/S3Service.java b/kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/component/storage/s3/S3Service.java index 419e66e0..9519efd2 100644 --- a/kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/component/storage/s3/S3Service.java +++ b/kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/component/storage/s3/S3Service.java @@ -54,7 +54,7 @@ public class S3Service extends AbstractStorageService { InputStream inputStream = null; try { if (!createBucketIfNotExist()) { - return false; + return false; } inputStream = uploadFile.getInputStream(); @@ -95,7 +95,10 @@ public class S3Service extends AbstractStorageService { @Override public String getDownloadBaseUrl() { - return this.endpoint + "/" + this.bucket; + if (this.endpoint.startsWith("http://")) { + return this.endpoint + "/" + this.bucket; + } + return "http://" + this.endpoint + "/" + this.bucket; } private boolean createBucketIfNotExist() { diff --git a/kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/impl/ClusterTaskServiceImpl.java b/kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/impl/ClusterTaskServiceImpl.java index a190350a..b3ef959a 100644 --- a/kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/impl/ClusterTaskServiceImpl.java +++ b/kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/impl/ClusterTaskServiceImpl.java @@ -6,6 +6,7 @@ import com.xiaojukeji.kafka.manager.kcm.ClusterTaskService; import com.xiaojukeji.kafka.manager.kcm.common.Converters; import com.xiaojukeji.kafka.manager.kcm.common.bizenum.ClusterTaskActionEnum; import com.xiaojukeji.kafka.manager.kcm.common.entry.ClusterTaskConstant; +import com.xiaojukeji.kafka.manager.kcm.common.entry.ao.ClusterTaskLog; import com.xiaojukeji.kafka.manager.kcm.common.entry.ao.ClusterTaskSubStatus; import com.xiaojukeji.kafka.manager.kcm.common.bizenum.ClusterTaskStateEnum; import com.xiaojukeji.kafka.manager.kcm.common.bizenum.ClusterTaskSubStateEnum; @@ -34,7 +35,7 @@ import java.util.*; */ @Service("clusterTaskService") public class ClusterTaskServiceImpl implements ClusterTaskService { - private final static Logger LOGGER = LoggerFactory.getLogger(ClusterTaskServiceImpl.class); + private static final Logger LOGGER = LoggerFactory.getLogger(ClusterTaskServiceImpl.class); @Autowired private AbstractAgent abstractAgent; @@ -63,13 +64,13 @@ public class ClusterTaskServiceImpl implements ClusterTaskService { } // 创建任务 - Long agentTaskId = abstractAgent.createTask(dtoResult.getData()); - if (ValidateUtils.isNull(agentTaskId)) { + Result createResult = abstractAgent.createTask(dtoResult.getData()); + if (ValidateUtils.isNull(createResult) || createResult.failed()) { return Result.buildFrom(ResultStatus.CALL_CLUSTER_TASK_AGENT_FAILED); } try { - if (clusterTaskDao.insert(Converters.convert2ClusterTaskDO(agentTaskId, dtoResult.getData(), operator)) > 0) { + if (clusterTaskDao.insert(Converters.convert2ClusterTaskDO(createResult.getData(), dtoResult.getData(), operator)) > 0) { return Result.buildFrom(ResultStatus.SUCCESS); } } catch (Exception e) { @@ -87,45 +88,44 @@ public class ClusterTaskServiceImpl implements ClusterTaskService { Long agentTaskId = getActiveAgentTaskId(clusterTaskDO); Boolean rollback = inRollback(clusterTaskDO); - ClusterTaskStateEnum stateEnum = abstractAgent.getTaskState(agentTaskId); - if (ClusterTaskActionEnum.START.getMessage().equals(action) - && ClusterTaskStateEnum.BLOCKED.equals(stateEnum)) { + Result stateEnumResult = abstractAgent.getTaskExecuteState(agentTaskId); + if (ValidateUtils.isNull(stateEnumResult) || stateEnumResult.failed()) { + return ResultStatus.CALL_CLUSTER_TASK_AGENT_FAILED; + } + + if (ClusterTaskActionEnum.START.getAction().equals(action) && ClusterTaskStateEnum.BLOCKED.equals(stateEnumResult.getData())) { // 暂停状态, 可以执行开始 - return actionTaskExceptRollbackAction(agentTaskId, action, ""); + return actionTaskExceptRollbackAction(agentTaskId, ClusterTaskActionEnum.START, ""); } - if (ClusterTaskActionEnum.PAUSE.getMessage().equals(action) - && ClusterTaskStateEnum.RUNNING.equals(stateEnum)) { + if (ClusterTaskActionEnum.PAUSE.getAction().equals(action) && ClusterTaskStateEnum.RUNNING.equals(stateEnumResult.getData())) { // 运行状态, 可以执行暂停 - return actionTaskExceptRollbackAction(agentTaskId, action, ""); + return actionTaskExceptRollbackAction(agentTaskId, ClusterTaskActionEnum.PAUSE, ""); } - if (ClusterTaskActionEnum.IGNORE.getMessage().equals(action) - || ClusterTaskActionEnum.CANCEL.getMessage().equals(action)) { + if (ClusterTaskActionEnum.IGNORE.getAction().equals(action)) { // 忽略 & 取消随时都可以操作 - return actionTaskExceptRollbackAction(agentTaskId, action, hostname); + return actionTaskExceptRollbackAction(agentTaskId, ClusterTaskActionEnum.IGNORE, hostname); } - if ((!ClusterTaskStateEnum.FINISHED.equals(stateEnum) || !rollback) - && ClusterTaskActionEnum.ROLLBACK.getMessage().equals(action)) { + if (ClusterTaskActionEnum.CANCEL.getAction().equals(action)) { + // 忽略 & 取消随时都可以操作 + return actionTaskExceptRollbackAction(agentTaskId, ClusterTaskActionEnum.CANCEL, hostname); + } + if ((!ClusterTaskStateEnum.FINISHED.equals(stateEnumResult.getData()) || !rollback) + && ClusterTaskActionEnum.ROLLBACK.getAction().equals(action)) { // 暂未操作完时可以回滚, 回滚所有操作过的机器到上一个版本 return actionTaskRollback(clusterTaskDO); } return ResultStatus.OPERATION_FAILED; } - private ResultStatus actionTaskExceptRollbackAction(Long agentId, String action, String hostname) { + private ResultStatus actionTaskExceptRollbackAction(Long agentId, ClusterTaskActionEnum actionEnum, String hostname) { if (!ValidateUtils.isBlank(hostname)) { - return actionHostTaskExceptRollbackAction(agentId, action, hostname); + return actionHostTaskExceptRollbackAction(agentId, actionEnum, hostname); } - if (abstractAgent.actionTask(agentId, action)) { - return ResultStatus.SUCCESS; - } - return ResultStatus.OPERATION_FAILED; + return abstractAgent.actionTask(agentId, actionEnum)? ResultStatus.SUCCESS: ResultStatus.OPERATION_FAILED; } - private ResultStatus actionHostTaskExceptRollbackAction(Long agentId, String action, String hostname) { - if (abstractAgent.actionHostTask(agentId, action, hostname)) { - return ResultStatus.SUCCESS; - } - return ResultStatus.OPERATION_FAILED; + private ResultStatus actionHostTaskExceptRollbackAction(Long agentId, ClusterTaskActionEnum actionEnum, String hostname) { + return abstractAgent.actionHostTask(agentId, actionEnum, hostname)? ResultStatus.SUCCESS: ResultStatus.OPERATION_FAILED; } private ResultStatus actionTaskRollback(ClusterTaskDO clusterTaskDO) { @@ -133,9 +133,9 @@ public class ClusterTaskServiceImpl implements ClusterTaskService { return ResultStatus.OPERATION_FORBIDDEN; } - Map subStatusEnumMap = + Result> subStatusEnumMapResult = abstractAgent.getTaskResult(clusterTaskDO.getAgentTaskId()); - if (ValidateUtils.isNull(subStatusEnumMap)) { + if (ValidateUtils.isNull(subStatusEnumMapResult) || subStatusEnumMapResult.failed()) { return ResultStatus.CALL_CLUSTER_TASK_AGENT_FAILED; } @@ -143,7 +143,7 @@ public class ClusterTaskServiceImpl implements ClusterTaskService { List rollbackHostList = new ArrayList<>(); List rollbackPauseHostList = new ArrayList<>(); for (String host: ListUtils.string2StrList(clusterTaskDO.getHostList())) { - ClusterTaskSubStateEnum subStateEnum = subStatusEnumMap.get(host); + ClusterTaskSubStateEnum subStateEnum = subStatusEnumMapResult.getData().get(host); if (ValidateUtils.isNull(subStateEnum)) { // 机器对应的任务查询失败 return ResultStatus.OPERATION_FAILED; @@ -166,17 +166,17 @@ public class ClusterTaskServiceImpl implements ClusterTaskService { clusterTaskDO.setRollbackPauseHostList(ListUtils.strList2String(rollbackPauseHostList)); // 创建任务 - Long agentTaskId = abstractAgent.createTask(Converters.convert2CreationTaskData(clusterTaskDO)); - if (ValidateUtils.isNull(agentTaskId)) { + Result createResult = abstractAgent.createTask(Converters.convert2CreationTaskData(clusterTaskDO)); + if (ValidateUtils.isNull(createResult) || createResult.failed()) { return ResultStatus.CALL_CLUSTER_TASK_AGENT_FAILED; } try { - clusterTaskDO.setAgentRollbackTaskId(agentTaskId); + clusterTaskDO.setAgentRollbackTaskId(createResult.getData()); if (clusterTaskDao.updateRollback(clusterTaskDO) <= 0) { return ResultStatus.MYSQL_ERROR; } - abstractAgent.actionTask(clusterTaskDO.getAgentTaskId(), ClusterTaskActionEnum.CANCEL.getMessage()); + abstractAgent.actionTask(clusterTaskDO.getAgentTaskId(), ClusterTaskActionEnum.CANCEL); return ResultStatus.SUCCESS; } catch (Exception e) { LOGGER.error("create cluster task failed, clusterTaskDO:{}.", clusterTaskDO, e); @@ -191,11 +191,11 @@ public class ClusterTaskServiceImpl implements ClusterTaskService { return Result.buildFrom(ResultStatus.TASK_NOT_EXIST); } - String stdoutLog = abstractAgent.getTaskLog(getActiveAgentTaskId(clusterTaskDO, hostname), hostname); - if (ValidateUtils.isNull(stdoutLog)) { + Result stdoutLogResult = abstractAgent.getTaskLog(getActiveAgentTaskId(clusterTaskDO, hostname), hostname); + if (ValidateUtils.isNull(stdoutLogResult) || stdoutLogResult.failed()) { return Result.buildFrom(ResultStatus.CALL_CLUSTER_TASK_AGENT_FAILED); } - return new Result<>(stdoutLog); + return new Result<>(stdoutLogResult.getData().getStdout()); } @Override @@ -205,24 +205,33 @@ public class ClusterTaskServiceImpl implements ClusterTaskService { return Result.buildFrom(ResultStatus.TASK_NOT_EXIST); } + Result statusEnumResult = abstractAgent.getTaskExecuteState(getActiveAgentTaskId(clusterTaskDO)); + if (ValidateUtils.isNull(statusEnumResult) || statusEnumResult.failed()) { + return new Result<>(statusEnumResult.getCode(), statusEnumResult.getMessage()); + } + return new Result<>(new ClusterTaskStatus( clusterTaskDO.getId(), clusterTaskDO.getClusterId(), inRollback(clusterTaskDO), - abstractAgent.getTaskState(getActiveAgentTaskId(clusterTaskDO)), + statusEnumResult.getData(), getTaskSubStatus(clusterTaskDO) )); } @Override public ClusterTaskStateEnum getTaskState(Long agentTaskId) { - return abstractAgent.getTaskState(agentTaskId); + Result statusEnumResult = abstractAgent.getTaskExecuteState(agentTaskId); + if (ValidateUtils.isNull(statusEnumResult) || statusEnumResult.failed()) { + return null; + } + return statusEnumResult.getData(); } private List getTaskSubStatus(ClusterTaskDO clusterTaskDO) { Map statusMap = this.getClusterTaskSubState(clusterTaskDO); if (ValidateUtils.isNull(statusMap)) { - return null; + return Collections.emptyList(); } List pauseList = ListUtils.string2StrList(clusterTaskDO.getPauseHostList()); @@ -242,20 +251,22 @@ public class ClusterTaskServiceImpl implements ClusterTaskService { } private Map getClusterTaskSubState(ClusterTaskDO clusterTaskDO) { - Map statusMap = abstractAgent.getTaskResult(clusterTaskDO.getAgentTaskId()); - if (ValidateUtils.isNull(statusMap)) { + Result> statusMapResult = abstractAgent.getTaskResult(clusterTaskDO.getAgentTaskId()); + if (ValidateUtils.isNull(statusMapResult) || statusMapResult.failed()) { return null; } + Map statusMap = statusMapResult.getData(); if (!inRollback(clusterTaskDO)) { return statusMap; } - Map rollbackStatusMap = + Result> rollbackStatusMapResult = abstractAgent.getTaskResult(clusterTaskDO.getAgentRollbackTaskId()); - if (ValidateUtils.isNull(rollbackStatusMap)) { + if (ValidateUtils.isNull(rollbackStatusMapResult) || rollbackStatusMapResult.failed()) { return null; } - statusMap.putAll(rollbackStatusMap); + + statusMap.putAll(rollbackStatusMapResult.getData()); return statusMap; } @@ -276,7 +287,7 @@ public class ClusterTaskServiceImpl implements ClusterTaskService { } catch (Exception e) { LOGGER.error("get all cluster task failed."); } - return null; + return Collections.emptyList(); } @Override @@ -302,9 +313,6 @@ public class ClusterTaskServiceImpl implements ClusterTaskService { } private boolean inRollback(ClusterTaskDO clusterTaskDO) { - if (ClusterTaskConstant.INVALID_AGENT_TASK_ID.equals(clusterTaskDO.getAgentRollbackTaskId())) { - return false; - } - return true; + return !ClusterTaskConstant.INVALID_AGENT_TASK_ID.equals(clusterTaskDO.getAgentRollbackTaskId()); } } \ No newline at end of file diff --git a/kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/impl/KafkaFileServiceImpl.java b/kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/impl/KafkaFileServiceImpl.java index 37f8753a..bef2fb89 100644 --- a/kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/impl/KafkaFileServiceImpl.java +++ b/kafka-manager-extends/kafka-manager-kcm/src/main/java/com/xiaojukeji/kafka/manager/kcm/impl/KafkaFileServiceImpl.java @@ -4,12 +4,12 @@ import com.xiaojukeji.kafka.manager.common.bizenum.KafkaFileEnum; import com.xiaojukeji.kafka.manager.common.entity.Result; import com.xiaojukeji.kafka.manager.common.entity.ResultStatus; import com.xiaojukeji.kafka.manager.common.entity.dto.normal.KafkaFileDTO; -import com.xiaojukeji.kafka.manager.common.entity.pojo.KafkaFileDO; import com.xiaojukeji.kafka.manager.common.utils.CopyUtils; import com.xiaojukeji.kafka.manager.common.utils.ValidateUtils; import com.xiaojukeji.kafka.manager.dao.KafkaFileDao; -import com.xiaojukeji.kafka.manager.kcm.KafkaFileService; +import com.xiaojukeji.kafka.manager.common.entity.pojo.KafkaFileDO; import com.xiaojukeji.kafka.manager.kcm.component.storage.AbstractStorageService; +import com.xiaojukeji.kafka.manager.kcm.KafkaFileService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; diff --git a/kafka-manager-web/src/main/java/com/xiaojukeji/kafka/manager/web/api/versionone/gateway/GatewayHeartbeatController.java b/kafka-manager-web/src/main/java/com/xiaojukeji/kafka/manager/web/api/versionone/gateway/GatewayHeartbeatController.java index 4fe01e22..02a11497 100644 --- a/kafka-manager-web/src/main/java/com/xiaojukeji/kafka/manager/web/api/versionone/gateway/GatewayHeartbeatController.java +++ b/kafka-manager-web/src/main/java/com/xiaojukeji/kafka/manager/web/api/versionone/gateway/GatewayHeartbeatController.java @@ -50,7 +50,7 @@ public class GatewayHeartbeatController { doList = JsonUtils.parseTopicConnections(clusterId, jsonObject, System.currentTimeMillis()); } catch (Exception e) { LOGGER.error("class=GatewayHeartbeatController||method=receiveTopicConnections||clusterId={}||brokerId={}||msg=parse data failed||exception={}", clusterId, brokerId, e.getMessage()); - return Result.buildFailure("fail"); + return Result.buildGatewayFailure("fail"); } topicConnectionService.batchAdd(doList); diff --git a/kafka-manager-web/src/main/java/com/xiaojukeji/kafka/manager/web/api/versionone/gateway/GatewayServiceDiscoveryController.java b/kafka-manager-web/src/main/java/com/xiaojukeji/kafka/manager/web/api/versionone/gateway/GatewayServiceDiscoveryController.java index e490368d..425eba75 100644 --- a/kafka-manager-web/src/main/java/com/xiaojukeji/kafka/manager/web/api/versionone/gateway/GatewayServiceDiscoveryController.java +++ b/kafka-manager-web/src/main/java/com/xiaojukeji/kafka/manager/web/api/versionone/gateway/GatewayServiceDiscoveryController.java @@ -31,7 +31,6 @@ import java.util.Map; @RestController @RequestMapping(ApiPrefix.GATEWAY_API_V1_PREFIX) public class GatewayServiceDiscoveryController { - private final static Logger LOGGER = LoggerFactory.getLogger(GatewayHeartbeatController.class); @Autowired @@ -65,7 +64,7 @@ public class GatewayServiceDiscoveryController { KafkaBootstrapServerConfig config = gatewayConfigService.getKafkaBootstrapServersConfig(Long.MIN_VALUE); if (ValidateUtils.isNull(config) || ValidateUtils.isNull(config.getClusterIdBootstrapServersMap())) { - return Result.buildFailure("call init kafka bootstrap servers failed"); + return Result.buildGatewayFailure("call init kafka bootstrap servers failed"); } if (ValidateUtils.isEmptyMap(config.getClusterIdBootstrapServersMap())) { return Result.buildSuc(); @@ -81,7 +80,7 @@ public class GatewayServiceDiscoveryController { KafkaBootstrapServerConfig config = gatewayConfigService.getKafkaBootstrapServersConfig(versionNumber); if (ValidateUtils.isNull(config) || ValidateUtils.isNull(config.getClusterIdBootstrapServersMap())) { - return Result.buildFailure("call update kafka bootstrap servers failed"); + return Result.buildGatewayFailure("call update kafka bootstrap servers failed"); } if (ValidateUtils.isEmptyMap(config.getClusterIdBootstrapServersMap())) { return Result.buildSuc(); @@ -99,7 +98,7 @@ public class GatewayServiceDiscoveryController { public Result getMaxRequestNum(@RequestParam("versionNumber") long versionNumber) { RequestQueueConfig config = gatewayConfigService.getRequestQueueConfig(versionNumber); if (ValidateUtils.isNull(config)) { - return Result.buildFailure("call get request queue size config failed"); + return Result.buildGatewayFailure("call get request queue size config failed"); } if (ValidateUtils.isNull(config.getMaxRequestQueueSize())) { return Result.buildSuc(); @@ -119,7 +118,7 @@ public class GatewayServiceDiscoveryController { public Result getAppIdRate(@RequestParam("versionNumber") long versionNumber) { AppRateConfig config = gatewayConfigService.getAppRateConfig(versionNumber); if (ValidateUtils.isNull(config)) { - return Result.buildFailure("call get app rate config failed"); + return Result.buildGatewayFailure("call get app rate config failed"); } if (ValidateUtils.isNull(config.getAppRateLimit())) { return Result.buildSuc(); @@ -139,7 +138,7 @@ public class GatewayServiceDiscoveryController { public Result getIpRate(@RequestParam("versionNumber") long versionNumber) { IpRateConfig config = gatewayConfigService.getIpRateConfig(versionNumber); if (ValidateUtils.isNull(config)) { - return Result.buildFailure("call get ip rate config failed"); + return Result.buildGatewayFailure("call get ip rate config failed"); } if (ValidateUtils.isNull(config.getIpRateLimit())) { return Result.buildSuc(); @@ -160,7 +159,7 @@ public class GatewayServiceDiscoveryController { SpRateConfig config = gatewayConfigService.getSpRateConfig(versionNumber); if (ValidateUtils.isNull(config) || ValidateUtils.isNull(config.getSpRateMap())) { - return Result.buildFailure("call update kafka bootstrap servers failed"); + return Result.buildGatewayFailure("call update kafka bootstrap servers failed"); } if (ValidateUtils.isEmptyMap(config.getSpRateMap())) { return Result.buildSuc(); diff --git a/kafka-manager-web/src/main/java/com/xiaojukeji/kafka/manager/web/api/versionone/op/OpClusterController.java b/kafka-manager-web/src/main/java/com/xiaojukeji/kafka/manager/web/api/versionone/op/OpClusterController.java index 07b7dbc4..2caaa69b 100644 --- a/kafka-manager-web/src/main/java/com/xiaojukeji/kafka/manager/web/api/versionone/op/OpClusterController.java +++ b/kafka-manager-web/src/main/java/com/xiaojukeji/kafka/manager/web/api/versionone/op/OpClusterController.java @@ -2,6 +2,7 @@ package com.xiaojukeji.kafka.manager.web.api.versionone.op; import com.xiaojukeji.kafka.manager.common.entity.Result; import com.xiaojukeji.kafka.manager.common.entity.ResultStatus; +import com.xiaojukeji.kafka.manager.common.entity.dto.op.ControllerPreferredCandidateDTO; import com.xiaojukeji.kafka.manager.common.entity.dto.rd.ClusterDTO; import com.xiaojukeji.kafka.manager.common.utils.ValidateUtils; import com.xiaojukeji.kafka.manager.service.service.ClusterService; @@ -13,6 +14,7 @@ import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; + /** * @author zengqiao * @date 20/4/23 @@ -25,48 +27,56 @@ public class OpClusterController { private ClusterService clusterService; @ApiOperation(value = "接入集群") - @RequestMapping(value = "clusters", method = RequestMethod.POST) + @PostMapping(value = "clusters") @ResponseBody public Result addNew(@RequestBody ClusterDTO dto) { if (ValidateUtils.isNull(dto) || !dto.legal()) { return Result.buildFrom(ResultStatus.PARAM_ILLEGAL); } return Result.buildFrom( - clusterService.addNew( - ClusterModelConverter.convert2ClusterDO(dto), - SpringTool.getUserName() - ) + clusterService.addNew(ClusterModelConverter.convert2ClusterDO(dto), SpringTool.getUserName()) ); } @ApiOperation(value = "删除集群") - @RequestMapping(value = "clusters", method = RequestMethod.DELETE) + @DeleteMapping(value = "clusters") @ResponseBody public Result delete(@RequestParam(value = "clusterId") Long clusterId) { return Result.buildFrom(clusterService.deleteById(clusterId, SpringTool.getUserName())); } @ApiOperation(value = "修改集群信息") - @RequestMapping(value = "clusters", method = RequestMethod.PUT) + @PutMapping(value = "clusters") @ResponseBody public Result modify(@RequestBody ClusterDTO reqObj) { if (ValidateUtils.isNull(reqObj) || !reqObj.legal() || ValidateUtils.isNull(reqObj.getClusterId())) { return Result.buildFrom(ResultStatus.PARAM_ILLEGAL); } - ResultStatus rs = clusterService.updateById( - ClusterModelConverter.convert2ClusterDO(reqObj), - SpringTool.getUserName() + return Result.buildFrom( + clusterService.updateById(ClusterModelConverter.convert2ClusterDO(reqObj), SpringTool.getUserName()) ); - return Result.buildFrom(rs); } @ApiOperation(value = "开启|关闭集群监控") - @RequestMapping(value = "clusters/{clusterId}/monitor", method = RequestMethod.PUT) + @PutMapping(value = "clusters/{clusterId}/monitor") @ResponseBody - public Result modifyStatus(@PathVariable Long clusterId, - @RequestParam("status") Integer status) { + public Result modifyStatus(@PathVariable Long clusterId, @RequestParam("status") Integer status) { return Result.buildFrom( clusterService.modifyStatus(clusterId, status, SpringTool.getUserName()) ); } + + @ApiOperation(value = "增加Controller优先候选的Broker", notes = "滴滴内部引擎特性") + @PostMapping(value = "cluster-controller/preferred-candidates") + @ResponseBody + public Result addControllerPreferredCandidates(@RequestBody ControllerPreferredCandidateDTO dto) { + return clusterService.addControllerPreferredCandidates(dto.getClusterId(), dto.getBrokerIdList()); + } + + @ApiOperation(value = "删除Controller优先候选的Broker", notes = "滴滴内部引擎特性") + @DeleteMapping(value = "cluster-controller/preferred-candidates") + @ResponseBody + public Result deleteControllerPreferredCandidates(@RequestBody ControllerPreferredCandidateDTO dto) { + return clusterService.deleteControllerPreferredCandidates(dto.getClusterId(), dto.getBrokerIdList()); + } } \ No newline at end of file diff --git a/kafka-manager-web/src/main/java/com/xiaojukeji/kafka/manager/web/api/versionone/op/OpGatewayConfigController.java b/kafka-manager-web/src/main/java/com/xiaojukeji/kafka/manager/web/api/versionone/op/OpGatewayConfigController.java index a97bb386..66eb3b7e 100644 --- a/kafka-manager-web/src/main/java/com/xiaojukeji/kafka/manager/web/api/versionone/op/OpGatewayConfigController.java +++ b/kafka-manager-web/src/main/java/com/xiaojukeji/kafka/manager/web/api/versionone/op/OpGatewayConfigController.java @@ -3,8 +3,11 @@ package com.xiaojukeji.kafka.manager.web.api.versionone.op; import com.xiaojukeji.kafka.manager.bpm.common.entry.apply.gateway.OrderExtensionAddGatewayConfigDTO; import com.xiaojukeji.kafka.manager.bpm.common.entry.apply.gateway.OrderExtensionDeleteGatewayConfigDTO; import com.xiaojukeji.kafka.manager.bpm.common.entry.apply.gateway.OrderExtensionModifyGatewayConfigDTO; +import com.xiaojukeji.kafka.manager.common.bizenum.gateway.GatewayConfigKeyEnum; +import com.xiaojukeji.kafka.manager.common.constant.ApiPrefix; import com.xiaojukeji.kafka.manager.common.entity.Result; import com.xiaojukeji.kafka.manager.common.entity.ResultStatus; +import com.xiaojukeji.kafka.manager.common.utils.JsonUtils; import com.xiaojukeji.kafka.manager.common.utils.ValidateUtils; import com.xiaojukeji.kafka.manager.service.service.gateway.GatewayConfigService; import com.xiaojukeji.kafka.manager.web.converters.GatewayModelConverter; @@ -16,12 +19,20 @@ import org.springframework.web.bind.annotation.*; @Api(tags = "OP-Gateway配置相关接口(REST)") @RestController +@RequestMapping(ApiPrefix.API_V1_OP_PREFIX) public class OpGatewayConfigController { @Autowired private GatewayConfigService gatewayConfigService; + @ApiOperation(value = "Gateway配置类型", notes = "") + @GetMapping(value = "gateway-configs/type-enums") + @ResponseBody + public Result getClusterModesEnum() { + return new Result<>(JsonUtils.toJson(GatewayConfigKeyEnum.class)); + } + @ApiOperation(value = "创建Gateway配置", notes = "") - @RequestMapping(value = "gateway-configs", method = RequestMethod.POST) + @PostMapping(value = "gateway-configs") @ResponseBody public Result createGatewayConfig(@RequestBody OrderExtensionAddGatewayConfigDTO dto) { if (ValidateUtils.isNull(dto) || !dto.legal()) { @@ -31,7 +42,7 @@ public class OpGatewayConfigController { } @ApiOperation(value = "修改Gateway配置", notes = "") - @RequestMapping(value = "gateway-configs", method = RequestMethod.PUT) + @PutMapping(value = "gateway-configs") @ResponseBody public Result modifyGatewayConfig(@RequestBody OrderExtensionModifyGatewayConfigDTO dto) { if (ValidateUtils.isNull(dto) || !dto.legal()) { @@ -41,7 +52,7 @@ public class OpGatewayConfigController { } @ApiOperation(value = "删除Gateway配置", notes = "") - @RequestMapping(value = "gateway-configs", method = RequestMethod.DELETE) + @DeleteMapping(value = "gateway-configs") @ResponseBody public Result deleteGatewayConfig(@RequestBody OrderExtensionDeleteGatewayConfigDTO dto) { if (ValidateUtils.isNull(dto) || !dto.legal()) { diff --git a/kafka-manager-web/src/main/java/com/xiaojukeji/kafka/manager/web/api/versionone/rd/RdGatewayConfigController.java b/kafka-manager-web/src/main/java/com/xiaojukeji/kafka/manager/web/api/versionone/rd/RdGatewayConfigController.java index 3748c3ca..6a46ff0a 100644 --- a/kafka-manager-web/src/main/java/com/xiaojukeji/kafka/manager/web/api/versionone/rd/RdGatewayConfigController.java +++ b/kafka-manager-web/src/main/java/com/xiaojukeji/kafka/manager/web/api/versionone/rd/RdGatewayConfigController.java @@ -1,5 +1,6 @@ package com.xiaojukeji.kafka.manager.web.api.versionone.rd; +import com.xiaojukeji.kafka.manager.common.constant.ApiPrefix; import com.xiaojukeji.kafka.manager.common.entity.Result; import com.xiaojukeji.kafka.manager.common.entity.pojo.gateway.GatewayConfigDO; import com.xiaojukeji.kafka.manager.common.entity.vo.rd.GatewayConfigVO; @@ -15,12 +16,13 @@ import java.util.List; @Api(tags = "RD-Gateway配置相关接口(REST)") @RestController +@RequestMapping(ApiPrefix.API_V1_RD_PREFIX) public class RdGatewayConfigController { @Autowired private GatewayConfigService gatewayConfigService; @ApiOperation(value = "Gateway相关配置信息", notes = "") - @RequestMapping(value = "gateway-configs", method = RequestMethod.GET) + @GetMapping(value = "gateway-configs") @ResponseBody public Result> getGatewayConfigs() { List doList = gatewayConfigService.list(); diff --git a/kafka-manager-web/src/main/java/com/xiaojukeji/kafka/manager/web/api/versionone/rd/RdKafkaFileController.java b/kafka-manager-web/src/main/java/com/xiaojukeji/kafka/manager/web/api/versionone/rd/RdKafkaFileController.java index 009d540a..eaab7dc9 100644 --- a/kafka-manager-web/src/main/java/com/xiaojukeji/kafka/manager/web/api/versionone/rd/RdKafkaFileController.java +++ b/kafka-manager-web/src/main/java/com/xiaojukeji/kafka/manager/web/api/versionone/rd/RdKafkaFileController.java @@ -1,17 +1,17 @@ package com.xiaojukeji.kafka.manager.web.api.versionone.rd; import com.xiaojukeji.kafka.manager.common.bizenum.KafkaFileEnum; -import com.xiaojukeji.kafka.manager.common.constant.ApiPrefix; import com.xiaojukeji.kafka.manager.common.entity.Result; import com.xiaojukeji.kafka.manager.common.entity.dto.normal.KafkaFileDTO; -import com.xiaojukeji.kafka.manager.common.entity.pojo.KafkaFileDO; import com.xiaojukeji.kafka.manager.common.entity.vo.rd.KafkaFileVO; +import com.xiaojukeji.kafka.manager.common.utils.ValidateUtils; +import com.xiaojukeji.kafka.manager.kcm.component.storage.common.StorageEnum; +import com.xiaojukeji.kafka.manager.common.entity.pojo.KafkaFileDO; +import com.xiaojukeji.kafka.manager.service.service.ClusterService; +import com.xiaojukeji.kafka.manager.kcm.KafkaFileService; import com.xiaojukeji.kafka.manager.common.utils.JsonUtils; import com.xiaojukeji.kafka.manager.common.utils.SpringTool; -import com.xiaojukeji.kafka.manager.common.utils.ValidateUtils; -import com.xiaojukeji.kafka.manager.kcm.KafkaFileService; -import com.xiaojukeji.kafka.manager.kcm.component.storage.common.StorageEnum; -import com.xiaojukeji.kafka.manager.service.service.ClusterService; +import com.xiaojukeji.kafka.manager.common.constant.ApiPrefix; import com.xiaojukeji.kafka.manager.web.converters.KafkaFileConverter; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; diff --git a/kafka-manager-web/src/main/java/com/xiaojukeji/kafka/manager/web/api/versionone/rd/RdOperateRecordController.java b/kafka-manager-web/src/main/java/com/xiaojukeji/kafka/manager/web/api/versionone/rd/RdOperateRecordController.java index 11f063e6..68068f97 100644 --- a/kafka-manager-web/src/main/java/com/xiaojukeji/kafka/manager/web/api/versionone/rd/RdOperateRecordController.java +++ b/kafka-manager-web/src/main/java/com/xiaojukeji/kafka/manager/web/api/versionone/rd/RdOperateRecordController.java @@ -24,14 +24,13 @@ import java.util.List; @RestController @RequestMapping(ApiPrefix.API_V1_RD_PREFIX) public class RdOperateRecordController { - private static final int MAX_RECORD_COUNT = 200; @Autowired private OperateRecordService operateRecordService; @ApiOperation(value = "查询操作记录", notes = "") - @RequestMapping(value = "operate-record", method = RequestMethod.POST) + @PostMapping(value = "operate-record") @ResponseBody public Result> geOperateRecords(@RequestBody OperateRecordDTO dto) { if (ValidateUtils.isNull(dto) || !dto.legal()) { diff --git a/kafka-manager-web/src/main/java/com/xiaojukeji/kafka/manager/web/converters/GatewayModelConverter.java b/kafka-manager-web/src/main/java/com/xiaojukeji/kafka/manager/web/converters/GatewayModelConverter.java index f032e921..6a8b5f79 100644 --- a/kafka-manager-web/src/main/java/com/xiaojukeji/kafka/manager/web/converters/GatewayModelConverter.java +++ b/kafka-manager-web/src/main/java/com/xiaojukeji/kafka/manager/web/converters/GatewayModelConverter.java @@ -67,6 +67,7 @@ public class GatewayModelConverter { vo.setName(configDO.getName()); vo.setValue(configDO.getValue()); vo.setVersion(configDO.getVersion()); + vo.setDescription(configDO.getDescription()); vo.setCreateTime(configDO.getCreateTime()); vo.setModifyTime(configDO.getModifyTime()); voList.add(vo); @@ -76,18 +77,20 @@ public class GatewayModelConverter { public static GatewayConfigDO convert2GatewayConfigDO(OrderExtensionAddGatewayConfigDTO configDTO) { GatewayConfigDO configDO = new GatewayConfigDO(); - configDO.setType(configDO.getType()); - configDO.setName(configDO.getName()); - configDO.setValue(configDO.getValue()); + configDO.setType(configDTO.getType()); + configDO.setName(configDTO.getName()); + configDO.setValue(configDTO.getValue()); + configDO.setDescription(ValidateUtils.isNull(configDTO.getDescription())? "": configDTO.getDescription()); return configDO; } public static GatewayConfigDO convert2GatewayConfigDO(OrderExtensionModifyGatewayConfigDTO configDTO) { GatewayConfigDO configDO = new GatewayConfigDO(); - configDO.setId(configDO.getId()); - configDO.setType(configDO.getType()); - configDO.setName(configDO.getName()); - configDO.setValue(configDO.getValue()); + configDO.setId(configDTO.getId()); + configDO.setType(configDTO.getType()); + configDO.setName(configDTO.getName()); + configDO.setValue(configDTO.getValue()); + configDO.setDescription(ValidateUtils.isNull(configDTO.getDescription())? "": configDTO.getDescription()); return configDO; } } \ No newline at end of file diff --git a/kafka-manager-web/src/main/java/com/xiaojukeji/kafka/manager/web/inteceptor/WebMetricsInterceptor.java b/kafka-manager-web/src/main/java/com/xiaojukeji/kafka/manager/web/inteceptor/WebMetricsInterceptor.java index bf8bc1e1..576fe036 100644 --- a/kafka-manager-web/src/main/java/com/xiaojukeji/kafka/manager/web/inteceptor/WebMetricsInterceptor.java +++ b/kafka-manager-web/src/main/java/com/xiaojukeji/kafka/manager/web/inteceptor/WebMetricsInterceptor.java @@ -119,7 +119,7 @@ public class WebMetricsInterceptor { ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); String uri = attributes.getRequest().getRequestURI(); if (uri.contains(ApiPrefix.GATEWAY_API_V1_PREFIX)) { - return Result.buildFailure("api limited"); + return Result.buildGatewayFailure("api limited"); } return new Result<>(ResultStatus.OPERATION_FORBIDDEN); } diff --git a/kafka-manager-web/src/main/resources/application.yml b/kafka-manager-web/src/main/resources/application.yml index 5b01d321..6f5438a3 100644 --- a/kafka-manager-web/src/main/resources/application.yml +++ b/kafka-manager-web/src/main/resources/application.yml @@ -31,7 +31,7 @@ logging: custom: idc: cn jmx: - max-conn: 10 + max-conn: 10 # 2.3版本配置不在这个地方生效 store-metrics-task: community: broker-metrics-enabled: true @@ -53,7 +53,7 @@ account: kcm: enabled: false s3: - endpoint: 127.0.0.1 + endpoint: s3.didiyunapi.com access-key: 1234567890 secret-key: 0987654321 bucket: logi-kafka From 25e84b2a6c9af494bd365880540f54d58ebd305e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E6=B0=91?= Date: Tue, 9 Feb 2021 11:33:54 +0800 Subject: [PATCH 28/33] =?UTF-8?q?=E6=96=B0=E5=8A=9F=E8=83=BD=EF=BC=9A?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=AF=B9LDAP=E7=9A=84=E7=99=BB=E5=BD=95?= =?UTF-8?q?=E7=9A=84=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/bizenum/AccountRoleEnum.java | 9 ++ .../common/utils/ldap/LDAPAuthentication.java | 112 ++++++++++++++++++ .../component/sso/BaseSessionSignOn.java | 40 +++++++ .../src/main/resources/application.yml | 16 ++- 4 files changed, 175 insertions(+), 2 deletions(-) create mode 100644 kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/utils/ldap/LDAPAuthentication.java diff --git a/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/bizenum/AccountRoleEnum.java b/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/bizenum/AccountRoleEnum.java index 9c3cc06c..55412490 100644 --- a/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/bizenum/AccountRoleEnum.java +++ b/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/bizenum/AccountRoleEnum.java @@ -47,4 +47,13 @@ public enum AccountRoleEnum { } return AccountRoleEnum.UNKNOWN; } + + public static AccountRoleEnum getUserRoleEnum(String roleName) { + for (AccountRoleEnum elem: AccountRoleEnum.values()) { + if (elem.message.equalsIgnoreCase(roleName)) { + return elem; + } + } + return AccountRoleEnum.UNKNOWN; + } } diff --git a/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/utils/ldap/LDAPAuthentication.java b/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/utils/ldap/LDAPAuthentication.java new file mode 100644 index 00000000..3dbc6c99 --- /dev/null +++ b/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/utils/ldap/LDAPAuthentication.java @@ -0,0 +1,112 @@ +package com.xiaojukeji.kafka.manager.common.utils.ldap; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import javax.naming.AuthenticationException; +import javax.naming.Context; +import javax.naming.NamingEnumeration; +import javax.naming.NamingException; +import javax.naming.directory.SearchControls; +import javax.naming.directory.SearchResult; +import javax.naming.ldap.InitialLdapContext; +import javax.naming.ldap.LdapContext; +import java.util.Hashtable; + +@Component +public class LDAPAuthentication { + + @Value(value = "${ldap.url}") + private String ldapUrl; + + @Value(value = "${ldap.basedn}") + private String ldapBasedn; + + @Value(value = "${ldap.factory}") + private String ldapFactory; + + @Value(value = "${ldap.auth-user-registration-role}") + private String authUserRegistrationRole; + + @Value(value = "${ldap.security.authentication}") + private String securityAuthentication; + + @Value(value = "${ldap.security.principal}") + private String securityPrincipal; + + @Value(value = "${ldap.security.credentials}") + private String securityCredentials; + + private LdapContext getConnect() { + Hashtable env = new Hashtable(); + env.put(Context.INITIAL_CONTEXT_FACTORY, ldapFactory); + env.put(Context.PROVIDER_URL, ldapUrl + ldapBasedn); + env.put(Context.SECURITY_AUTHENTICATION, securityAuthentication); + + // 此处若不指定用户名和密码,则自动转换为匿名登录 + env.put(Context.SECURITY_PRINCIPAL, securityPrincipal); + env.put(Context.SECURITY_CREDENTIALS, securityCredentials); + try { + return new InitialLdapContext(env, null); + } catch (AuthenticationException e) { + e.printStackTrace(); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + private String getUserDN(String account) { + String userDN = null; + LdapContext ctx = getConnect(); + try { + SearchControls constraints = new SearchControls(); + constraints.setSearchScope(SearchControls.SUBTREE_SCOPE); + NamingEnumeration en = ctx.search("", "account=" + account, constraints); + if (en == null || !en.hasMoreElements()) { + return null; + } + // maybe more than one element + while (en.hasMoreElements()) { + Object obj = en.nextElement(); + if (obj instanceof SearchResult) { + SearchResult si = (SearchResult) obj; + userDN += si.getName(); + userDN += "," + ldapBasedn; + break; + } + } + } catch (Exception e) { + e.printStackTrace(); + } + + return userDN; + } + + /** + * LDAP账密验证 + * @param account + * @param password + * @return + */ + public boolean authenricate(String account, String password) { + LdapContext ctx = getConnect(); + + boolean valide = false; + String userDN = getUserDN(account); + try { + assert ctx != null; + ctx.addToEnvironment(Context.SECURITY_PRINCIPAL, userDN); + ctx.addToEnvironment(Context.SECURITY_CREDENTIALS, password); + ctx.reconnect(null); + valide = true; + } catch (AuthenticationException e) { + System.out.println(e.toString()); + } catch (NamingException e) { + e.printStackTrace(); + } + + return valide; + } + +} diff --git a/kafka-manager-extends/kafka-manager-account/src/main/java/com/xiaojukeji/kafka/manager/account/component/sso/BaseSessionSignOn.java b/kafka-manager-extends/kafka-manager-account/src/main/java/com/xiaojukeji/kafka/manager/account/component/sso/BaseSessionSignOn.java index 1e2dbb97..ec9324b9 100644 --- a/kafka-manager-extends/kafka-manager-account/src/main/java/com/xiaojukeji/kafka/manager/account/component/sso/BaseSessionSignOn.java +++ b/kafka-manager-extends/kafka-manager-account/src/main/java/com/xiaojukeji/kafka/manager/account/component/sso/BaseSessionSignOn.java @@ -2,12 +2,15 @@ package com.xiaojukeji.kafka.manager.account.component.sso; import com.xiaojukeji.kafka.manager.account.AccountService; import com.xiaojukeji.kafka.manager.account.component.AbstractSingleSignOn; +import com.xiaojukeji.kafka.manager.common.bizenum.AccountRoleEnum; import com.xiaojukeji.kafka.manager.common.constant.LoginConstant; import com.xiaojukeji.kafka.manager.common.entity.dto.normal.LoginDTO; import com.xiaojukeji.kafka.manager.common.entity.pojo.AccountDO; import com.xiaojukeji.kafka.manager.common.utils.EncryptUtil; import com.xiaojukeji.kafka.manager.common.utils.ValidateUtils; +import com.xiaojukeji.kafka.manager.common.utils.ldap.LDAPAuthentication; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import javax.servlet.http.HttpServletRequest; @@ -22,12 +25,49 @@ public class BaseSessionSignOn extends AbstractSingleSignOn { @Autowired private AccountService accountService; + @Autowired + private LDAPAuthentication ldapAuthentication; + + //是否开启ldap验证 + @Value(value = "${ldap.enabled}") + private boolean ldapEnabled; + + //ldap自动注册的默认角色。请注意:它通常来说都是低权限角色 + @Value(value = "${ldap.auth-user-registration-role}") + private String authUserRegistrationRole; + + //ldap自动注册是否开启 + @Value(value = "${ldap.auth-user-registration}") + private boolean authUserRegistration; + @Override public String loginAndGetLdap(HttpServletRequest request, HttpServletResponse response, LoginDTO dto) { if (ValidateUtils.isBlank(dto.getUsername()) || ValidateUtils.isNull(dto.getPassword())) { return null; } + AccountDO accountDO = accountService.getAccountDO(dto.getUsername()); + + //modifier limin + //判断是否激活了LDAP验证。若激活并且数据库无此用户则自动注册 + if(ldapEnabled){ + //验证账密 + if(!ldapAuthentication.authenricate(dto.getUsername(),dto.getPassword())){ + return null; + } + + if(authUserRegistration){ + //自动注册 + accountDO = new AccountDO(); + accountDO.setUsername(dto.getUsername()); + accountDO.setRole(AccountRoleEnum.getUserRoleEnum(authUserRegistrationRole).getRole()); + accountDO.setPassword(EncryptUtil.md5(dto.getPassword())); + accountService.createAccount(accountDO); + return dto.getUsername(); + } + + } + if (ValidateUtils.isNull(accountDO)) { return null; } diff --git a/kafka-manager-web/src/main/resources/application.yml b/kafka-manager-web/src/main/resources/application.yml index 73773981..7e982756 100644 --- a/kafka-manager-web/src/main/resources/application.yml +++ b/kafka-manager-web/src/main/resources/application.yml @@ -12,8 +12,8 @@ spring: datasource: kafka-manager: jdbc-url: jdbc:mysql://127.0.0.1:3306/logi_kafka_manager?characterEncoding=UTF-8&serverTimezone=GMT%2B8 - username: admin - password: admin + username: root + password: root driver-class-name: com.mysql.jdbc.Driver main: allow-bean-definition-overriding: true @@ -79,3 +79,15 @@ notify: topic-name: didi-kafka-notify order: detail-url: http://127.0.0.1 + +ldap: + enabled: false + url: ldap://127.0.0.1:389/ + basedn: dc=tsign,dc=cn + factory: com.sun.jndi.ldap.LdapCtxFactory + security: + authentication: simple + principal: cn=admin,dc=tsign,dc=cn + credentials: admin + auth-user-registration-role: normal + auth-user-registration: true From 2e2907ea099375dba0b20566d1f027c3721a6129 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E6=B0=91?= Date: Tue, 9 Feb 2021 14:33:53 +0800 Subject: [PATCH 29/33] =?UTF-8?q?=E4=BF=AE=E6=94=B9LDAP=E8=8E=B7=E5=8F=96U?= =?UTF-8?q?serDN=E7=9A=84=E6=97=B6=E5=80=99=E5=8F=AF=E8=83=BD=E5=87=BA?= =?UTF-8?q?=E9=94=99=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../manager/common/utils/ldap/LDAPAuthentication.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/utils/ldap/LDAPAuthentication.java b/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/utils/ldap/LDAPAuthentication.java index 3dbc6c99..c1694b4c 100644 --- a/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/utils/ldap/LDAPAuthentication.java +++ b/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/utils/ldap/LDAPAuthentication.java @@ -56,15 +56,14 @@ public class LDAPAuthentication { return null; } - private String getUserDN(String account) { - String userDN = null; - LdapContext ctx = getConnect(); + private String getUserDN(String account,LdapContext ctx) { + String userDN = ""; try { SearchControls constraints = new SearchControls(); constraints.setSearchScope(SearchControls.SUBTREE_SCOPE); NamingEnumeration en = ctx.search("", "account=" + account, constraints); if (en == null || !en.hasMoreElements()) { - return null; + return ""; } // maybe more than one element while (en.hasMoreElements()) { @@ -93,7 +92,7 @@ public class LDAPAuthentication { LdapContext ctx = getConnect(); boolean valide = false; - String userDN = getUserDN(account); + String userDN = getUserDN(account,ctx); try { assert ctx != null; ctx.addToEnvironment(Context.SECURITY_PRINCIPAL, userDN); From e7349161f39cada6238995a9adeda118e139074e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E6=B0=91?= Date: Tue, 9 Feb 2021 15:22:26 +0800 Subject: [PATCH 30/33] =?UTF-8?q?BUG=20FIX:=E4=BF=AE=E6=94=B9LDAP=E7=99=BB?= =?UTF-8?q?=E5=BD=95=E9=87=8D=E5=A4=8D=E6=B3=A8=E5=86=8C=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E7=9A=84BUG?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/utils/ldap/LDAPAuthentication.java | 13 +++++++++++-- .../account/component/sso/BaseSessionSignOn.java | 4 +++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/utils/ldap/LDAPAuthentication.java b/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/utils/ldap/LDAPAuthentication.java index c1694b4c..2419901a 100644 --- a/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/utils/ldap/LDAPAuthentication.java +++ b/kafka-manager-common/src/main/java/com/xiaojukeji/kafka/manager/common/utils/ldap/LDAPAuthentication.java @@ -92,9 +92,10 @@ public class LDAPAuthentication { LdapContext ctx = getConnect(); boolean valide = false; - String userDN = getUserDN(account,ctx); + try { - assert ctx != null; + String userDN = getUserDN(account,ctx); + ctx.addToEnvironment(Context.SECURITY_PRINCIPAL, userDN); ctx.addToEnvironment(Context.SECURITY_CREDENTIALS, password); ctx.reconnect(null); @@ -103,6 +104,14 @@ public class LDAPAuthentication { System.out.println(e.toString()); } catch (NamingException e) { e.printStackTrace(); + }finally { + if(ctx!=null) { + try { + ctx.close(); + } catch (NamingException e) { + e.printStackTrace(); + } + } } return valide; diff --git a/kafka-manager-extends/kafka-manager-account/src/main/java/com/xiaojukeji/kafka/manager/account/component/sso/BaseSessionSignOn.java b/kafka-manager-extends/kafka-manager-account/src/main/java/com/xiaojukeji/kafka/manager/account/component/sso/BaseSessionSignOn.java index ec9324b9..25ca16e1 100644 --- a/kafka-manager-extends/kafka-manager-account/src/main/java/com/xiaojukeji/kafka/manager/account/component/sso/BaseSessionSignOn.java +++ b/kafka-manager-extends/kafka-manager-account/src/main/java/com/xiaojukeji/kafka/manager/account/component/sso/BaseSessionSignOn.java @@ -56,7 +56,7 @@ public class BaseSessionSignOn extends AbstractSingleSignOn { return null; } - if(authUserRegistration){ + if(accountDO==null && authUserRegistration){ //自动注册 accountDO = new AccountDO(); accountDO.setUsername(dto.getUsername()); @@ -66,6 +66,8 @@ public class BaseSessionSignOn extends AbstractSingleSignOn { return dto.getUsername(); } + return dto.getUsername(); + } if (ValidateUtils.isNull(accountDO)) { From 6080f76a9c436d159c614310a983e7e96cbb8774 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AD=99=E8=B6=85?= Date: Tue, 9 Feb 2021 15:26:47 +0800 Subject: [PATCH 31/33] =?UTF-8?q?=E8=BF=AD=E4=BB=A3V2.5,=20=E4=BF=AE?= =?UTF-8?q?=E5=A4=8Dbroker=E7=9B=91=E6=8E=A7=E9=97=AE=E9=A2=98=EF=BC=8C?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0JMX=E8=AE=A4=E8=AF=81=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E7=AD=89...?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/component/antd/index.tsx | 6 +- .../src/component/editor/index.less | 2 +- .../src/component/editor/monacoEditor.tsx | 18 +- .../src/component/x-form/index.tsx | 2 + .../src/constants/strategy.ts | 2 +- .../admin/cluster-detail/cluster-consumer.tsx | 2 +- .../cluster-detail/cluster-controller.tsx | 191 +++++++++++++++++- .../container/admin/cluster-detail/index.less | 6 + .../container/admin/cluster-detail/index.tsx | 14 +- .../container/admin/cluster-list/index.tsx | 20 ++ .../src/container/admin/config.tsx | 86 +++++++- .../container/admin/configure-management.tsx | 38 +++- .../src/container/admin/data-curve/index.tsx | 4 +- .../container/admin/platform-management.tsx | 25 ++- .../src/container/admin/user-management.tsx | 2 +- .../alarm/add-alarm/alarm-select.tsx | 6 +- .../src/container/alarm/alarm-list.tsx | 10 +- .../src/container/cluster/my-cluster.tsx | 2 +- .../src/container/drawer/data-migration.tsx | 4 +- .../src/container/modal/admin/cluster.ts | 1 + .../src/container/modal/admin/task.ts | 12 +- .../src/container/modal/admin/user.ts | 103 +++++++++- .../src/container/modal/admin/version.ts | 77 ++++++- .../src/container/modal/app.tsx | 2 +- .../src/container/modal/expert.tsx | 6 +- .../src/container/modal/order.tsx | 4 +- .../src/container/modal/topic.tsx | 2 +- .../src/container/search-filter.tsx | 2 +- .../topic-detail/connect-information.tsx | 4 +- .../container/topic/topic-detail/group-id.tsx | 26 ++- .../topic/topic-detail/reset-offset.tsx | 34 ++-- kafka-manager-console/src/lib/api.ts | 47 ++++- .../src/lib/line-charts-config.ts | 10 +- .../src/store/admin-monitor.ts | 36 +++- kafka-manager-console/src/store/admin.ts | 146 ++++++++++--- kafka-manager-console/src/store/alarm.ts | 1 + kafka-manager-console/src/store/users.ts | 8 + kafka-manager-console/src/types/base-type.ts | 18 +- 38 files changed, 810 insertions(+), 169 deletions(-) diff --git a/kafka-manager-console/src/component/antd/index.tsx b/kafka-manager-console/src/component/antd/index.tsx index 2d771efe..d0958daf 100644 --- a/kafka-manager-console/src/component/antd/index.tsx +++ b/kafka-manager-console/src/component/antd/index.tsx @@ -94,6 +94,9 @@ import 'antd/es/divider/style'; import Upload from 'antd/es/upload'; import 'antd/es/upload/style'; +import Transfer from 'antd/es/transfer'; +import 'antd/es/transfer/style'; + import TimePicker from 'antd/es/time-picker'; import 'antd/es/time-picker/style'; @@ -142,5 +145,6 @@ export { TimePicker, RangePickerValue, Badge, - Popover + Popover, + Transfer }; diff --git a/kafka-manager-console/src/component/editor/index.less b/kafka-manager-console/src/component/editor/index.less index 4ff05854..36c52cde 100644 --- a/kafka-manager-console/src/component/editor/index.less +++ b/kafka-manager-console/src/component/editor/index.less @@ -25,7 +25,7 @@ .editor{ height: 100%; position: absolute; - left: -14%; + left: -12%; width: 120%; } } diff --git a/kafka-manager-console/src/component/editor/monacoEditor.tsx b/kafka-manager-console/src/component/editor/monacoEditor.tsx index 7a0dd44c..ac0a297a 100644 --- a/kafka-manager-console/src/component/editor/monacoEditor.tsx +++ b/kafka-manager-console/src/component/editor/monacoEditor.tsx @@ -21,24 +21,12 @@ class Monacoeditor extends React.Component { public state = { placeholder: '', }; - // public arr = '{"clusterId":95,"startId":37397856,"step":100,"topicName":"kmo_topic_metrics_tempory_zq"}'; - // public Ars(a: string) { - // const obj = JSON.parse(a); - // const newobj: any = {}; - // for (const item in obj) { - // if (typeof obj[item] === 'object') { - // this.Ars(obj[item]); - // } else { - // newobj[item] = obj[item]; - // } - // } - // return JSON.stringify(newobj); - // } + public async componentDidMount() { const { value, onChange } = this.props; const format: any = await format2json(value); this.editor = monaco.editor.create(this.ref, { - value: format.result, + value: format.result || value, language: 'json', lineNumbers: 'off', scrollBeyondLastLine: false, @@ -48,7 +36,7 @@ class Monacoeditor extends React.Component { minimap: { enabled: false, }, - // automaticLayout: true, // 自动布局 + automaticLayout: true, // 自动布局 glyphMargin: true, // 字形边缘 {},[] // useTabStops: false, // formatOnPaste: true, diff --git a/kafka-manager-console/src/component/x-form/index.tsx b/kafka-manager-console/src/component/x-form/index.tsx index 9530f2bb..dc435d0f 100755 --- a/kafka-manager-console/src/component/x-form/index.tsx +++ b/kafka-manager-console/src/component/x-form/index.tsx @@ -130,6 +130,8 @@ class XForm extends React.Component { this.renderFormItem(formItem), )} {formItem.renderExtraElement ? formItem.renderExtraElement() : null} + {/* 添加保存时间提示文案 */} + {formItem.attrs?.prompttype ? {formItem.attrs.prompttype} : null} ); })} diff --git a/kafka-manager-console/src/constants/strategy.ts b/kafka-manager-console/src/constants/strategy.ts index c0d19001..e92563e6 100644 --- a/kafka-manager-console/src/constants/strategy.ts +++ b/kafka-manager-console/src/constants/strategy.ts @@ -67,7 +67,7 @@ export const timeMonthStr = 'YYYY/MM'; // tslint:disable-next-line:max-line-length export const indexUrl ={ - indexUrl:'https://github.com/didi/kafka-manager', + indexUrl:'https://github.com/didi/Logi-KafkaManager/blob/master/docs/user_guide/kafka_metrics_desc.md', // 指标说明 cagUrl:'https://github.com/didi/Logi-KafkaManager/blob/master/docs/user_guide/add_cluster/add_cluster.md', // 集群接入指南 Cluster access Guide } diff --git a/kafka-manager-console/src/container/admin/cluster-detail/cluster-consumer.tsx b/kafka-manager-console/src/container/admin/cluster-detail/cluster-consumer.tsx index 5605a372..911f44d2 100644 --- a/kafka-manager-console/src/container/admin/cluster-detail/cluster-consumer.tsx +++ b/kafka-manager-console/src/container/admin/cluster-detail/cluster-consumer.tsx @@ -100,7 +100,7 @@ export class ClusterConsumer extends SearchAndFilterContainer {
  • {this.props.tab}
  • - {this.renderSearch()} + {this.renderSearch('', '请输入消费组名称')}
(origin: T[]) { + let data: T[] = origin; + let { searchCandidateKey } = this.state; + searchCandidateKey = (searchCandidateKey + '').trim().toLowerCase(); + + data = searchCandidateKey ? origin.filter((item: IController) => + (item.host !== undefined && item.host !== null) && item.host.toLowerCase().includes(searchCandidateKey as string), + ) : origin; + return data; + } + + // 候选controller + public renderCandidateController() { + const columns = [ + { + title: 'BrokerId', + dataIndex: 'brokerId', + key: 'brokerId', + width: '20%', + sorter: (a: IController, b: IController) => b.brokerId - a.brokerId, + render: (r: string, t: IController) => { + return ( + {r} + + ); + }, + }, + { + title: 'BrokerHost', + key: 'host', + dataIndex: 'host', + width: '20%', + // render: (r: string, t: IController) => { + // return ( + // {r} + // + // ); + // }, + }, + { + title: 'Broker状态', + key: 'status', + dataIndex: 'status', + width: '20%', + render: (r: number, t: IController) => { + return ( + {r === 1 ? '不在线' : '在线'} + ); + }, + }, + { + title: '创建时间', + dataIndex: 'startTime', + key: 'startTime', + width: '25%', + sorter: (a: IController, b: IController) => b.timestamp - a.timestamp, + render: (t: number) => moment(t).format(timeFormat), + }, + { + title: '操作', + dataIndex: 'operation', + key: 'operation', + width: '15%', + render: (r: string, t: IController) => { + return ( + this.deleteCandidateCancel(t)} + cancelText="取消" + okText="确认" + > + 删除 + + ); + }, + }, + ]; + + return ( +
+ ); + } + public renderController() { const columns = [ @@ -58,12 +151,6 @@ export class ClusterController extends SearchAndFilterContainer { key: 'host', dataIndex: 'host', width: '30%', - // render: (r: string, t: IController) => { - // return ( - // {r} - // - // ); - // }, }, { title: '变更时间', @@ -87,16 +174,104 @@ export class ClusterController extends SearchAndFilterContainer { public componentDidMount() { admin.getControllerHistory(this.clusterId); + admin.getCandidateController(this.clusterId); + admin.getBrokersMetadata(this.clusterId); + } + + public addController = () => { + this.setState({ isCandidateModel: true, targetKeys: [] }) + } + + public addCandidateChange = (targetKeys: any) => { + this.setState({ targetKeys }) + } + + + + public handleCandidateCancel = () => { + this.setState({ isCandidateModel: false }); + } + + public handleCandidateOk = () => { + let brokerIdList = this.state.targetKeys.map((item: any) => { + return admin.brokersMetadata[item].brokerId + }) + admin.addCandidateController(this.clusterId, brokerIdList).then(data => { + notification.success({ message: '新增成功' }); + admin.getCandidateController(this.clusterId); + }).catch(err => { + notification.error({ message: '新增失败' }); + }) + this.setState({ isCandidateModel: false, targetKeys: [] }); + } + + public deleteCandidateCancel = (target: any) => { + admin.deleteCandidateCancel(this.clusterId, [target.brokerId]).then(() => { + notification.success({ message: '删除成功' }); + }); + this.setState({ isCandidateModel: false }); + } + + public renderAddCandidateController() { + let filterControllerCandidate = admin.brokersMetadata.filter((item: any) => { + return !admin.filtercontrollerCandidate.includes(item.brokerId) + }) + + return ( + this.handleCandidateOk()} + onCancel={() => this.handleCandidateCancel()} + footer={<> + + + + } + > + item.host} + onChange={(targetKeys) => this.addCandidateChange(targetKeys)} + titles={['未选', '已选']} + locale={{ + itemUnit: '项', + itemsUnit: '项', + }} + listStyle={{ + width: "45%", + }} + /> + + ); } public render() { return (
    +
  • + 候选Controller + Controller将会优先从以下Broker中选举 +
  • +
    +
    + +
    + {this.renderSearch('', '请查找Host', 'searchCandidateKey')} +
    +
+ {this.renderCandidateController()} +
  • {this.props.tab}
  • {this.renderSearch('', '请输入Host')}
{this.renderController()} + {this.renderAddCandidateController()}
); } diff --git a/kafka-manager-console/src/container/admin/cluster-detail/index.less b/kafka-manager-console/src/container/admin/cluster-detail/index.less index 65c45b9c..0dd4d106 100644 --- a/kafka-manager-console/src/container/admin/cluster-detail/index.less +++ b/kafka-manager-console/src/container/admin/cluster-detail/index.less @@ -94,4 +94,10 @@ .region-prompt{ font-weight: bold; text-align: center; +} + +.asd{ + display: flex; + justify-content: space-around; + align-items: center; } \ No newline at end of file diff --git a/kafka-manager-console/src/container/admin/cluster-detail/index.tsx b/kafka-manager-console/src/container/admin/cluster-detail/index.tsx index 5882dd57..027dde27 100644 --- a/kafka-manager-console/src/container/admin/cluster-detail/index.tsx +++ b/kafka-manager-console/src/container/admin/cluster-detail/index.tsx @@ -32,9 +32,9 @@ export class ClusterDetail extends React.Component { } public render() { - let content = {} as IMetaData; - content = admin.basicInfo ? admin.basicInfo : content; - return ( + let content = {} as IMetaData; + content = admin.basicInfo ? admin.basicInfo : content; + return ( <> - + @@ -60,11 +60,11 @@ export class ClusterDetail extends React.Component { - + - - + + diff --git a/kafka-manager-console/src/container/admin/cluster-list/index.tsx b/kafka-manager-console/src/container/admin/cluster-list/index.tsx index 63ccd93e..dfac45d7 100644 --- a/kafka-manager-console/src/container/admin/cluster-list/index.tsx +++ b/kafka-manager-console/src/container/admin/cluster-list/index.tsx @@ -12,6 +12,7 @@ import { urlPrefix } from 'constants/left-menu'; import { indexUrl } from 'constants/strategy' import { region } from 'store'; import './index.less'; +import Monacoeditor from 'component/editor/monacoEditor'; import { getAdminClusterColumns } from '../config'; const { confirm } = Modal; @@ -132,6 +133,25 @@ export class ClusterList extends SearchAndFilterContainer { "security.protocol": "SASL_PLAINTEXT", "sasl.mechanism": "PLAIN", "sasl.jaas.config": "org.apache.kafka.common.security.plain.PlainLoginModule required username=\\"xxxxxx\\" password=\\"xxxxxx\\";" +}`, + rows: 8, + }, + }, + { + key: 'jmxProperties', + label: 'JMX认证', + type: 'text_area', + rules: [{ + required: false, + message: '请输入JMX认证', + }], + attrs: { + placeholder: `请输入JMX认证,例如: +{ +"maxConn": 10, #KM对单台Broker对最大连接数 +"username": "xxxxx", #用户名 +"password": "xxxxx", #密码 +"openSSL": true, #开启SSL,true表示开启SSL,false表示关闭 }`, rows: 8, }, diff --git a/kafka-manager-console/src/container/admin/config.tsx b/kafka-manager-console/src/container/admin/config.tsx index bce02cce..09a70f83 100644 --- a/kafka-manager-console/src/container/admin/config.tsx +++ b/kafka-manager-console/src/container/admin/config.tsx @@ -1,8 +1,8 @@ import * as React from 'react'; -import { IUser, IUploadFile, IConfigure, IMetaData, IBrokersPartitions } from 'types/base-type'; +import { IUser, IUploadFile, IConfigure, IConfigGateway, IMetaData } from 'types/base-type'; import { users } from 'store/users'; import { version } from 'store/version'; -import { showApplyModal, showModifyModal, showConfigureModal } from 'container/modal/admin'; +import { showApplyModal, showApplyModalModifyPassword, showModifyModal, showConfigureModal, showConfigGatewayModal } from 'container/modal/admin'; import { Popconfirm, Tooltip } from 'component/antd'; import { admin } from 'store/admin'; import { cellStyle } from 'constants/table'; @@ -27,6 +27,7 @@ export const getUserColumns = () => { return ( showApplyModal(record)}>编辑 + showApplyModalModifyPassword(record)}>修改密码 users.deleteUser(record.username)} @@ -184,6 +185,87 @@ export const getConfigureColumns = () => { return columns; }; +// 网关配置 +export const getConfigColumns = () => { + const columns = [ + { + title: '配置类型', + dataIndex: 'type', + key: 'type', + width: '25%', + ellipsis: true, + sorter: (a: IConfigGateway, b: IConfigGateway) => a.type.charCodeAt(0) - b.type.charCodeAt(0), + }, + { + title: '配置键', + dataIndex: 'name', + key: 'name', + width: '15%', + ellipsis: true, + sorter: (a: IConfigGateway, b: IConfigGateway) => a.name.charCodeAt(0) - b.name.charCodeAt(0), + }, + { + title: '配置值', + dataIndex: 'value', + key: 'value', + width: '20%', + ellipsis: true, + sorter: (a: IConfigGateway, b: IConfigGateway) => a.value.charCodeAt(0) - b.value.charCodeAt(0), + render: (t: string) => { + return t.substr(0, 1) === '{' && t.substr(0, -1) === '}' ? JSON.stringify(JSON.parse(t), null, 4) : t; + }, + }, + { + title: '修改时间', + dataIndex: 'modifyTime', + key: 'modifyTime', + width: '15%', + sorter: (a: IConfigGateway, b: IConfigGateway) => b.modifyTime - a.modifyTime, + render: (t: number) => moment(t).format(timeFormat), + }, + { + title: '版本号', + dataIndex: 'version', + key: 'version', + width: '10%', + ellipsis: true, + sorter: (a: IConfigGateway, b: IConfigGateway) => b.version.charCodeAt(0) - a.version.charCodeAt(0), + }, + { + title: '描述信息', + dataIndex: 'description', + key: 'description', + width: '20%', + ellipsis: true, + onCell: () => ({ + style: { + maxWidth: 180, + ...cellStyle, + }, + }), + }, + { + title: '操作', + width: '10%', + render: (text: string, record: IConfigGateway) => { + return ( + + showConfigGatewayModal(record)}>编辑 + admin.deleteConfigGateway({ id: record.id })} + cancelText="取消" + okText="确认" + > + 删除 + + ); + }, + }, + ]; + return columns; +}; + const renderClusterHref = (value: number | string, item: IMetaData, key: number) => { return ( // 0 暂停监控--不可点击 1 监控中---可正常点击 <> diff --git a/kafka-manager-console/src/container/admin/configure-management.tsx b/kafka-manager-console/src/container/admin/configure-management.tsx index 680d1da7..5c3494b9 100644 --- a/kafka-manager-console/src/container/admin/configure-management.tsx +++ b/kafka-manager-console/src/container/admin/configure-management.tsx @@ -3,11 +3,11 @@ import { SearchAndFilterContainer } from 'container/search-filter'; import { Table, Button, Spin } from 'component/antd'; import { admin } from 'store/admin'; import { observer } from 'mobx-react'; -import { IConfigure } from 'types/base-type'; +import { IConfigure, IConfigGateway } from 'types/base-type'; import { users } from 'store/users'; import { pagination } from 'constants/table'; -import { getConfigureColumns } from './config'; -import { showConfigureModal } from 'container/modal/admin'; +import { getConfigureColumns, getConfigColumns } from './config'; +import { showConfigureModal, showConfigGatewayModal } from 'container/modal/admin'; @observer export class ConfigureManagement extends SearchAndFilterContainer { @@ -17,7 +17,12 @@ export class ConfigureManagement extends SearchAndFilterContainer { }; public componentDidMount() { - admin.getConfigure(); + if (this.props.isShow) { + admin.getGatewayList(); + admin.getGatewayType(); + } else { + admin.getConfigure(); + } } public getData(origin: T[]) { @@ -34,15 +39,34 @@ export class ConfigureManagement extends SearchAndFilterContainer { return data; } + public getGatewayData(origin: T[]) { + let data: T[] = origin; + let { searchKey } = this.state; + searchKey = (searchKey + '').trim().toLowerCase(); + + data = searchKey ? origin.filter((item: IConfigGateway) => + ((item.name !== undefined && item.name !== null) && item.name.toLowerCase().includes(searchKey as string)) + || ((item.value !== undefined && item.value !== null) && item.value.toLowerCase().includes(searchKey as string)) + || ((item.description !== undefined && item.description !== null) && + item.description.toLowerCase().includes(searchKey as string)), + ) : origin; + return data; + } + public renderTable() { return ( -
:
+ />} ); @@ -53,7 +77,7 @@ export class ConfigureManagement extends SearchAndFilterContainer {
    {this.renderSearch('', '请输入配置键、值或描述')}
  • - +
); diff --git a/kafka-manager-console/src/container/admin/data-curve/index.tsx b/kafka-manager-console/src/container/admin/data-curve/index.tsx index bd113aeb..b822957c 100644 --- a/kafka-manager-console/src/container/admin/data-curve/index.tsx +++ b/kafka-manager-console/src/container/admin/data-curve/index.tsx @@ -6,6 +6,7 @@ import { curveKeys, CURVE_KEY_MAP, PERIOD_RADIO_MAP, PERIOD_RADIO } from './conf import moment = require('moment'); import { observer } from 'mobx-react'; import { timeStampStr } from 'constants/strategy'; +import { adminMonitor } from 'store/admin-monitor'; @observer export class DataCurveFilter extends React.Component { @@ -21,6 +22,7 @@ export class DataCurveFilter extends React.Component { } public refreshAll = () => { + adminMonitor.setRequestId(null); Object.keys(curveKeys).forEach((c: curveKeys) => { const { typeInfo, curveInfo: option } = CURVE_KEY_MAP.get(c); const { parser } = typeInfo; @@ -32,7 +34,7 @@ export class DataCurveFilter extends React.Component { return ( <> - {PERIOD_RADIO.map(p => {p.label})} + {PERIOD_RADIO.map(p => {p.label})} - - - - - - - - - - - + + + + + + + + + + + + + + ); } diff --git a/kafka-manager-console/src/container/admin/user-management.tsx b/kafka-manager-console/src/container/admin/user-management.tsx index 757ceabb..1dc38e06 100644 --- a/kafka-manager-console/src/container/admin/user-management.tsx +++ b/kafka-manager-console/src/container/admin/user-management.tsx @@ -29,7 +29,7 @@ export class UserManagement extends SearchAndFilterContainer { searchKey = (searchKey + '').trim().toLowerCase(); data = searchKey ? origin.filter((item: IUser) => - (item.username !== undefined && item.username !== null) && item.username.toLowerCase().includes(searchKey as string)) : origin ; + (item.username !== undefined && item.username !== null) && item.username.toLowerCase().includes(searchKey as string)) : origin; return data; } diff --git a/kafka-manager-console/src/container/alarm/add-alarm/alarm-select.tsx b/kafka-manager-console/src/container/alarm/add-alarm/alarm-select.tsx index 6d19ec26..5cd1f4f0 100644 --- a/kafka-manager-console/src/container/alarm/add-alarm/alarm-select.tsx +++ b/kafka-manager-console/src/container/alarm/add-alarm/alarm-select.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { alarm } from 'store/alarm'; import { IMonitorGroups } from 'types/base-type'; -import { getValueFromLocalStorage, setValueToLocalStorage } from 'lib/local-storage'; +import { getValueFromLocalStorage, setValueToLocalStorage, deleteValueFromLocalStorage } from 'lib/local-storage'; import { VirtualScrollSelect } from '../../../component/virtual-scroll-select'; interface IAlarmSelectProps { @@ -36,6 +36,10 @@ export class AlarmSelect extends React.Component { onChange && onChange(params); } + public componentWillUnmount() { + deleteValueFromLocalStorage('monitorGroups'); + } + public render() { const { value, isDisabled } = this.props; return ( diff --git a/kafka-manager-console/src/container/alarm/alarm-list.tsx b/kafka-manager-console/src/container/alarm/alarm-list.tsx index 6dd6680b..54d266f1 100644 --- a/kafka-manager-console/src/container/alarm/alarm-list.tsx +++ b/kafka-manager-console/src/container/alarm/alarm-list.tsx @@ -9,6 +9,7 @@ import { pagination } from 'constants/table'; import { urlPrefix } from 'constants/left-menu'; import { alarm } from 'store/alarm'; import 'styles/table-filter.less'; +import { Link } from 'react-router-dom'; @observer export class AlarmList extends SearchAndFilterContainer { @@ -24,7 +25,7 @@ export class AlarmList extends SearchAndFilterContainer { if (app.active !== '-1' || searchKey !== '') { data = origin.filter(d => ((d.name !== undefined && d.name !== null) && d.name.toLowerCase().includes(searchKey as string) - || ((d.operator !== undefined && d.operator !== null) && d.operator.toLowerCase().includes(searchKey as string))) + || ((d.operator !== undefined && d.operator !== null) && d.operator.toLowerCase().includes(searchKey as string))) && (app.active === '-1' || d.appId === (app.active + '')), ); } else { @@ -55,9 +56,7 @@ export class AlarmList extends SearchAndFilterContainer { {this.renderSearch('名称:', '请输入告警规则或者操作人')}
  • @@ -68,6 +67,9 @@ export class AlarmList extends SearchAndFilterContainer { if (!alarm.monitorStrategies.length) { alarm.getMonitorStrategies(); } + if (!app.data.length) { + app.getAppList(); + } } public render() { diff --git a/kafka-manager-console/src/container/cluster/my-cluster.tsx b/kafka-manager-console/src/container/cluster/my-cluster.tsx index 3c54763a..e017b0dd 100644 --- a/kafka-manager-console/src/container/cluster/my-cluster.tsx +++ b/kafka-manager-console/src/container/cluster/my-cluster.tsx @@ -91,7 +91,7 @@ export class MyCluster extends SearchAndFilterContainer { ], formData: {}, visible: true, - title: '申请集群', + title:
    申请集群资源申请文档
    , okText: '确认', onSubmit: (value: any) => { value.idc = region.currentRegion; diff --git a/kafka-manager-console/src/container/drawer/data-migration.tsx b/kafka-manager-console/src/container/drawer/data-migration.tsx index 28353004..4da64f5c 100644 --- a/kafka-manager-console/src/container/drawer/data-migration.tsx +++ b/kafka-manager-console/src/container/drawer/data-migration.tsx @@ -117,12 +117,12 @@ class DataMigrationFormTable extends React.Component { key: 'maxThrottle', editable: true, }, { - title: '迁移保存时间(h)', + title: '迁移后Topic保存时间(h)', dataIndex: 'reassignRetentionTime', key: 'reassignRetentionTime', editable: true, }, { - title: '原本保存时间(h)', + title: '原Topic保存时间(h)', dataIndex: 'retentionTime', key: 'retentionTime', // originalRetentionTime width: '132px', diff --git a/kafka-manager-console/src/container/modal/admin/cluster.ts b/kafka-manager-console/src/container/modal/admin/cluster.ts index cea987f6..20ed9098 100644 --- a/kafka-manager-console/src/container/modal/admin/cluster.ts +++ b/kafka-manager-console/src/container/modal/admin/cluster.ts @@ -61,6 +61,7 @@ export const showEditClusterTopic = (item: IClusterTopics) => { attrs: { placeholder: '请输入保存时间', suffix: '小时', + prompttype:'修改保存时间,预计一分钟左右生效!' }, }, { diff --git a/kafka-manager-console/src/container/modal/admin/task.ts b/kafka-manager-console/src/container/modal/admin/task.ts index be240f7a..d9a609ac 100644 --- a/kafka-manager-console/src/container/modal/admin/task.ts +++ b/kafka-manager-console/src/container/modal/admin/task.ts @@ -158,26 +158,26 @@ export const createMigrationTasks = () => { }, { key: 'originalRetentionTime', - label: '原本保存时间', + label: '原Topic保存时间', rules: [{ required: true, - message: '请输入原本保存时间', + message: '请输入原Topic保存时间', }], attrs: { disabled: true, - placeholder: '请输入原本保存时间', + placeholder: '请输入原Topic保存时间', suffix: '小时', }, }, { key: 'reassignRetentionTime', - label: '迁移保存时间', + label: '迁移后Topic保存时间', rules: [{ required: true, - message: '请输入迁移保存时间', + message: '请输入迁移后Topic保存时间', }], attrs: { - placeholder: '请输入迁移保存时间', + placeholder: '请输入迁移后Topic保存时间', suffix: '小时', }, }, diff --git a/kafka-manager-console/src/container/modal/admin/user.ts b/kafka-manager-console/src/container/modal/admin/user.ts index 9f35e4cf..51ca360d 100644 --- a/kafka-manager-console/src/container/modal/admin/user.ts +++ b/kafka-manager-console/src/container/modal/admin/user.ts @@ -24,26 +24,111 @@ export const showApplyModal = (record?: IUser) => { value: +item, })), rules: [{ required: true, message: '请选择角色' }], - }, { - key: 'password', - label: '密码', - type: FormItemType.inputPassword, - rules: [{ required: !record, message: '请输入密码' }], - }, + }, + // { + // key: 'password', + // label: '密码', + // type: FormItemType.inputPassword, + // rules: [{ required: !record, message: '请输入密码' }], + // }, ], formData: record || {}, visible: true, title: record ? '修改用户' : '新增用户', onSubmit: (value: IUser) => { if (record) { - return users.modfiyUser(value).then(() => { - message.success('操作成功'); - }); + return users.modfiyUser(value) } return users.addUser(value).then(() => { message.success('操作成功'); }); }, }; + if(!record){ + let formMap: any = xFormModal.formMap + formMap.splice(2, 0,{ + key: 'password', + label: '密码', + type: FormItemType.inputPassword, + rules: [{ required: !record, message: '请输入密码' }], + },) + } wrapper.open(xFormModal); }; + +// const handleCfPassword = (rule:any, value:any, callback:any)=>{ +// if() +// } +export const showApplyModalModifyPassword = (record: IUser) => { + const xFormModal:any = { + formMap: [ + // { + // key: 'oldPassword', + // label: '旧密码', + // type: FormItemType.inputPassword, + // rules: [{ + // required: true, + // message: '请输入旧密码', + // }] + // }, + { + key: 'newPassword', + label: '新密码', + type: FormItemType.inputPassword, + rules: [ + { + required: true, + message: '请输入新密码', + } + ], + attrs:{ + onChange:(e:any)=>{ + users.setNewPassWord(e.target.value) + } + } + }, + { + key: 'confirmPassword', + label: '确认密码', + type: FormItemType.inputPassword, + rules: [ + { + required: true, + message: '请确认密码', + validator:(rule:any, value:any, callback:any) => { + // 验证新密码的一致性 + if(users.newPassWord){ + if(value!==users.newPassWord){ + rule.message = "两次密码输入不一致"; + callback('两次密码输入不一致') + }else{ + callback() + } + }else if(!value){ + rule.message = "请确认密码"; + callback('请确认密码'); + }else{ + callback() + } + }, + } + ], + }, + ], + formData: record || {}, + visible: true, + title: '修改密码', + onSubmit: (value: IUser) => { + let params:any = { + username:record?.username, + password:value.confirmPassword, + role:record?.role, + } + return users.modfiyUser(params).then(() => { + message.success('操作成功'); + }); + }, + } + wrapper.open(xFormModal); +}; + diff --git a/kafka-manager-console/src/container/modal/admin/version.ts b/kafka-manager-console/src/container/modal/admin/version.ts index ea642a8f..c863eba1 100644 --- a/kafka-manager-console/src/container/modal/admin/version.ts +++ b/kafka-manager-console/src/container/modal/admin/version.ts @@ -1,6 +1,6 @@ import * as React from 'react'; -import { notification } from 'component/antd'; -import { IUploadFile, IConfigure } from 'types/base-type'; +import { notification, Select } from 'component/antd'; +import { IUploadFile, IConfigure, IConfigGateway } from 'types/base-type'; import { version } from 'store/version'; import { admin } from 'store/admin'; import { wrapper } from 'store'; @@ -97,8 +97,8 @@ const updateFormModal = (type: number) => { formMap[2].attrs = { accept: version.fileSuffix, }, - // tslint:disable-next-line:no-unused-expression - wrapper.ref && wrapper.ref.updateFormMap$(formMap, wrapper.xFormWrapper.formData, true); + // tslint:disable-next-line:no-unused-expression + wrapper.ref && wrapper.ref.updateFormMap$(formMap, wrapper.xFormWrapper.formData, true); } }; @@ -157,8 +157,8 @@ export const showModifyModal = (record: IUploadFile) => { export const showConfigureModal = async (record?: IConfigure) => { if (record) { - const result:any = await format2json(record.configValue); - record.configValue = result.result; + const result: any = await format2json(record.configValue); + record.configValue = result.result || record.configValue; } const xFormModal = { formMap: [ @@ -193,10 +193,69 @@ export const showConfigureModal = async (record?: IConfigure) => { return admin.editConfigure(value).then(data => { notification.success({ message: '编辑配置成功' }); }); + } else { + return admin.addNewConfigure(value).then(data => { + notification.success({ message: '新建配置成功' }); + }); + } + }, + }; + wrapper.open(xFormModal); +}; + +export const showConfigGatewayModal = async (record?: IConfigGateway) => { + const xFormModal = { + formMap: [ + { + key: 'type', + label: '配置类型', + rules: [{ required: true, message: '请选择配置类型' }], + type: "select", + options: admin.gatewayType.map((item: any, index: number) => ({ + key: index, + label: item.configName, + value: item.configType, + })), + attrs: { + disabled: record ? true : false, + } + }, { + key: 'name', + label: '配置键', + rules: [{ required: true, message: '请输入配置键' }], + attrs: { + disabled: record ? true : false, + }, + }, { + key: 'value', + label: '配置值', + type: 'text_area', + rules: [{ + required: true, + message: '请输入配置值', + }], + }, { + key: 'description', + label: '描述', + type: 'text_area', + rules: [{ required: true, message: '请输入备注' }], + }, + ], + formData: record || {}, + visible: true, + isWaitting: true, + title: `${record ? '编辑配置' : '新建配置'}`, + onSubmit: async (parmas: IConfigGateway) => { + if (record) { + parmas.id = record.id; + return admin.editConfigGateway(parmas).then(data => { + notification.success({ message: '编辑配置成功' }); + }); + } else { + return admin.addNewConfigGateway(parmas).then(data => { + notification.success({ message: '新建配置成功' }); + }); } - return admin.addNewConfigure(value).then(data => { - notification.success({ message: '新建配置成功' }); - }); }, }; wrapper.open(xFormModal); diff --git a/kafka-manager-console/src/container/modal/app.tsx b/kafka-manager-console/src/container/modal/app.tsx index bda37418..bb0320ec 100644 --- a/kafka-manager-console/src/container/modal/app.tsx +++ b/kafka-manager-console/src/container/modal/app.tsx @@ -85,7 +85,7 @@ export const showEditModal = (record?: IAppItem, from?: string, isDisabled?: boo ], formData: record, visible: true, - title: isDisabled ? '详情' : record ? '编辑' :
    应用申请应用申请文档
    , + title: isDisabled ? '详情' : record ? '编辑' :
    应用申请资源申请文档
    , // customRenderElement: isDisabled ? '' : record ? '' : 集群资源充足时,预计1分钟自动审批通过, isWaitting: true, onSubmit: (value: IAppItem) => { diff --git a/kafka-manager-console/src/container/modal/expert.tsx b/kafka-manager-console/src/container/modal/expert.tsx index 96a1f312..2ff5e5f2 100644 --- a/kafka-manager-console/src/container/modal/expert.tsx +++ b/kafka-manager-console/src/container/modal/expert.tsx @@ -20,14 +20,14 @@ export interface IRenderData { } export const migrationModal = (renderData: IRenderData[]) => { - const xFormWrapper = { + const xFormWrapper = { type: 'drawer', visible: true, width: 1000, title: '新建迁移任务', - customRenderElement: , + customRenderElement: , nofooter: true, noform: true, }; - wrapper.open(xFormWrapper as IXFormWrapper); + wrapper.open(xFormWrapper as IXFormWrapper); }; diff --git a/kafka-manager-console/src/container/modal/order.tsx b/kafka-manager-console/src/container/modal/order.tsx index 2930e3df..c982db4c 100644 --- a/kafka-manager-console/src/container/modal/order.tsx +++ b/kafka-manager-console/src/container/modal/order.tsx @@ -75,8 +75,8 @@ export const showApprovalModal = (info: IOrderInfo, status: number, from?: strin // }], rules: [{ required: true, - message: '请输入大于12小于999的整数', - pattern: /^([1-9]{1}[0-9]{2})$|^([2-9]{1}[0-9]{1})$|^(1[2-9]{1})$/, + message: '请输入大于0小于10000的整数', + pattern: /^\+?[1-9]\d{0,3}(\.\d*)?$/, }], }, { key: 'species', diff --git a/kafka-manager-console/src/container/modal/topic.tsx b/kafka-manager-console/src/container/modal/topic.tsx index 7053a497..d4df0318 100644 --- a/kafka-manager-console/src/container/modal/topic.tsx +++ b/kafka-manager-console/src/container/modal/topic.tsx @@ -88,7 +88,7 @@ export const applyTopic = () => { ], formData: {}, visible: true, - title: '申请Topic', + title: , okText: '确认', // customRenderElement: 集群资源充足时,预计1分钟自动审批通过, isWaitting: true, diff --git a/kafka-manager-console/src/container/search-filter.tsx b/kafka-manager-console/src/container/search-filter.tsx index 12603d40..ac5d6bc1 100644 --- a/kafka-manager-console/src/container/search-filter.tsx +++ b/kafka-manager-console/src/container/search-filter.tsx @@ -126,7 +126,7 @@ export class SearchAndFilterContainer extends React.Component diff --git a/kafka-manager-console/src/container/topic/topic-detail/connect-information.tsx b/kafka-manager-console/src/container/topic/topic-detail/connect-information.tsx index f323310c..1e5ab182 100644 --- a/kafka-manager-console/src/container/topic/topic-detail/connect-information.tsx +++ b/kafka-manager-console/src/container/topic/topic-detail/connect-information.tsx @@ -101,7 +101,9 @@ export class ConnectInformation extends SearchAndFilterContainer { <>
      -
    • 连接信息
    • +
    • + 连接信息 展示近20分钟的连接信息 +
    • {this.renderSearch('', '请输入连接信息', 'searchKey')}
    {this.renderConnectionInfo(this.getData(topic.connectionInfo))} diff --git a/kafka-manager-console/src/container/topic/topic-detail/group-id.tsx b/kafka-manager-console/src/container/topic/topic-detail/group-id.tsx index 20b7642f..b173ac41 100644 --- a/kafka-manager-console/src/container/topic/topic-detail/group-id.tsx +++ b/kafka-manager-console/src/container/topic/topic-detail/group-id.tsx @@ -138,7 +138,7 @@ export class GroupID extends SearchAndFilterContainer { public renderConsumerDetails() { const consumerGroup = this.consumerGroup; - const columns = [{ + const columns: any = [{ title: 'Partition ID', dataIndex: 'partitionId', key: 'partitionId', @@ -179,7 +179,8 @@ export class GroupID extends SearchAndFilterContainer { <>
    {consumerGroup} -
    +
    + {this.renderSearch('', '请输入Consumer ID')} @@ -187,7 +188,7 @@ export class GroupID extends SearchAndFilterContainer {
    @@ -214,7 +215,12 @@ export class GroupID extends SearchAndFilterContainer { dataIndex: 'location', key: 'location', width: '34%', - }, + }, { + title: '状态', + dataIndex: 'state', + key: 'state', + width: '34%', + } ]; return ( <> @@ -236,7 +242,17 @@ export class GroupID extends SearchAndFilterContainer { data = searchKey ? origin.filter((item: IConsumerGroups) => (item.consumerGroup !== undefined && item.consumerGroup !== null) && item.consumerGroup.toLowerCase().includes(searchKey as string), - ) : origin ; + ) : origin; + return data; + } + + public getDetailData(origin: T[]) { + let data: T[] = origin; + let { searchKey } = this.state; + searchKey = (searchKey + '').trim().toLowerCase(); + data = searchKey ? origin.filter((item: IConsumeDetails) => + (item.clientId !== undefined && item.clientId !== null) && item.clientId.toLowerCase().includes(searchKey as string), + ) : origin; return data; } diff --git a/kafka-manager-console/src/container/topic/topic-detail/reset-offset.tsx b/kafka-manager-console/src/container/topic/topic-detail/reset-offset.tsx index be0767e8..531f69c6 100644 --- a/kafka-manager-console/src/container/topic/topic-detail/reset-offset.tsx +++ b/kafka-manager-console/src/container/topic/topic-detail/reset-offset.tsx @@ -71,32 +71,32 @@ class ResetOffset extends React.Component { const { getFieldDecorator } = this.props.form; const { typeValue, offsetValue } = this.state; return ( - <> - - - + <> + + + {/* */}
    - + 重置到指定时间
    - - 最新offset - 自定义 - + + 最新offset + 自定义 + {typeValue === 'time' && offsetValue === 'custom' && getFieldDecorator('timestamp', { rules: [{ required: false, message: '' }], initialValue: moment(), - })( + })( { 重置指定分区及偏移 - + diff --git a/kafka-manager-console/src/lib/api.ts b/kafka-manager-console/src/lib/api.ts index f53f6852..8716d4ea 100644 --- a/kafka-manager-console/src/lib/api.ts +++ b/kafka-manager-console/src/lib/api.ts @@ -1,5 +1,5 @@ import fetch, { formFetch } from './fetch'; -import { IUploadFile, IUser, IQuotaModelItem, ILimitsItem, ITopic, IOrderParams, ISample, IMigration, IExecute, IEepand, IUtils, ITopicMetriceParams, IRegister, IEditTopic, IExpand, IDeleteTopic, INewRegions, INewLogical, IRebalance, INewBulidEnums, ITrigger, IApprovalOrder, IMonitorSilences, IConfigure, IBatchApproval } from 'types/base-type'; +import { IUploadFile, IUser, IQuotaModelItem, ILimitsItem, ITopic, IOrderParams, ISample, IMigration, IExecute, IEepand, IUtils, ITopicMetriceParams, IRegister, IEditTopic, IExpand, IDeleteTopic, INewRegions, INewLogical, IRebalance, INewBulidEnums, ITrigger, IApprovalOrder, IMonitorSilences, IConfigure, IConfigGateway, IBatchApproval } from 'types/base-type'; import { IRequestParams } from 'types/alarm'; import { apiCache } from 'lib/api-cache'; @@ -442,6 +442,34 @@ export const deleteConfigure = (configKey: string) => { }); }; +export const getGatewayList = () => { + return fetch(`/rd/gateway-configs`); +}; + +export const getGatewayType = () => { + return fetch(`/op/gateway-configs/type-enums`); +}; + +export const addNewConfigGateway = (params: IConfigGateway) => { + return fetch(`/op/gateway-configs`, { + method: 'POST', + body: JSON.stringify(params), + }); +}; + +export const editConfigGateway = (params: IConfigGateway) => { + return fetch(`/op/gateway-configs`, { + method: 'PUT', + body: JSON.stringify(params), + }); +}; +export const deleteConfigGateway = (params: IConfigure) => { + return fetch(`/op/gateway-configs`, { + method: 'DELETE', + body: JSON.stringify(params), + }); +}; + export const getDataCenter = () => { return fetch(`/normal/configs/idc`); }; @@ -530,6 +558,23 @@ export const getControllerHistory = (clusterId: number) => { return fetch(`/rd/clusters/${clusterId}/controller-history`); }; +export const getCandidateController = (clusterId: number) => { + return fetch(`/rd/clusters/${clusterId}/controller-preferred-candidates`); +}; + +export const addCandidateController = (params:any) => { + return fetch(`/op/cluster-controller/preferred-candidates`, { + method: 'POST', + body: JSON.stringify(params), + }); +}; + +export const deleteCandidateCancel = (params:any)=>{ + return fetch(`/op/cluster-controller/preferred-candidates`, { + method: 'DELETE', + body: JSON.stringify(params), + }); +} /** * 运维管控 broker */ diff --git a/kafka-manager-console/src/lib/line-charts-config.ts b/kafka-manager-console/src/lib/line-charts-config.ts index fe9880a6..4a667c0c 100644 --- a/kafka-manager-console/src/lib/line-charts-config.ts +++ b/kafka-manager-console/src/lib/line-charts-config.ts @@ -77,7 +77,7 @@ export const getControlMetricOption = (type: IOptionType, data: IClusterMetrics[ name = '条'; data.map(item => { item.messagesInPerSec = item.messagesInPerSec !== null ? Number(item.messagesInPerSec.toFixed(2)) : null; - }); + }); break; case 'brokerNum': case 'topicNum': @@ -224,7 +224,7 @@ export const getClusterMetricOption = (type: IOptionType, record: IClusterMetric name = '条'; data.map(item => { item.messagesInPerSec = item.messagesInPerSec !== null ? Number(item.messagesInPerSec.toFixed(2)) : null; - }); + }); break; default: const { name: unitName, data: xData } = dealFlowData(metricTypeMap[type], data); @@ -248,8 +248,8 @@ export const getClusterMetricOption = (type: IOptionType, record: IClusterMetric const unitSeries = item.data[item.seriesName] !== null ? Number(item.data[item.seriesName]) : null; // tslint:disable-next-line:max-line-length result += ''; - if ( (item.data.produceThrottled && item.seriesName === 'appIdBytesInPerSec') - || (item.data.consumeThrottled && item.seriesName === 'appIdBytesOutPerSec') ) { + if ((item.data.produceThrottled && item.seriesName === 'appIdBytesInPerSec') + || (item.data.consumeThrottled && item.seriesName === 'appIdBytesOutPerSec')) { return result += item.seriesName + ': ' + unitSeries + '(被限流)' + '
    '; } return result += item.seriesName + ': ' + unitSeries + '
    '; @@ -317,7 +317,7 @@ export const getMonitorMetricOption = (seriesName: string, data: IMetricPoint[]) if (ele.name === item.seriesName) { // tslint:disable-next-line:max-line-length result += ''; - return result += item.seriesName + ': ' + (item.data.value === null ? '' : item.data.value.toFixed(2)) + '
    '; + return result += item.seriesName + ': ' + (item.data.value === null ? '' : item.data.value.toFixed(2)) + '
    '; } }); }); diff --git a/kafka-manager-console/src/store/admin-monitor.ts b/kafka-manager-console/src/store/admin-monitor.ts index 4071e1c5..7e257637 100644 --- a/kafka-manager-console/src/store/admin-monitor.ts +++ b/kafka-manager-console/src/store/admin-monitor.ts @@ -3,6 +3,11 @@ import { observable, action } from 'mobx'; import { getBrokersMetricsHistory } from 'lib/api'; import { IClusterMetrics } from 'types/base-type'; +const STATUS = { + PENDING: 'pending', + REJECT: 'reject', + FULLFILLED: 'fullfilled' +} class AdminMonitor { @observable public currentClusterId = null as number; @@ -33,33 +38,42 @@ class AdminMonitor { @action.bound public setBrokersChartsData(data: IClusterMetrics[]) { this.brokersMetricsHistory = data; - this.setRequestId(null); + this.setRequestId(STATUS.FULLFILLED); + Promise.all(this.taskQueue).then(() => { + this.setRequestId(null); + this.taskQueue = []; + }) return data; } + public taskQueue = [] as any[]; public getBrokersMetricsList = async (startTime: string, endTime: string) => { - if (this.requestId && this.requestId !== 'error') { - return new Promise((res, rej) => { - window.setTimeout(() => { - if (this.requestId === 'error') { - rej(); - } else { + if (this.requestId) { + //逐条定时查询任务状态 + const p = new Promise((res, rej) => { + const timer = window.setInterval(() => { + if (this.requestId === STATUS.REJECT) { + rej(this.brokersMetricsHistory); + window.clearInterval(timer); + } else if (this.requestId === STATUS.FULLFILLED) { res(this.brokersMetricsHistory); + window.clearInterval(timer); } - }, 800); // TODO: 该实现方式待优化 + }, (this.taskQueue.length + 1) * 100); }); + this.taskQueue.push(p); + return p; } - this.setRequestId('requesting'); + this.setRequestId(STATUS.PENDING); return getBrokersMetricsHistory(this.currentClusterId, this.currentBrokerId, startTime, endTime) - .then(this.setBrokersChartsData).catch(() => this.setRequestId('error')); + .then(this.setBrokersChartsData).catch(() => this.setRequestId(STATUS.REJECT)); } public getBrokersChartsData = async (startTime: string, endTime: string, reload?: boolean) => { if (this.brokersMetricsHistory && !reload) { return new Promise(res => res(this.brokersMetricsHistory)); } - return this.getBrokersMetricsList(startTime, endTime); } } diff --git a/kafka-manager-console/src/store/admin.ts b/kafka-manager-console/src/store/admin.ts index f3d08264..bd641773 100644 --- a/kafka-manager-console/src/store/admin.ts +++ b/kafka-manager-console/src/store/admin.ts @@ -1,5 +1,5 @@ import { observable, action } from 'mobx'; -import { INewBulidEnums, ILabelValue, IClusterReal, IOptionType, IClusterMetrics, IClusterTopics, IKafkaFiles, IMetaData, IConfigure, IBrokerData, IOffset, IController, IBrokersBasicInfo, IBrokersStatus, IBrokersTopics, IBrokersPartitions, IBrokersAnalysis, IAnalysisTopicVO, IBrokersMetadata, IBrokersRegions, IThrottles, ILogicalCluster, INewRegions, INewLogical, ITaskManage, IPartitionsLocation, ITaskType, ITasksEnums, ITasksMetaData, ITaskStatusDetails, IKafkaRoles, IEnumsMap, IStaffSummary, IBill, IBillDetail } from 'types/base-type'; +import { INewBulidEnums, ILabelValue, IClusterReal, IOptionType, IClusterMetrics, IClusterTopics, IKafkaFiles, IMetaData, IConfigure, IConfigGateway, IBrokerData, IOffset, IController, IBrokersBasicInfo, IBrokersStatus, IBrokersTopics, IBrokersPartitions, IBrokersAnalysis, IAnalysisTopicVO, IBrokersMetadata, IBrokersRegions, IThrottles, ILogicalCluster, INewRegions, INewLogical, ITaskManage, IPartitionsLocation, ITaskType, ITasksEnums, ITasksMetaData, ITaskStatusDetails, IKafkaRoles, IEnumsMap, IStaffSummary, IBill, IBillDetail } from 'types/base-type'; import { deleteCluster, getBasicInfo, @@ -12,7 +12,12 @@ import { getConfigure, addNewConfigure, editConfigure, + addNewConfigGateway, deleteConfigure, + getGatewayList, + getGatewayType, + editConfigGateway, + deleteConfigGateway, getDataCenter, getClusterBroker, getClusterConsumer, @@ -49,6 +54,9 @@ import { getStaffSummary, getBillStaffSummary, getBillStaffDetail, + getCandidateController, + addCandidateController, + deleteCandidateCancel } from 'lib/api'; import { getControlMetricOption, getClusterMetricOption } from 'lib/line-charts-config'; @@ -59,6 +67,7 @@ import { transBToMB } from 'lib/utils'; import moment from 'moment'; import { timestore } from './time'; +import { message } from 'component/antd'; class Admin { @observable @@ -97,6 +106,12 @@ class Admin { @observable public configureList: IConfigure[] = []; + @observable + public configGatewayList: IConfigGateway[] = []; + + @observable + public gatewayType: []; + @observable public dataCenterList: string[] = []; @@ -142,6 +157,12 @@ class Admin { @observable public controllerHistory: IController[] = []; + @observable + public controllerCandidate: IController[] = []; + + @observable + public filtercontrollerCandidate: string = ''; + @observable public brokersPartitions: IBrokersPartitions[] = []; @@ -152,7 +173,7 @@ class Admin { public brokersAnalysisTopic: IAnalysisTopicVO[] = []; @observable - public brokersMetadata: IBrokersMetadata[] = []; + public brokersMetadata: IBrokersMetadata[] | any = []; @observable public brokersRegions: IBrokersRegions[] = []; @@ -206,10 +227,10 @@ class Admin { public kafkaRoles: IKafkaRoles[]; @observable - public controlType: IOptionType = 'byteIn/byteOut' ; + public controlType: IOptionType = 'byteIn/byteOut'; @observable - public type: IOptionType = 'byteIn/byteOut' ; + public type: IOptionType = 'byteIn/byteOut'; @observable public currentClusterId = null as number; @@ -241,7 +262,7 @@ class Admin { @action.bound public setClusterRealTime(data: IClusterReal) { - this.clusterRealData = data; + this.clusterRealData = data; this.getRealClusterLoading(false); } @@ -284,7 +305,7 @@ class Admin { return { ...item, label: item.fileName, - value: item.fileName + ',' + item.fileMd5, + value: item.fileName + ',' + item.fileMd5, }; })); } @@ -306,6 +327,20 @@ class Admin { }) : []; } + @action.bound + public setConfigGatewayList(data: IConfigGateway[]) { + this.configGatewayList = data ? data.map((item, index) => { + item.key = index; + return item; + }) : []; + } + + @action.bound + public setConfigGatewayType(data: any) { + this.setLoading(false); + this.gatewayType = data || []; + } + @action.bound public setDataCenter(data: string[]) { this.dataCenterList = data || []; @@ -335,6 +370,17 @@ class Admin { }) : []; } + @action.bound + public setCandidateController(data: IController[]) { + this.controllerCandidate = data ? data.map((item, index) => { + item.key = index; + return item; + }) : []; + this.filtercontrollerCandidate = data?data.map((item,index)=>{ + return item.brokerId + }).join(','):'' + } + @action.bound public setBrokersBasicInfo(data: IBrokersBasicInfo) { this.brokersBasicInfo = data; @@ -356,10 +402,10 @@ class Admin { this.replicaStatus = data.brokerReplicaStatusList.slice(1); this.bytesInStatus.forEach((item, index) => { - this.peakValueList.push({ name: peakValueMap[index], value: item}); + this.peakValueList.push({ name: peakValueMap[index], value: item }); }); this.replicaStatus.forEach((item, index) => { - this.copyValueList.push({name: copyValueMap[index], value: item}); + this.copyValueList.push({ name: copyValueMap[index], value: item }); }); } @@ -415,16 +461,16 @@ class Admin { } @action.bound - public setBrokersMetadata(data: IBrokersMetadata[]) { - this.brokersMetadata = data ? data.map((item, index) => { - item.key = index; - return { - ...item, - text: `${item.host} (BrokerID:${item.brokerId})`, - label: item.host, - value: item.brokerId, - }; - }) : []; + public setBrokersMetadata(data: IBrokersMetadata[]|any) { + this.brokersMetadata = data ? data.map((item:any, index:any) => { + item.key = index; + return { + ...item, + text: `${item.host} (BrokerID:${item.brokerId})`, + label: item.host, + value: item.brokerId, + }; + }) : []; } @action.bound @@ -461,9 +507,9 @@ class Admin { @action.bound public setLogicalClusters(data: ILogicalCluster[]) { this.logicalClusters = data ? data.map((item, index) => { - item.key = index; - return item; - }) : []; + item.key = index; + return item; + }) : []; } @action.bound @@ -474,25 +520,25 @@ class Admin { @action.bound public setClustersThrottles(data: IThrottles[]) { this.clustersThrottles = data ? data.map((item, index) => { - item.key = index; - return item; - }) : []; + item.key = index; + return item; + }) : []; } @action.bound public setPartitionsLocation(data: IPartitionsLocation[]) { this.partitionsLocation = data ? data.map((item, index) => { - item.key = index; - return item; - }) : []; + item.key = index; + return item; + }) : []; } @action.bound public setTaskManagement(data: ITaskManage[]) { this.taskManagement = data ? data.map((item, index) => { - item.key = index; - return item; - }) : []; + item.key = index; + return item; + }) : []; } @action.bound @@ -568,7 +614,7 @@ class Admin { return deleteCluster(clusterId).then(() => this.getMetaData(true)); } - public getPeakFlowChartData(value: ILabelValue[], map: string []) { + public getPeakFlowChartData(value: ILabelValue[], map: string[]) { return getPieChartOption(value, map); } @@ -627,6 +673,30 @@ class Admin { deleteConfigure(configKey).then(() => this.getConfigure()); } + public getGatewayList() { + getGatewayList().then(this.setConfigGatewayList); + } + + public getGatewayType() { + this.setLoading(true); + getGatewayType().then(this.setConfigGatewayType); + } + + public addNewConfigGateway(params: IConfigGateway) { + return addNewConfigGateway(params).then(() => this.getGatewayList()); + } + + public editConfigGateway(params: IConfigGateway) { + return editConfigGateway(params).then(() => this.getGatewayList()); + } + + public deleteConfigGateway(params: any) { + deleteConfigGateway(params).then(() => { + // message.success('删除成功') + this.getGatewayList() + }); + } + public getDataCenter() { getDataCenter().then(this.setDataCenter); } @@ -643,6 +713,20 @@ class Admin { return getControllerHistory(clusterId).then(this.setControllerHistory); } + public getCandidateController(clusterId: number) { + return getCandidateController(clusterId).then(data=>{ + return this.setCandidateController(data) + }); + } + + public addCandidateController(clusterId: number, brokerIdList: any) { + return addCandidateController({clusterId, brokerIdList}).then(()=>this.getCandidateController(clusterId)); + } + + public deleteCandidateCancel(clusterId: number, brokerIdList: any){ + return deleteCandidateCancel({clusterId, brokerIdList}).then(()=>this.getCandidateController(clusterId)); + } + public getBrokersBasicInfo(clusterId: number, brokerId: number) { return getBrokersBasicInfo(clusterId, brokerId).then(this.setBrokersBasicInfo); } diff --git a/kafka-manager-console/src/store/alarm.ts b/kafka-manager-console/src/store/alarm.ts index b3e004df..e57631f0 100644 --- a/kafka-manager-console/src/store/alarm.ts +++ b/kafka-manager-console/src/store/alarm.ts @@ -181,6 +181,7 @@ class Alarm { public modifyMonitorStrategy(params: IRequestParams) { return modifyMonitorStrategy(params).then(() => { message.success('操作成功'); + window.location.href = `${urlPrefix}/alarm`; }).finally(() => this.setLoading(false)); } diff --git a/kafka-manager-console/src/store/users.ts b/kafka-manager-console/src/store/users.ts index 8d53114e..249a0187 100644 --- a/kafka-manager-console/src/store/users.ts +++ b/kafka-manager-console/src/store/users.ts @@ -19,6 +19,9 @@ export class Users { @observable public staff: IStaff[] = []; + @observable + public newPassWord: any = null; + @action.bound public setAccount(data: IUser) { setCookie([{ key: 'role', value: `${data.role}`, time: 1 }]); @@ -42,6 +45,11 @@ export class Users { this.loading = value; } + @action.bound + public setNewPassWord(value: boolean) { + this.newPassWord = value; + } + public getAccount() { getAccount().then(this.setAccount); } diff --git a/kafka-manager-console/src/types/base-type.ts b/kafka-manager-console/src/types/base-type.ts index 1170a696..605fd4fc 100644 --- a/kafka-manager-console/src/types/base-type.ts +++ b/kafka-manager-console/src/types/base-type.ts @@ -190,6 +190,7 @@ export interface IUser { chineseName?: string; department?: string; key?: number; + confirmPassword?:string } export interface IOffset { @@ -486,6 +487,17 @@ export interface IConfigure { key?: number; } +export interface IConfigGateway { + id: number; + key?: number; + modifyTime: number; + name: string; + value: string; + version: string; + type: string; + description: string; +} + export interface IEepand { brokerIdList: number[]; clusterId: number; @@ -650,8 +662,10 @@ export interface IBrokerData { export interface IController { brokerId: number; host: string; - timestamp: number; - version: number; + timestamp?: number; + version?: number; + startTime?: number; + status?: number; key?: number; } From 0270f77eaab6b14e430c142f7a4d8cc620ba41e2 Mon Sep 17 00:00:00 2001 From: zengqiao Date: Tue, 9 Feb 2021 21:46:55 +0800 Subject: [PATCH 32/33] add upgrade doc --- docs/dev_guide/upgrade_manual/logi-km-v2.3.0.md | 17 +++++++++++++++++ docs/install_guide/create_mysql_table.sql | 3 ++- 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 docs/dev_guide/upgrade_manual/logi-km-v2.3.0.md diff --git a/docs/dev_guide/upgrade_manual/logi-km-v2.3.0.md b/docs/dev_guide/upgrade_manual/logi-km-v2.3.0.md new file mode 100644 index 00000000..3a4196f8 --- /dev/null +++ b/docs/dev_guide/upgrade_manual/logi-km-v2.3.0.md @@ -0,0 +1,17 @@ + +--- + +![kafka-manager-logo](../../assets/images/common/logo_name.png) + +**一站式`Apache Kafka`集群指标监控与运维管控平台** + +--- + +# 升级至`2.3.0`版本 + +`2.3.0`版本在`gateway_config`表增加了一个描述说明的字段,因此需要执行下面的sql进行字段的增加。 + +```sql +ALTER TABLE `gateway_config` +ADD COLUMN `description` TEXT NULL COMMENT '描述信息' AFTER `version`; +``` diff --git a/docs/install_guide/create_mysql_table.sql b/docs/install_guide/create_mysql_table.sql index 2a015de1..065532eb 100644 --- a/docs/install_guide/create_mysql_table.sql +++ b/docs/install_guide/create_mysql_table.sql @@ -203,7 +203,8 @@ CREATE TABLE `gateway_config` ( `type` varchar(128) NOT NULL DEFAULT '' COMMENT '配置类型', `name` varchar(128) NOT NULL DEFAULT '' COMMENT '配置名称', `value` text COMMENT '配置值', - `version` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '版本信息', + `version` bigint(20) unsigned NOT NULL DEFAULT '1' COMMENT '版本信息', + `description` text COMMENT '描述信息', `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `modify_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', PRIMARY KEY (`id`), From 0f8aca382eeb1360a742026d0fa421953e5ad3c2 Mon Sep 17 00:00:00 2001 From: zengqiao Date: Tue, 9 Feb 2021 21:47:56 +0800 Subject: [PATCH 33/33] bump version to 2.3.0 --- build.sh | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.sh b/build.sh index 03b1087e..f3ea8642 100644 --- a/build.sh +++ b/build.sh @@ -4,7 +4,7 @@ cd $workspace ## constant OUTPUT_DIR=./output -KM_VERSION=2.2.0 +KM_VERSION=2.3.0 APP_NAME=kafka-manager APP_DIR=${APP_NAME}-${KM_VERSION} diff --git a/pom.xml b/pom.xml index 6588d335..d4165a85 100644 --- a/pom.xml +++ b/pom.xml @@ -16,7 +16,7 @@ - 2.2.0-SNAPSHOT + 2.3.0-SNAPSHOT 2.7.0 1.5.13