From 2e9aade054413e6e371fb1cb75044c6d83efd078 Mon Sep 17 00:00:00 2001 From: tinawenqiao <315524513@qq.com> Date: Thu, 22 Nov 2018 17:45:31 +0800 Subject: [PATCH 01/24] Modify topic and conf related code to support 1.1 --- docs/index.html | 116 +------- docs/index.pdf | Bin 1041895 -> 1023450 bytes docs/paths.adoc | 43 +-- kafka-zk-rest-springboot.iml | 158 ---------- pom.xml | 5 +- .../bigdata/componet/OffsetStorage.java | 2 +- .../gnuhpc/bigdata/config/KafkaConfig.java | 24 +- .../bigdata/controller/DebugController.java | 2 +- .../bigdata/controller/KafkaController.java | 76 ++--- .../model/ConsumerGroupDescFactory.java | 6 +- .../model/CustomTopicPartitionInfo.java | 47 +++ .../org/gnuhpc/bigdata/model/TopicMeta.java | 5 +- .../bigdata/model/TopicPartitionInfo.java | 45 --- .../bigdata/service/KafkaAdminService.java | 272 ++++++++++-------- .../bigdata/service/KafkaConsumerService.java | 6 +- .../org/gnuhpc/bigdata/utils/KafkaUtils.java | 14 +- .../gnuhpc/bigdata/utils/ZookeeperUtils.java | 7 + src/main/resources/application-tina.yml | 7 +- .../KafkaRestSpringbootApplicationTests.java | 6 +- 19 files changed, 286 insertions(+), 555 deletions(-) delete mode 100644 kafka-zk-rest-springboot.iml create mode 100644 src/main/java/org/gnuhpc/bigdata/model/CustomTopicPartitionInfo.java delete mode 100644 src/main/java/org/gnuhpc/bigdata/model/TopicPartitionInfo.java diff --git a/docs/index.html b/docs/index.html index d0a57a4..b1cb3b3 100644 --- a/docs/index.html +++ b/docs/index.html @@ -491,9 +491,8 @@

Kafka REST API SwaggerUI

@@ -4297,10 +4296,10 @@
Produces
-

4.4.3. Get data of a zookeeper path

+

4.4.3. List a zookeeper path

-
GET /zk/get/path
+
GET /zk/ls/{path}
@@ -4322,7 +4321,7 @@
Parameters< -

Query

+

Path

path
required

path

@@ -4350,7 +4349,7 @@
Responses

200

OK

-

< string, string > map

+

< string > array

401

@@ -4392,41 +4391,13 @@
Produces
-

4.4.4. List a zookeeper path

+

4.4.4. Get the service state of zookeeper

-
GET /zk/ls/path
+
GET /zk/stat
-
Parameters
- ------ - - - - - - - - - - - - - - - - -
TypeNameDescriptionSchema

Query

path
-required

path

string

-
-
Responses
@@ -4445,7 +4416,7 @@
Responses
- + @@ -4486,73 +4457,6 @@
Produces
-
-

4.4.5. Get the service state of zookeeper

-
-
-
GET /zk/stat
-
-
-
-
Responses
-

200

OK

< string > array

< string, ZkServerStat > map

401

----- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

200

OK

< string, ZkServerStat > map

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
@@ -5433,7 +5337,7 @@

5.24. ZkServe diff --git a/docs/index.pdf b/docs/index.pdf index c3c581aafd4cb40dd637dd2eae8576c45556a9bc..b2a7a2e94e7f85c328bf137b2fd9a3d00562d9d7 100644 GIT binary patch delta 26105 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 map -|**401**|Unauthorized|No Content -|**403**|Forbidden|No Content -|**404**|Not Found|No Content -|=== - - -===== Consumes - -* `application/json` - - -===== Produces - -* `*/*` - - [[_lsusingget]] ==== List a zookeeper path .... -GET /zk/ls/path +GET /zk/ls/{path} .... @@ -1552,7 +1513,7 @@ GET /zk/ls/path [options="header", cols=".^2,.^3,.^9,.^4"] |=== |Type|Name|Description|Schema -|**Query**|**path** + +|**Path**|**path** + __required__|path|string |=== diff --git a/kafka-zk-rest-springboot.iml b/kafka-zk-rest-springboot.iml deleted file mode 100644 index dc8a831..0000000 --- a/kafka-zk-rest-springboot.iml +++ /dev/null @@ -1,158 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/pom.xml b/pom.xml index b9405ee..db06d4f 100644 --- a/pom.xml +++ b/pom.xml @@ -166,7 +166,7 @@ org.apache.kafka kafka-clients - 0.10.0.1 + 1.1.1 @@ -179,7 +179,7 @@ org.apache.kafka kafka_2.11 - 0.10.0.1 + 1.1.1 com.101tec @@ -444,6 +444,7 @@ src/main/resources/distribution/kafka-zk-springboot-distribution.xml kafka-zk-api-${project.version}-dist + false diff --git a/src/main/java/org/gnuhpc/bigdata/componet/OffsetStorage.java b/src/main/java/org/gnuhpc/bigdata/componet/OffsetStorage.java index 1e7edb0..41927da 100644 --- a/src/main/java/org/gnuhpc/bigdata/componet/OffsetStorage.java +++ b/src/main/java/org/gnuhpc/bigdata/componet/OffsetStorage.java @@ -1,7 +1,7 @@ package org.gnuhpc.bigdata.componet; import kafka.common.OffsetAndMetadata; -import kafka.coordinator.GroupTopicPartition; +import kafka.coordinator.group.GroupTopicPartition; import lombok.Data; import org.springframework.stereotype.Component; diff --git a/src/main/java/org/gnuhpc/bigdata/config/KafkaConfig.java b/src/main/java/org/gnuhpc/bigdata/config/KafkaConfig.java index 35d4388..e4b1b26 100644 --- a/src/main/java/org/gnuhpc/bigdata/config/KafkaConfig.java +++ b/src/main/java/org/gnuhpc/bigdata/config/KafkaConfig.java @@ -1,5 +1,9 @@ package org.gnuhpc.bigdata.config; +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; import lombok.Data; import lombok.Getter; import lombok.extern.log4j.Log4j; @@ -14,6 +18,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; import org.springframework.kafka.annotation.EnableKafka; import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory; import org.springframework.kafka.config.KafkaListenerContainerFactory; @@ -60,10 +65,10 @@ public OffsetStorage offsetStorage() { return new OffsetStorage(); } - @Bean - public KafkaConsumerService kafkaConsumerService() { - return new KafkaConsumerService(internalTopicPartitions); - } +// @Bean +// public KafkaConsumerService kafkaConsumerService() { +// return new KafkaConsumerService(internalTopicPartitions); +// } @Bean public KafkaListenerContainerFactory> @@ -93,4 +98,15 @@ public Map consumerConfigs() { return props; } + @Bean + public Jackson2ObjectMapperBuilder objectMapperBuilder() { + return new Jackson2ObjectMapperBuilder() { + @Override + public void configure(ObjectMapper objectMapper) { + super.configure(objectMapper); + objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY); + objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); + } + }; + } } diff --git a/src/main/java/org/gnuhpc/bigdata/controller/DebugController.java b/src/main/java/org/gnuhpc/bigdata/controller/DebugController.java index c0bd1d1..e6eee8d 100644 --- a/src/main/java/org/gnuhpc/bigdata/controller/DebugController.java +++ b/src/main/java/org/gnuhpc/bigdata/controller/DebugController.java @@ -2,7 +2,7 @@ import io.swagger.annotations.Api; import kafka.common.OffsetAndMetadata; -import kafka.coordinator.GroupTopicPartition; +import kafka.coordinator.group.GroupTopicPartition; import org.gnuhpc.bigdata.componet.OffsetStorage; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; diff --git a/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java b/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java index 8872643..ead3c62 100644 --- a/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java +++ b/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java @@ -5,8 +5,10 @@ import io.swagger.annotations.ApiResponse; import io.swagger.annotations.ApiResponses; import joptsimple.internal.Strings; +import kafka.cluster.Broker; import kafka.common.TopicAndPartition; import lombok.extern.log4j.Log4j; +import org.apache.kafka.clients.admin.ConfigEntry; import org.apache.kafka.common.errors.ApiException; import org.apache.kafka.common.errors.InvalidTopicException; import org.gnuhpc.bigdata.constant.ConsumerType; @@ -19,10 +21,8 @@ import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.*; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import java.util.Set; +import java.util.*; +import java.util.concurrent.ExecutionException; /** * Created by gnuhpc on 2017/7/16. @@ -39,13 +39,13 @@ public class KafkaController { @GetMapping("/topics") @ApiOperation(value = "List topics") - public List listTopics() { + public List listTopics() throws InterruptedException, ExecutionException { return kafkaAdminService.listTopics(); } @GetMapping("/topicsbrief") @ApiOperation(value = "List topics Brief") - public List listTopicBrief() { + public List listTopicBrief() throws InterruptedException, ExecutionException { return kafkaAdminService.listTopicBrief(); } @@ -53,13 +53,14 @@ public List listTopicBrief() { @ResponseStatus(HttpStatus.CREATED) @ApiOperation(value = "Create a topic") @ApiParam(value = "if reassignStr set, partitions and repli-factor will be ignored.") - public TopicMeta createTopic(@RequestBody TopicDetail topic, @RequestParam(required = false) String reassignStr) { + public TopicMeta createTopic(@RequestBody TopicDetail topic, @RequestParam(required = false) String reassignStr) + throws InterruptedException, ExecutionException { return kafkaAdminService.createTopic(topic, reassignStr); } @ApiOperation(value = "Tell if a topic exists") @GetMapping(value = "/topics/{topic}/exist") - public boolean existTopic(@PathVariable String topic) { + public boolean existTopic(@PathVariable String topic) throws InterruptedException, ExecutionException { return kafkaAdminService.existTopic(topic); } @@ -67,7 +68,7 @@ public boolean existTopic(@PathVariable String topic) { @ResponseStatus(HttpStatus.CREATED) @ApiOperation(value = "Write a message to the topic, for testing purpose") public GeneralResponse writeMessage(@PathVariable String topic, @RequestBody String message) { - kafkaProducerService.send(topic, message); +// kafkaProducerService.send(topic, message); return new GeneralResponse(GeneralResponseState.success, message + " has been sent"); } @@ -82,7 +83,7 @@ public String getMessage(@PathVariable String topic, @GetMapping(value = "/topics/{topic}") @ApiOperation(value = "Describe a topic by fetching the metadata and config") - public TopicMeta describeTopic(@PathVariable String topic) { + public TopicMeta describeTopic(@PathVariable String topic) throws InterruptedException, ExecutionException { return kafkaAdminService.describeTopic(topic); } @@ -94,67 +95,40 @@ public List listBrokers() { @DeleteMapping(value = "/topics/{topic}") @ApiOperation(value = "Delete a topic (you should enable topic deletion") - public GeneralResponse deleteTopic(@PathVariable String topic) { + public GeneralResponse deleteTopic(@PathVariable String topic) throws InterruptedException, ExecutionException { return kafkaAdminService.deleteTopic(topic); } - @PostMapping(value = "/topics/{topic}/conf") - @ApiOperation(value = "Create topic configs") - public Properties createTopicConfig(@PathVariable String topic, - @RequestBody Properties prop) { - return kafkaAdminService.createTopicConf(topic, prop); - } - @PutMapping(value = "/topics/{topic}/conf") @ApiOperation(value = "Update topic configs") - public Properties updateTopicConfig(@PathVariable String topic, - @RequestBody Properties prop) { - return kafkaAdminService.updateTopicConf(topic, prop); - } - - @DeleteMapping(value = "/topics/{topic}/conf") - @ApiOperation(value = "Delete topic configs") - public Properties deleteTopicConfig(@PathVariable String topic, - @RequestBody List delProps) { - return kafkaAdminService.deleteTopicConf(topic, delProps); + public Collection updateTopicConfig(@PathVariable String topic, + @RequestBody Properties props) + throws InterruptedException, ExecutionException { + return kafkaAdminService.updateTopicConf(topic, props); } @GetMapping(value = "/topics/{topic}/conf") @ApiOperation(value = "Get topic configs") - public Properties getTopicConfig(@PathVariable String topic) { + public Collection getTopicConfig(@PathVariable String topic) throws InterruptedException, ExecutionException { return kafkaAdminService.getTopicConf(topic); } @GetMapping(value = "/topics/{topic}/conf/{key}") @ApiOperation(value = "Get topic config by key") public Properties getTopicConfigByKey(@PathVariable String topic, - @PathVariable String key) { + @PathVariable String key) throws InterruptedException, ExecutionException { return kafkaAdminService.getTopicConfByKey(topic, key); } - @PostMapping(value = "/topics/{topic}/conf/{key}={value}") - @ApiOperation(value = "Create a topic config by key") - public Properties createTopicConfigByKey(@PathVariable String topic, - @PathVariable String key, - @PathVariable String value) { - return kafkaAdminService.createTopicConfByKey(topic, key, value); - } - @PutMapping(value = "/topics/{topic}/conf/{key}={value}") @ApiOperation(value = "Update a topic config by key") - public Properties updateTopicConfigByKey(@PathVariable String topic, - @PathVariable String key, - @PathVariable String value) { + public Collection updateTopicConfigByKey(@PathVariable String topic, + @PathVariable String key, + @PathVariable String value) throws InterruptedException, ExecutionException { return kafkaAdminService.updateTopicConfByKey(topic, key, value); } - @DeleteMapping(value = "/topics/{topic}/conf/{key}") - @ApiOperation(value = "Delete a topic config by key") - public boolean deleteTopicConfigByKey(@PathVariable String topic, - @PathVariable String key) { - return kafkaAdminService.deleteTopicConfByKey(topic, key); - } - + /* @PostMapping(value = "/partitions/add") @ApiOperation(value = "Add a partition to the topic") public TopicMeta addPartition(@RequestBody AddPartition addPartition) { @@ -220,7 +194,7 @@ public Set listTopicByCG(@PathVariable String consumerGroup, @ApiOperation(value = "Describe consumer groups by topic, showing lag and offset") public List describeCGByTopic(@ConsumerGroupExistConstraint @PathVariable String consumerGroup, @PathVariable ConsumerType type, - @PathVariable String topic) { + @PathVariable String topic) throws InterruptedException, ExecutionException { if (!Strings.isNullOrEmpty(topic)) { existTopic(topic); } else { @@ -265,8 +239,8 @@ public Map> getLastCommitTimestamp( public GeneralResponse deleteOldConsumerGroup(@PathVariable String consumergroup) { return kafkaAdminService.deleteConsumerGroup(consumergroup); } - - private void isTopicExist(String topic) throws InvalidTopicException { + */ + private void isTopicExist(String topic) throws InvalidTopicException, InterruptedException, ExecutionException { if (!kafkaAdminService.existTopic(topic)) { throw new InvalidTopicException("Topic " + topic + " non-exist!"); } diff --git a/src/main/java/org/gnuhpc/bigdata/model/ConsumerGroupDescFactory.java b/src/main/java/org/gnuhpc/bigdata/model/ConsumerGroupDescFactory.java index 306161e..bcdd604 100644 --- a/src/main/java/org/gnuhpc/bigdata/model/ConsumerGroupDescFactory.java +++ b/src/main/java/org/gnuhpc/bigdata/model/ConsumerGroupDescFactory.java @@ -2,7 +2,7 @@ import kafka.admin.AdminClient; import kafka.common.OffsetAndMetadata; -import kafka.coordinator.GroupTopicPartition; +import kafka.coordinator.group.GroupTopicPartition; import lombok.AllArgsConstructor; import org.apache.kafka.clients.consumer.KafkaConsumer; import org.apache.kafka.common.TopicPartition; @@ -30,7 +30,7 @@ public ConsumerGroupDesc makeOldConsumerGroupDesc( .setCurrentOffset(fetchOffSetFromZKResultList.get(op.getKey())) .setLogEndOffset( topicMeta.getTopicPartitionInfos().stream() - .filter(tpi -> tpi.getPartitionId() == op.getKey()).findFirst().get().getEndOffset()); + .filter(tpi -> tpi.getTopicPartitionInfo().partition() == op.getKey()).findFirst().get().getEndOffset()); if (op.getValue().equals("none")) { @@ -57,7 +57,7 @@ public ConsumerGroupDesc makeNewRunningConsumerGroupDesc( .setTopic(tp.topic()) .setPartitionId(tp.partition()) .setConsumerId(cs.clientId()) - .setHost(cs.clientHost()) + .setHost(cs.host()) .setState(ConsumerState.RUNNING) .setType(ConsumerType.NEW); diff --git a/src/main/java/org/gnuhpc/bigdata/model/CustomTopicPartitionInfo.java b/src/main/java/org/gnuhpc/bigdata/model/CustomTopicPartitionInfo.java new file mode 100644 index 0000000..1c51250 --- /dev/null +++ b/src/main/java/org/gnuhpc/bigdata/model/CustomTopicPartitionInfo.java @@ -0,0 +1,47 @@ +package org.gnuhpc.bigdata.model; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import lombok.*; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.kafka.common.TopicPartitionInfo; + +import java.io.Serializable; + +@Getter +@Setter +@ToString +@EqualsAndHashCode +public class CustomTopicPartitionInfo implements Comparable, Serializable +{ + private TopicPartitionInfo topicPartitionInfo; + private boolean in_sync; + private long startOffset; + private long endOffset; + @Setter(AccessLevel.NONE) + private long messageAvailable; + + public void setIn_sync(){ + if(topicPartitionInfo.isr()!=null && topicPartitionInfo.replicas() !=null && + topicPartitionInfo.isr().size() == topicPartitionInfo.replicas().size()){ + in_sync = CollectionUtils.isEqualCollection(topicPartitionInfo.isr(), topicPartitionInfo.replicas()); + } + else{ + in_sync = false; + } + } + + public void setMessageAvailable() { + this.messageAvailable = this.endOffset - this.startOffset; + } + + @Override + public int compareTo(CustomTopicPartitionInfo topicPartitionInfo) { + if (this.topicPartitionInfo.partition() < topicPartitionInfo.topicPartitionInfo.partition()) return -1; + else if (this.topicPartitionInfo.partition() == topicPartitionInfo.topicPartitionInfo.partition()) return 0; + else return 1; + } + + public TopicPartitionInfo getTopicPartitionInfo() { + return this.topicPartitionInfo; + } +} diff --git a/src/main/java/org/gnuhpc/bigdata/model/TopicMeta.java b/src/main/java/org/gnuhpc/bigdata/model/TopicMeta.java index 6c79f60..bedb93a 100644 --- a/src/main/java/org/gnuhpc/bigdata/model/TopicMeta.java +++ b/src/main/java/org/gnuhpc/bigdata/model/TopicMeta.java @@ -17,12 +17,9 @@ public class TopicMeta { private String topicName; private int partitionCount; private int replicationFactor; - private List topicPartitionInfos; - private Properties topicCustomConfigs; + private List topicPartitionInfos; public TopicMeta(String topicName){ this.topicName = topicName; } - - } diff --git a/src/main/java/org/gnuhpc/bigdata/model/TopicPartitionInfo.java b/src/main/java/org/gnuhpc/bigdata/model/TopicPartitionInfo.java deleted file mode 100644 index b64e9cf..0000000 --- a/src/main/java/org/gnuhpc/bigdata/model/TopicPartitionInfo.java +++ /dev/null @@ -1,45 +0,0 @@ -package org.gnuhpc.bigdata.model; - -import lombok.*; -import org.apache.commons.collections4.CollectionUtils; -import org.apache.kafka.common.Node; - -import java.util.List; - -@Getter -@Setter -@ToString -@EqualsAndHashCode -public class TopicPartitionInfo implements Comparable -{ - private int partitionId; - private String leader; - private List replicas; - private List isr; - private boolean in_sync; - private long startOffset; - private long endOffset; - @Setter(AccessLevel.NONE) - private long messageAvailable; - - public void setIn_sync(){ - if(isr!=null && replicas !=null && isr.size() == replicas.size()){ - in_sync = CollectionUtils.isEqualCollection(isr,replicas); - } - else{ - in_sync = false; - } - } - - public void setMessageAvailable() { - this.messageAvailable = this.endOffset - this.startOffset; - } - - - @Override - public int compareTo(TopicPartitionInfo topicPartitionInfo) { - if (this.partitionId < topicPartitionInfo.partitionId) return -1; - else if (this.partitionId == topicPartitionInfo.partitionId) return 0; - else return 1; - } -} diff --git a/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java b/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java index 88ffc9d..a66e5a6 100644 --- a/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java +++ b/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java @@ -1,27 +1,33 @@ package org.gnuhpc.bigdata.service; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.base.Strings; import com.google.common.collect.ImmutableMap; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonDeserializer; -import kafka.admin.*; +import io.swagger.models.auth.In; +import kafka.admin.AdminClient; +import kafka.admin.AdminUtils; +import kafka.admin.RackAwareMode; +import kafka.admin.TopicCommand; import kafka.api.PartitionOffsetRequestInfo; import kafka.cluster.Broker; import kafka.common.OffsetAndMetadata; -import kafka.common.Topic; import kafka.common.TopicAndPartition; -import kafka.coordinator.GroupOverview; -import kafka.coordinator.GroupTopicPartition; +import kafka.coordinator.group.GroupOverview; +import kafka.coordinator.group.GroupTopicPartition; import kafka.javaapi.OffsetRequest; import kafka.javaapi.OffsetResponse; import kafka.javaapi.consumer.SimpleConsumer; import kafka.server.ConfigType; import kafka.utils.ZKGroupTopicDirs; import kafka.utils.ZkUtils; +import kafka.zk.KafkaZkClient; import lombok.extern.log4j.Log4j; -import org.apache.commons.lang3.StringUtils; import org.apache.curator.framework.CuratorFramework; +import org.apache.kafka.clients.admin.*; import org.apache.kafka.clients.consumer.ConsumerRecord; import org.apache.kafka.clients.consumer.ConsumerRecords; import org.apache.kafka.clients.consumer.KafkaConsumer; @@ -31,9 +37,11 @@ import org.apache.kafka.common.Node; import org.apache.kafka.common.PartitionInfo; import org.apache.kafka.common.TopicPartition; +import org.apache.kafka.common.TopicPartitionInfo; +import org.apache.kafka.common.config.ConfigResource; import org.apache.kafka.common.errors.ApiException; import org.apache.kafka.common.errors.InvalidTopicException; -import org.apache.kafka.common.requests.MetadataResponse; +import org.apache.kafka.common.internals.Topic; import org.gnuhpc.bigdata.CollectionConvertor; import org.gnuhpc.bigdata.componet.OffsetStorage; import org.gnuhpc.bigdata.config.KafkaConfig; @@ -51,7 +59,6 @@ import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; import scala.Option; -import scala.Tuple2; import scala.collection.JavaConverters; import scala.collection.Seq; @@ -96,28 +103,31 @@ public class KafkaAdminService { //For zookeeper connection private CuratorFramework zkClient; + private KafkaZkClient kafkaZkClient; + + private org.apache.kafka.clients.admin.AdminClient kafkaAdminClient; //For Json serialized private Gson gson; private scala.Option NONE = scala.Option.apply(null); - @PostConstruct private void init() { this.zkUtils = zookeeperUtils.getZkUtils(); this.zkClient = zookeeperUtils.getCuratorClient(); + this.kafkaZkClient = zookeeperUtils.getKafkaZkClient(); + Properties adminClientProp = new Properties(); + adminClientProp.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaConfig.getBrokers()); + //TODO eliminate the init connection + kafkaAdminClient = KafkaAdminClient.create(adminClientProp); GsonBuilder builder = new GsonBuilder(); builder.registerTypeAdapter(DateTime.class, (JsonDeserializer) (jsonElement, type, jsonDeserializationContext) -> new DateTime(jsonElement.getAsJsonPrimitive().getAsLong())); this.gson = builder.create(); } - public TopicMeta createTopic(TopicDetail topic, String reassignStr) { - if (StringUtils.isEmpty(topic.getName())) { - throw new InvalidTopicException("Empty topic name"); - } - + public TopicMeta createTopic(TopicDetail topic, String reassignStr) throws InterruptedException, ExecutionException { if (Topic.hasCollisionChars(topic.getName())) { - throw new InvalidTopicException("Invalid topic name"); + throw new InvalidTopicException("Invalid topic name, it contains '.' or '_'."); } if (Strings.isNullOrEmpty(reassignStr) && topic.getPartitions() <= 0) { @@ -145,7 +155,7 @@ public TopicMeta createTopic(TopicDetail topic, String reassignStr) { argsList.add("--replica-assignment"); argsList.add(reassignStr); - TopicCommand.createTopic(zkUtils, new TopicCommand.TopicCommandOptions(argsList.stream().toArray(String[]::new))); + TopicCommand.createTopic(kafkaZkClient, new TopicCommand.TopicCommandOptions(argsList.stream().toArray(String[]::new))); } @@ -158,40 +168,60 @@ public TopicMeta createTopic(TopicDetail topic, String reassignStr) { return describeTopic(topic.getName()); } - public List listTopics() { - return CollectionConvertor.seqConvertJavaList(zkUtils.getAllTopics()); + public List listTopics() throws InterruptedException, ExecutionException { + List topicNamesList = new ArrayList(); + topicNamesList.addAll(getAllTopics()); + + return topicNamesList; } - public List listTopicBrief() { - KafkaConsumer consumer = kafkaUtils.createNewConsumer(); - Map> topicMap = consumer.listTopics(); - List result = topicMap.entrySet().parallelStream().map(e -> { - String topic = e.getKey(); - long replicateCount = e.getValue().parallelStream().flatMap(pi -> Arrays.stream(pi.replicas())).count(); - long isrCount = e.getValue().parallelStream().flatMap(pi -> Arrays.stream(pi.inSyncReplicas())).count(); - if (replicateCount == 0) { - return new TopicBrief(topic, e.getValue().size(), 0); - } else { - return new TopicBrief(topic, e.getValue().size(), ((double) isrCount / replicateCount)); - } - } - ).collect(toList()); + public Set getAllTopics() throws InterruptedException, ExecutionException { + ListTopicsOptions options = new ListTopicsOptions(); + // includes internal topics such as __consumer_offsets + options.listInternal(true); - consumer.close(); + ListTopicsResult topics = kafkaAdminClient.listTopics(options); + Set topicNames = topics.names().get(); + log.info("Current topics in this cluster: " + topicNames); + + return topicNames; + } + + public List listTopicBrief() throws InterruptedException, ExecutionException { + DescribeTopicsResult describeTopicsResult = kafkaAdminClient.describeTopics(listTopics()); + Map topicMap = describeTopicsResult.all().get(); + List result = topicMap.entrySet().parallelStream().map(e -> { + String topic = e.getKey(); + TopicDescription topicDescription = e.getValue(); + List topicPartitionInfoList = topicDescription.partitions(); + int replicateCount = 0; + int isrCount = 0; + for (org.apache.kafka.common.TopicPartitionInfo topicPartitionInfo: topicPartitionInfoList) { + replicateCount += topicPartitionInfo.replicas().size(); + isrCount += topicPartitionInfo.isr().size(); + } + if (replicateCount == 0) { + return new TopicBrief(topic, topicDescription.partitions().size(), 0); + } else { + return new TopicBrief(topic, topicDescription.partitions().size(), ((double) isrCount / replicateCount)); + } + }).collect(toList()); return result; } public boolean existTopic(String topicName) { - return AdminUtils.topicExists(zkUtils, topicName); + return kafkaZkClient.topicExists(topicName); } public List listBrokers() { - List brokerList = CollectionConvertor.seqConvertJavaList(zkUtils.getAllBrokersInCluster()); + List brokerList = CollectionConvertor.seqConvertJavaList(kafkaZkClient.getAllBrokersInCluster()); + return brokerList.parallelStream().collect(Collectors.toMap(Broker::id, Broker::rack)).entrySet().parallelStream() .map(entry -> { String brokerInfoStr = null; try { + //TODO replace zkClient with kafkaZKClient brokerInfoStr = new String( zkClient.getData().forPath(ZkUtils.BrokerIdsPath() + "/" + entry.getKey()) ); @@ -209,109 +239,94 @@ public List listBrokers() { }).collect(toList()); } - public TopicMeta describeTopic(@TopicExistConstraint String topicName) { - KafkaConsumer consumer = kafkaUtils.createNewConsumer(); + public TopicMeta describeTopic(@TopicExistConstraint String topicName) throws InterruptedException, ExecutionException { + DescribeTopicsResult describeTopicsResult = kafkaAdminClient.describeTopics(Collections.singletonList(topicName)); + Map topicMap = describeTopicsResult.all().get(); TopicMeta topicMeta = new TopicMeta(topicName); - List tmList = consumer.partitionsFor(topicName); - topicMeta.setPartitionCount(tmList.size()); - topicMeta.setReplicationFactor(tmList.get(0).replicas().length); - topicMeta.setTopicCustomConfigs(getTopicPropsFromZk(topicName)); - topicMeta.setTopicPartitionInfos(tmList.parallelStream().map( - tm -> { - TopicPartitionInfo topicPartitionInfo = new TopicPartitionInfo(); - topicPartitionInfo.setLeader(tm.leader().host()); - topicPartitionInfo.setIsr(Arrays.stream(tm.inSyncReplicas()).map(node -> node.host()).collect(toList())); - topicPartitionInfo.setPartitionId(tm.partition()); - topicPartitionInfo.setReplicas(Arrays.stream(tm.replicas()).map(node -> node.host()).collect(toList())); - topicPartitionInfo.setIn_sync(); - topicPartitionInfo.setStartOffset(getBeginningOffset(tm.leader(), tm.topic(), tm.partition())); - topicPartitionInfo.setEndOffset(getEndOffset(tm.leader(), tm.topic(), tm.partition())); - topicPartitionInfo.setMessageAvailable(); - return topicPartitionInfo; - - }).collect(toList()) - ); - - Collections.sort(topicMeta.getTopicPartitionInfos()); - - consumer.close(); + if (topicMap.containsKey(topicName)) { + TopicDescription topicDescription = topicMap.get(topicName); + List tmList = topicDescription.partitions(); + topicMeta.setPartitionCount(topicDescription.partitions().size()); + topicMeta.setReplicationFactor(tmList.get(0).replicas().size()); + topicMeta.setTopicPartitionInfos(tmList.parallelStream().map( + tm -> { + CustomTopicPartitionInfo customTopicPartitionInfo = new CustomTopicPartitionInfo(); + customTopicPartitionInfo.setTopicPartitionInfo(tm); + customTopicPartitionInfo.setIn_sync(); + customTopicPartitionInfo.setStartOffset(getBeginningOffset(tm.leader(), topicName, tm.partition())); + customTopicPartitionInfo.setEndOffset(getEndOffset(tm.leader(), topicName, tm.partition())); + customTopicPartitionInfo.setMessageAvailable(); + return customTopicPartitionInfo; + + }).collect(toList()) + ); + Collections.sort(topicMeta.getTopicPartitionInfos()); + } return topicMeta; } - public GeneralResponse deleteTopic(@TopicExistConstraint String topic) { + public GeneralResponse deleteTopic(@TopicExistConstraint String topic) + throws InterruptedException, ExecutionException { log.warn("Delete topic " + topic); - AdminUtils.deleteTopic(zkUtils, topic); + kafkaAdminClient.deleteTopics(Collections.singletonList(topic)).all().get(); return new GeneralResponse(GeneralResponseState.success, topic + " has been deleted."); } - public Properties createTopicConf(@TopicExistConstraint String topic, Properties prop) { - Properties configs = getTopicPropsFromZk(topic); - configs.putAll(prop); - AdminUtils.changeTopicConfig(zkUtils, topic, configs); - log.info("Create config for topic: " + topic + "Configs:" + configs); - return getTopicPropsFromZk(topic); - } + public Collection describeConfig(ConfigResource.Type type, String name) throws ExecutionException, InterruptedException { + DescribeConfigsResult ret = kafkaAdminClient.describeConfigs(Collections.singleton(new ConfigResource(type, name))); + Map configs = ret.all().get(); + Collection configEntries = null; - public Properties deleteTopicConf(@TopicExistConstraint String topic, List deleteProps) { - // compile the final set of configs - Properties configs = getTopicPropsFromZk(topic); - deleteProps.stream().forEach(config -> configs.remove(config)); - AdminUtils.changeTopicConfig(zkUtils, topic, configs); - log.info("Delete config for topic: " + topic); - return getTopicPropsFromZk(topic); - } + for (Map.Entry entry : configs.entrySet()) { + Config value = entry.getValue(); + configEntries = value.entries(); + } - public Properties updateTopicConf(@TopicExistConstraint String topic, Properties prop) { - AdminUtils.changeTopicConfig(zkUtils, topic, prop); - return getTopicPropsFromZk(topic); + return configEntries; } - public Properties getTopicConf(@TopicExistConstraint String topic) { - return getTopicPropsFromZk(topic); + public void alterConfig(ConfigResource.Type type, String name, Collection configEntries) + throws InterruptedException, ExecutionException{ + Config config = new Config(configEntries); + kafkaAdminClient.alterConfigs(Collections.singletonMap(new ConfigResource(type, name), config)).all().get(); } - public Properties getTopicConfByKey(@TopicExistConstraint String topic, String key) { - String value = String.valueOf(AdminUtils.fetchEntityConfig(zkUtils, ConfigType.Topic(), topic).get(key)); - Properties returnProps = new Properties(); - if (!value.equals("null")) { - returnProps.setProperty(key, value); - return returnProps; - } else - return null; + public Collection updateTopicConf(@TopicExistConstraint String topic, Properties props) + throws InterruptedException, ExecutionException { + Collection configEntries = props.entrySet().stream().map( + e -> new ConfigEntry(e.getKey().toString(), e.getValue().toString())).collect(Collectors.toList()); + alterConfig(ConfigResource.Type.TOPIC, topic, configEntries); + + return describeConfig(ConfigResource.Type.TOPIC, topic); } - public boolean deleteTopicConfByKey(@TopicExistConstraint String topic, String key) { - Properties configs = getTopicPropsFromZk(topic); - configs.remove(key); - AdminUtils.changeTopicConfig(zkUtils, topic, configs); - return getTopicPropsFromZk(topic).get(key) == null; + public Collection getTopicConf(@TopicExistConstraint String topic) + throws InterruptedException, ExecutionException { + return describeConfig(ConfigResource.Type.TOPIC, topic); } - public Properties updateTopicConfByKey(@TopicExistConstraint String topic, String key, String value) { - Properties props = getTopicConf(topic); - props.setProperty(key, value); - String validValue = String.valueOf(updateTopicConf(topic, props).get(key)); - if (!validValue.equals("null") && validValue.equals(value)) { - return props; - } else { - throw new ApiException("Update Topic Config failed: " + key + ":" + value); - } + public Properties getTopicConfByKey(@TopicExistConstraint String topic, String key) + throws InterruptedException, ExecutionException{ + Collection configEntries = describeConfig(ConfigResource.Type.TOPIC, topic); + Properties returnProps = new Properties(); + for (ConfigEntry entry : configEntries) + if (entry.name().equals(key)) { + returnProps.put(key, entry.value()); + return returnProps; + } + + return null; } - public Properties createTopicConfByKey(@TopicExistConstraint String topic, String key, String value) { - Properties props = new Properties(); - props.setProperty(key, value); - String validValue = String.valueOf(createTopicConf(topic, props).get(key)); - if (!validValue.equals("null") && validValue.equals(value)) { - return props; - } else { - throw new ApiException("Update Topic Config failed: " + key + ":" + value); - } + public Collection updateTopicConfByKey(@TopicExistConstraint String topic, String key, String value) throws InterruptedException, ExecutionException{ + alterConfig(ConfigResource.Type.TOPIC, topic, Collections.singletonList(new ConfigEntry(key, value))); + return describeConfig(ConfigResource.Type.TOPIC, topic); } + /* public TopicMeta addPartition(@TopicExistConstraint String topic, AddPartition addPartition) { List partitionMataData = AdminUtils.fetchTopicMetadataFromZk(topic, zkUtils).partitionMetadata(); int numPartitions = partitionMataData.size(); @@ -400,7 +415,7 @@ public Map checkReassignStatus(String reassignStr) { r -> r.getValue().status() )); } - + */ private Set listAllOldConsumerGroups() { log.info("Finish getting old consumers"); return CollectionConvertor.seqConvertJavaList(zkUtils.getConsumerGroups()).stream().collect(toSet()); @@ -444,6 +459,7 @@ private Set listAllNewConsumerGroups() { return activeGroups; } + /* private Set listNewConsumerGroupsByTopic(@TopicExistConstraint String topic) { Set result = new HashSet(); Set consumersList = listAllNewConsumerGroups(); @@ -463,9 +479,9 @@ private Set listNewConsumerGroupsByTopic(@TopicExistConstraint String to adminClient.close(); } return result; - } + }*/ - public List describeOldCGByTopic(String consumerGroup, @TopicExistConstraint String topic) { + public List describeOldCGByTopic(String consumerGroup, @TopicExistConstraint String topic) throws InterruptedException, ExecutionException{ if (!isOldConsumerGroup(consumerGroup)) { throw new RuntimeException(consumerGroup + " non-exist"); } @@ -542,7 +558,7 @@ private List setOldCGD( ) .collect(toList()); } - +/* public List describeNewCGByTopic(String consumerGroup, String topic) { if (!isNewConsumerGroup(consumerGroup)) { @@ -552,6 +568,7 @@ public List describeNewCGByTopic(String consumerGroup, return setNewCGD(consumerGroup, topic); } + private List setNewCGD(String consumerGroup, String topic) { List cgdList = new ArrayList<>(); AdminClient adminClient = kafkaUtils.createAdminClient(); @@ -623,7 +640,7 @@ private List setNewCGD(String consumerGroup, String topic) { return cgdList; } - +*/ public String getMessage(@TopicExistConstraint String topic, int partition, long offset, String decoder, String avroSchema) { KafkaConsumer consumer = kafkaUtils.createNewConsumer(String.valueOf(System.currentTimeMillis())); @@ -847,7 +864,7 @@ public Map> listAllConsumerGroups(ConsumerType type) { return result; } - + /* public Map> listConsumerGroupsByTopic( @TopicExistConstraint String topic, ConsumerType type) { @@ -875,7 +892,7 @@ public Map> listConsumerGroupsByTopic( return result; } - + */ private List getTopicPartitions(String t) { List tpList = new ArrayList<>(); List l = Arrays.asList(t); @@ -887,9 +904,6 @@ private List getTopicPartitions(String t) { return tpList; } - private Properties getTopicPropsFromZk(String topic) { - return AdminUtils.fetchEntityConfig(zkUtils, ConfigType.Topic(), topic); - } private long getOffsets(Node leader, String topic, int partitionId, long time) { @@ -948,7 +962,7 @@ public boolean isNewConsumerGroup(String consumerGroup) { return (listAllNewConsumerGroups().contains(consumerGroup)); } - + /* public Set listTopicsByCG(String consumerGroup, ConsumerType type) { Set topicList = new HashSet<>(); @@ -1025,7 +1039,7 @@ public Map> describeConsumerGroup(String consume return result; } - +*/ public Map countPartition(String topic) { KafkaConsumer consumer = kafkaUtils.createNewConsumer(); List piList = consumer.partitionsFor(topic); @@ -1112,4 +1126,14 @@ public HealthCheckResult healthCheck() { } return healthCheckResult; } + + public void printObjectInJson(Object object) { + try { + ObjectMapper objectMapper = new ObjectMapper(); + //objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY); + System.out.println("object:" + objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(object)); + } catch (JsonProcessingException jsonProcessingException) { + jsonProcessingException.printStackTrace(); + } + } } diff --git a/src/main/java/org/gnuhpc/bigdata/service/KafkaConsumerService.java b/src/main/java/org/gnuhpc/bigdata/service/KafkaConsumerService.java index 56cccde..23e95e7 100644 --- a/src/main/java/org/gnuhpc/bigdata/service/KafkaConsumerService.java +++ b/src/main/java/org/gnuhpc/bigdata/service/KafkaConsumerService.java @@ -1,9 +1,9 @@ package org.gnuhpc.bigdata.service; import kafka.common.OffsetAndMetadata; -import kafka.coordinator.GroupMetadataManager; -import kafka.coordinator.GroupTopicPartition; -import kafka.coordinator.OffsetKey; +import kafka.coordinator.group.GroupMetadataManager; +import kafka.coordinator.group.GroupTopicPartition; +import kafka.coordinator.group.OffsetKey; import lombok.extern.log4j.Log4j; import org.apache.kafka.clients.consumer.ConsumerRecord; import org.apache.kafka.common.TopicPartition; diff --git a/src/main/java/org/gnuhpc/bigdata/utils/KafkaUtils.java b/src/main/java/org/gnuhpc/bigdata/utils/KafkaUtils.java index 6795012..f74aedd 100644 --- a/src/main/java/org/gnuhpc/bigdata/utils/KafkaUtils.java +++ b/src/main/java/org/gnuhpc/bigdata/utils/KafkaUtils.java @@ -2,10 +2,11 @@ import kafka.admin.AdminClient; import kafka.admin.AdminUtils; -import kafka.coordinator.GroupOverview; +import kafka.coordinator.group.GroupOverview; import lombok.Getter; import lombok.Setter; import lombok.extern.log4j.Log4j; +import org.apache.kafka.clients.admin.*; import org.apache.kafka.clients.consumer.ConsumerConfig; import org.apache.kafka.clients.consumer.KafkaConsumer; import org.apache.kafka.clients.producer.KafkaProducer; @@ -21,10 +22,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; -import java.util.Collections; -import java.util.List; -import java.util.Properties; - +import java.util.*; +import java.util.concurrent.ExecutionException; +import org.apache.kafka.clients.admin.KafkaAdminClient; /** * Created by gnuhpc on 2017/7/12. */ @@ -38,7 +38,7 @@ public class KafkaUtils { @Autowired private ZookeeperConfig zookeeperConfig; - private AdminClient kafkaAdminClient; + private AdminClient adminClient; private KafkaProducer producer; private Properties prop; @@ -55,7 +55,7 @@ public void init(){ producer = new KafkaProducer(prop); log.info("Kafka initing..."); - kafkaAdminClient = AdminClient.create(prop); + adminClient = AdminClient.create(prop); } diff --git a/src/main/java/org/gnuhpc/bigdata/utils/ZookeeperUtils.java b/src/main/java/org/gnuhpc/bigdata/utils/ZookeeperUtils.java index f4189fb..0a022fa 100644 --- a/src/main/java/org/gnuhpc/bigdata/utils/ZookeeperUtils.java +++ b/src/main/java/org/gnuhpc/bigdata/utils/ZookeeperUtils.java @@ -3,6 +3,7 @@ import com.google.common.base.Charsets; import kafka.utils.ZKStringSerializer$; import kafka.utils.ZkUtils; +import kafka.zk.KafkaZkClient; import lombok.Getter; import lombok.Setter; import lombok.extern.log4j.Log4j; @@ -16,6 +17,7 @@ import org.apache.curator.framework.api.GetDataBuilder; import org.apache.curator.retry.ExponentialBackoffRetry; import org.apache.curator.utils.ZKPaths; +import org.apache.kafka.common.utils.Time; import org.apache.zookeeper.data.Stat; import org.gnuhpc.bigdata.config.ZookeeperConfig; import org.gnuhpc.bigdata.constant.ZkServerMode; @@ -72,6 +74,7 @@ public class ZookeeperUtils { private CuratorFramework curatorClient; private ZkClient zkClient; private ZkUtils zkUtils; + private KafkaZkClient kafkaZkClient; public void init() { @@ -97,11 +100,15 @@ public void init() { e.printStackTrace(); log.error("Exception:", e); } + kafkaZkClient = KafkaZkClient.apply(zookeeperConfig.getUris(), false, SESSION_TIMEOUT, + CONNECTION_TIMEOUT, Integer.MAX_VALUE, Time.SYSTEM, "testMetricGroup", + "testMetricType"); } public void destroy() { curatorClient.close(); zkClient.close(); + kafkaZkClient.close(); log.info("zookeeper closed."); } diff --git a/src/main/resources/application-tina.yml b/src/main/resources/application-tina.yml index 267faa7..5ff1e32 100644 --- a/src/main/resources/application-tina.yml +++ b/src/main/resources/application-tina.yml @@ -1,5 +1,5 @@ kafka: - brokers: localhost:9092,localhost:9093,localhost:9094 + brokers: localhost:19092,localhost:19093,localhost:9094 offset: topic: "__consumer_offsets" partitions: 50 @@ -8,7 +8,7 @@ kafka: topic: "health" zookeeper: - uris: 127.0.0.1:2181 + uris: 127.0.0.1:2183 jmx: kafka: @@ -39,3 +39,6 @@ spring: aop: auto: true + + + diff --git a/src/test/java/org/gnuhpc/bigdata/KafkaRestSpringbootApplicationTests.java b/src/test/java/org/gnuhpc/bigdata/KafkaRestSpringbootApplicationTests.java index 2ec9906..d42f702 100644 --- a/src/test/java/org/gnuhpc/bigdata/KafkaRestSpringbootApplicationTests.java +++ b/src/test/java/org/gnuhpc/bigdata/KafkaRestSpringbootApplicationTests.java @@ -4,7 +4,7 @@ import kafka.common.ErrorMapping; import kafka.common.OffsetMetadataAndError; import kafka.common.TopicAndPartition; -import kafka.coordinator.GroupOverview; +import kafka.coordinator.group.GroupOverview; import kafka.javaapi.OffsetFetchRequest; import kafka.javaapi.OffsetFetchResponse; import kafka.network.BlockingChannel; @@ -58,7 +58,7 @@ public void contextLoads() { @Test public void testListBrokers() throws Exception { - List brokerInfoList = kafkaAdminService.listBrokers(); - brokerInfoList.stream().forEach(log::info); + //List brokerInfoList = kafkaAdminService.listBrokers(); + //brokerInfoList.stream().forEach(log::info); } } From a4c312045c6dc6a3804326fa74064315a049b23e Mon Sep 17 00:00:00 2001 From: tinawenqiao <315524513@qq.com> Date: Thu, 20 Dec 2018 15:12:30 +0800 Subject: [PATCH 02/24] Modify consumer group related api. --- pom.xml | 6 + .../bigdata/constant/ConsumerGroupState.java | 41 + .../bigdata/controller/KafkaController.java | 449 ++-- .../bigdata/controller/UserController.java | 4 +- .../bigdata/model/ConsumerGroupDesc.java | 106 +- .../model/ConsumerGroupDescFactory.java | 71 +- .../bigdata/model/ConsumerGroupMeta.java | 18 + .../gnuhpc/bigdata/model/GeneralResponse.java | 3 + .../bigdata/model/MemberDescription.java | 17 + .../model/PartitionAssignmentState.java | 22 + .../java/org/gnuhpc/bigdata/model/Record.java | 11 + .../org/gnuhpc/bigdata/model/TopicMeta.java | 1 + .../bigdata/service/KafkaAdminService.java | 2323 ++++++++++------- .../gnuhpc/bigdata/service/UserService.java | 19 +- .../org/gnuhpc/bigdata/utils/KafkaUtils.java | 202 +- src/main/resources/application-tina.yml | 2 +- .../CollectionConvertor.scala | 12 +- 17 files changed, 1926 insertions(+), 1381 deletions(-) create mode 100644 src/main/java/org/gnuhpc/bigdata/constant/ConsumerGroupState.java create mode 100644 src/main/java/org/gnuhpc/bigdata/model/ConsumerGroupMeta.java create mode 100644 src/main/java/org/gnuhpc/bigdata/model/MemberDescription.java create mode 100644 src/main/java/org/gnuhpc/bigdata/model/PartitionAssignmentState.java create mode 100644 src/main/java/org/gnuhpc/bigdata/model/Record.java diff --git a/pom.xml b/pom.xml index db06d4f..602ab0a 100644 --- a/pom.xml +++ b/pom.xml @@ -34,6 +34,7 @@ ${swagger.output.dir}/swagger.json 2.9.1 + 2.9.5 1.19 @@ -220,6 +221,11 @@ jackson-dataformat-yaml ${jackson.version} + + com.fasterxml.jackson.module + jackson-module-scala_2.11 + ${jackson.module.scala.version} + org.apache.commons diff --git a/src/main/java/org/gnuhpc/bigdata/constant/ConsumerGroupState.java b/src/main/java/org/gnuhpc/bigdata/constant/ConsumerGroupState.java new file mode 100644 index 0000000..a1a7123 --- /dev/null +++ b/src/main/java/org/gnuhpc/bigdata/constant/ConsumerGroupState.java @@ -0,0 +1,41 @@ +package org.gnuhpc.bigdata.constant; + +import java.util.HashMap; + +public enum ConsumerGroupState { + UNKNOWN("Unknown"), + PREPARING_REBALANCE("PreparingRebalance"), + COMPLETING_REBALANCE("CompletingRebalance"), + STABLE("Stable"), + DEAD("Dead"), + EMPTY("Empty"); + + private final static HashMap NAME_TO_ENUM; + + static { + NAME_TO_ENUM = new HashMap<>(); + for (ConsumerGroupState state : ConsumerGroupState.values()) { + NAME_TO_ENUM.put(state.name, state); + } + } + + private final String name; + + ConsumerGroupState(String name) { + this.name = name; + } + + + /** + * Parse a string into a consumer group state. + */ + public static ConsumerGroupState parse(String name) { + ConsumerGroupState state = NAME_TO_ENUM.get(name); + return state == null ? UNKNOWN : state; + } + + @Override + public String toString() { + return name; + } +} diff --git a/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java b/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java index ead3c62..cec4a14 100644 --- a/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java +++ b/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java @@ -4,11 +4,16 @@ import io.swagger.annotations.ApiParam; import io.swagger.annotations.ApiResponse; import io.swagger.annotations.ApiResponses; +import java.util.concurrent.TimeoutException; import joptsimple.internal.Strings; +import kafka.admin.AdminClient.ConsumerGroupSummary; +import kafka.admin.AdminClient.ConsumerSummary; import kafka.cluster.Broker; import kafka.common.TopicAndPartition; import lombok.extern.log4j.Log4j; import org.apache.kafka.clients.admin.ConfigEntry; +import org.apache.kafka.clients.admin.NewPartitions; +import org.apache.kafka.common.TopicPartition; import org.apache.kafka.common.errors.ApiException; import org.apache.kafka.common.errors.InvalidTopicException; import org.gnuhpc.bigdata.constant.ConsumerType; @@ -31,224 +36,248 @@ @RequestMapping("/kafka") @RestController public class KafkaController { - @Autowired - private KafkaAdminService kafkaAdminService; - @Autowired - private KafkaProducerService kafkaProducerService; - - @GetMapping("/topics") - @ApiOperation(value = "List topics") - public List listTopics() throws InterruptedException, ExecutionException { - return kafkaAdminService.listTopics(); - } - - @GetMapping("/topicsbrief") - @ApiOperation(value = "List topics Brief") - public List listTopicBrief() throws InterruptedException, ExecutionException { - return kafkaAdminService.listTopicBrief(); - } - - @PostMapping(value = "/topics/create", consumes = "application/json") - @ResponseStatus(HttpStatus.CREATED) - @ApiOperation(value = "Create a topic") - @ApiParam(value = "if reassignStr set, partitions and repli-factor will be ignored.") - public TopicMeta createTopic(@RequestBody TopicDetail topic, @RequestParam(required = false) String reassignStr) - throws InterruptedException, ExecutionException { - return kafkaAdminService.createTopic(topic, reassignStr); - } - - @ApiOperation(value = "Tell if a topic exists") - @GetMapping(value = "/topics/{topic}/exist") - public boolean existTopic(@PathVariable String topic) throws InterruptedException, ExecutionException { - return kafkaAdminService.existTopic(topic); - } - - @PostMapping(value = "/topics/{topic}/write", consumes = "text/plain") - @ResponseStatus(HttpStatus.CREATED) - @ApiOperation(value = "Write a message to the topic, for testing purpose") - public GeneralResponse writeMessage(@PathVariable String topic, @RequestBody String message) { + @Autowired + private KafkaAdminService kafkaAdminService; + + @Autowired + private KafkaProducerService kafkaProducerService; + + @GetMapping(value = "/brokers") + @ApiOperation(value = "List brokers in this cluster") + public List listBrokers() { + return kafkaAdminService.listBrokers(); + } + + @GetMapping(value = "/controller") + @ApiOperation(value = "Get controller id in this cluster") + public int getControllerId() { + return kafkaAdminService.getControllerId(); + } + + @GetMapping("/topics") + @ApiOperation(value = "List topics") + public List listTopics() throws InterruptedException, ExecutionException { + return kafkaAdminService.listTopics(); + } + + @GetMapping("/topicsbrief") + @ApiOperation(value = "List topics Brief") + public List listTopicBrief() throws InterruptedException, ExecutionException { + return kafkaAdminService.listTopicBrief(); + } + + @PostMapping(value = "/topics/create", consumes = "application/json") + @ResponseStatus(HttpStatus.CREATED) + @ApiOperation(value = "Create a topic") + @ApiParam(value = "if reassignStr set, partitions and repli-factor will be ignored.") + public TopicMeta createTopic(@RequestBody TopicDetail topic, + @RequestParam(required = false) String reassignStr) { + return kafkaAdminService.createTopic(topic, reassignStr); + } + + @ApiOperation(value = "Tell if a topic exists") + @GetMapping(value = "/topics/{topic}/exist") + public boolean existTopic(@PathVariable String topic) { + return kafkaAdminService.existTopic(topic); + } + + @PostMapping(value = "/topics/{topic}/write", consumes = "text/plain") + @ResponseStatus(HttpStatus.CREATED) + @ApiOperation(value = "Write a message to the topic, for testing purpose") + public GeneralResponse writeMessage(@PathVariable String topic, @RequestBody String message) { // kafkaProducerService.send(topic, message); - return new GeneralResponse(GeneralResponseState.success, message + " has been sent"); + return GeneralResponse.builder().state(GeneralResponseState.success) + .msg(message + " has been sent").build(); + } + + @GetMapping(value = "/consumer/{topic}/{partition}/{offset}") + @ApiOperation(value = "Get the message from the offset of the partition in the topic" + + ", decoder is not supported yet") + public String getMessage(@PathVariable String topic, + @PathVariable int partition, + @PathVariable long offset, @RequestParam(required = false) String decoder) { + return kafkaAdminService.getRecordByOffset(topic, partition, offset, decoder, "").getValue(); + } + + @GetMapping(value = "/topics/{topic}") + @ApiOperation(value = "Describe a topic by fetching the metadata and config") + public TopicMeta describeTopic(@PathVariable String topic) { + return kafkaAdminService.describeTopic(topic); + } + + @DeleteMapping(value = "/topics") + @ApiOperation(value = "Delete a topic list (you should enable topic deletion") + public Map deleteTopicList(@RequestParam List topicList) { + return kafkaAdminService.deleteTopicList(topicList); + } + + @PutMapping(value = "/topics/{topic}/conf") + @ApiOperation(value = "Update topic configs") + public Collection updateTopicConfig(@PathVariable String topic, + @RequestBody Properties props) + throws InterruptedException, ExecutionException { + return kafkaAdminService.updateTopicConf(topic, props); + } + + @GetMapping(value = "/topics/{topic}/conf") + @ApiOperation(value = "Get topic configs") + public Collection getTopicConfig(@PathVariable String topic) + throws InterruptedException, ExecutionException { + return kafkaAdminService.getTopicConf(topic); + } + + @GetMapping(value = "/topics/{topic}/conf/{key}") + @ApiOperation(value = "Get topic config by key") + public Properties getTopicConfigByKey(@PathVariable String topic, + @PathVariable String key) throws InterruptedException, ExecutionException { + return kafkaAdminService.getTopicConfByKey(topic, key); + } + + @PutMapping(value = "/topics/{topic}/conf/{key}={value}") + @ApiOperation(value = "Update a topic config by key") + public Collection updateTopicConfigByKey(@PathVariable String topic, + @PathVariable String key, + @PathVariable String value) throws InterruptedException, ExecutionException { + return kafkaAdminService.updateTopicConfByKey(topic, key, value); + } + + @PostMapping(value = "/partitions/add") + @ApiOperation(value = "Add partitions to the topics") + public Map addPartition(@RequestBody List addPartitions) { + return kafkaAdminService.addPartitions(addPartitions); + } + + /* + @PostMapping(value = "/partitions/add") + @ApiOperation(value = "Add a partition to the topic") + public TopicMeta addPartition(@RequestBody AddPartition addPartition) { + String topic = addPartition.getTopic(); + isTopicExist(topic); + + if (addPartition.getReplicaAssignment() != null && !addPartition.getReplicaAssignment().equals("") && addPartition.getReplicaAssignment().split(",").length + != addPartition.getNumPartitionsAdded()) { + throw new InvalidTopicException("Topic " + topic + ": num of partitions added not equal to manual reassignment str!"); + } + + if (addPartition.getNumPartitionsAdded() == 0) { + throw new InvalidTopicException("Num of paritions added must be specified and should not be 0"); + } + return kafkaAdminService.addPartition(topic, addPartition); + } + + @PostMapping(value = "/partitions/reassign/generate") + @ApiOperation(value = "Generate plan for the partition reassignment") + public List generateReassignPartitions(@RequestBody ReassignWrapper reassignWrapper) { + return kafkaAdminService.generateReassignPartition(reassignWrapper); + + } + + @PutMapping(value = "/partitions/reassign/execute") + @ApiOperation(value = "Execute the partition reassignment") + public Map executeReassignPartitions( + @RequestBody String reassignStr) { + return kafkaAdminService.executeReassignPartition(reassignStr); + } + + @PutMapping(value = "/partitions/reassign/check") + @ApiOperation(value = "Check the partition reassignment process") + @ApiResponses(value = {@ApiResponse(code = 1, message = "Reassignment Completed"), + @ApiResponse(code = 0, message = "Reassignment In Progress"), + @ApiResponse(code = -1, message = "Reassignment Failed")}) + public Map checkReassignPartitions(@RequestBody String reassignStr) { + return kafkaAdminService.checkReassignStatus(reassignStr); + } + */ + @GetMapping(value = "/consumergroups") + @ApiOperation(value = "List all consumer groups from zk and kafka") + public Map> listAllConsumerGroups( + @RequestParam(required = false) ConsumerType type, + @RequestParam(required = false) String topic) { + if (topic != null) { + return kafkaAdminService.listConsumerGroupsByTopic(topic, type); + } else { + return kafkaAdminService.listAllConsumerGroups(type); } - - @GetMapping(value = "/consumer/{topic}/{partition}/{offset}") - @ApiOperation(value = "Get the message from the offset of the partition in the topic" + - ", decoder is not supported yet") - public String getMessage(@PathVariable String topic, - @PathVariable int partition, - @PathVariable long offset, @RequestParam(required = false) String decoder) { - return kafkaAdminService.getMessage(topic, partition, offset, decoder,""); + } + + @GetMapping(value = "/consumergroups/{consumerGroup}/{type}/topic") + @ApiOperation(value = "Get the topics involved of the specify consumer group") + public Set listTopicByCG(@PathVariable String consumerGroup, + @PathVariable ConsumerType type) { + return kafkaAdminService.listTopicsByCG(consumerGroup, type); + } + + @GetMapping(value = "/consumergroups/{consumerGroup}/meta") + @ApiOperation(value = "Get the meta data of the specify new consumer group, including state, coordinator, assignmentStrategy, members") + public ConsumerGroupMeta getConsumerGroupMeta(@PathVariable String consumerGroup) { + if (kafkaAdminService.isNewConsumerGroup(consumerGroup)) { + return kafkaAdminService.getConsumerGroupMeta(consumerGroup); } - @GetMapping(value = "/topics/{topic}") - @ApiOperation(value = "Describe a topic by fetching the metadata and config") - public TopicMeta describeTopic(@PathVariable String topic) throws InterruptedException, ExecutionException { - return kafkaAdminService.describeTopic(topic); + throw new ApiException("New consumer group:" + consumerGroup + " non-exist."); + } + + @GetMapping(value = "/consumergroups/{consumerGroup}/{type}/topic/{topic}") + @ApiOperation(value = "Describe consumer groups by topic, showing lag and offset") + public List describeCGByTopic( + @ConsumerGroupExistConstraint @PathVariable String consumerGroup, + @PathVariable ConsumerType type, + @PathVariable String topic) throws InterruptedException, ExecutionException { + if (!Strings.isNullOrEmpty(topic)) { + existTopic(topic); + } else { + throw new ApiException("Topic must be set!"); } - - @GetMapping(value = "/brokers") - @ApiOperation(value = "List brokers in this cluster") - public List listBrokers() { - return kafkaAdminService.listBrokers(); + if (type != null && type == ConsumerType.NEW) { + return kafkaAdminService.describeNewCGByTopic(consumerGroup, topic); } - @DeleteMapping(value = "/topics/{topic}") - @ApiOperation(value = "Delete a topic (you should enable topic deletion") - public GeneralResponse deleteTopic(@PathVariable String topic) throws InterruptedException, ExecutionException { - return kafkaAdminService.deleteTopic(topic); + if (type != null && type == ConsumerType.OLD) { + return kafkaAdminService.describeOldCGByTopic(consumerGroup, topic); } - @PutMapping(value = "/topics/{topic}/conf") - @ApiOperation(value = "Update topic configs") - public Collection updateTopicConfig(@PathVariable String topic, - @RequestBody Properties props) - throws InterruptedException, ExecutionException { - return kafkaAdminService.updateTopicConf(topic, props); - } - - @GetMapping(value = "/topics/{topic}/conf") - @ApiOperation(value = "Get topic configs") - public Collection getTopicConfig(@PathVariable String topic) throws InterruptedException, ExecutionException { - return kafkaAdminService.getTopicConf(topic); - } - - @GetMapping(value = "/topics/{topic}/conf/{key}") - @ApiOperation(value = "Get topic config by key") - public Properties getTopicConfigByKey(@PathVariable String topic, - @PathVariable String key) throws InterruptedException, ExecutionException { - return kafkaAdminService.getTopicConfByKey(topic, key); - } - - @PutMapping(value = "/topics/{topic}/conf/{key}={value}") - @ApiOperation(value = "Update a topic config by key") - public Collection updateTopicConfigByKey(@PathVariable String topic, - @PathVariable String key, - @PathVariable String value) throws InterruptedException, ExecutionException { - return kafkaAdminService.updateTopicConfByKey(topic, key, value); - } - - /* - @PostMapping(value = "/partitions/add") - @ApiOperation(value = "Add a partition to the topic") - public TopicMeta addPartition(@RequestBody AddPartition addPartition) { - String topic = addPartition.getTopic(); - isTopicExist(topic); - - if (addPartition.getReplicaAssignment() != null && !addPartition.getReplicaAssignment().equals("") && addPartition.getReplicaAssignment().split(",").length - != addPartition.getNumPartitionsAdded()) { - throw new InvalidTopicException("Topic " + topic + ": num of partitions added not equal to manual reassignment str!"); - } - - if (addPartition.getNumPartitionsAdded() == 0) { - throw new InvalidTopicException("Num of paritions added must be specified and should not be 0"); - } - return kafkaAdminService.addPartition(topic, addPartition); - } - - @PostMapping(value = "/partitions/reassign/generate") - @ApiOperation(value = "Generate plan for the partition reassignment") - public List generateReassignPartitions(@RequestBody ReassignWrapper reassignWrapper) { - return kafkaAdminService.generateReassignPartition(reassignWrapper); - - } - - @PutMapping(value = "/partitions/reassign/execute") - @ApiOperation(value = "Execute the partition reassignment") - public Map executeReassignPartitions( - @RequestBody String reassignStr) { - return kafkaAdminService.executeReassignPartition(reassignStr); - } - - @PutMapping(value = "/partitions/reassign/check") - @ApiOperation(value = "Check the partition reassignment process") - @ApiResponses(value = {@ApiResponse(code = 1, message = "Reassignment Completed"), - @ApiResponse(code = 0, message = "Reassignment In Progress"), - @ApiResponse(code = -1, message = "Reassignment Failed")}) - public Map checkReassignPartitions(@RequestBody String reassignStr) { - return kafkaAdminService.checkReassignStatus(reassignStr); - } - - @GetMapping(value = "/consumergroups") - @ApiOperation(value = "List all consumer groups from zk and kafka") - public Map> listAllConsumerGroups( - @RequestParam(required = false) ConsumerType type, - @RequestParam(required = false) String topic - ) { - if(topic!=null){ - return kafkaAdminService.listConsumerGroupsByTopic(topic,type); - } else{ - return kafkaAdminService.listAllConsumerGroups(type); - } - } - - @GetMapping(value = "/consumergroups/{consumerGroup}/{type}/topic") - @ApiOperation(value = "Get the topics involved of the specify consumer group") - public Set listTopicByCG(@PathVariable String consumerGroup, - @PathVariable ConsumerType type){ - return kafkaAdminService.listTopicsByCG(consumerGroup,type); - - } - - @GetMapping(value = "/consumergroups/{consumerGroup}/{type}/topic/{topic}") - @ApiOperation(value = "Describe consumer groups by topic, showing lag and offset") - public List describeCGByTopic(@ConsumerGroupExistConstraint @PathVariable String consumerGroup, - @PathVariable ConsumerType type, - @PathVariable String topic) throws InterruptedException, ExecutionException { - if (!Strings.isNullOrEmpty(topic)) { - existTopic(topic); - } else { - throw new ApiException("Topic must be set!"); - } - if (type != null && type == ConsumerType.NEW) - return kafkaAdminService.describeNewCGByTopic(consumerGroup, topic); - - if (type != null && type == ConsumerType.OLD) - return kafkaAdminService.describeOldCGByTopic(consumerGroup, topic); - - throw new ApiException("Unknown type specified!"); - } - - @GetMapping(value = "/consumergroups/{consumerGroup}/{type}") - @ApiOperation(value = "Describe consumer groups, showing lag and offset, may be slow if multi topic are listened") - public Map> describeCG(@ConsumerGroupExistConstraint @PathVariable String consumerGroup, - @PathVariable ConsumerType type){ - return kafkaAdminService.describeConsumerGroup(consumerGroup,type); - } - - @PutMapping(value = "/consumergroup/{consumergroup}/{type}/topic/{topic}/{partition}/{offset}") - @ApiOperation(value = "Reset consumer group offset, earliest/latest can be used") - public GeneralResponse resetOffset(@PathVariable String topic, - @PathVariable int partition, - @PathVariable String consumergroup, - @PathVariable String offset, - @PathVariable ConsumerType type) { - return kafkaAdminService.resetOffset(topic, partition, consumergroup, type, offset); - } - - @GetMapping(value = "/consumergroup/{consumergroup}/{type}/topic/{topic}/lastcommittime") - public Map> getLastCommitTimestamp( - @PathVariable String consumergroup, - @PathVariable String topic, - @PathVariable ConsumerType type) { - return kafkaAdminService.getLastCommitTime(consumergroup, topic, type); - } - - @DeleteMapping(value = "/consumergroup/{consumergroup}") - @ApiOperation(value = "Delete old Consumer Group") - public GeneralResponse deleteOldConsumerGroup(@PathVariable String consumergroup) { - return kafkaAdminService.deleteConsumerGroup(consumergroup); - } - */ - private void isTopicExist(String topic) throws InvalidTopicException, InterruptedException, ExecutionException { - if (!kafkaAdminService.existTopic(topic)) { - throw new InvalidTopicException("Topic " + topic + " non-exist!"); - } - } - - @GetMapping(value = "/health") - @ApiOperation(value = "Check the cluster health.") - public HealthCheckResult healthCheck() { - return kafkaAdminService.healthCheck(); - } + throw new ApiException("Unknown type specified!"); + } + + @GetMapping(value = "/consumergroups/{consumerGroup}/{type}") + @ApiOperation(value = "Describe consumer groups, showing lag and offset, may be slow if multi topic are listened") + public Map> describeCG( + @ConsumerGroupExistConstraint @PathVariable String consumerGroup, + @PathVariable ConsumerType type) { + return kafkaAdminService.describeConsumerGroup(consumerGroup, type); + } + + @PutMapping(value = "/consumergroup/{consumergroup}/{type}/topic/{topic}/{partition}/{offset}") + @ApiOperation(value = "Reset consumer group offset, earliest/latest can be used. Support reset by time for new consumer group, pass a parameter that satisfies yyyy-MM-dd HH:mm:ss to offset.") + public GeneralResponse resetOffset(@PathVariable String topic, + @PathVariable int partition, + @PathVariable String consumergroup, + @PathVariable @ApiParam( + value = "[earliest/latest/{long}/yyyy-MM-dd HH:mm:ss] can be supported. The date type is only valid for new consumer group.") String offset, + @PathVariable ConsumerType type) throws InterruptedException, ExecutionException { + return kafkaAdminService.resetOffset(topic, partition, consumergroup, type, offset); + } + + + @GetMapping(value = "/consumergroup/{consumergroup}/{type}/topic/{topic}/lastcommittime") + public Map> getLastCommitTimestamp( + @PathVariable String consumergroup, + @PathVariable String topic, + @PathVariable ConsumerType type) { + return kafkaAdminService.getLastCommitTime(consumergroup, topic, type); + } + + @DeleteMapping(value = "/consumergroup/{consumergroup}/{type}") + @ApiOperation(value = "Delete Consumer Group") + public GeneralResponse deleteOldConsumerGroup(@PathVariable String consumergroup, + @PathVariable ConsumerType type) { + return kafkaAdminService.deleteConsumerGroup(consumergroup, type); + } + + @GetMapping(value = "/health") + @ApiOperation(value = "Check the cluster health.") + public HealthCheckResult healthCheck() { + return kafkaAdminService.healthCheck(); + } } diff --git a/src/main/java/org/gnuhpc/bigdata/controller/UserController.java b/src/main/java/org/gnuhpc/bigdata/controller/UserController.java index 1fd2045..fba7f50 100644 --- a/src/main/java/org/gnuhpc/bigdata/controller/UserController.java +++ b/src/main/java/org/gnuhpc/bigdata/controller/UserController.java @@ -31,7 +31,7 @@ public List listUser() { @ApiOperation(value = "Add user.") public GeneralResponse addUser(@RequestBody@Valid User user, BindingResult results) { if (results.hasErrors()) { - return new GeneralResponse(GeneralResponseState.failure, results.getFieldError().getDefaultMessage()); + return GeneralResponse.builder().state(GeneralResponseState.failure).msg(results.getFieldError().getDefaultMessage()).build(); } log.info("Receive add user request: username:" + user.getUsername()); return userService.addUser(user); @@ -41,7 +41,7 @@ public GeneralResponse addUser(@RequestBody@Valid User user, BindingResult resul @ApiOperation(value = "Modify user information.") public GeneralResponse modifyUser(@RequestBody@Valid User user, BindingResult results) { if (results.hasErrors()) { - return new GeneralResponse(GeneralResponseState.failure, results.getFieldError().getDefaultMessage()); + return GeneralResponse.builder().state(GeneralResponseState.failure).msg(results.getFieldError().getDefaultMessage()).build(); } log.info("Receive modify user request: username:" + user.getUsername()); return userService.modifyUser(user); diff --git a/src/main/java/org/gnuhpc/bigdata/model/ConsumerGroupDesc.java b/src/main/java/org/gnuhpc/bigdata/model/ConsumerGroupDesc.java index 31327da..a5b62c2 100644 --- a/src/main/java/org/gnuhpc/bigdata/model/ConsumerGroupDesc.java +++ b/src/main/java/org/gnuhpc/bigdata/model/ConsumerGroupDesc.java @@ -1,13 +1,15 @@ package org.gnuhpc.bigdata.model; -import lombok.*; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; import lombok.extern.log4j.Log4j; -import org.apache.kafka.common.TopicPartition; -import org.gnuhpc.bigdata.constant.ConsumerState; +import org.apache.kafka.common.Node; +import org.gnuhpc.bigdata.constant.ConsumerGroupState; import org.gnuhpc.bigdata.constant.ConsumerType; -import org.gnuhpc.bigdata.utils.KafkaUtils; - -import java.util.Map; /** * Created by gnuhpc on 2017/7/27. @@ -17,8 +19,12 @@ @Log4j @ToString @EqualsAndHashCode +@Builder(builderClassName = "ConsumerGroupDescBuilder") public class ConsumerGroupDesc implements Comparable { private String groupName; + private ConsumerGroupState state; + private String assignmentStrategy; + private Node coordinator; private String topic; private int partitionId; private long currentOffset; @@ -27,26 +33,8 @@ public class ConsumerGroupDesc implements Comparable { private long lag; private String consumerId; private String host="-"; - private ConsumerState state; private ConsumerType type; - private ConsumerGroupDesc(Builder builder) { - setGroupName(builder.groupName); - setTopic(builder.topic); - setPartitionId(builder.partitionId); - setCurrentOffset(builder.currentOffset); - setLogEndOffset(builder.logEndOffset); - lag = builder.logEndOffset-builder.currentOffset; - setConsumerId(builder.consumerId); - setHost(builder.host); - setState(builder.state); - setType(builder.type); - } - - public static Builder newBuilder() { - return new Builder(); - } - @Override public int compareTo(ConsumerGroupDesc o) { if (this.topic.equals(o.topic)) { @@ -61,74 +49,4 @@ public int compareTo(ConsumerGroupDesc o) { return this.topic.compareTo(this.topic); } } - - public static final class Builder { - private String groupName; - private String topic; - private int partitionId; - private long currentOffset; - private long logEndOffset; - private long lag; - private String consumerId; - private String host; - private ConsumerState state; - private ConsumerType type; - - private Builder() { - } - - public Builder setGroupName(String val) { - groupName = val; - return this; - } - - public Builder setTopic(String val) { - topic = val; - return this; - } - - public Builder setPartitionId(int val) { - partitionId = val; - return this; - } - - public Builder setCurrentOffset(long val) { - currentOffset = val; - return this; - } - - public Builder setLogEndOffset(long val) { - logEndOffset = val; - return this; - } - - public Builder setLag(long val) { - lag = val; - return this; - } - - public Builder setConsumerId(String val) { - consumerId = val; - return this; - } - - public Builder setHost(String val) { - host = val; - return this; - } - - public Builder setState(ConsumerState val) { - state = val; - return this; - } - - public Builder setType(ConsumerType val) { - type = val; - return this; - } - - public ConsumerGroupDesc build() { - return new ConsumerGroupDesc(this); - } - } } diff --git a/src/main/java/org/gnuhpc/bigdata/model/ConsumerGroupDescFactory.java b/src/main/java/org/gnuhpc/bigdata/model/ConsumerGroupDescFactory.java index bcdd604..9f97bb3 100644 --- a/src/main/java/org/gnuhpc/bigdata/model/ConsumerGroupDescFactory.java +++ b/src/main/java/org/gnuhpc/bigdata/model/ConsumerGroupDescFactory.java @@ -6,6 +6,7 @@ import lombok.AllArgsConstructor; import org.apache.kafka.clients.consumer.KafkaConsumer; import org.apache.kafka.common.TopicPartition; +import org.gnuhpc.bigdata.constant.ConsumerGroupState; import org.gnuhpc.bigdata.constant.ConsumerState; import org.gnuhpc.bigdata.constant.ConsumerType; import org.gnuhpc.bigdata.utils.KafkaUtils; @@ -23,26 +24,26 @@ public ConsumerGroupDesc makeOldConsumerGroupDesc( Map fetchOffSetFromZKResultList, String topic, String consumerGroup, TopicMeta topicMeta) { - ConsumerGroupDesc.Builder cgdBuilder = ConsumerGroupDesc.newBuilder() - .setGroupName(consumerGroup) - .setTopic(topic) - .setPartitionId(op.getKey()) - .setCurrentOffset(fetchOffSetFromZKResultList.get(op.getKey())) - .setLogEndOffset( + ConsumerGroupDesc.ConsumerGroupDescBuilder cgdBuilder = ConsumerGroupDesc.builder() + .groupName(consumerGroup) + .topic(topic) + .partitionId(op.getKey()) + .currentOffset(fetchOffSetFromZKResultList.get(op.getKey())) + .logEndOffset( topicMeta.getTopicPartitionInfos().stream() .filter(tpi -> tpi.getTopicPartitionInfo().partition() == op.getKey()).findFirst().get().getEndOffset()); if (op.getValue().equals("none")) { - cgdBuilder.setConsumerId("-"); - cgdBuilder.setHost("-"); - cgdBuilder.setState(ConsumerState.PENDING); + cgdBuilder.consumerId("-"); + cgdBuilder.host("-"); + cgdBuilder.state(ConsumerGroupState.EMPTY); } else { - cgdBuilder.setConsumerId(op.getValue()); - cgdBuilder.setHost(op.getValue().replace(consumerGroup + "_", "")); - cgdBuilder.setState(ConsumerState.RUNNING); + cgdBuilder.consumerId(op.getValue()); + cgdBuilder.host(op.getValue().replace(consumerGroup + "_", "")); + cgdBuilder.state(ConsumerGroupState.STABLE); } - cgdBuilder.setType(ConsumerType.OLD); + cgdBuilder.type(ConsumerType.OLD); return cgdBuilder.build(); } @@ -52,14 +53,14 @@ public ConsumerGroupDesc makeNewRunningConsumerGroupDesc( Map partitionEndOffsetMap, AdminClient.ConsumerSummary cs) { KafkaConsumer consumer = kafkaUtils.createNewConsumer(consumerGroup); - ConsumerGroupDesc.Builder cgdBuilder = ConsumerGroupDesc.newBuilder() - .setGroupName(consumerGroup) - .setTopic(tp.topic()) - .setPartitionId(tp.partition()) - .setConsumerId(cs.clientId()) - .setHost(cs.host()) - .setState(ConsumerState.RUNNING) - .setType(ConsumerType.NEW); + ConsumerGroupDesc.ConsumerGroupDescBuilder cgdBuilder = ConsumerGroupDesc.builder() + .groupName(consumerGroup) + .topic(tp.topic()) + .partitionId(tp.partition()) + .consumerId(cs.clientId()) + .host(cs.host()) + .state(ConsumerGroupState.STABLE) + .type(ConsumerType.NEW); long currentOffset = -1L; @@ -67,13 +68,13 @@ public ConsumerGroupDesc makeNewRunningConsumerGroupDesc( if (offset != null) { currentOffset = offset.offset(); } - cgdBuilder.setCurrentOffset(currentOffset); + cgdBuilder.currentOffset(currentOffset); Long endOffset = partitionEndOffsetMap.get(tp.partition()); if (endOffset == null) { //if endOffset is null ,the partition of this topic has no leader replication - cgdBuilder.setLogEndOffset(-1l); + cgdBuilder.logEndOffset(-1l); } else { - cgdBuilder.setLogEndOffset(endOffset); + cgdBuilder.logEndOffset(endOffset); } consumer.close(); @@ -88,22 +89,22 @@ public ConsumerGroupDesc makeNewPendingConsumerGroupDesc( Long partitionCurrentOffset = (topicStorage.getValue() == null) ? -1l: topicStorage.getValue().offset(); int partitionId = topicStorage.getKey().topicPartition().partition(); - ConsumerGroupDesc.Builder cgdBuilder = ConsumerGroupDesc.newBuilder() - .setGroupName(consumerGroup) - .setTopic(topic) - .setConsumerId("-") - .setPartitionId(partitionId) - .setCurrentOffset(partitionCurrentOffset) - .setHost("-") - .setState(ConsumerState.PENDING) - .setType(ConsumerType.NEW); + ConsumerGroupDesc.ConsumerGroupDescBuilder cgdBuilder = ConsumerGroupDesc.builder() + .groupName(consumerGroup) + .topic(topic) + .consumerId("-") + .partitionId(partitionId) + .currentOffset(partitionCurrentOffset) + .host("-") + .state(ConsumerGroupState.EMPTY) + .type(ConsumerType.NEW); Long endOffset = partitionEndOffsetMap.get(partitionId); if (endOffset == null) { //if endOffset is null ,the partition of this topic has no leader replication - cgdBuilder.setLogEndOffset(-1l); + cgdBuilder.logEndOffset(-1l); } else { - cgdBuilder.setLogEndOffset(endOffset); + cgdBuilder.logEndOffset(endOffset); } return cgdBuilder.build(); diff --git a/src/main/java/org/gnuhpc/bigdata/model/ConsumerGroupMeta.java b/src/main/java/org/gnuhpc/bigdata/model/ConsumerGroupMeta.java new file mode 100644 index 0000000..e117aeb --- /dev/null +++ b/src/main/java/org/gnuhpc/bigdata/model/ConsumerGroupMeta.java @@ -0,0 +1,18 @@ +package org.gnuhpc.bigdata.model; + +import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import org.apache.kafka.common.Node; +import org.gnuhpc.bigdata.constant.ConsumerGroupState; + +@Data +@Builder +public class ConsumerGroupMeta { + private String groupId; + private ConsumerGroupState state; + private String assignmentStrategy; + private Node coordinator; + private List members; +} diff --git a/src/main/java/org/gnuhpc/bigdata/model/GeneralResponse.java b/src/main/java/org/gnuhpc/bigdata/model/GeneralResponse.java index 8b98b19..e86606c 100644 --- a/src/main/java/org/gnuhpc/bigdata/model/GeneralResponse.java +++ b/src/main/java/org/gnuhpc/bigdata/model/GeneralResponse.java @@ -1,6 +1,7 @@ package org.gnuhpc.bigdata.model; import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Data; import lombok.extern.log4j.Log4j; import org.gnuhpc.bigdata.constant.GeneralResponseState; @@ -8,7 +9,9 @@ @Data @Log4j @AllArgsConstructor +@Builder public class GeneralResponse { private GeneralResponseState state; private String msg; + private Object data; } diff --git a/src/main/java/org/gnuhpc/bigdata/model/MemberDescription.java b/src/main/java/org/gnuhpc/bigdata/model/MemberDescription.java new file mode 100644 index 0000000..ddb5331 --- /dev/null +++ b/src/main/java/org/gnuhpc/bigdata/model/MemberDescription.java @@ -0,0 +1,17 @@ +package org.gnuhpc.bigdata.model; + +import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; +import org.apache.kafka.common.TopicPartition; + +@AllArgsConstructor +@Getter +@Setter +public class MemberDescription { + private String memberId; + private String clientId; + private String host; + private List assignment; +} diff --git a/src/main/java/org/gnuhpc/bigdata/model/PartitionAssignmentState.java b/src/main/java/org/gnuhpc/bigdata/model/PartitionAssignmentState.java new file mode 100644 index 0000000..16cd5b7 --- /dev/null +++ b/src/main/java/org/gnuhpc/bigdata/model/PartitionAssignmentState.java @@ -0,0 +1,22 @@ +package org.gnuhpc.bigdata.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import org.apache.kafka.common.Node; + +@Data +@Builder +@AllArgsConstructor +public class PartitionAssignmentState { + private String group; + private Node coordinator; + private String topic; + private int partition; + private long offset; + private long lag; + private String consumerId; + private String host; + private String clientId; + private long logEndOffset; +} diff --git a/src/main/java/org/gnuhpc/bigdata/model/Record.java b/src/main/java/org/gnuhpc/bigdata/model/Record.java new file mode 100644 index 0000000..cd5f3af --- /dev/null +++ b/src/main/java/org/gnuhpc/bigdata/model/Record.java @@ -0,0 +1,11 @@ +package org.gnuhpc.bigdata.model; + +import lombok.Data; + +@Data +public class Record { + long offset; + String key; + String value; + long timestamp; +} diff --git a/src/main/java/org/gnuhpc/bigdata/model/TopicMeta.java b/src/main/java/org/gnuhpc/bigdata/model/TopicMeta.java index bedb93a..d744550 100644 --- a/src/main/java/org/gnuhpc/bigdata/model/TopicMeta.java +++ b/src/main/java/org/gnuhpc/bigdata/model/TopicMeta.java @@ -15,6 +15,7 @@ @Setter public class TopicMeta { private String topicName; + private boolean internal; private int partitionCount; private int replicationFactor; private List topicPartitionInfos; diff --git a/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java b/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java index a66e5a6..5be08b2 100644 --- a/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java +++ b/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java @@ -1,39 +1,86 @@ package org.gnuhpc.bigdata.service; +import static java.lang.String.format; +import static java.util.stream.Collectors.toList; +import static java.util.stream.Collectors.toMap; +import static java.util.stream.Collectors.toSet; + import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.module.scala.DefaultScalaModule; import com.google.common.base.Strings; import com.google.common.collect.ImmutableMap; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonDeserializer; import io.swagger.models.auth.In; +import java.io.IOException; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.function.Function; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import javax.annotation.PostConstruct; import kafka.admin.AdminClient; +import kafka.admin.AdminClient.ConsumerGroupSummary; +import kafka.admin.AdminClient.ConsumerSummary; import kafka.admin.AdminUtils; +import kafka.admin.ConsumerGroupCommand; import kafka.admin.RackAwareMode; import kafka.admin.TopicCommand; import kafka.api.PartitionOffsetRequestInfo; import kafka.cluster.Broker; -import kafka.common.OffsetAndMetadata; import kafka.common.TopicAndPartition; import kafka.coordinator.group.GroupOverview; import kafka.coordinator.group.GroupTopicPartition; import kafka.javaapi.OffsetRequest; import kafka.javaapi.OffsetResponse; import kafka.javaapi.consumer.SimpleConsumer; -import kafka.server.ConfigType; -import kafka.utils.ZKGroupTopicDirs; import kafka.utils.ZkUtils; import kafka.zk.KafkaZkClient; import lombok.extern.log4j.Log4j; import org.apache.curator.framework.CuratorFramework; -import org.apache.kafka.clients.admin.*; +import org.apache.kafka.clients.admin.AdminClientConfig; +import org.apache.kafka.clients.admin.Config; +import org.apache.kafka.clients.admin.ConfigEntry; +import org.apache.kafka.clients.admin.CreatePartitionsResult; +import org.apache.kafka.clients.admin.DeleteTopicsResult; +import org.apache.kafka.clients.admin.DescribeConfigsResult; +import org.apache.kafka.clients.admin.DescribeTopicsResult; +import org.apache.kafka.clients.admin.KafkaAdminClient; +import org.apache.kafka.clients.admin.ListTopicsOptions; +import org.apache.kafka.clients.admin.ListTopicsResult; +import org.apache.kafka.clients.admin.NewPartitions; +import org.apache.kafka.clients.admin.TopicDescription; import org.apache.kafka.clients.consumer.ConsumerRecord; import org.apache.kafka.clients.consumer.ConsumerRecords; import org.apache.kafka.clients.consumer.KafkaConsumer; +import org.apache.kafka.clients.consumer.OffsetAndMetadata; +import org.apache.kafka.clients.consumer.OffsetAndTimestamp; import org.apache.kafka.clients.producer.KafkaProducer; import org.apache.kafka.clients.producer.ProducerRecord; import org.apache.kafka.clients.producer.RecordMetadata; +import org.apache.kafka.common.KafkaFuture; import org.apache.kafka.common.Node; import org.apache.kafka.common.PartitionInfo; import org.apache.kafka.common.TopicPartition; @@ -42,14 +89,26 @@ import org.apache.kafka.common.errors.ApiException; import org.apache.kafka.common.errors.InvalidTopicException; import org.apache.kafka.common.internals.Topic; +import org.apache.kafka.common.protocol.Errors; import org.gnuhpc.bigdata.CollectionConvertor; import org.gnuhpc.bigdata.componet.OffsetStorage; import org.gnuhpc.bigdata.config.KafkaConfig; +import org.gnuhpc.bigdata.constant.ConsumerGroupState; import org.gnuhpc.bigdata.constant.ConsumerType; import org.gnuhpc.bigdata.constant.GeneralResponseState; -import org.gnuhpc.bigdata.model.*; -import org.gnuhpc.bigdata.task.FetchOffSetFromZKResult; -import org.gnuhpc.bigdata.task.FetchOffsetFromZKTask; +import org.gnuhpc.bigdata.model.AddPartition; +import org.gnuhpc.bigdata.model.BrokerInfo; +import org.gnuhpc.bigdata.model.ConsumerGroupDesc; +import org.gnuhpc.bigdata.model.ConsumerGroupMeta; +import org.gnuhpc.bigdata.model.CustomTopicPartitionInfo; +import org.gnuhpc.bigdata.model.GeneralResponse; +import org.gnuhpc.bigdata.model.HealthCheckResult; +import org.gnuhpc.bigdata.model.MemberDescription; +import org.gnuhpc.bigdata.model.PartitionAssignmentState; +import org.gnuhpc.bigdata.model.Record; +import org.gnuhpc.bigdata.model.TopicBrief; +import org.gnuhpc.bigdata.model.TopicDetail; +import org.gnuhpc.bigdata.model.TopicMeta; import org.gnuhpc.bigdata.utils.KafkaUtils; import org.gnuhpc.bigdata.utils.ZookeeperUtils; import org.gnuhpc.bigdata.validator.ConsumerGroupExistConstraint; @@ -58,19 +117,11 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; -import scala.Option; +import org.springframework.web.method.support.HandlerMethodReturnValueHandler; import scala.collection.JavaConverters; import scala.collection.Seq; - -import javax.annotation.PostConstruct; -import java.util.*; -import java.util.concurrent.*; -import java.util.function.Function; -import java.util.stream.Collectors; - -import static java.lang.String.format; -import static java.util.stream.Collectors.toList; -import static java.util.stream.Collectors.toSet; +import scala.collection.generic.BitOperations.Int; +import scala.reflect.internal.Trees.New; /** * Created by gnuhpc on 2017/7/17. @@ -81,1059 +132,1459 @@ @Validated public class KafkaAdminService { - private static final int channelSocketTimeoutMs = 600; - private static final int channelRetryBackoffMs = 600; - private static final String CONSUMERPATHPREFIX = "/consumers/"; - private static final String OFFSETSPATHPREFIX = "/offsets/"; - @Autowired - private ZookeeperUtils zookeeperUtils; + private static final int channelSocketTimeoutMs = 600; + private static final int channelRetryBackoffMs = 600; + private static final long kafkaAdminClientGetTimeoutMs = 30000; + private static final String CONSUMERPATHPREFIX = "/consumers/"; + private static final String OFFSETSPATHPREFIX = "/offsets/"; + @Autowired + private ZookeeperUtils zookeeperUtils; - @Autowired - private KafkaUtils kafkaUtils; + @Autowired + private KafkaUtils kafkaUtils; - @Autowired - private KafkaConfig kafkaConfig; + @Autowired + private KafkaConfig kafkaConfig; - @Autowired - private OffsetStorage storage; + @Autowired + private OffsetStorage storage; - //For AdminUtils use - private ZkUtils zkUtils; + //For AdminUtils use + private ZkUtils zkUtils; - //For zookeeper connection - private CuratorFramework zkClient; + //For zookeeper connection + private CuratorFramework zkClient; - private KafkaZkClient kafkaZkClient; + private KafkaZkClient kafkaZkClient; - private org.apache.kafka.clients.admin.AdminClient kafkaAdminClient; - //For Json serialized - private Gson gson; + // private org.apache.kafka.clients.admin.AdminClient kafkaAdminClient; + //For Json serialized + private Gson gson; - private scala.Option NONE = scala.Option.apply(null); - @PostConstruct - private void init() { - this.zkUtils = zookeeperUtils.getZkUtils(); - this.zkClient = zookeeperUtils.getCuratorClient(); - this.kafkaZkClient = zookeeperUtils.getKafkaZkClient(); - Properties adminClientProp = new Properties(); - adminClientProp.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaConfig.getBrokers()); - //TODO eliminate the init connection - kafkaAdminClient = KafkaAdminClient.create(adminClientProp); - GsonBuilder builder = new GsonBuilder(); - builder.registerTypeAdapter(DateTime.class, (JsonDeserializer) (jsonElement, type, jsonDeserializationContext) -> new DateTime(jsonElement.getAsJsonPrimitive().getAsLong())); + private scala.Option NONE = scala.Option.apply(null); - this.gson = builder.create(); + //TODO use autocloseable interface + /* + public KafkaAdminService(final KafkaZkClient kafkaZkClient) { + this.kafkaZkClient = kafkaZkClient; } - public TopicMeta createTopic(TopicDetail topic, String reassignStr) throws InterruptedException, ExecutionException { - if (Topic.hasCollisionChars(topic.getName())) { - throw new InvalidTopicException("Invalid topic name, it contains '.' or '_'."); - } - - if (Strings.isNullOrEmpty(reassignStr) && topic.getPartitions() <= 0) { - throw new InvalidTopicException("Number of partitions must be larger than 0"); - } - Topic.validate(topic.getName()); - - - if (Strings.isNullOrEmpty(reassignStr)) { - AdminUtils.createTopic(zkUtils, - topic.getName(), topic.getPartitions(), topic.getFactor(), - topic.getProp(), RackAwareMode.Enforced$.MODULE$); - } else { - List argsList = new ArrayList<>(); - argsList.add("--topic"); - argsList.add(topic.getName()); - - if (topic.getProp().stringPropertyNames().size() != 0) { - argsList.add("--config"); - - for (String key : topic.getProp().stringPropertyNames()) { - argsList.add(key + "=" + topic.getProp().get(key)); - } - } - argsList.add("--replica-assignment"); - argsList.add(reassignStr); - - TopicCommand.createTopic(kafkaZkClient, new TopicCommand.TopicCommandOptions(argsList.stream().toArray(String[]::new))); - } - - - try { - //Wait for a second for metadata propergating - Thread.sleep(3000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - return describeTopic(topic.getName()); + public KafkaAdminService(final org.apache.kafka.clients.admin.AdminClient kafkaAdminClient) { + this.kafkaAdminClient = kafkaAdminClient; } - public List listTopics() throws InterruptedException, ExecutionException { - List topicNamesList = new ArrayList(); - topicNamesList.addAll(getAllTopics()); - - return topicNamesList; + public KafkaAdminService(final KafkaZkClient kafkaZkClient, final org.apache.kafka.clients.admin.AdminClient kafkaAdminClient) { + this.kafkaZkClient = kafkaZkClient; + this.kafkaAdminClient = kafkaAdminClient; + } */ + + @PostConstruct + private void init() { + this.zkUtils = zookeeperUtils.getZkUtils(); + this.zkClient = zookeeperUtils.getCuratorClient(); + this.kafkaZkClient = zookeeperUtils.getKafkaZkClient(); +// Properties adminClientProp = new Properties(); +// adminClientProp.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaConfig.getBrokers()); +// //TODO eliminate the init connection +// kafkaAdminClient = KafkaAdminClient.create(adminClientProp); + GsonBuilder builder = new GsonBuilder(); + builder.registerTypeAdapter(DateTime.class, + (JsonDeserializer) (jsonElement, type, jsonDeserializationContext) -> new DateTime( + jsonElement.getAsJsonPrimitive().getAsLong())); + + this.gson = builder.create(); + } + + private org.apache.kafka.clients.admin.AdminClient createKafkaAdminClient() { + Properties adminClientProp = new Properties(); + adminClientProp.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaConfig.getBrokers()); + return KafkaAdminClient.create(adminClientProp); + } + + //TODO use autocloseable interface + /* + @Override + public void close() { + System.out.println("++++++++Kafka Admin Service close ++++++++"); + kafkaAdminClient.close(); + kafkaZkClient.close(); + zkClient.close(); } - - public Set getAllTopics() throws InterruptedException, ExecutionException { - ListTopicsOptions options = new ListTopicsOptions(); - // includes internal topics such as __consumer_offsets - options.listInternal(true); - - ListTopicsResult topics = kafkaAdminClient.listTopics(options); - Set topicNames = topics.names().get(); - log.info("Current topics in this cluster: " + topicNames); - - return topicNames; + */ + public TopicMeta createTopic(TopicDetail topic, String reassignStr) { + if (Topic.hasCollisionChars(topic.getName())) { + throw new InvalidTopicException("Invalid topic name, it contains '.' or '_'."); } - public List listTopicBrief() throws InterruptedException, ExecutionException { - DescribeTopicsResult describeTopicsResult = kafkaAdminClient.describeTopics(listTopics()); - Map topicMap = describeTopicsResult.all().get(); - List result = topicMap.entrySet().parallelStream().map(e -> { - String topic = e.getKey(); - TopicDescription topicDescription = e.getValue(); - List topicPartitionInfoList = topicDescription.partitions(); - int replicateCount = 0; - int isrCount = 0; - for (org.apache.kafka.common.TopicPartitionInfo topicPartitionInfo: topicPartitionInfoList) { - replicateCount += topicPartitionInfo.replicas().size(); - isrCount += topicPartitionInfo.isr().size(); - } - if (replicateCount == 0) { - return new TopicBrief(topic, topicDescription.partitions().size(), 0); - } else { - return new TopicBrief(topic, topicDescription.partitions().size(), ((double) isrCount / replicateCount)); - } - }).collect(toList()); - - return result; + if (Strings.isNullOrEmpty(reassignStr) && topic.getPartitions() <= 0) { + throw new InvalidTopicException("Number of partitions must be larger than 0"); } + Topic.validate(topic.getName()); + + if (Strings.isNullOrEmpty(reassignStr)) { + AdminUtils.createTopic(zkUtils, + topic.getName(), topic.getPartitions(), topic.getFactor(), + topic.getProp(), RackAwareMode.Enforced$.MODULE$); + } else { + List argsList = new ArrayList<>(); + argsList.add("--topic"); + argsList.add(topic.getName()); + + if (topic.getProp().stringPropertyNames().size() != 0) { + argsList.add("--config"); + + for (String key : topic.getProp().stringPropertyNames()) { + argsList.add(key + "=" + topic.getProp().get(key)); + } + } + argsList.add("--replica-assignment"); + argsList.add(reassignStr); - public boolean existTopic(String topicName) { - return kafkaZkClient.topicExists(topicName); + TopicCommand.createTopic(kafkaZkClient, + new TopicCommand.TopicCommandOptions(argsList.stream().toArray(String[]::new))); } - public List listBrokers() { - List brokerList = CollectionConvertor.seqConvertJavaList(kafkaZkClient.getAllBrokersInCluster()); - - return brokerList.parallelStream().collect(Collectors.toMap(Broker::id, Broker::rack)).entrySet().parallelStream() - .map(entry -> { - String brokerInfoStr = null; - try { - //TODO replace zkClient with kafkaZKClient - brokerInfoStr = new String( - zkClient.getData().forPath(ZkUtils.BrokerIdsPath() + "/" + entry.getKey()) - ); - } catch (Exception e) { - e.printStackTrace(); - } - BrokerInfo brokerInfo = gson.fromJson(brokerInfoStr, BrokerInfo.class); - if (entry.getValue().isEmpty()) - brokerInfo.setRack(""); - else { - brokerInfo.setRack(entry.getValue().get()); - } - brokerInfo.setId(entry.getKey()); - return brokerInfo; - }).collect(toList()); + try { + //Wait for a second for metadata propergating + Thread.sleep(3000); + } catch (InterruptedException e) { + e.printStackTrace(); } - - public TopicMeta describeTopic(@TopicExistConstraint String topicName) throws InterruptedException, ExecutionException { - DescribeTopicsResult describeTopicsResult = kafkaAdminClient.describeTopics(Collections.singletonList(topicName)); - Map topicMap = describeTopicsResult.all().get(); - TopicMeta topicMeta = new TopicMeta(topicName); - if (topicMap.containsKey(topicName)) { - TopicDescription topicDescription = topicMap.get(topicName); - List tmList = topicDescription.partitions(); - topicMeta.setPartitionCount(topicDescription.partitions().size()); - topicMeta.setReplicationFactor(tmList.get(0).replicas().size()); - topicMeta.setTopicPartitionInfos(tmList.parallelStream().map( - tm -> { - CustomTopicPartitionInfo customTopicPartitionInfo = new CustomTopicPartitionInfo(); - customTopicPartitionInfo.setTopicPartitionInfo(tm); - customTopicPartitionInfo.setIn_sync(); - customTopicPartitionInfo.setStartOffset(getBeginningOffset(tm.leader(), topicName, tm.partition())); - customTopicPartitionInfo.setEndOffset(getEndOffset(tm.leader(), topicName, tm.partition())); - customTopicPartitionInfo.setMessageAvailable(); - return customTopicPartitionInfo; - - }).collect(toList()) + return describeTopic(topic.getName()); + } + + public List listTopics() throws InterruptedException, ExecutionException { + List topicNamesList = new ArrayList(); + topicNamesList.addAll(getAllTopics()); + + return topicNamesList; + } + + public Set getAllTopics() throws InterruptedException, ExecutionException { + org.apache.kafka.clients.admin.AdminClient kafkaAdminClient = createKafkaAdminClient(); + ListTopicsOptions options = new ListTopicsOptions(); + // includes internal topics such as __consumer_offsets + options.listInternal(true); + + ListTopicsResult topics = kafkaAdminClient.listTopics(options); + Set topicNames = topics.names().get(); + log.info("Current topics in this cluster: " + topicNames); + + kafkaAdminClient.close(); + return topicNames; + } + + public List listTopicBrief() throws InterruptedException, ExecutionException { + org.apache.kafka.clients.admin.AdminClient kafkaAdminClient = createKafkaAdminClient(); + + DescribeTopicsResult describeTopicsResult = kafkaAdminClient.describeTopics(listTopics()); + Map topicMap = describeTopicsResult.all().get(); + List result = topicMap.entrySet().parallelStream().map(e -> { + String topic = e.getKey(); + TopicDescription topicDescription = e.getValue(); + List topicPartitionInfoList = topicDescription + .partitions(); + int replicateCount = 0; + int isrCount = 0; + for (org.apache.kafka.common.TopicPartitionInfo topicPartitionInfo : topicPartitionInfoList) { + replicateCount += topicPartitionInfo.replicas().size(); + isrCount += topicPartitionInfo.isr().size(); + } + if (replicateCount == 0) { + return new TopicBrief(topic, topicDescription.partitions().size(), 0); + } else { + return new TopicBrief(topic, topicDescription.partitions().size(), + ((double) isrCount / replicateCount)); + } + }).collect(toList()); + + kafkaAdminClient.close(); + return result; + } + + public boolean existTopic(String topicName) { + return kafkaZkClient.topicExists(topicName); + } + + public List listBrokers() { + List brokerList = CollectionConvertor + .seqConvertJavaList(kafkaZkClient.getAllBrokersInCluster()); + + return brokerList.parallelStream().collect(Collectors.toMap(Broker::id, Broker::rack)) + .entrySet().parallelStream() + .map(entry -> { + String brokerInfoStr = null; + try { + //TODO replace zkClient with kafkaZKClient + brokerInfoStr = new String( + zkClient.getData().forPath(ZkUtils.BrokerIdsPath() + "/" + entry.getKey()) ); - Collections.sort(topicMeta.getTopicPartitionInfos()); - } + } catch (Exception e) { + e.printStackTrace(); + } + BrokerInfo brokerInfo = gson.fromJson(brokerInfoStr, BrokerInfo.class); + if (entry.getValue().isEmpty()) { + brokerInfo.setRack(""); + } else { + brokerInfo.setRack(entry.getValue().get()); + } + brokerInfo.setId(entry.getKey()); + return brokerInfo; + }).collect(toList()); + } - return topicMeta; + public int getControllerId() { + int controllerId = -1; + if (!kafkaZkClient.getControllerId().equals(NONE)) { + controllerId = (int) kafkaZkClient.getControllerId().get(); } - public GeneralResponse deleteTopic(@TopicExistConstraint String topic) - throws InterruptedException, ExecutionException { - log.warn("Delete topic " + topic); - kafkaAdminClient.deleteTopics(Collections.singletonList(topic)).all().get(); - - return new GeneralResponse(GeneralResponseState.success, topic + " has been deleted."); + return controllerId; + } + + public TopicMeta describeTopic(@TopicExistConstraint String topicName) { + org.apache.kafka.clients.admin.AdminClient kafkaAdminClient = createKafkaAdminClient(); + + DescribeTopicsResult describeTopicsResult = kafkaAdminClient + .describeTopics(Collections.singletonList(topicName)); + TopicMeta topicMeta = new TopicMeta(topicName); + try { + Map topicMap = describeTopicsResult.all() + .get(kafkaAdminClientGetTimeoutMs, TimeUnit.MILLISECONDS); + if (topicMap.containsKey(topicName)) { + TopicDescription topicDescription = topicMap.get(topicName); + List tmList = topicDescription.partitions(); + topicMeta.setInternal(topicDescription.isInternal()); + topicMeta.setPartitionCount(topicDescription.partitions().size()); + topicMeta.setReplicationFactor(tmList.get(0).replicas().size()); + topicMeta.setTopicPartitionInfos(tmList.parallelStream().map( + tm -> { + CustomTopicPartitionInfo customTopicPartitionInfo = new CustomTopicPartitionInfo(); + customTopicPartitionInfo.setTopicPartitionInfo(tm); + customTopicPartitionInfo.setIn_sync(); + customTopicPartitionInfo + .setStartOffset(getBeginningOffset(topicName, tm.partition())); + customTopicPartitionInfo + .setEndOffset(getEndOffset(tm.leader(), topicName, tm.partition())); + customTopicPartitionInfo.setMessageAvailable(); + return customTopicPartitionInfo; + + }).collect(toList()) + ); + Collections.sort(topicMeta.getTopicPartitionInfos()); + } + } catch (Exception exception) { + exception.printStackTrace(); + throw new ApiException("Describe topic exception." + exception); } - public Collection describeConfig(ConfigResource.Type type, String name) throws ExecutionException, InterruptedException { - DescribeConfigsResult ret = kafkaAdminClient.describeConfigs(Collections.singleton(new ConfigResource(type, name))); - Map configs = ret.all().get(); - Collection configEntries = null; - - for (Map.Entry entry : configs.entrySet()) { - Config value = entry.getValue(); - configEntries = value.entries(); + kafkaAdminClient.close(); + return topicMeta; + } + + public Map deleteTopicList(List topicList) { + org.apache.kafka.clients.admin.AdminClient kafkaAdminClient = createKafkaAdminClient(); + HashMap deleteResults = new HashMap<>(); + + log.warn("Delete topic " + topicList); + DeleteTopicsResult deleteTopicsResult = kafkaAdminClient.deleteTopics(topicList); + try { + deleteTopicsResult.all().get(kafkaAdminClientGetTimeoutMs, TimeUnit.MILLISECONDS); + } catch (Exception exception) { + log.info("Delete topic exception:" + exception); + } finally { + deleteTopicsResult.values().forEach((topic, result) -> { + GeneralResponse generalResponse; + if (result.isDone() && !result.isCompletedExceptionally()) { + generalResponse = GeneralResponse.builder().state(GeneralResponseState.success).build(); + } else { + generalResponse = GeneralResponse.builder().state(GeneralResponseState.failure) + .msg(result.toString()).build(); } - - return configEntries; + deleteResults.put(topic, generalResponse); + }); } - public void alterConfig(ConfigResource.Type type, String name, Collection configEntries) - throws InterruptedException, ExecutionException{ - Config config = new Config(configEntries); - kafkaAdminClient.alterConfigs(Collections.singletonMap(new ConfigResource(type, name), config)).all().get(); + kafkaAdminClient.close(); + return deleteResults; + } + + public Collection describeConfig(ConfigResource.Type type, String name) + throws ExecutionException, InterruptedException { + org.apache.kafka.clients.admin.AdminClient kafkaAdminClient = createKafkaAdminClient(); + ConfigResource configResource = new ConfigResource(type, name); + + DescribeConfigsResult ret = kafkaAdminClient + .describeConfigs(Collections.singleton(configResource)); + Map configs = ret.all().get(); + + kafkaAdminClient.close(); + return configs.get(configResource).entries(); + } + + public void alterConfig(ConfigResource.Type type, String name, + Collection configEntries) + throws InterruptedException, ExecutionException { + org.apache.kafka.clients.admin.AdminClient kafkaAdminClient = createKafkaAdminClient(); + Config config = new Config(configEntries); + kafkaAdminClient.alterConfigs(Collections.singletonMap(new ConfigResource(type, name), config)) + .all().get(); + kafkaAdminClient.close(); + } + + public Collection updateTopicConf(@TopicExistConstraint String topic, + Properties props) + throws InterruptedException, ExecutionException { + Collection configEntries = props.entrySet().stream().map( + e -> new ConfigEntry(e.getKey().toString(), e.getValue().toString())) + .collect(Collectors.toList()); + alterConfig(ConfigResource.Type.TOPIC, topic, configEntries); + + return describeConfig(ConfigResource.Type.TOPIC, topic); + } + + public Collection getTopicConf(@TopicExistConstraint String topic) + throws InterruptedException, ExecutionException { + return describeConfig(ConfigResource.Type.TOPIC, topic); + } + + public Properties getTopicConfByKey(@TopicExistConstraint String topic, String key) + throws InterruptedException, ExecutionException { + Collection configEntries = describeConfig(ConfigResource.Type.TOPIC, topic); + Properties returnProps = new Properties(); + for (ConfigEntry entry : configEntries) { + if (entry.name().equals(key)) { + returnProps.put(key, entry.value()); + return returnProps; + } } - public Collection updateTopicConf(@TopicExistConstraint String topic, Properties props) - throws InterruptedException, ExecutionException { - Collection configEntries = props.entrySet().stream().map( - e -> new ConfigEntry(e.getKey().toString(), e.getValue().toString())).collect(Collectors.toList()); - alterConfig(ConfigResource.Type.TOPIC, topic, configEntries); + return null; + } - return describeConfig(ConfigResource.Type.TOPIC, topic); - } + public Collection updateTopicConfByKey(@TopicExistConstraint String topic, + String key, String value) throws InterruptedException, ExecutionException { + alterConfig(ConfigResource.Type.TOPIC, topic, + Collections.singletonList(new ConfigEntry(key, value))); + + return describeConfig(ConfigResource.Type.TOPIC, topic); + } + + public Map> listAllConsumerGroups(ConsumerType type) { + Map> result = new HashMap<>(); - public Collection getTopicConf(@TopicExistConstraint String topic) - throws InterruptedException, ExecutionException { - return describeConfig(ConfigResource.Type.TOPIC, topic); + if (type == null || type == ConsumerType.OLD) { + Set oldCGList = listAllOldConsumerGroups(); + if (oldCGList.size() != 0) { + result.put("old", oldCGList); + } } - public Properties getTopicConfByKey(@TopicExistConstraint String topic, String key) - throws InterruptedException, ExecutionException{ - Collection configEntries = describeConfig(ConfigResource.Type.TOPIC, topic); - Properties returnProps = new Properties(); - for (ConfigEntry entry : configEntries) - if (entry.name().equals(key)) { - returnProps.put(key, entry.value()); - return returnProps; - } + if (type == null || type == ConsumerType.NEW) { + Set newCGList = listAllNewConsumerGroups(); + if (newCGList.size() != 0) { + result.put("new", newCGList); + } + } - return null; + return result; + } + + private Set listAllNewConsumerGroups() { + AdminClient adminClient = kafkaUtils.createAdminClient(); + log.info("Calling the listAllConsumerGroupsFlattened"); + Set activeGroups = CollectionConvertor + .seqConvertJavaList(adminClient.listAllConsumerGroupsFlattened()).stream() + .map(GroupOverview::groupId).collect(toSet()); + + log.info("Finish getting new consumers"); + adminClient.close(); + return activeGroups; + } + + private Set listAllOldConsumerGroups() { + log.info("Finish getting old consumers"); + return CollectionConvertor + .seqConvertJavaList(kafkaZkClient.getChildren(ZkUtils.ConsumersPath())).stream() + .collect(toSet()); + } + + public Map> listConsumerGroupsByTopic( + @TopicExistConstraint String topic, + ConsumerType type) { + Map> result = new HashMap<>(); + + if (type == null || type == ConsumerType.OLD) { + Set oldCGList = null; + try { + oldCGList = listOldConsumerGroupsByTopic(topic); + } catch (Exception e) { + e.printStackTrace(); + } + if (oldCGList.size() != 0) { + result.put("old", oldCGList); + } } - public Collection updateTopicConfByKey(@TopicExistConstraint String topic, String key, String value) throws InterruptedException, ExecutionException{ - alterConfig(ConfigResource.Type.TOPIC, topic, Collections.singletonList(new ConfigEntry(key, value))); + if (type == null || type == ConsumerType.NEW) { + Set newCGList = listNewConsumerGroupsByTopic(topic); - return describeConfig(ConfigResource.Type.TOPIC, topic); + if (newCGList.size() != 0) { + result.put("new", newCGList); + } } - /* - public TopicMeta addPartition(@TopicExistConstraint String topic, AddPartition addPartition) { - List partitionMataData = AdminUtils.fetchTopicMetadataFromZk(topic, zkUtils).partitionMetadata(); - int numPartitions = partitionMataData.size(); - int numReplica = partitionMataData.get(0).replicas().size(); - List brokerIdList = listBrokers().stream().map(broker -> broker.getId()).collect(toList()); - List partitionIdList = partitionMataData.stream().map(p -> String.valueOf(p.partition())).collect(toList()); - String assignmentStr = addPartition.getReplicaAssignment(); - String toBeSetReplicaAssignmentStr = ""; + return result; + } - if (assignmentStr != null && !assignmentStr.equals("")) { - //Check out of index ids in replica assignment string - String[] ids = addPartition.getReplicaAssignment().split(",|:"); - if (Arrays.stream(ids).filter(id -> brokerIdList.contains(id)).count() != 0) { - throw new InvalidTopicException("Topic " + topic + ": manual reassignment str has wrong id!"); - } + private Set listNewConsumerGroupsByTopic(@TopicExistConstraint String topic) { + Set result = new HashSet(); + Set consumersList = listAllNewConsumerGroups(); - //Check if any ids duplicated in one partition in replica assignment - String[] assignPartitions = addPartition.getReplicaAssignment().split(","); - if (Arrays.stream(assignPartitions).filter(p -> - Arrays.stream(p.split(":")).collect(Collectors.toSet()).size() - != p.split(":").length).count() - != 0) { - throw new InvalidTopicException("Topic " + topic + ": manual reassignment str has duplicated id in one partition!"); - } + for (String c : consumersList) { + List topics = getTopicListByConsumerGroup(c); + if (topics.contains(topic)) { + result.add(c); + } + } - String replicaStr = Strings.repeat("0:", numReplica).replaceFirst(".$", ","); - toBeSetReplicaAssignmentStr = Strings.repeat(replicaStr, numPartitions) + addPartition.getReplicaAssignment(); - } else { - toBeSetReplicaAssignmentStr = ""; - } + return result; + } - AdminUtils.addPartitions(zkUtils, topic, addPartition.getNumPartitionsAdded() + numPartitions, - toBeSetReplicaAssignmentStr, true, - RackAwareMode.Enforced$.MODULE$); + private Set listOldConsumerGroupsByTopic(@TopicExistConstraint String topic) { + List consumersFromZk = CollectionConvertor + .seqConvertJavaList(kafkaZkClient.getChildren(ZkUtils.ConsumersPath())); + Set cList = new HashSet<>(); - return describeTopic(topic); + for (String consumer : consumersFromZk) { + String path = ZkUtils.ConsumersPath() + "/" + consumer + "/offsets"; + List topics = CollectionConvertor.seqConvertJavaList(kafkaZkClient.getChildren(path)); + if (topics != null && topics.contains(topic)) { + cList.add(consumer); + } } - //Return - public List generateReassignPartition(ReassignWrapper reassignWrapper) { - Seq brokerSeq = JavaConverters.asScalaBufferConverter(reassignWrapper.getBrokers()).asScala().toSeq(); - // - Tuple2 resultTuple2 = ReassignPartitionsCommand.generateAssignment(zkUtils, brokerSeq, reassignWrapper.generateReassignJsonString(), false); - List result = new ArrayList<>(); - result.add(zkUtils.formatAsReassignmentJson((scala.collection.Map>) resultTuple2._2())); - result.add(zkUtils.formatAsReassignmentJson((scala.collection.Map>) resultTuple2._1())); + return cList; + } + + public Set listTopicsByCG(String consumerGroup, ConsumerType type) { + Set topicList = new HashSet<>(); - return result; + if (type == null) { + throw new ApiException("Unknown Type " + type); } - public Map executeReassignPartition(String reassignStr) { - ReassignPartitionsCommand.executeAssignment( - zkUtils, - reassignStr - ); - return checkReassignStatus(reassignStr); + if (type == ConsumerType.OLD) { + if (!isOldConsumerGroup(consumerGroup)) { + throw new RuntimeException(consumerGroup + " non-exist"); + } + String path = ZkUtils.ConsumersPath() + "/" + consumerGroup + "/offsets"; + topicList = CollectionConvertor.seqConvertJavaList(kafkaZkClient.getChildren(path)).stream() + .collect(toSet()); + } else if (type == ConsumerType.NEW) { + if (!isNewConsumerGroup(consumerGroup)) { + throw new RuntimeException(consumerGroup + " non-exist!"); + } + topicList.addAll(getTopicListByConsumerGroup(consumerGroup)); + } else { + throw new ApiException("Unknown Type " + type); } - public Map checkReassignStatus(String reassignStr) { - Map> partitionsToBeReassigned = JavaConverters.mapAsJavaMapConverter( - zkUtils.parsePartitionReassignmentData(reassignStr)).asJava(); - - Map> partitionsBeingReassigned = JavaConverters.mapAsJavaMapConverter( - zkUtils.getPartitionsBeingReassigned()).asJava().entrySet().stream().collect( - Collectors.toMap( - Map.Entry::getKey, - data -> data.getValue().newReplicas() - )); - - - java.util.Map reassignedPartitionsStatus = - partitionsToBeReassigned.entrySet().stream().collect(Collectors.toMap( - Map.Entry::getKey, - pbr -> ReassignPartitionsCommand.checkIfPartitionReassignmentSucceeded( - zkUtils, - pbr.getKey(), - pbr.getValue(), - JavaConverters.mapAsScalaMapConverter(partitionsToBeReassigned).asScala(), - JavaConverters.mapAsScalaMapConverter(partitionsBeingReassigned).asScala() - ) - )); - - - return reassignedPartitionsStatus.entrySet().stream().collect(Collectors.toMap( - Map.Entry::getKey, - r -> r.getValue().status() - )); + return topicList; + + } + + private List getTopicListByConsumerGroup(String consumerGroup) { + AdminClient adminClient = kafkaUtils.createAdminClient(); + Map groupOffsets = CollectionConvertor + .mapConvertJavaMap(adminClient.listGroupOffsets(consumerGroup)); + adminClient.close(); + + return groupOffsets.entrySet().stream() + .map(topicPartitionObjectEntry -> topicPartitionObjectEntry.getKey().topic()) + .collect(toList()); + } + + public ConsumerGroupMeta getConsumerGroupMeta(String consumerGroup) { + List members = new ArrayList<>(); + AdminClient adminClient = kafkaUtils.createAdminClient(); + + ConsumerGroupSummary consumerGroupSummary = adminClient.describeConsumerGroup(consumerGroup, 0); + List consumerSummaryList = CollectionConvertor + .optionListConvertJavaList(consumerGroupSummary.consumers().get()); + + ConsumerGroupMeta consumerGroupMeta = ConsumerGroupMeta.builder().groupId(consumerGroup) + .state(ConsumerGroupState.parse(consumerGroupSummary.state())) + .assignmentStrategy(consumerGroupSummary.assignmentStrategy()) + .coordinator(consumerGroupSummary.coordinator()).build(); + + consumerSummaryList.forEach(consumerSummary -> { + List topicPartitions = CollectionConvertor + .listConvertJavaList(consumerSummary.assignment()); + members.add(new MemberDescription(consumerSummary.consumerId(), + consumerSummary.clientId(), consumerSummary.host(), topicPartitions)); + }); + + consumerGroupMeta.setMembers(members); + adminClient.close(); + + return consumerGroupMeta; + } + + public boolean isOldConsumerGroup(String consumerGroup) { + return listAllOldConsumerGroups().contains(consumerGroup); + } + + public boolean isNewConsumerGroup(String consumerGroup) { + //Active Consumergroup or Dead ConsumerGroup is OK + return (listAllNewConsumerGroups().contains(consumerGroup)); + } + + public Map> describeConsumerGroup(String consumerGroup, + ConsumerType type) { + Map> result = new HashMap<>(); + Set topicList = listTopicsByCG(consumerGroup, type); + if (topicList == null) { + //Return empty result + return result; } - */ - private Set listAllOldConsumerGroups() { - log.info("Finish getting old consumers"); - return CollectionConvertor.seqConvertJavaList(zkUtils.getConsumerGroups()).stream().collect(toSet()); + if (type == ConsumerType.NEW) { + if (!isNewConsumerGroup(consumerGroup)) { + throw new RuntimeException(consumerGroup + " non-exist!"); + } + List partitionAssignmentStateList = describeNewConsumerGroup( + consumerGroup, false, null); + result = transPasListToMap(consumerGroup, partitionAssignmentStateList, ConsumerType.NEW); + } else if (type == ConsumerType.OLD) { + if (!isOldConsumerGroup(consumerGroup)) { + throw new RuntimeException(consumerGroup + " non-exist"); + } + List partitionAssignmentStateList = describeOldConsumerGroup( + consumerGroup, false, null); + result = transPasListToMap(consumerGroup, partitionAssignmentStateList, ConsumerType.OLD); } - private Set listOldConsumerGroupsByTopic(@TopicExistConstraint String topic) throws Exception { - - List consumersFromZk = zkClient.getChildren().forPath(ZkUtils.ConsumersPath()); - Set cList = new HashSet<>(); - - for (String consumer : consumersFromZk) { - String path = ZkUtils.ConsumersPath() + "/" + consumer + "/offsets"; - if (zkClient.checkExists().forPath(path) != null) { - if (zkClient.getChildren().forPath(path).size() != 0) { - if (!Strings.isNullOrEmpty(topic)) { - if (zkClient.getChildren().forPath(path).stream().filter(p -> p.equals(topic)).count() != 0) - cList.add(consumer); - } else { - cList.add(consumer); - } - } - } - } - - return cList; - - //May cause keeperexception, deprecated - //return JavaConverters.asJavaCollectionConverter(zkUtils.getAllConsumerGroupsForTopic(topic)).asJavaCollection().stream().collect(toList()); + return result; + } + + private Map> transPasListToMap(String consumerGroup, + List pasList, ConsumerType type) { + Map> result = new HashMap<>(); + ConsumerGroupSummary consumerGroupSummary; + + if (type.equals(ConsumerType.NEW)) { + AdminClient adminClient = kafkaUtils.createAdminClient(); + consumerGroupSummary = adminClient + .describeConsumerGroup(consumerGroup, 0); + adminClient.close(); + } else { + //Old consumer group has no state, coordinator, assignmentStrategy info + consumerGroupSummary = null; } - private Set listAllNewConsumerGroups() { - AdminClient adminClient = kafkaUtils.createAdminClient(); - log.info("Calling the listAllConsumerGroupsFlattened"); - Set activeGroups = CollectionConvertor.seqConvertJavaList(adminClient.listAllConsumerGroupsFlattened()).stream() - .map(GroupOverview::groupId).collect(toSet()); - log.info("Checking the groups in storage"); - Set usedTobeGroups = storage.getMap().entrySet().stream().map(Map.Entry::getKey).collect(toSet()); - activeGroups.addAll(usedTobeGroups); - log.info("Finish getting new consumers"); - adminClient.close(); - return activeGroups; + pasList.forEach(partitionAssignmentState -> { + String topic = partitionAssignmentState.getTopic(); + List consumerGroupDescs; + if (result.containsKey(topic)) { + consumerGroupDescs = result.get(topic); + } else { + consumerGroupDescs = new ArrayList<>(); + } + consumerGroupDescs.add( + convertParitionAssignmentStateToGroupDesc(consumerGroup, consumerGroupSummary, + partitionAssignmentState, type)); + result.put(topic, consumerGroupDescs); + }); + + return result; + } + + private ConsumerGroupDesc convertParitionAssignmentStateToGroupDesc(String consumerGroup, + ConsumerGroupSummary consumerGroupSummary, PartitionAssignmentState pas, ConsumerType type) { + ConsumerGroupDesc.ConsumerGroupDescBuilder consumerGroupDescBuilder = ConsumerGroupDesc + .builder().groupName(consumerGroup) + .topic(pas.getTopic()) + .partitionId(pas.getPartition()).currentOffset(pas.getOffset()) + .logEndOffset(pas.getLogEndOffset()).lag(pas.getLag()).consumerId(pas.getConsumerId()) + .consumerId(pas.getConsumerId()).host(pas.getHost()).type(type); + + if (consumerGroupSummary != null) { + consumerGroupDescBuilder = consumerGroupDescBuilder + .state(ConsumerGroupState.parse(consumerGroupSummary.state())) + .assignmentStrategy(consumerGroupSummary.assignmentStrategy()). + coordinator(consumerGroupSummary.coordinator()); } - /* - private Set listNewConsumerGroupsByTopic(@TopicExistConstraint String topic) { - Set result = new HashSet(); - Set consumersList = listAllNewConsumerGroups(); - - for (String c : consumersList) { - AdminClient adminClient = kafkaUtils.createAdminClient(); - - List consumerSummaryList = CollectionConvertor.listConvertJavaList(adminClient.describeConsumerGroup(c)); - Set topicSet = consumerSummaryList.stream() - .flatMap(cs -> CollectionConvertor.listConvertJavaList(cs.assignment()).stream()) - .map(TopicPartition::topic).filter(t -> t.equals(topic)).distinct() - .collect(toSet()); - - if (topicSet.size() != 0) { - result.add(c); - } - adminClient.close(); - } - return result; - }*/ - - public List describeOldCGByTopic(String consumerGroup, @TopicExistConstraint String topic) throws InterruptedException, ExecutionException{ - if (!isOldConsumerGroup(consumerGroup)) { - throw new RuntimeException(consumerGroup + " non-exist"); - } - List cgdList = new ArrayList<>(); - Map fetchOffSetFromZKResultList = new HashMap<>(); - - List topicList = CollectionConvertor.seqConvertJavaList(zkUtils.getTopicsByConsumerGroup(consumerGroup)); - if (topicList.size() == 0) { - log.info("No topic for the consumer group, nothing return"); - return null; - } - - List topicPartitions = getTopicPartitions(topic); - ZKGroupTopicDirs groupDirs = new ZKGroupTopicDirs(consumerGroup, topic); - Map ownerPartitionMap = topicPartitions.stream().collect(Collectors.toMap( - TopicAndPartition::partition, - tp -> { - Option owner = zkUtils.readDataMaybeNull(groupDirs.consumerOwnerDir() + "/" + tp.partition())._1; - if (owner != NONE) { - return owner.get(); - } else { - return "none"; - } - } - ) - ); - - ExecutorService executor = Executors.newCachedThreadPool(); + return consumerGroupDescBuilder.build(); + } - List taskList = topicPartitions.stream().map( - tp -> new FetchOffsetFromZKTask(zookeeperUtils, tp.topic(), consumerGroup, tp.partition())) - .collect(toList()); - List> resultList = null; + public List describeNewConsumerGroup(String consumerGroup, + boolean filtered, String topic) { + List partitionAssignmentStateList = new ArrayList<>(); - try { - resultList = executor.invokeAll(taskList); - } catch (InterruptedException e) { - e.printStackTrace(); - } - - executor.shutdown(); - - for (int i = 0; i < resultList.size(); i++) { - Future future = resultList.get(i); - try { - FetchOffSetFromZKResult offsetResult = future.get(); - fetchOffSetFromZKResultList.put( - offsetResult.getParition(), - offsetResult.getOffset()); - } catch (InterruptedException | ExecutionException e) { - e.printStackTrace(); - } - } - - - log.info("Getting topic Metadata " + topic); - TopicMeta topicMeta = describeTopic(topic); - - cgdList.addAll(setOldCGD(fetchOffSetFromZKResultList, ownerPartitionMap, topic, consumerGroup, topicMeta)); - Collections.sort(cgdList); - - return cgdList; + if (filtered && !existTopic(topic)) { + return partitionAssignmentStateList; } - private List setOldCGD( - Map fetchOffSetFromZKResultList, - Map ownerPartitionMap, - String topic, String consumerGroup, TopicMeta topicMeta) { - ConsumerGroupDescFactory factory = new ConsumerGroupDescFactory(kafkaUtils); - return ownerPartitionMap.entrySet().stream().map(op -> - factory.makeOldConsumerGroupDesc( - op, fetchOffSetFromZKResultList, topic, consumerGroup, topicMeta - ) - ) - .collect(toList()); + AdminClient adminClient = kafkaUtils.createAdminClient(); + ConsumerGroupSummary consumerGroupSummary = adminClient.describeConsumerGroup(consumerGroup, 0); + List consumerSummaryList = CollectionConvertor + .listConvertJavaList(consumerGroupSummary.consumers().get()); + if (consumerSummaryList != null) { + Map offsets = CollectionConvertor + .mapConvertJavaMap(adminClient.listGroupOffsets(consumerGroup)); + Map offsetsFiltered; + if (filtered && existTopic(topic)) { + offsetsFiltered = offsets.entrySet().stream().filter( + topicPartitionObjectEntry -> topicPartitionObjectEntry.getKey().topic().equals(topic)) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + } else { + offsetsFiltered = offsets; + } + if (offsetsFiltered.isEmpty()) { + return partitionAssignmentStateList; + } else { + ArrayList assignedTopicPartitions = new ArrayList<>(); + List rowsWithConsumer = consumerSummaryList.stream() + .flatMap(consumerSummary -> { + List topicPartitions = CollectionConvertor + .listConvertJavaList(consumerSummary.assignment()); + List topicPartitionsFiltered = topicPartitions; + if (filtered) { + topicPartitionsFiltered = topicPartitions.stream() + .filter(topicPartition -> topicPartition.topic().equals(topic)) + .collect(toList()); + } + assignedTopicPartitions.addAll(topicPartitionsFiltered); + Map partitionOffsets = topicPartitionsFiltered.stream() + .collect( + toMap(Function.identity(), + topicPartition -> offsetsFiltered.get(topicPartition))); + return collectConsumerAssignment(consumerGroup, consumerGroupSummary.coordinator(), + topicPartitionsFiltered, partitionOffsets, consumerSummary.consumerId(), + consumerSummary.host(), consumerSummary.clientId()).stream(); + }).collect(toList()); + + List topicPartitionList = new ArrayList<>(); + Map partitionOffsets = new HashMap<>(); + + offsetsFiltered.entrySet().forEach(topicPartitionObjectEntry -> { + if (!assignedTopicPartitions.contains(topicPartitionObjectEntry.getKey())) { + topicPartitionList.add(topicPartitionObjectEntry.getKey()); + partitionOffsets + .put(topicPartitionObjectEntry.getKey(), topicPartitionObjectEntry.getValue()); + } + }); + List rowsWithoutConsumer = collectConsumerAssignment( + consumerGroup, consumerGroupSummary.coordinator(), + topicPartitionList, partitionOffsets, "-", "-", "-"); + + partitionAssignmentStateList.addAll(rowsWithConsumer); + partitionAssignmentStateList.addAll(rowsWithoutConsumer); + } } -/* - public List describeNewCGByTopic(String consumerGroup, - String topic) { - if (!isNewConsumerGroup(consumerGroup)) { - throw new RuntimeException(consumerGroup + " non-exist!"); - } - return setNewCGD(consumerGroup, topic); + adminClient.close(); + return partitionAssignmentStateList; + } + + private List collectConsumerAssignment(String group, Node coordinator, + List topicPartitions, Map partitionOffsets, + String consumerId, String host, String clientId) { + if (topicPartitions.size() == 0) { + return new ArrayList(); + } else { + List list = new ArrayList<>(); + topicPartitions.forEach(topicPartition -> { + long logEndOffset = getEndOffset(topicPartition.topic(), topicPartition.partition()); + long offset = (Long) partitionOffsets.get(topicPartition); + long lag = logEndOffset - offset; + list.add(new PartitionAssignmentState(group, coordinator, topicPartition.topic(), + topicPartition.partition(), offset, lag, consumerId, host, clientId, logEndOffset)); + }); + return list; } + } + public List describeOldConsumerGroup(String consumerGroup, + boolean filtered, String topic) { + List partitionAssignmentStateList = new ArrayList<>(); - private List setNewCGD(String consumerGroup, String topic) { - List cgdList = new ArrayList<>(); - AdminClient adminClient = kafkaUtils.createAdminClient(); - - List consumerSummaryList = - CollectionConvertor.listConvertJavaList(adminClient.describeConsumerGroup(consumerGroup)); - //Nothing about this consumer group obtained, return an empty map directly - adminClient.close(); + if (filtered && !existTopic(topic)) { + return partitionAssignmentStateList; + } - List filteredCSList = consumerSummaryList.parallelStream() - .filter(cs -> - CollectionConvertor.listConvertJavaList(cs.assignment()).parallelStream() - .filter(tp -> tp.topic().equals(topic)).count() != 0) - .collect(toList()); + String[] agrs = {"--describe", "--zookeeper", zookeeperUtils.getZookeeperConfig().getUris(), + "--group", consumerGroup}; + ConsumerGroupCommand.ConsumerGroupCommandOptions options = + new ConsumerGroupCommand.ConsumerGroupCommandOptions(agrs); + ConsumerGroupCommand.ZkConsumerGroupService zkConsumerGroupService = + new ConsumerGroupCommand.ZkConsumerGroupService(options); + + ObjectMapper mapper = new ObjectMapper(); + mapper.registerModule(new DefaultScalaModule()); + mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + try { + String source = mapper + .writeValueAsString(zkConsumerGroupService.collectGroupOffsets()._2().get()); + System.out.println("source:" + source); + partitionAssignmentStateList = mapper.readValue(source, + getCollectionType(mapper, List.class, PartitionAssignmentState.class)); + List partitionAssignmentStateListFiltered; + if (filtered && existTopic(topic)) { + partitionAssignmentStateListFiltered = partitionAssignmentStateList.stream() + .filter(partitionAssignmentState -> partitionAssignmentState.getTopic().equals(topic)) + .collect(Collectors.toList()); + } else { + partitionAssignmentStateListFiltered = partitionAssignmentStateList; + } + partitionAssignmentStateListFiltered + .sort(Comparator.comparing(PartitionAssignmentState::getTopic) + .thenComparing(PartitionAssignmentState::getPartition)); + } catch (JsonProcessingException jsonProcessingException) { + log.error("Describe old consumer group exception.", jsonProcessingException); + } catch (IOException ioexception) { + log.error("Describe old consumer group exception.", ioexception); + } - //Prepare the common metrics no matter the cg is active or not. + return partitionAssignmentStateList; + } - //1. Get the meta information of the topic - TopicMeta topicMeta = describeTopic(topic); + private JavaType getCollectionType(ObjectMapper mapper, + Class collectionClass, + Class... elementClasses) { + return mapper.getTypeFactory().constructParametricType(collectionClass, elementClasses); + } - //2. Get the log end offset for every partition - Map partitionEndOffsetMap = topicMeta.getTopicPartitionInfos().stream() - .collect(Collectors.toMap( - tpi -> tpi.getPartitionId(), - tpi -> tpi.getEndOffset() - ) - ); - if (filteredCSList.size() == 0) {//For Pending consumer group - - //Even from the offsetstorage, nothing about this consumer group obtained - // In this case, return an empty map directly. - Map storageMap = storage.get(consumerGroup); - if (storageMap == null) { - return null; - } + public List describeNewCGByTopic(String consumerGroup, + String topic) { + if (!isNewConsumerGroup(consumerGroup)) { + throw new RuntimeException(consumerGroup + " non-exist!"); + } - //Get the current offset of each partition in this topic. - Map topicStorage = new HashMap<>(); - for (Map.Entry e : storageMap.entrySet()) { - if (e.getKey().topicPartition().topic().equals(topic)) { - topicStorage.put(e.getKey(), e.getValue()); - } - } + List partitionAssignmentStateList = describeNewConsumerGroup( + consumerGroup, true, topic); - //Build consumer group description - ConsumerGroupDescFactory factory = new ConsumerGroupDescFactory(kafkaUtils); - cgdList.addAll( - topicStorage.entrySet().stream().map( - storage -> factory.makeNewPendingConsumerGroupDesc( - consumerGroup, - partitionEndOffsetMap, - storage, - topic) - ).collect(toList())); - - } else { //For running consumer group - //Build consumer group description - ConsumerGroupDescFactory factory = new ConsumerGroupDescFactory(kafkaUtils); - for (AdminClient.ConsumerSummary cs : filteredCSList) { - List assignment = CollectionConvertor.listConvertJavaList(cs.assignment()); - //Second get the current offset of each partition in this topic - - cgdList.addAll(assignment.parallelStream() - .filter(tp->tp.topic().equals(topic)) - .map(tp -> factory.makeNewRunningConsumerGroupDesc(tp, consumerGroup, partitionEndOffsetMap, cs) - ).collect(toList())); - } - } + AdminClient adminClient = kafkaUtils.createAdminClient(); + ConsumerGroupSummary consumerGroupSummary = adminClient + .describeConsumerGroup(consumerGroup, 0); + adminClient.close(); - return cgdList; - } -*/ + return partitionAssignmentStateList.stream().map( + partitionAssignmentState -> convertParitionAssignmentStateToGroupDesc(consumerGroup, + consumerGroupSummary, partitionAssignmentState, ConsumerType.NEW)) + .collect(Collectors.toList()); + } - public String getMessage(@TopicExistConstraint String topic, int partition, long offset, String decoder, String avroSchema) { - KafkaConsumer consumer = kafkaUtils.createNewConsumer(String.valueOf(System.currentTimeMillis())); - TopicPartition tp = new TopicPartition(topic, partition); - long beginningOffset = getBeginningOffset(topic, partition); - long endOffset = getEndOffset(topic, partition); - if (beginningOffset == endOffset) { - throw new ApiException("There is no message in this partition of this topic"); - } - if (offset < beginningOffset || offset >= endOffset) { - log.error(offset + " error"); - consumer.close(); - throw new ApiException( - "offsets must be between " + String.valueOf(beginningOffset - + " and " + (endOffset - 1) - ) - ); - } - consumer.assign(Collections.singletonList(tp)); - consumer.seek(tp, offset); - - String last = null; - - //ConsumerRecords crs = consumer.poll(channelRetryBackoffMs); - ConsumerRecords crs = consumer.poll(3000); - log.info("Seek to offset:" + offset + ", topic:" + topic + ", partition:" + partition + ", crs.count:" + crs.count()); - if (crs.count() != 0) { - Iterator> it = crs.iterator(); - while (it.hasNext()) { - ConsumerRecord initCr = it.next(); - last = "Value: " + initCr.value() + ", Offset: " + String.valueOf(initCr.offset()); - log.info("Value: " + initCr.value() + ", initCr.Offset: " + String.valueOf(initCr.offset())); - if (last != null && initCr.offset() == offset) { - break; - } - } - } - log.info("last:" + last); - consumer.close(); - return last; + public List describeOldCGByTopic(String consumerGroup, + @TopicExistConstraint String topic) throws InterruptedException, ExecutionException { + if (!isOldConsumerGroup(consumerGroup)) { + throw new RuntimeException(consumerGroup + " non-exist"); } - public GeneralResponse resetOffset(@TopicExistConstraint String topic, int partition, - String consumerGroup, - ConsumerType type, String offset) { - KafkaConsumer consumer = null; - log.info("To tell the consumergroup " + consumerGroup + " is new"); - if (type != null && type == ConsumerType.NEW) { - if (!isNewConsumerGroup(consumerGroup)) { - throw new ApiException("Consumer group " + consumerGroup + " is non-exist!"); + List partitionAssignmentStateList = describeOldConsumerGroup( + consumerGroup, true, topic); + ConsumerGroupSummary consumerGroupSummary = null; + + return partitionAssignmentStateList.stream().map( + partitionAssignmentState -> convertParitionAssignmentStateToGroupDesc(consumerGroup, + consumerGroupSummary, partitionAssignmentState, ConsumerType.NEW)) + .collect(Collectors.toList()); + } + + public Map addPartitions(List addPartitions) { + Map addPartitionsResult = new HashMap<>(); + org.apache.kafka.clients.admin.AdminClient kafkaAdminClient = createKafkaAdminClient(); + + Map newPartitionsMap = new HashMap<>(); + addPartitions.forEach(addPartition -> { + String topic = addPartition.getTopic(); + if (!existTopic(topic)) { + addPartitionsResult.put(topic, GeneralResponse.builder().state(GeneralResponseState.failure) + .msg("Topic:" + topic + " non-exist.").build()); + } else { + TopicMeta topicMeta = describeTopic(topic); + int currentPartionCount = topicMeta.getPartitionCount(); + int numPartitionsAdded = addPartition.getNumPartitionsAdded(); + int totalCount = currentPartionCount + numPartitionsAdded; + String assignmentStr = addPartition.getReplicaAssignment(); + List> newAssignments = new ArrayList<>(); + if (assignmentStr != null && !assignmentStr.equals("")) { + String[] ids = assignmentStr.split(","); + for (int i = 0; i < ids.length; i++) { + String[] replicas = ids[i].split(":"); + List replicasInt = new ArrayList<>(); + for (int j = 0; j < replicas.length; j++) { + replicasInt.add(Integer.parseInt(replicas[j])); } + newAssignments.add(replicasInt); + } } - - log.info("To tell the consumergroup " + consumerGroup + " is old"); - if (type != null && type == ConsumerType.OLD) { - if (!isOldConsumerGroup(consumerGroup)) { - throw new ApiException("Consumer group " + consumerGroup + " is non-exist!"); - } + System.out.println("++++++++newAssignments:" + newAssignments); + NewPartitions newPartitions; + if (newAssignments.isEmpty()) { + //The assignment of new replicas to brokers will be decided by the broker. + newPartitions = NewPartitions.increaseTo(totalCount); + } else { + newPartitions = NewPartitions.increaseTo(totalCount, newAssignments); } - - long offsetToBeReset; - long beginningOffset = getBeginningOffset(topic, partition); - long endOffset = getEndOffset(topic, partition); - - log.info("To tell the consumergroup " + consumerGroup + " is active now"); - if (isConsumerGroupActive(consumerGroup, type)) { - throw new ApiException("Assignments can only be reset if the group " + consumerGroup + " is inactive"); + newPartitionsMap.put(topic, newPartitions); + } + }); + CreatePartitionsResult createPartitionsResult = kafkaAdminClient + .createPartitions(newPartitionsMap); + try { + createPartitionsResult.all().get(kafkaAdminClientGetTimeoutMs, TimeUnit.MILLISECONDS); + } catch (Exception exception) { + log.warn("Add partitions exception: " + exception); + } finally { + Map> result = createPartitionsResult.values(); + result.forEach((topic, createResult) -> { + GeneralResponse generalResponse; + if (!createResult.isCompletedExceptionally() && createResult.isDone()) { + TopicMeta topicMeta = describeTopic(topic); + generalResponse = GeneralResponse.builder().state(GeneralResponseState.success) + .data(topicMeta).build(); + } else { + generalResponse = GeneralResponse.builder().state(GeneralResponseState.failure) + .msg(createResult.toString()).build(); } + addPartitionsResult.put(topic, generalResponse); + }); - - if (type != null && type == ConsumerType.NEW && isNewConsumerGroup(consumerGroup)) { - try { - log.info("The consumergroup " + consumerGroup + " is new. Reset offset now"); - consumer = kafkaUtils.createNewConsumer(consumerGroup); - //if type is new or the consumergroup itself is new - TopicPartition tp = new TopicPartition(topic, partition); - consumer.assign(Arrays.asList(tp)); - consumer.poll(channelSocketTimeoutMs); - if (offset.equals("earliest")) { - consumer.seekToBeginning(Arrays.asList(tp)); - log.info("Reset to" + consumer.position(tp)); - } else if (offset.equals("latest")) { - consumer.seekToEnd(Arrays.asList(tp)); - log.info("Reset to" + consumer.position(tp)); - } else { - if (Long.parseLong(offset) < beginningOffset || Long.parseLong(offset) > endOffset) { - log.error(offset + " error"); - throw new ApiException( - "offsets must be between " + String.valueOf(beginningOffset - + " and " + (endOffset - 1) - ) - ); - } - offsetToBeReset = Long.parseLong(offset); - consumer.seek(tp, offsetToBeReset); - } - consumer.commitSync(); - } catch (IllegalStateException e) { - storage.getMap().remove(consumerGroup); - throw new ApiException(e); - } finally { - consumer.close(); - } + kafkaAdminClient.close(); + return addPartitionsResult; + } + } + + /* + public TopicMeta addPartition(@TopicExistConstraint String topic, AddPartition addPartition) { + List partitionMataData = AdminUtils.fetchTopicMetadataFromZk(topic, zkUtils).partitionMetadata(); + int numPartitions = partitionMataData.size(); + int numReplica = partitionMataData.get(0).replicas().size(); + List brokerIdList = listBrokers().stream().map(broker -> broker.getId()).collect(toList()); + List partitionIdList = partitionMataData.stream().map(p -> String.valueOf(p.partition())).collect(toList()); + String assignmentStr = addPartition.getReplicaAssignment(); + String toBeSetReplicaAssignmentStr = ""; + + if (assignmentStr != null && !assignmentStr.equals("")) { + //Check out of index ids in replica assignment string + String[] ids = addPartition.getReplicaAssignment().split(",|:"); + if (Arrays.stream(ids).filter(id -> brokerIdList.contains(id)).count() != 0) { + throw new InvalidTopicException("Topic " + topic + ": manual reassignment str has wrong id!"); + } + + //Check if any ids duplicated in one partition in replica assignment + String[] assignPartitions = addPartition.getReplicaAssignment().split(","); + if (Arrays.stream(assignPartitions).filter(p -> + Arrays.stream(p.split(":")).collect(Collectors.toSet()).size() + != p.split(":").length).count() + != 0) { + throw new InvalidTopicException("Topic " + topic + ": manual reassignment str has duplicated id in one partition!"); + } + + String replicaStr = Strings.repeat("0:", numReplica).replaceFirst(".$", ","); + toBeSetReplicaAssignmentStr = Strings.repeat(replicaStr, numPartitions) + addPartition.getReplicaAssignment(); + } else { + toBeSetReplicaAssignmentStr = ""; + } + + AdminUtils.addPartitions(zkUtils, topic, addPartition.getNumPartitionsAdded() + numPartitions, + toBeSetReplicaAssignmentStr, true, + RackAwareMode.Enforced$.MODULE$); + + return describeTopic(topic); + } + + //Return + public List generateReassignPartition(ReassignWrapper reassignWrapper) { + Seq brokerSeq = JavaConverters.asScalaBufferConverter(reassignWrapper.getBrokers()).asScala().toSeq(); + // + Tuple2 resultTuple2 = ReassignPartitionsCommand.generateAssignment(zkUtils, brokerSeq, reassignWrapper.generateReassignJsonString(), false); + List result = new ArrayList<>(); + result.add(zkUtils.formatAsReassignmentJson((scala.collection.Map>) resultTuple2._2())); + result.add(zkUtils.formatAsReassignmentJson((scala.collection.Map>) resultTuple2._1())); + + return result; + } + + public Map executeReassignPartition(String reassignStr) { + ReassignPartitionsCommand.executeAssignment( + zkUtils, + reassignStr + ); + return checkReassignStatus(reassignStr); + } + + public Map checkReassignStatus(String reassignStr) { + Map> partitionsToBeReassigned = JavaConverters.mapAsJavaMapConverter( + zkUtils.parsePartitionReassignmentData(reassignStr)).asJava(); + + Map> partitionsBeingReassigned = JavaConverters.mapAsJavaMapConverter( + zkUtils.getPartitionsBeingReassigned()).asJava().entrySet().stream().collect( + Collectors.toMap( + Map.Entry::getKey, + data -> data.getValue().newReplicas() + )); + + + java.util.Map reassignedPartitionsStatus = + partitionsToBeReassigned.entrySet().stream().collect(Collectors.toMap( + Map.Entry::getKey, + pbr -> ReassignPartitionsCommand.checkIfPartitionReassignmentSucceeded( + zkUtils, + pbr.getKey(), + pbr.getValue(), + JavaConverters.mapAsScalaMapConverter(partitionsToBeReassigned).asScala(), + JavaConverters.mapAsScalaMapConverter(partitionsBeingReassigned).asScala() + ) + )); + + + return reassignedPartitionsStatus.entrySet().stream().collect(Collectors.toMap( + Map.Entry::getKey, + r -> r.getValue().status() + )); + } + */ + public String getMessage(@TopicExistConstraint String topic, int partition, long offset, + String decoder, String avroSchema) { + KafkaConsumer consumer = kafkaUtils + .createNewConsumer(String.valueOf(System.currentTimeMillis())); + TopicPartition tp = new TopicPartition(topic, partition); + long beginningOffset = getBeginningOffset(topic, partition); + long endOffset = getEndOffset(topic, partition); + if (beginningOffset == endOffset) { + throw new ApiException("There is no message in this partition of this topic"); + } + if (offset < beginningOffset || offset >= endOffset) { + log.error(offset + " error"); + consumer.close(); + throw new ApiException( + "offsets must be between " + String.valueOf(beginningOffset + + " and " + (endOffset - 1) + ) + ); + } + consumer.assign(Collections.singletonList(tp)); + consumer.seek(tp, offset); + + String last = null; + + //ConsumerRecords crs = consumer.poll(channelRetryBackoffMs); + ConsumerRecords crs = consumer.poll(3000); + log.info("Seek to offset:" + offset + ", topic:" + topic + ", partition:" + partition + + ", crs.count:" + crs.count()); + if (crs.count() != 0) { + Iterator> it = crs.iterator(); + while (it.hasNext()) { + ConsumerRecord initCr = it.next(); + last = "Value: " + initCr.value() + ", Offset: " + String.valueOf(initCr.offset()) + + ", timestamp:" + initCr.timestamp(); + log.info( + "Value: " + initCr.value() + ", initCr.Offset: " + String.valueOf(initCr.offset())); + if (last != null && initCr.offset() == offset) { + break; } - - - //if type is old or the consumer group itself is old - if (type != null && type == ConsumerType.OLD && isOldConsumerGroup(consumerGroup)) { - log.info("The consumergroup " + consumerGroup + " is old. Reset offset now"); - if (offset.equals("earliest")) { - offset = String.valueOf(beginningOffset); - } else if (offset.equals("latest")) { - offset = String.valueOf(endOffset); - } - try { - if (Long.parseLong(offset) < beginningOffset || Long.parseLong(offset) > endOffset) { - log.info("Setting offset to " + offset + " error"); - throw new ApiException( - "offsets must be between " + String.valueOf(beginningOffset - + " and " + (endOffset - 1) - ) - ); - } - log.info("Offset will be reset to " + offset); - zkUtils.zkClient().writeData( - "/consumers/" + consumerGroup + "/offsets/" + topic + "/" + partition, - offset); - } catch (Exception e) { - throw new ApiException(e); - } + } + } + log.info("last:" + last); + consumer.close(); + return last; + } + + public Record getRecordByOffset(@TopicExistConstraint String topic, int partition, + long offset, + String decoder, String avroSchema) { + if (!isTopicPartitionValid(topic, partition)) { + throw new ApiException("Bad request. Topic:" + topic + " has no partition:" + partition); + } + KafkaConsumer consumer; + try { + consumer = kafkaUtils.createNewConsumer(String.valueOf(System.currentTimeMillis()), decoder); + } catch (ClassNotFoundException classNotFoundException) { + throw new ApiException("Bad request. Decoder class:" + decoder + + " not found. ByteArrayDeserializer, ByteBufferDeserializer, BytesDeserializer, " + + "DoubleDeserializer, ExtendedDeserializer.Wrapper, FloatDeserializer, " + + "IntegerDeserializer, LongDeserializer, ShortDeserializer, StringDeserializer " + + "are supported."); + } + TopicPartition tp = new TopicPartition(topic, partition); + long beginningOffset = getBeginningOffset(topic, partition); + long endOffset = getEndOffset(topic, partition); + System.out.println("++++++beginning offset:" + beginningOffset + ", endoffset:" + endOffset); + if (beginningOffset == endOffset) { + throw new ApiException("There is no message in this partition of this topic"); + } + if (offset < beginningOffset || offset >= endOffset) { + log.error(offset + " error"); + consumer.close(); + throw new ApiException( + "offsets must be between " + String.valueOf(beginningOffset + + " and " + (endOffset - 1) + ) + ); + } + consumer.assign(Collections.singletonList(tp)); + consumer.seek(tp, offset); + + Record record = new Record(); + + //ConsumerRecords crs = consumer.poll(channelRetryBackoffMs); + ConsumerRecords crs = consumer.poll(3000); + log.info("Seek to offset:" + offset + ", topic:" + topic + ", partition:" + partition + + ", crs.count:" + crs.count()); + System.out.println("Seek to offset:" + offset + ", topic:" + topic + ", partition:" + partition + + ", crs.count:" + crs.count()); + if (crs.count() != 0) { + Iterator> it = crs.iterator(); + while (it.hasNext()) { + ConsumerRecord initCr = it.next(); + if (initCr.offset() == offset) { + record.setOffset(offset); + record.setTimestamp(initCr.timestamp()); + record.setKey(initCr.key()); + record.setValue(initCr.value()); + break; } - return new GeneralResponse(GeneralResponseState.success, "Reset the offset successfully!"); + log.info( + "Value: " + initCr.value() + ", initCr.Offset: " + String.valueOf(initCr.offset()) + + ", timestamp:" + initCr.timestamp()); + System.out.println( + "Value: " + initCr.value() + ", initCr.Offset: " + String.valueOf(initCr.offset()) + + ", timestamp:" + initCr.timestamp()); + } } + consumer.close(); + return record; + } - public Map> getLastCommitTime(@ConsumerGroupExistConstraint String consumerGroup, - @TopicExistConstraint String topic, - ConsumerType type) { - Map> result = new ConcurrentHashMap<>(); - - if (type != null && type == ConsumerType.OLD) { - //Get Old Consumer commit time - try { - Map oldConsumerOffsetMap = new ConcurrentHashMap<>(); - if (zkClient.checkExists().forPath(CONSUMERPATHPREFIX + consumerGroup) != null - && zkClient.checkExists().forPath(CONSUMERPATHPREFIX + consumerGroup + OFFSETSPATHPREFIX + topic) != null) { - List offsets = zkClient.getChildren().forPath(CONSUMERPATHPREFIX + consumerGroup + OFFSETSPATHPREFIX + topic); - for (String offset : offsets) { - Integer id = Integer.valueOf(offset); - long mtime = zkClient.checkExists().forPath(CONSUMERPATHPREFIX + consumerGroup + OFFSETSPATHPREFIX + topic + "/" + offset).getMtime(); - oldConsumerOffsetMap.put(id, mtime); - } - - result.put("old", oldConsumerOffsetMap); - } - } catch (Exception e) { - e.printStackTrace(); - } + private boolean isTopicPartitionValid(String topic, int partition) { + TopicMeta topicMeta = describeTopic(topic); - } else { - //Get New consumer commit time, from offset storage instance - if (storage.get(consumerGroup) != null) { - Map storageResult = storage.get(consumerGroup); - result.put("new", (storageResult.entrySet().parallelStream().filter(s -> s.getKey().topicPartition().topic().equals(topic)) - .collect( - Collectors.toMap( - s -> s.getKey().topicPartition().partition(), - s -> { - if (s.getValue() != null) { - return s.getValue().commitTimestamp(); - } else { - return -1l; - } - } - ) - ) - ) - ); - } - - } - - return result; + for (CustomTopicPartitionInfo topicPartitionInfo : topicMeta.getTopicPartitionInfos()) { + if (topicPartitionInfo.getTopicPartitionInfo().partition() == partition) { + return true; + } } - public GeneralResponse deleteConsumerGroup(String consumerGroup) { - if (!isOldConsumerGroup(consumerGroup)) { - throw new RuntimeException(consumerGroup + " non-exist"); - } - try { - if (zookeeperUtils.getCuratorClient().checkExists().forPath(CONSUMERPATHPREFIX + consumerGroup + "/ids") == null) { - zookeeperUtils.getCuratorClient().delete().deletingChildrenIfNeeded().forPath(CONSUMERPATHPREFIX + consumerGroup); - } else { - if (!AdminUtils.deleteConsumerGroupInZK(zkUtils, consumerGroup)) { - throw new ApiException("The consumer " + consumerGroup + " is still active.Please stop it first"); - } - } - } catch (Exception e) { - throw new RuntimeException(e); - } + return false; + } + + public GeneralResponse resetOffset(@TopicExistConstraint String topic, int partition, + String consumerGroup, + ConsumerType type, String offset) { + KafkaConsumer consumer = null; + log.info("To tell the consumergroup " + consumerGroup + " is new"); + if (type != null && type == ConsumerType.NEW) { + if (!isNewConsumerGroup(consumerGroup)) { + throw new ApiException("Consumer group " + consumerGroup + " is non-exist!"); + } + } - return new GeneralResponse(GeneralResponseState.success, consumerGroup + " has been deleted."); + log.info("To tell the consumergroup " + consumerGroup + " is old"); + if (type != null && type == ConsumerType.OLD) { + if (!isOldConsumerGroup(consumerGroup)) { + throw new ApiException("Consumer group " + consumerGroup + " is non-exist!"); + } } - public Map> listAllConsumerGroups(ConsumerType type) { - Map> result = new HashMap<>(); + long offsetToBeReset = -1; + long beginningOffset = getBeginningOffset(topic, partition); + long endOffset = getEndOffset(topic, partition); - if (type == null || type == ConsumerType.OLD) { - Set oldCGList = listAllOldConsumerGroups(); - if (oldCGList.size() != 0) { - result.put("old", oldCGList); - } - } + log.info("To tell the consumergroup " + consumerGroup + " is active now"); + if (isConsumerGroupActive(consumerGroup, type)) { + throw new ApiException( + "Offsets can only be reset if the group " + consumerGroup + " is inactive"); + } - if (type == null || type == ConsumerType.NEW) { - Set newCGList = listAllNewConsumerGroups(); - if (newCGList.size() != 0) { - result.put("new", newCGList); + if (type != null && type == ConsumerType.NEW && isNewConsumerGroup(consumerGroup)) { + try { + log.info("The consumergroup " + consumerGroup + " is new. Reset offset now"); + consumer = kafkaUtils.createNewConsumer(consumerGroup); + //if type is new or the consumergroup itself is new + TopicPartition tp = new TopicPartition(topic, partition); + consumer.assign(Arrays.asList(tp)); + consumer.poll(channelSocketTimeoutMs); + if (offset.equals("earliest")) { + consumer.seekToBeginning(Arrays.asList(tp)); + offsetToBeReset = beginningOffset; + log.info("Reset to" + consumer.position(tp)); + } else if (offset.equals("latest")) { + consumer.seekToEnd(Arrays.asList(tp)); + offsetToBeReset = endOffset; + log.info("Reset to" + consumer.position(tp)); + } else if (isDateTime(offset)) { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + Map timestampsToSearch = new HashMap<>(); + try { + timestampsToSearch.put(tp, sdf.parse(offset).getTime()); + Map results = consumer + .offsetsForTimes(timestampsToSearch); + OffsetAndTimestamp offsetAndTimestamp = results.get(tp); + if (offsetAndTimestamp != null) { + offsetToBeReset = offsetAndTimestamp.offset(); + log.info("Reset offset by time" + offsetAndTimestamp.offset() + ", timestamp:" + + offsetAndTimestamp.timestamp() + ", timestampToDate:" + sdf + .format(new Date(offsetAndTimestamp.timestamp()))); + consumer.seek(tp, offsetToBeReset); + consumer.commitSync(); + } else { + throw new ApiException( + "No offset whose timestamp is greater than or equal to the given timestamp:" + + offset); } + } catch (ParseException parseException) { + throw new ApiException( + "Invalid offset format. Date format should be yyyy-MM-dd HH:mm:ss."); + } + } else { + if (Long.parseLong(offset) < beginningOffset || Long.parseLong(offset) > endOffset) { + log.error(offset + " error"); + throw new ApiException( + "Invalid request offset:" + offset + ". Topic " + topic + "'s beginning offset:" + + beginningOffset + ", endoffset:" + endOffset); + } + offsetToBeReset = Long.parseLong(offset); + consumer.seek(tp, offsetToBeReset); + consumer.commitSync(); } - - - return result; + } catch (IllegalStateException e) { + storage.getMap().remove(consumerGroup); + throw new ApiException(e); + } finally { + consumer.close(); + } } - /* - public Map> listConsumerGroupsByTopic( - @TopicExistConstraint String topic, - ConsumerType type) { - Map> result = new HashMap<>(); - - if (type == null || type == ConsumerType.OLD) { - Set oldCGList = null; - try { - oldCGList = listOldConsumerGroupsByTopic(topic); - } catch (Exception e) { - e.printStackTrace(); - } - if (oldCGList.size() != 0) { - result.put("old", oldCGList); - } - } - if (type == null || type == ConsumerType.NEW) { - Set newCGList = listNewConsumerGroupsByTopic(topic); - - if (newCGList.size() != 0) { - result.put("new", newCGList); - } + //if type is old or the consumer group itself is old + if (type != null && type == ConsumerType.OLD && isOldConsumerGroup(consumerGroup)) { + log.info("The consumergroup " + consumerGroup + " is old. Reset offset now"); + if (offset.equals("earliest")) { + offset = String.valueOf(beginningOffset); + offsetToBeReset = beginningOffset; + } else if (offset.equals("latest")) { + offset = String.valueOf(endOffset); + offsetToBeReset = endOffset; + } else { + try { + if (Long.parseLong(offset) < beginningOffset || Long.parseLong(offset) > endOffset) { + log.info("Setting offset to " + offset + " error"); + throw new ApiException( + "Invalid request offset:" + offset + ". Topic " + topic + "'s beginning offset:" + + beginningOffset + ", endoffset:" + endOffset); + } + log.info("Offset will be reset to " + offset); + offsetToBeReset = Long.parseLong(offset); + zkUtils.zkClient().writeData( + "/consumers/" + consumerGroup + "/offsets/" + topic + "/" + partition, + offset); + } catch (Exception e) { + throw new ApiException(e); } - - return result; + } } - */ - private List getTopicPartitions(String t) { - List tpList = new ArrayList<>(); - List l = Arrays.asList(t); - java.util.Map> tpMap = JavaConverters.mapAsJavaMapConverter(zkUtils.getPartitionsForTopics(JavaConverters.asScalaIteratorConverter(l.iterator()).asScala().toSeq())).asJava(); - if (tpMap != null) { - ArrayList partitionLists = new ArrayList<>(JavaConverters.seqAsJavaListConverter(tpMap.get(t)).asJava()); - tpList = partitionLists.stream().map(p -> new TopicAndPartition(t, (Integer) p)).collect(toList()); + return GeneralResponse.builder().state(GeneralResponseState.success) + .msg("Reset the offset successfully!").data(Long.toString(offsetToBeReset)).build(); + } + + private boolean isDateTime(String offset) { + String patternStr = "\\d\\d\\d\\d-[0-1]\\d-[0-3]\\d\\s+[0-2]\\d:[0-5]\\d:[0-5]\\d"; + Pattern timePattern = Pattern.compile(patternStr); + return timePattern.matcher(offset).find(); + } + + public Map> getLastCommitTime( + @ConsumerGroupExistConstraint String consumerGroup, + @TopicExistConstraint String topic, + ConsumerType type) { + Map> result = new ConcurrentHashMap<>(); + + if (type != null && type == ConsumerType.OLD) { + //Get Old Consumer commit time + try { + Map oldConsumerOffsetMap = new ConcurrentHashMap<>(); + if (zkClient.checkExists().forPath(CONSUMERPATHPREFIX + consumerGroup) != null + && zkClient.checkExists() + .forPath(CONSUMERPATHPREFIX + consumerGroup + OFFSETSPATHPREFIX + topic) != null) { + List offsets = zkClient.getChildren() + .forPath(CONSUMERPATHPREFIX + consumerGroup + OFFSETSPATHPREFIX + topic); + for (String offset : offsets) { + Integer id = Integer.valueOf(offset); + long mtime = zkClient.checkExists().forPath( + CONSUMERPATHPREFIX + consumerGroup + OFFSETSPATHPREFIX + topic + "/" + offset) + .getMtime(); + oldConsumerOffsetMap.put(id, mtime); + } + + result.put("old", oldConsumerOffsetMap); } - return tpList; - } - - - - private long getOffsets(Node leader, String topic, int partitionId, long time) { - TopicAndPartition topicAndPartition = new TopicAndPartition(topic, partitionId); - - SimpleConsumer consumer = new SimpleConsumer( - leader.host(), - leader.port(), - 10000, - 1024, - "Kafka-zk-simpleconsumer" + } catch (Exception e) { + e.printStackTrace(); + } + + } else { +// Get New consumer commit time, from offset storage instance + + if (storage.get(consumerGroup) != null) { + Map storageResult = storage.get(consumerGroup); + result.put("new", (storageResult.entrySet().parallelStream() + .filter(s -> s.getKey().topicPartition().topic().equals(topic)) + .collect( + Collectors.toMap( + s -> s.getKey().topicPartition().partition(), + s -> { + if (s.getValue() != null) { + return s.getValue().commitTimestamp(); + } else { + return -1l; + } + } + ) + ) + ) ); + } - PartitionOffsetRequestInfo partitionOffsetRequestInfo = new PartitionOffsetRequestInfo(time, 10000); - OffsetRequest offsetRequest = new OffsetRequest(ImmutableMap.of(topicAndPartition, partitionOffsetRequestInfo), kafka.api.OffsetRequest.CurrentVersion(), consumer.clientId()); - OffsetResponse offsetResponse = consumer.getOffsetsBefore(offsetRequest); - - if (offsetResponse.hasError()) { - short errorCode = offsetResponse.errorCode(topic, partitionId); - log.warn(format("Offset response has error: %d", errorCode)); - throw new ApiException("could not fetch data from Kafka, error code is '" + errorCode + "'Exception Message:" + offsetResponse.toString()); - } - - long[] offsets = offsetResponse.offsets(topic, partitionId); - consumer.close(); - return offsets[0]; } - private long getOffsets(PartitionInfo partitionInfo, long time) { - return getOffsets(partitionInfo.leader(), partitionInfo.topic(), partitionInfo.partition(), time); - } + return result; + } - public long getBeginningOffset(String topic, int partitionId) { - return getOffsets(kafkaUtils.getLeader(topic, partitionId), topic, partitionId, kafka.api.OffsetRequest.EarliestTime()); + public GeneralResponse deleteConsumerGroup(String consumerGroup, ConsumerType type) { + if (type == ConsumerType.OLD && !isOldConsumerGroup(consumerGroup)) { + throw new RuntimeException("Old consumer group:" + consumerGroup + " non-exist"); } - - public long getEndOffset(String topic, int partitionId) { - return getOffsets(kafkaUtils.getLeader(topic, partitionId), topic, partitionId, kafka.api.OffsetRequest.LatestTime()); + if (type == ConsumerType.NEW && !isNewConsumerGroup(consumerGroup)) { + throw new RuntimeException("New consumer group:" + consumerGroup + " non-exist"); } - - private long getBeginningOffset(Node leader, String topic, int partitionId) { - return getOffsets(leader, topic, partitionId, kafka.api.OffsetRequest.EarliestTime()); + if (type == ConsumerType.OLD) { + if (!AdminUtils.deleteConsumerGroupInZK(zkUtils, consumerGroup)) { + throw new ApiException( + "The consumer " + consumerGroup + " is still active.Please stop it first"); + } + } else if (type == ConsumerType.NEW) { + AdminClient adminClient = kafkaUtils.createAdminClient(); + List groups = new ArrayList<>(); + groups.add(consumerGroup); + + scala.collection.immutable.List sList = JavaConverters.asScalaBufferConverter(groups) + .asScala().toList(); + scala.collection.immutable.Map stringErrorsMap = adminClient + .deleteConsumerGroups((scala.collection.immutable.List) (sList)); + + if (!stringErrorsMap.get(consumerGroup).get().equals(Errors.NONE)) { + throw new ApiException( + "Consumer group:" + consumerGroup + " could not be deleted. Error Code:" + + stringErrorsMap.get(consumerGroup).get() + ". Error msg:" + stringErrorsMap + .get(consumerGroup).get().exception()); + } + adminClient.close(); } - private long getEndOffset(Node leader, String topic, int partitionId) { - return getOffsets(leader, topic, partitionId, kafka.api.OffsetRequest.LatestTime()); + return GeneralResponse.builder().state(GeneralResponseState.success) + .msg("Consumer group:" + consumerGroup + " has been deleted.").build(); + } + + private List getTopicPartitions(String t) { + List tpList = new ArrayList<>(); + List l = Arrays.asList(t); + java.util.Map> tpMap = JavaConverters.mapAsJavaMapConverter(zkUtils + .getPartitionsForTopics( + JavaConverters.asScalaIteratorConverter(l.iterator()).asScala().toSeq())).asJava(); + if (tpMap != null) { + ArrayList partitionLists = new ArrayList<>( + JavaConverters.seqAsJavaListConverter(tpMap.get(t)).asJava()); + tpList = partitionLists.stream().map(p -> new TopicAndPartition(t, (Integer) p)) + .collect(toList()); } - - - public boolean isOldConsumerGroup(String consumerGroup) { - return listAllOldConsumerGroups().contains(consumerGroup); + return tpList; + } + + private long getOffsets(Node leader, String topic, int partitionId, long time) { + TopicAndPartition topicAndPartition = new TopicAndPartition(topic, partitionId); + + SimpleConsumer consumer = new SimpleConsumer( + leader.host(), + leader.port(), + 10000, + 1024, + "Kafka-zk-simpleconsumer" + ); + + PartitionOffsetRequestInfo partitionOffsetRequestInfo = new PartitionOffsetRequestInfo(time, + 10000); + OffsetRequest offsetRequest = new OffsetRequest( + ImmutableMap.of(topicAndPartition, partitionOffsetRequestInfo), + kafka.api.OffsetRequest.CurrentVersion(), consumer.clientId()); + OffsetResponse offsetResponse = consumer.getOffsetsBefore(offsetRequest); + + if (offsetResponse.hasError()) { + short errorCode = offsetResponse.errorCode(topic, partitionId); + log.warn(format("Offset response has error: %d", errorCode)); + throw new ApiException( + "could not fetch data from Kafka, error code is '" + errorCode + "'Exception Message:" + + offsetResponse.toString()); } - public boolean isNewConsumerGroup(String consumerGroup) { - //Active Consumergroup or Dead ConsumerGroup is OK - return (listAllNewConsumerGroups().contains(consumerGroup)); + long[] offsets = offsetResponse.offsets(topic, partitionId); + consumer.close(); + return offsets[0]; + } + + private long getOffsets(PartitionInfo partitionInfo, long time) { + return getOffsets(partitionInfo.leader(), partitionInfo.topic(), partitionInfo.partition(), + time); + } + + public long getBeginningOffset(String topic, int partitionId) { + KafkaConsumer consumer = kafkaUtils.createNewConsumer(KafkaUtils.DEFAULTCP); + TopicPartition tp = new TopicPartition(topic, partitionId); + consumer.assign(Arrays.asList(tp)); + Map beginningOffsets = consumer + .beginningOffsets(Collections.singletonList(tp)); + consumer.close(); + + if (beginningOffsets != null) { + return beginningOffsets.get(tp); } - /* - public Set listTopicsByCG(String consumerGroup, ConsumerType type) { - Set topicList = new HashSet<>(); - - if (type == null) { - throw new ApiException("Unknown Type " + type); - } + return -1; + } - if (type == ConsumerType.OLD) { - if (!isOldConsumerGroup(consumerGroup)) { - throw new RuntimeException(consumerGroup + " non-exist"); - } + public long getEndOffset(String topic, int partitionId) { + KafkaConsumer consumer = kafkaUtils.createNewConsumer(KafkaUtils.DEFAULTCP); + TopicPartition tp = new TopicPartition(topic, partitionId); + consumer.assign(Arrays.asList(tp)); + Map endOffsets = consumer.endOffsets(Collections.singletonList(tp)); + consumer.close(); - topicList = new HashSet<>( - CollectionConvertor.seqConvertJavaList(zkUtils.getTopicsByConsumerGroup(consumerGroup)) - ); - } else if (type == ConsumerType.NEW) { - if (!isNewConsumerGroup(consumerGroup)) { - throw new RuntimeException(consumerGroup + " non-exist!"); - } - - AdminClient adminClient = kafkaUtils.createAdminClient(); - - List consumerSummaryList = - CollectionConvertor.listConvertJavaList(adminClient.describeConsumerGroup(consumerGroup)); - //Nothing about this consumer group obtained, return an empty map directly - adminClient.close(); + if (endOffsets != null) { + return endOffsets.get(tp); + } - if (isConsumerGroupActive(consumerGroup, ConsumerType.NEW) && - consumerSummaryList.size() != 0) { + return -1; + } - //Get topic list and filter if topic is set - topicList.addAll(consumerSummaryList.stream().flatMap( - cs -> CollectionConvertor.listConvertJavaList(cs.assignment()).stream()) - .map(tp -> tp.topic()).distinct() - .collect(toList())); - } + private long getBeginningOffset(Node leader, String topic, int partitionId) { + return getOffsets(leader, topic, partitionId, kafka.api.OffsetRequest.EarliestTime()); + } - if (consumerSummaryList.size() == 0) { //PENDING Consumer Group - Map storageMap = storage.get(consumerGroup); - if (storageMap == null) { - return null; - } + private long getEndOffset(Node leader, String topic, int partitionId) { + return getOffsets(leader, topic, partitionId, kafka.api.OffsetRequest.LatestTime()); + } - //Fetch the topics involved by consumer. And filter it by topic name - topicList.addAll(storageMap.entrySet().stream() - .map(e -> e.getKey().topicPartition().topic()).distinct() - .collect(toList())); - } - } else { - throw new ApiException("Unknown Type " + type); - } - return topicList; + public Map countPartition(String topic) { + KafkaConsumer consumer = kafkaUtils.createNewConsumer(); + List piList = consumer.partitionsFor(topic); + Map result = piList.stream().flatMap(pi -> Arrays.stream(pi.replicas())) + .map(node -> node.id()).collect(Collectors.groupingBy( + Function.identity(), Collectors.counting() + )); + consumer.close(); + + return result; + } + + private boolean isConsumerGroupActive(String consumerGroup, ConsumerType type) { + if (type == ConsumerType.NEW) { + ConsumerGroupMeta groupMeta = getConsumerGroupMeta(consumerGroup); + ConsumerGroupState groupState = groupMeta.getState(); + if (groupState.equals(ConsumerGroupState.STABLE) || groupState + .equals(ConsumerGroupState.PREPARING_REBALANCE) || groupState + .equals(ConsumerGroupState.COMPLETING_REBALANCE)) { + return true; + } + if (groupState.equals(ConsumerGroupState.EMPTY) || groupState + .equals(ConsumerGroupState.DEAD)) { + return false; + } + throw new ApiException( + "Consumer group:" + consumerGroup + " state:" + groupState + " unkown."); + } else if (type == ConsumerType.OLD) { + return zkUtils.getConsumersInGroup(consumerGroup).nonEmpty(); + } else { + throw new ApiException("Unknown type " + type); } - - public Map> describeConsumerGroup(String consumerGroup, ConsumerType type) { - Map> result = new HashMap<>(); - Set topicList = listTopicsByCG(consumerGroup, type); - if (topicList == null) { - //Return empty result - return result; - } - if (type == ConsumerType.NEW) { - for (String topic : topicList) { - result.put(topic, describeNewCGByTopic(consumerGroup, topic)); - } - - } else if (type == ConsumerType.OLD) { - for (String topic : topicList) { - result.put(topic, describeOldCGByTopic(consumerGroup, topic)); - } - } - - return result; + } + + public HealthCheckResult healthCheck() { + String healthCheckTopic = kafkaConfig.getHealthCheckTopic(); + HealthCheckResult healthCheckResult = new HealthCheckResult(); + KafkaProducer producer = kafkaUtils.createProducer(); + KafkaConsumer consumer = kafkaUtils.createNewConsumerByTopic(healthCheckTopic); + + boolean healthCheckTopicExist = existTopic(healthCheckTopic); + log.info("HealthCheckTopic:" + healthCheckTopic + " existed:" + healthCheckTopicExist); + if (!healthCheckTopicExist) { + healthCheckResult.setStatus("unknown"); + healthCheckResult.setMsg("HealthCheckTopic: " + healthCheckTopic + + " Non-Exist. Please create it before doing health check."); + return healthCheckResult; } -*/ - public Map countPartition(String topic) { - KafkaConsumer consumer = kafkaUtils.createNewConsumer(); - List piList = consumer.partitionsFor(topic); - Map result = piList.stream().flatMap(pi -> Arrays.stream(pi.replicas())) - .map(node -> node.id()).collect(Collectors.groupingBy( - Function.identity(), Collectors.counting() - )); - consumer.close(); - - return result; + String message = "healthcheck_" + System.currentTimeMillis(); + ProducerRecord record = new ProducerRecord(healthCheckTopic, null, message); + log.info("Generate message:" + message); + try { + RecordMetadata recordMetadata = (RecordMetadata) producer.send(record).get(); + log.info("Message:" + message + " has been sent to Partition:" + recordMetadata.partition()); + } catch (Exception e) { + healthCheckResult.setStatus("error"); + healthCheckResult + .setMsg("Health Check: Produce Message Failure. Exception: " + e.getMessage()); + log.error("Health Check: Produce Message Failure.", e); + return healthCheckResult; + } finally { + producer.close(); } - private boolean isConsumerGroupActive(String consumerGroup, ConsumerType type) { - if (type == ConsumerType.NEW) { - AdminClient adminClient = kafkaUtils.createAdminClient(); - boolean isActive = CollectionConvertor.seqConvertJavaList(adminClient.listAllConsumerGroupsFlattened()).stream() - .map(GroupOverview::groupId).filter(c -> c.equals(consumerGroup)).count() == 1; - adminClient.close(); - return isActive; - } else if (type == ConsumerType.OLD) { - return AdminUtils.isConsumerGroupActive(zookeeperUtils.getZkUtils(), consumerGroup); + int retries = 30; + int noRecordsCount = 0; + while (true) { + final ConsumerRecords consumerRecords = + consumer.poll(1000); + if (consumerRecords.count() == 0) { + noRecordsCount++; + if (noRecordsCount > retries) { + break; } else { - throw new ApiException("Unknown type " + type); + continue; } - } - - public HealthCheckResult healthCheck() { - String healthCheckTopic = kafkaConfig.getHealthCheckTopic(); - HealthCheckResult healthCheckResult = new HealthCheckResult(); - KafkaProducer producer = kafkaUtils.createProducer(); - KafkaConsumer consumer = kafkaUtils.createNewConsumerByTopic(healthCheckTopic); - - boolean healthCheckTopicExist = existTopic(healthCheckTopic); - log.info("HealthCheckTopic:" + healthCheckTopic + " existed:" + healthCheckTopicExist); - if (!healthCheckTopicExist) { - healthCheckResult.setStatus("unknown"); - healthCheckResult.setMsg("HealthCheckTopic: " + healthCheckTopic + " Non-Exist. Please create it before doing health check."); - return healthCheckResult; - } - - String message = "healthcheck_" + System.currentTimeMillis(); - ProducerRecord record = new ProducerRecord(healthCheckTopic, null, message); - log.info("Generate message:" + message); - try { - RecordMetadata recordMetadata = (RecordMetadata) producer.send(record).get(); - log.info("Message:" + message + " has been sent to Partition:" + recordMetadata.partition()); - } catch (Exception e){ - healthCheckResult.setStatus("error"); - healthCheckResult.setMsg("Health Check: Produce Message Failure. Exception: " + e.getMessage()); - log.error("Health Check: Produce Message Failure.", e); - return healthCheckResult; - } finally { - producer.close(); - } - - int retries = 30; - int noRecordsCount = 0; - while (true) { - final ConsumerRecords consumerRecords = - consumer.poll(1000); - if (consumerRecords.count() == 0) { - noRecordsCount++; - if (noRecordsCount > retries) break; - else continue; - } - Iterator> iterator = consumerRecords.iterator(); - while(iterator.hasNext()) { - ConsumerRecord msg = iterator.next(); - log.info("Health Check: Fetch Message " + msg.value() + ", offset:" + msg.offset()); - if(msg.value().equals(message)) { - healthCheckResult.setStatus("ok"); - healthCheckResult.setMsg(message); - return healthCheckResult; - } - } - consumer.commitAsync(); - } - consumer.close(); - - if(healthCheckResult.getStatus() == null) { - healthCheckResult.setStatus("error"); - healthCheckResult.setMsg("Health Check: Consume Message Failure. Consumer can't fetch the message."); + } + Iterator> iterator = consumerRecords.iterator(); + while (iterator.hasNext()) { + ConsumerRecord msg = iterator.next(); + log.info("Health Check: Fetch Message " + msg.value() + ", offset:" + msg.offset()); + if (msg.value().equals(message)) { + healthCheckResult.setStatus("ok"); + healthCheckResult.setMsg(message); + return healthCheckResult; } - return healthCheckResult; + } + consumer.commitAsync(); } + consumer.close(); - public void printObjectInJson(Object object) { - try { - ObjectMapper objectMapper = new ObjectMapper(); - //objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY); - System.out.println("object:" + objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(object)); - } catch (JsonProcessingException jsonProcessingException) { - jsonProcessingException.printStackTrace(); - } + if (healthCheckResult.getStatus() == null) { + healthCheckResult.setStatus("error"); + healthCheckResult + .setMsg("Health Check: Consume Message Failure. Consumer can't fetch the message."); + } + return healthCheckResult; + } + + public void printObjectInJson(Object object) { + try { + ObjectMapper objectMapper = new ObjectMapper(); + //objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY); + System.out.println( + "object:" + objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(object)); + } catch (JsonProcessingException jsonProcessingException) { + jsonProcessingException.printStackTrace(); } + } } diff --git a/src/main/java/org/gnuhpc/bigdata/service/UserService.java b/src/main/java/org/gnuhpc/bigdata/service/UserService.java index 6f4fe64..84dfe34 100644 --- a/src/main/java/org/gnuhpc/bigdata/service/UserService.java +++ b/src/main/java/org/gnuhpc/bigdata/service/UserService.java @@ -42,12 +42,11 @@ public GeneralResponse addUser(User user) { return saveUserInfo(user); } else { log.info("Failed to add user. Reason : User " + username + " already exists."); - return new GeneralResponse(GeneralResponseState.failure, "Failed to add user. Reason : User " + username + " already exists."); + return GeneralResponse.builder().state(GeneralResponseState.failure).msg("Failed to add user. Reason : User " + username + " already exists.").build(); } } catch (IOException ioException) { log.error("Failed to add user " + username + ". Reason : " + ioException.getLocalizedMessage()); - return new GeneralResponse(GeneralResponseState.failure, - "Failed to add user " + username + ". Reason : " + ioException.getLocalizedMessage()); + return GeneralResponse.builder().state(GeneralResponseState.failure).msg("Failed to add user " + username + ". Reason : " + ioException.getLocalizedMessage()).build(); } } @@ -59,12 +58,11 @@ public GeneralResponse modifyUser(User user) { return saveUserInfo(user); } else { log.info("Failed to modify user. Reason : User " + username + " does not exist."); - return new GeneralResponse(GeneralResponseState.failure, "Failed to modify user. Reason : User " + username + " does not exist."); + return GeneralResponse.builder().state(GeneralResponseState.failure).msg("Failed to modify user. Reason : User " + username + " does not exist.").build(); } } catch (IOException ioException) { log.error("Failed to modify user " + username + ". Reason : " + ioException.getLocalizedMessage()); - return new GeneralResponse(GeneralResponseState.failure, - "Failed to modify user " + username + ". Reason : " + ioException.getLocalizedMessage()); + return GeneralResponse.builder().state(GeneralResponseState.failure).msg("Failed to modify user " + username + ". Reason : " + ioException.getLocalizedMessage()).build(); } } @@ -74,15 +72,14 @@ public GeneralResponse delUser(String username) { if (exist) { accounts.remove(username); CommonUtils.yamlWrite(WebSecurityConfig.SECURITY_FILE_PATH, accounts); - return new GeneralResponse(GeneralResponseState.success, "Delete user " + username + " successfully."); + return GeneralResponse.builder().state(GeneralResponseState.success).msg("Delete user " + username + " successfully.").build(); } else { log.info("Failed to delete user. Reason : User " + username + " does not exist."); - return new GeneralResponse(GeneralResponseState.failure, "Failed to delete user. Reason : User " + username + " does not exist."); + return GeneralResponse.builder().state(GeneralResponseState.failure).msg("Failed to delete user. Reason : User " + username + " does not exist.").build(); } } catch (IOException ioException) { log.error("Failed to delete user " + username + ". Reason : " + ioException.getLocalizedMessage()); - return new GeneralResponse(GeneralResponseState.failure, - "Failed to delete user " + username + ". Reason : " + ioException.getLocalizedMessage()); + return GeneralResponse.builder().state(GeneralResponseState.failure).msg("Failed to delete user " + username + ". Reason : " + ioException.getLocalizedMessage()).build(); } } @@ -103,6 +100,6 @@ public GeneralResponse saveUserInfo(User user) throws IOException { userInfo.put("role", user.getRole()); accounts.put(username, userInfo); CommonUtils.yamlWrite(WebSecurityConfig.SECURITY_FILE_PATH, accounts); - return new GeneralResponse(GeneralResponseState.success, "Save user " + username + " info successfully."); + return GeneralResponse.builder().state(GeneralResponseState.success).msg("Save user " + username + " info successfully.").build(); } } diff --git a/src/main/java/org/gnuhpc/bigdata/utils/KafkaUtils.java b/src/main/java/org/gnuhpc/bigdata/utils/KafkaUtils.java index f74aedd..1d66802 100644 --- a/src/main/java/org/gnuhpc/bigdata/utils/KafkaUtils.java +++ b/src/main/java/org/gnuhpc/bigdata/utils/KafkaUtils.java @@ -25,6 +25,7 @@ import java.util.*; import java.util.concurrent.ExecutionException; import org.apache.kafka.clients.admin.KafkaAdminClient; + /** * Created by gnuhpc on 2017/7/12. */ @@ -33,94 +34,119 @@ @Setter @Configuration public class KafkaUtils { - @Autowired - private KafkaConfig kafkaConfig; - @Autowired - private ZookeeperConfig zookeeperConfig; - - private AdminClient adminClient; - - private KafkaProducer producer; - private Properties prop; - - private static final String DEFAULTCP = "kafka-rest-consumergroup"; - - public void init(){ - prop = new Properties(); - prop.setProperty(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaConfig.getBrokers()); - prop.setProperty(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, - "org.apache.kafka.common.serialization.StringSerializer"); - prop.setProperty(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, - "org.apache.kafka.common.serialization.StringSerializer"); - producer = new KafkaProducer(prop); - log.info("Kafka initing..."); - - adminClient = AdminClient.create(prop); - } - - - public void destroy(){ - log.info("Kafka destorying..."); - } - - public KafkaConsumer createNewConsumer(){ - return createNewConsumer(DEFAULTCP); - } - - public KafkaConsumer createNewConsumer(String consumerGroup) { - Properties properties = new Properties(); - properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, getKafkaConfig().getBrokers()); - properties.put(ConsumerConfig.GROUP_ID_CONFIG, consumerGroup); - properties.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "false"); - properties.put(ConsumerConfig.SESSION_TIMEOUT_MS_CONFIG, "30000"); - properties.put(ConsumerConfig.MAX_PARTITION_FETCH_BYTES_CONFIG, "100000000"); - properties.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, "500"); - properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, - StringDeserializer.class.getCanonicalName()); - properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, - StringDeserializer.class.getCanonicalName()); - - return new KafkaConsumer(properties); - } - - public KafkaConsumer createNewConsumerByTopic(String topic){ - Properties properties = new Properties(); - properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, getKafkaConfig().getBrokers()); - properties.put(ConsumerConfig.GROUP_ID_CONFIG, DEFAULTCP); - properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, - StringDeserializer.class.getCanonicalName()); - properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, - StringDeserializer.class.getCanonicalName()); - KafkaConsumer kafkaConsumer = new KafkaConsumer(properties); - kafkaConsumer.subscribe(Collections.singletonList(topic)); - - return kafkaConsumer; - } - - public KafkaProducer createProducer() { - Properties prop = new Properties(); - prop.setProperty(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaConfig.getBrokers()); - prop.setProperty(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, - "org.apache.kafka.common.serialization.StringSerializer"); - prop.setProperty(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, - "org.apache.kafka.common.serialization.StringSerializer"); - prop.setProperty(ProducerConfig.RETRIES_CONFIG, "3"); - prop.setProperty(ProducerConfig.REQUEST_TIMEOUT_MS_CONFIG, "10000"); - producer = new KafkaProducer(prop); - - return producer; - } - - public Node getLeader(String topic, int partitionId) { - KafkaConsumer consumer = createNewConsumer(DEFAULTCP); - List tmList = consumer.partitionsFor(topic); - - PartitionInfo partitionInfo = tmList.stream().filter(pi -> pi.partition() == partitionId).findFirst().get(); - consumer.close(); - return partitionInfo.leader(); - } - public AdminClient createAdminClient() { - return AdminClient.createSimplePlaintext(getKafkaConfig().getBrokers()); + @Autowired + private KafkaConfig kafkaConfig; + @Autowired + private ZookeeperConfig zookeeperConfig; + + private AdminClient adminClient; + + private KafkaProducer producer; + private Properties prop; + + public static final String DEFAULTCP = "kafka-rest-consumergroup"; + + public void init() { + prop = new Properties(); + prop.setProperty(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaConfig.getBrokers()); + prop.setProperty(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, + "org.apache.kafka.common.serialization.StringSerializer"); + prop.setProperty(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, + "org.apache.kafka.common.serialization.StringSerializer"); + producer = new KafkaProducer(prop); + log.info("Kafka initing..."); + + adminClient = AdminClient.create(prop); + } + + + public void destroy() { + log.info("Kafka destorying..."); + } + + public KafkaConsumer createNewConsumer() { + return createNewConsumer(DEFAULTCP); + } + + public KafkaConsumer createNewConsumer(String consumerGroup) { + Properties properties = new Properties(); + properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, getKafkaConfig().getBrokers()); + properties.put(ConsumerConfig.GROUP_ID_CONFIG, consumerGroup); + properties.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "false"); + properties.put(ConsumerConfig.SESSION_TIMEOUT_MS_CONFIG, "30000"); + properties.put(ConsumerConfig.MAX_PARTITION_FETCH_BYTES_CONFIG, "100000000"); + properties.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, "5"); + properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, + StringDeserializer.class.getCanonicalName()); + properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, + StringDeserializer.class.getCanonicalName()); + + return new KafkaConsumer(properties); + } + + public KafkaConsumer createNewConsumer(String consumerGroup, String decoder) + throws ClassNotFoundException { + Properties properties = new Properties(); + properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, getKafkaConfig().getBrokers()); + properties.put(ConsumerConfig.GROUP_ID_CONFIG, consumerGroup); + properties.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "false"); + properties.put(ConsumerConfig.SESSION_TIMEOUT_MS_CONFIG, "30000"); + properties.put(ConsumerConfig.MAX_PARTITION_FETCH_BYTES_CONFIG, "100000000"); + properties.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, "5"); + if (decoder == null || decoder.isEmpty()) { + properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, + StringDeserializer.class.getCanonicalName()); + properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, + StringDeserializer.class.getCanonicalName()); + } else { + properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, + Class.forName(decoder).getCanonicalName()); + properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, + Class.forName(decoder).getCanonicalName()); } + return new KafkaConsumer(properties); + } + + public KafkaConsumer createNewConsumerByTopic(String topic) { + Properties properties = new Properties(); + properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, getKafkaConfig().getBrokers()); + properties.put(ConsumerConfig.GROUP_ID_CONFIG, DEFAULTCP); + properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, + StringDeserializer.class.getCanonicalName()); + properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, + StringDeserializer.class.getCanonicalName()); + KafkaConsumer kafkaConsumer = new KafkaConsumer(properties); + kafkaConsumer.subscribe(Collections.singletonList(topic)); + + return kafkaConsumer; + } + + public KafkaProducer createProducer() { + Properties prop = new Properties(); + prop.setProperty(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaConfig.getBrokers()); + prop.setProperty(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, + "org.apache.kafka.common.serialization.StringSerializer"); + prop.setProperty(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, + "org.apache.kafka.common.serialization.StringSerializer"); + prop.setProperty(ProducerConfig.RETRIES_CONFIG, "3"); + prop.setProperty(ProducerConfig.REQUEST_TIMEOUT_MS_CONFIG, "10000"); + producer = new KafkaProducer(prop); + + return producer; + } + + public Node getLeader(String topic, int partitionId) { + KafkaConsumer consumer = createNewConsumer(DEFAULTCP); + List tmList = consumer.partitionsFor(topic); + + PartitionInfo partitionInfo = tmList.stream().filter(pi -> pi.partition() == partitionId) + .findFirst().get(); + consumer.close(); + return partitionInfo.leader(); + } + + public AdminClient createAdminClient() { + return AdminClient.createSimplePlaintext(getKafkaConfig().getBrokers()); + } } diff --git a/src/main/resources/application-tina.yml b/src/main/resources/application-tina.yml index 5ff1e32..fcbb51b 100644 --- a/src/main/resources/application-tina.yml +++ b/src/main/resources/application-tina.yml @@ -1,5 +1,5 @@ kafka: - brokers: localhost:19092,localhost:19093,localhost:9094 + brokers: localhost:19092,localhost:19093,localhost:19095 offset: topic: "__consumer_offsets" partitions: 50 diff --git a/src/main/scala/org.gnuhpc.bigdata/CollectionConvertor.scala b/src/main/scala/org.gnuhpc.bigdata/CollectionConvertor.scala index a2ce20a..e84cc91 100644 --- a/src/main/scala/org.gnuhpc.bigdata/CollectionConvertor.scala +++ b/src/main/scala/org.gnuhpc.bigdata/CollectionConvertor.scala @@ -1,18 +1,22 @@ package org.gnuhpc.bigdata -import scala.collection.JavaConversions +import scala.collection.JavaConverters object CollectionConvertor { def seqConvertJavaList[A](seq: Seq[A]): java.util.List[A] = { - return JavaConversions.seqAsJavaList(seq) + return JavaConverters.seqAsJavaListConverter(seq).asJava } def mapConvertJavaMap[A, B](map: scala.collection.Map[A, B]): java.util.Map[A, B] = { - return JavaConversions.mapAsJavaMap(map); + return JavaConverters.mapAsJavaMapConverter(map).asJava } def listConvertJavaList[A](list: List[A]): java.util.List[A] = { - return JavaConversions.bufferAsJavaList(list.toBuffer); + return JavaConverters.bufferAsJavaListConverter(list.toBuffer).asJava; + } + + def optionListConvertJavaList[A](list: List[A]): java.util.List[A] = { + return JavaConverters.bufferAsJavaListConverter(list.toBuffer).asJava; } } From ca84ce87fe3b814ea67c10eb58f0f46d5afff03b Mon Sep 17 00:00:00 2001 From: tinawenqiao <315524513@qq.com> Date: Wed, 26 Dec 2018 10:19:35 +0800 Subject: [PATCH 03/24] Add api related with generate and execute reassign paritions. --- pom.xml | 2 +- .../bigdata/controller/KafkaController.java | 29 +- .../bigdata/service/KafkaAdminService.java | 328 +++++++++++------- 3 files changed, 230 insertions(+), 129 deletions(-) diff --git a/pom.xml b/pom.xml index 602ab0a..b46e5f2 100644 --- a/pom.xml +++ b/pom.xml @@ -99,7 +99,7 @@ org.springframework.boot spring-boot-starter-log4j - RELEASE + 1.3.8.RELEASE diff --git a/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java b/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java index cec4a14..feba45d 100644 --- a/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java +++ b/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java @@ -57,13 +57,13 @@ public int getControllerId() { @GetMapping("/topics") @ApiOperation(value = "List topics") - public List listTopics() throws InterruptedException, ExecutionException { + public List listTopics() { return kafkaAdminService.listTopics(); } @GetMapping("/topicsbrief") @ApiOperation(value = "List topics Brief") - public List listTopicBrief() throws InterruptedException, ExecutionException { + public List listTopicBrief() { return kafkaAdminService.listTopicBrief(); } @@ -115,22 +115,21 @@ public Map deleteTopicList(@RequestParam List t @PutMapping(value = "/topics/{topic}/conf") @ApiOperation(value = "Update topic configs") public Collection updateTopicConfig(@PathVariable String topic, - @RequestBody Properties props) - throws InterruptedException, ExecutionException { + @RequestBody Properties props) { return kafkaAdminService.updateTopicConf(topic, props); } @GetMapping(value = "/topics/{topic}/conf") @ApiOperation(value = "Get topic configs") - public Collection getTopicConfig(@PathVariable String topic) - throws InterruptedException, ExecutionException { + public Collection getTopicConfig(@PathVariable String topic) { + //Todo KIP-226 - Dynamic Broker Configuration dynamic confs are stored in zk /config/ return kafkaAdminService.getTopicConf(topic); } @GetMapping(value = "/topics/{topic}/conf/{key}") @ApiOperation(value = "Get topic config by key") public Properties getTopicConfigByKey(@PathVariable String topic, - @PathVariable String key) throws InterruptedException, ExecutionException { + @PathVariable String key) { return kafkaAdminService.getTopicConfByKey(topic, key); } @@ -138,7 +137,7 @@ public Properties getTopicConfigByKey(@PathVariable String topic, @ApiOperation(value = "Update a topic config by key") public Collection updateTopicConfigByKey(@PathVariable String topic, @PathVariable String key, - @PathVariable String value) throws InterruptedException, ExecutionException { + @PathVariable String value) { return kafkaAdminService.updateTopicConfByKey(topic, key, value); } @@ -148,6 +147,20 @@ public Map addPartition(@RequestBody List return kafkaAdminService.addPartitions(addPartitions); } + @PostMapping(value = "/partitions/reassign/generate") + @ApiOperation(value = "Generate plan for the partition reassignment") + public List generateReassignPartitions(@RequestBody ReassignWrapper reassignWrapper) { + return kafkaAdminService.generateReassignPartition(reassignWrapper); + + } + + @PutMapping(value = "/partitions/reassign/execute") + @ApiOperation(value = "Execute the partition reassignment") + public Map executeReassignPartitions( + @RequestBody String reassignStr) { + return kafkaAdminService.executeReassignPartition(reassignStr, -1l, -1l, 10000l); + } + /* @PostMapping(value = "/partitions/add") @ApiOperation(value = "Add a partition to the topic") diff --git a/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java b/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java index 5be08b2..b6e61de 100644 --- a/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java +++ b/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java @@ -1,9 +1,11 @@ package org.gnuhpc.bigdata.service; import static java.lang.String.format; +import static java.sql.DriverManager.println; import static java.util.stream.Collectors.toList; import static java.util.stream.Collectors.toMap; import static java.util.stream.Collectors.toSet; +import static org.apache.coyote.http11.Constants.A; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; @@ -15,6 +17,7 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonDeserializer; +import io.swagger.annotations.Api; import io.swagger.models.auth.In; import java.io.IOException; import java.text.ParseException; @@ -47,6 +50,9 @@ import kafka.admin.AdminUtils; import kafka.admin.ConsumerGroupCommand; import kafka.admin.RackAwareMode; +import kafka.admin.ReassignPartitionsCommand; +import kafka.admin.ReassignPartitionsCommand.Throttle; +import kafka.admin.ReassignmentStatus; import kafka.admin.TopicCommand; import kafka.api.PartitionOffsetRequestInfo; import kafka.cluster.Broker; @@ -57,10 +63,12 @@ import kafka.javaapi.OffsetResponse; import kafka.javaapi.consumer.SimpleConsumer; import kafka.utils.ZkUtils; +import kafka.zk.AdminZkClient; import kafka.zk.KafkaZkClient; import lombok.extern.log4j.Log4j; import org.apache.curator.framework.CuratorFramework; import org.apache.kafka.clients.admin.AdminClientConfig; +import org.apache.kafka.clients.admin.AlterConfigsResult; import org.apache.kafka.clients.admin.Config; import org.apache.kafka.clients.admin.ConfigEntry; import org.apache.kafka.clients.admin.CreatePartitionsResult; @@ -85,6 +93,7 @@ import org.apache.kafka.common.PartitionInfo; import org.apache.kafka.common.TopicPartition; import org.apache.kafka.common.TopicPartitionInfo; +import org.apache.kafka.common.TopicPartitionReplica; import org.apache.kafka.common.config.ConfigResource; import org.apache.kafka.common.errors.ApiException; import org.apache.kafka.common.errors.InvalidTopicException; @@ -105,6 +114,7 @@ import org.gnuhpc.bigdata.model.HealthCheckResult; import org.gnuhpc.bigdata.model.MemberDescription; import org.gnuhpc.bigdata.model.PartitionAssignmentState; +import org.gnuhpc.bigdata.model.ReassignWrapper; import org.gnuhpc.bigdata.model.Record; import org.gnuhpc.bigdata.model.TopicBrief; import org.gnuhpc.bigdata.model.TopicDetail; @@ -118,10 +128,15 @@ import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; import org.springframework.web.method.support.HandlerMethodReturnValueHandler; +import scala.Function0; +import scala.Int; +import scala.Tuple2; import scala.collection.JavaConverters; import scala.collection.Seq; -import scala.collection.generic.BitOperations.Int; +import scala.collection.mutable.ListBuffer; import scala.reflect.internal.Trees.New; +import scala.runtime.AbstractFunction0; +import scala.runtime.BoxedUnit; /** * Created by gnuhpc on 2017/7/17. @@ -135,6 +150,7 @@ public class KafkaAdminService { private static final int channelSocketTimeoutMs = 600; private static final int channelRetryBackoffMs = 600; private static final long kafkaAdminClientGetTimeoutMs = 30000; + private static final long kafkaAdminClientAlterTimeoutMs = 60000; private static final String CONSUMERPATHPREFIX = "/consumers/"; private static final String OFFSETSPATHPREFIX = "/offsets/"; @Autowired @@ -253,50 +269,64 @@ public TopicMeta createTopic(TopicDetail topic, String reassignStr) { return describeTopic(topic.getName()); } - public List listTopics() throws InterruptedException, ExecutionException { + public List listTopics() { List topicNamesList = new ArrayList(); topicNamesList.addAll(getAllTopics()); return topicNamesList; } - public Set getAllTopics() throws InterruptedException, ExecutionException { + public Set getAllTopics() { + Set topicNames; org.apache.kafka.clients.admin.AdminClient kafkaAdminClient = createKafkaAdminClient(); ListTopicsOptions options = new ListTopicsOptions(); // includes internal topics such as __consumer_offsets options.listInternal(true); ListTopicsResult topics = kafkaAdminClient.listTopics(options); - Set topicNames = topics.names().get(); - log.info("Current topics in this cluster: " + topicNames); + try { + topicNames = topics.names().get(kafkaAdminClientGetTimeoutMs, TimeUnit.MILLISECONDS); + log.info("Current topics in this cluster: " + topicNames); + } catch (Exception exception) { + log.warn("List topic exception : " + exception); + throw new ApiException("List topic exception : " + exception); + } kafkaAdminClient.close(); return topicNames; } - public List listTopicBrief() throws InterruptedException, ExecutionException { + public List listTopicBrief() { org.apache.kafka.clients.admin.AdminClient kafkaAdminClient = createKafkaAdminClient(); DescribeTopicsResult describeTopicsResult = kafkaAdminClient.describeTopics(listTopics()); - Map topicMap = describeTopicsResult.all().get(); - List result = topicMap.entrySet().parallelStream().map(e -> { - String topic = e.getKey(); - TopicDescription topicDescription = e.getValue(); - List topicPartitionInfoList = topicDescription - .partitions(); - int replicateCount = 0; - int isrCount = 0; - for (org.apache.kafka.common.TopicPartitionInfo topicPartitionInfo : topicPartitionInfoList) { - replicateCount += topicPartitionInfo.replicas().size(); - isrCount += topicPartitionInfo.isr().size(); - } - if (replicateCount == 0) { - return new TopicBrief(topic, topicDescription.partitions().size(), 0); - } else { - return new TopicBrief(topic, topicDescription.partitions().size(), - ((double) isrCount / replicateCount)); - } - }).collect(toList()); + Map topicMap; + List result; + try { + topicMap = describeTopicsResult.all() + .get(kafkaAdminClientGetTimeoutMs, TimeUnit.MILLISECONDS); + result = topicMap.entrySet().parallelStream().map(e -> { + String topic = e.getKey(); + TopicDescription topicDescription = e.getValue(); + List topicPartitionInfoList = topicDescription + .partitions(); + int replicateCount = 0; + int isrCount = 0; + for (org.apache.kafka.common.TopicPartitionInfo topicPartitionInfo : topicPartitionInfoList) { + replicateCount += topicPartitionInfo.replicas().size(); + isrCount += topicPartitionInfo.isr().size(); + } + if (replicateCount == 0) { + return new TopicBrief(topic, topicDescription.partitions().size(), 0); + } else { + return new TopicBrief(topic, topicDescription.partitions().size(), + ((double) isrCount / replicateCount)); + } + }).collect(toList()); + } catch (Exception exception) { + log.warn("Describe all topics exception:" + exception); + throw new ApiException("Describe all topics exception:" + exception); + } kafkaAdminClient.close(); return result; @@ -391,7 +421,7 @@ public Map deleteTopicList(List topicList) { try { deleteTopicsResult.all().get(kafkaAdminClientGetTimeoutMs, TimeUnit.MILLISECONDS); } catch (Exception exception) { - log.info("Delete topic exception:" + exception); + log.warn("Delete topic exception:" + exception); } finally { deleteTopicsResult.values().forEach((topic, result) -> { GeneralResponse generalResponse; @@ -409,32 +439,45 @@ public Map deleteTopicList(List topicList) { return deleteResults; } - public Collection describeConfig(ConfigResource.Type type, String name) - throws ExecutionException, InterruptedException { + public Collection describeConfig(ConfigResource.Type type, String name) { + Map configs; org.apache.kafka.clients.admin.AdminClient kafkaAdminClient = createKafkaAdminClient(); ConfigResource configResource = new ConfigResource(type, name); DescribeConfigsResult ret = kafkaAdminClient .describeConfigs(Collections.singleton(configResource)); - Map configs = ret.all().get(); + try { + configs = ret.all().get(kafkaAdminClientGetTimeoutMs, TimeUnit.MILLISECONDS); + } catch (Exception exception) { + log.warn("Describe config type:" + type + ", name:" + name + " exception:" + exception); + throw new ApiException("Describe config exception:" + exception.getLocalizedMessage()); + } kafkaAdminClient.close(); return configs.get(configResource).entries(); } - public void alterConfig(ConfigResource.Type type, String name, - Collection configEntries) - throws InterruptedException, ExecutionException { + public boolean alterConfig(ConfigResource.Type type, String name, + Collection configEntries) { org.apache.kafka.clients.admin.AdminClient kafkaAdminClient = createKafkaAdminClient(); Config config = new Config(configEntries); - kafkaAdminClient.alterConfigs(Collections.singletonMap(new ConfigResource(type, name), config)) - .all().get(); + AlterConfigsResult alterConfigsResult = kafkaAdminClient + .alterConfigs(Collections.singletonMap(new ConfigResource(type, name), config)); + + try { + alterConfigsResult.all().get(kafkaAdminClientAlterTimeoutMs, TimeUnit.MILLISECONDS); + } catch (Exception exception) { + log.warn("Alter config type:" + type + ", name:" + name + " exception:" + exception); + return false; + } + kafkaAdminClient.close(); + + return true; } public Collection updateTopicConf(@TopicExistConstraint String topic, - Properties props) - throws InterruptedException, ExecutionException { + Properties props) { Collection configEntries = props.entrySet().stream().map( e -> new ConfigEntry(e.getKey().toString(), e.getValue().toString())) .collect(Collectors.toList()); @@ -443,13 +486,11 @@ public Collection updateTopicConf(@TopicExistConstraint String topi return describeConfig(ConfigResource.Type.TOPIC, topic); } - public Collection getTopicConf(@TopicExistConstraint String topic) - throws InterruptedException, ExecutionException { + public Collection getTopicConf(@TopicExistConstraint String topic) { return describeConfig(ConfigResource.Type.TOPIC, topic); } - public Properties getTopicConfByKey(@TopicExistConstraint String topic, String key) - throws InterruptedException, ExecutionException { + public Properties getTopicConfByKey(@TopicExistConstraint String topic, String key) { Collection configEntries = describeConfig(ConfigResource.Type.TOPIC, topic); Properties returnProps = new Properties(); for (ConfigEntry entry : configEntries) { @@ -463,7 +504,7 @@ public Properties getTopicConfByKey(@TopicExistConstraint String topic, String k } public Collection updateTopicConfByKey(@TopicExistConstraint String topic, - String key, String value) throws InterruptedException, ExecutionException { + String key, String value) { alterConfig(ConfigResource.Type.TOPIC, topic, Collections.singletonList(new ConfigEntry(key, value))); @@ -934,7 +975,7 @@ public Map addPartitions(List addPartitio CreatePartitionsResult createPartitionsResult = kafkaAdminClient .createPartitions(newPartitionsMap); try { - createPartitionsResult.all().get(kafkaAdminClientGetTimeoutMs, TimeUnit.MILLISECONDS); + createPartitionsResult.all().get(kafkaAdminClientAlterTimeoutMs, TimeUnit.MILLISECONDS); } catch (Exception exception) { log.warn("Add partitions exception: " + exception); } finally { @@ -957,96 +998,141 @@ public Map addPartitions(List addPartitio } } - /* - public TopicMeta addPartition(@TopicExistConstraint String topic, AddPartition addPartition) { - List partitionMataData = AdminUtils.fetchTopicMetadataFromZk(topic, zkUtils).partitionMetadata(); - int numPartitions = partitionMataData.size(); - int numReplica = partitionMataData.get(0).replicas().size(); - List brokerIdList = listBrokers().stream().map(broker -> broker.getId()).collect(toList()); - List partitionIdList = partitionMataData.stream().map(p -> String.valueOf(p.partition())).collect(toList()); - String assignmentStr = addPartition.getReplicaAssignment(); - String toBeSetReplicaAssignmentStr = ""; - - if (assignmentStr != null && !assignmentStr.equals("")) { - //Check out of index ids in replica assignment string - String[] ids = addPartition.getReplicaAssignment().split(",|:"); - if (Arrays.stream(ids).filter(id -> brokerIdList.contains(id)).count() != 0) { - throw new InvalidTopicException("Topic " + topic + ": manual reassignment str has wrong id!"); - } + //Return + public List generateReassignPartition(ReassignWrapper reassignWrapper) { + Seq brokerSeq = JavaConverters.asScalaBufferConverter(reassignWrapper.getBrokers()).asScala() + .toSeq(); + // + Tuple2 resultTuple2 = ReassignPartitionsCommand + .generateAssignment(kafkaZkClient, brokerSeq, reassignWrapper.generateReassignJsonString(), + false); + List result = new ArrayList<>(); + HashMap emptyMap = new HashMap<>(); + result.add(ReassignPartitionsCommand + .formatAsReassignmentJson( + (scala.collection.Map>) resultTuple2._1(), + JavaConverters.mapAsScalaMapConverter(emptyMap).asScala())); + result.add(ReassignPartitionsCommand + .formatAsReassignmentJson( + (scala.collection.Map>) resultTuple2._2(), + JavaConverters.mapAsScalaMapConverter(emptyMap).asScala())); - //Check if any ids duplicated in one partition in replica assignment - String[] assignPartitions = addPartition.getReplicaAssignment().split(","); - if (Arrays.stream(assignPartitions).filter(p -> - Arrays.stream(p.split(":")).collect(Collectors.toSet()).size() - != p.split(":").length).count() - != 0) { - throw new InvalidTopicException("Topic " + topic + ": manual reassignment str has duplicated id in one partition!"); - } + return result; + } + + public Map executeReassignPartition(String reassignStr, + Long interBrokerThrottle, Long replicaAlterLogDirsThrottle, Long timeoutMs) { + //Set default value + interBrokerThrottle = (interBrokerThrottle == null) ? -1 : interBrokerThrottle; + replicaAlterLogDirsThrottle = + (replicaAlterLogDirsThrottle == null) ? -1 : replicaAlterLogDirsThrottle; + timeoutMs = (timeoutMs == null) ? 10000 : timeoutMs; - String replicaStr = Strings.repeat("0:", numReplica).replaceFirst(".$", ","); - toBeSetReplicaAssignmentStr = Strings.repeat(replicaStr, numPartitions) + addPartition.getReplicaAssignment(); + Tuple2 resultTuple2; + try { + //Parse and validate reassignment json string, return (partitionsToBeReassigned, replicaAssignment) + resultTuple2 = ReassignPartitionsCommand + .parseAndValidate(kafkaZkClient, reassignStr); + } catch (Exception exception) { + throw new ApiException("Bad Request. " + exception.getMessage()); + } + //Change list buffer to map + ListBuffer partitionsToBeReassignedList = (scala.collection.mutable.ListBuffer) resultTuple2 + ._1(); + scala.collection.mutable.HashMap> partitionsToBeReassignedMap = new scala.collection.mutable.HashMap<>(); + for (int i = 0; i < partitionsToBeReassignedList.size(); ++i) { + Tuple2 tup = (Tuple2) partitionsToBeReassignedList.apply(i); + partitionsToBeReassignedMap.put((TopicPartition) tup._1(), (Seq) tup._2()); + } + + org.apache.kafka.clients.admin.AdminClient kafkaAdminClient = createKafkaAdminClient(); + AdminZkClient adminZkClient = new AdminZkClient(kafkaZkClient); + + ReassignPartitionsCommand reassignPartitionsCommand = new ReassignPartitionsCommand( + kafkaZkClient, scala.Option.apply(null), + partitionsToBeReassignedMap, + (scala.collection.Map) resultTuple2._2(), adminZkClient); + Function0 postUpdateAction = new AbstractFunction0() { + @Override + public BoxedUnit apply() { + return null; + } + }; + Throttle throttle = new Throttle(interBrokerThrottle, replicaAlterLogDirsThrottle, + postUpdateAction); + + if (kafkaZkClient.reassignPartitionsInProgress()) { + reassignPartitionsCommand.maybeLimit(throttle); + throw new ApiException( + "Failed to reassign partitions because there is an existing assignment running."); + } else { + if (reassignPartitionsCommand.reassignPartitions(throttle, timeoutMs)) { + System.out.println("Successfully started reassignment of partitions."); } else { - toBeSetReplicaAssignmentStr = ""; + throw new ApiException("Failed to reassign partitions " + resultTuple2._1()); } + } +// return checkReassignStatus(reassignStr); - AdminUtils.addPartitions(zkUtils, topic, addPartition.getNumPartitionsAdded() + numPartitions, - toBeSetReplicaAssignmentStr, true, - RackAwareMode.Enforced$.MODULE$); + kafkaAdminClient.close(); + return new HashMap<>(); + } - return describeTopic(topic); + private void checkReassignStatus( + scala.collection.Map> partitionsToBeReassigned, + scala.collection.Map replicaAssignement) { + Map reassignedPartitionsStatus = checkIfPartitionReassignmentSucceeded( + partitionsToBeReassigned); + Map replicasReassignmentStatus = checkIfReplicaReassignmentSucceeded( + replicaAssignement); } - //Return - public List generateReassignPartition(ReassignWrapper reassignWrapper) { - Seq brokerSeq = JavaConverters.asScalaBufferConverter(reassignWrapper.getBrokers()).asScala().toSeq(); - // - Tuple2 resultTuple2 = ReassignPartitionsCommand.generateAssignment(zkUtils, brokerSeq, reassignWrapper.generateReassignJsonString(), false); - List result = new ArrayList<>(); - result.add(zkUtils.formatAsReassignmentJson((scala.collection.Map>) resultTuple2._2())); - result.add(zkUtils.formatAsReassignmentJson((scala.collection.Map>) resultTuple2._1())); + private Map checkIfPartitionReassignmentSucceeded( + scala.collection.Map> partitionsToBeReassigned) { + Map reassignedPartitionsStatus = new HashMap<>(); - return result; + return reassignedPartitionsStatus; } - public Map executeReassignPartition(String reassignStr) { - ReassignPartitionsCommand.executeAssignment( - zkUtils, - reassignStr - ); - return checkReassignStatus(reassignStr); - } + private Map checkIfReplicaReassignmentSucceeded( + scala.collection.Map replicaAssignement) { + Map replicasReassignmentStatus = new HashMap<>(); - public Map checkReassignStatus(String reassignStr) { - Map> partitionsToBeReassigned = JavaConverters.mapAsJavaMapConverter( - zkUtils.parsePartitionReassignmentData(reassignStr)).asJava(); - - Map> partitionsBeingReassigned = JavaConverters.mapAsJavaMapConverter( - zkUtils.getPartitionsBeingReassigned()).asJava().entrySet().stream().collect( - Collectors.toMap( - Map.Entry::getKey, - data -> data.getValue().newReplicas() - )); - - - java.util.Map reassignedPartitionsStatus = - partitionsToBeReassigned.entrySet().stream().collect(Collectors.toMap( - Map.Entry::getKey, - pbr -> ReassignPartitionsCommand.checkIfPartitionReassignmentSucceeded( - zkUtils, - pbr.getKey(), - pbr.getValue(), - JavaConverters.mapAsScalaMapConverter(partitionsToBeReassigned).asScala(), - JavaConverters.mapAsScalaMapConverter(partitionsBeingReassigned).asScala() - ) - )); - - - return reassignedPartitionsStatus.entrySet().stream().collect(Collectors.toMap( - Map.Entry::getKey, - r -> r.getValue().status() - )); + return replicasReassignmentStatus; } - */ + + /* + public Map checkReassignStatus(String reassignStr) { + Map> partitionsToBeReassigned = JavaConverters.mapAsJavaMapConverter( + zkUtils.parsePartitionReassignmentData(reassignStr)).asJava(); + + Map> partitionsBeingReassigned = JavaConverters.mapAsJavaMapConverter( + zkUtils.getPartitionsBeingReassigned()).asJava().entrySet().stream().collect( + Collectors.toMap( + Map.Entry::getKey, + data -> data.getValue().newReplicas() + )); + + + java.util.Map reassignedPartitionsStatus = + partitionsToBeReassigned.entrySet().stream().collect(Collectors.toMap( + Map.Entry::getKey, + pbr -> ReassignPartitionsCommand.checkIfPartitionReassignmentSucceeded( + zkUtils, + pbr.getKey(), + pbr.getValue(), + JavaConverters.mapAsScalaMapConverter(partitionsToBeReassigned).asScala(), + JavaConverters.mapAsScalaMapConverter(partitionsBeingReassigned).asScala() + ) + )); + + + return reassignedPartitionsStatus.entrySet().stream().collect(Collectors.toMap( + Map.Entry::getKey, + r -> r.getValue().status() + )); + } + */ public String getMessage(@TopicExistConstraint String topic, int partition, long offset, String decoder, String avroSchema) { KafkaConsumer consumer = kafkaUtils @@ -1216,6 +1302,7 @@ public GeneralResponse resetOffset(@TopicExistConstraint String topic, int parti offsetToBeReset = endOffset; log.info("Reset to" + consumer.position(tp)); } else if (isDateTime(offset)) { + //Reset offset by time SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Map timestampsToSearch = new HashMap<>(); try { @@ -1328,7 +1415,8 @@ public Map> getLastCommitTime( // Get New consumer commit time, from offset storage instance if (storage.get(consumerGroup) != null) { - Map storageResult = storage.get(consumerGroup); + Map storageResult = storage + .get(consumerGroup); result.put("new", (storageResult.entrySet().parallelStream() .filter(s -> s.getKey().topicPartition().topic().equals(topic)) .collect( From d7e7cc76750607358f3390e8fb1962d05b9d572f Mon Sep 17 00:00:00 2001 From: tinawenqiao <315524513@qq.com> Date: Fri, 28 Dec 2018 21:18:08 +0800 Subject: [PATCH 04/24] Add partition and replica reassign functions. Add get and modify dynamic conf of broker and topic. --- .../bigdata/constant/ReassignmentStatus.java | 30 ++ .../bigdata/controller/KafkaController.java | 167 ++++--- .../bigdata/model/CustomConfigEntry.java | 15 + .../org/gnuhpc/bigdata/model/TwoTuple.java | 14 + .../bigdata/service/KafkaAdminService.java | 465 ++++++++++++++++-- .../gnuhpc/bigdata/utils/ZookeeperUtils.java | 2 +- 6 files changed, 579 insertions(+), 114 deletions(-) create mode 100644 src/main/java/org/gnuhpc/bigdata/constant/ReassignmentStatus.java create mode 100644 src/main/java/org/gnuhpc/bigdata/model/CustomConfigEntry.java create mode 100644 src/main/java/org/gnuhpc/bigdata/model/TwoTuple.java diff --git a/src/main/java/org/gnuhpc/bigdata/constant/ReassignmentStatus.java b/src/main/java/org/gnuhpc/bigdata/constant/ReassignmentStatus.java new file mode 100644 index 0000000..e2d464a --- /dev/null +++ b/src/main/java/org/gnuhpc/bigdata/constant/ReassignmentStatus.java @@ -0,0 +1,30 @@ +package org.gnuhpc.bigdata.constant; + +public enum ReassignmentStatus { + ReassignmentFailed(-1, "Reassignment Failed"), ReassignmentInProgress(0, + "Reassignment In Progress"), ReassignmentCompleted(1, "Reassignment Completed"); + + private int status; + private String msg; + + private ReassignmentStatus(int status, String msg) { + this.status = status; + this.msg = msg; + } + + public static ReassignmentStatus valueOf(int status) { + ReassignmentStatus[] reassignmentStatusList = values(); + for (int i = 0; i describeCluster() { + return kafkaAdminService.describeCluster(); + } + @GetMapping(value = "/brokers") @ApiOperation(value = "List brokers in this cluster") public List listBrokers() { @@ -55,6 +78,53 @@ public int getControllerId() { return kafkaAdminService.getControllerId(); } + @GetMapping(value = "/brokers/logdirs") + @ApiOperation(value = "List log dirs by broker list") + public Map> listLogDirs( + @RequestParam(required = false) List brokerList) { + return kafkaAdminService.listLogDirsByBroker(brokerList); + } + + @GetMapping(value = "/brokers/logdirs/detail") + @ApiOperation(value = "Describe log dirs by broker list and topic list") + public Map> describeLogDirs( + @RequestParam(required = false) List brokerList, + @RequestParam(required = false) List topicList) { + return kafkaAdminService.describeLogDirsByBrokerAndTopic(brokerList, topicList); + } + + @GetMapping(value = "/brokers/replicalogdirs") + @ApiOperation(value = "Describe replicat log dirs.") + public Map describeReplicaLogDirs( + @RequestParam List replicas) { + return kafkaAdminService.describeReplicaLogDirs(replicas); + } + + @GetMapping(value = "/brokers/{brokerId}/conf") + @ApiOperation(value = "Get broker configs, including dynamic configs") + public Collection getBrokerConfig(@PathVariable int brokerId) { + return kafkaAdminService.getBrokerConf(brokerId); + } + + @GetMapping(value = "/brokers/{brokerId}/dynconf") + @ApiOperation(value = "Get broker dynamic configs") + public Properties getBrokerDynConfig(@PathVariable int brokerId) { + return kafkaAdminService.getConfigInZk(Type.BROKER, String.valueOf(brokerId)); + } + + @PutMapping(value = "/brokers/{brokerId}/dynconf") + @ApiOperation(value = "Update broker configs") + public Properties updateBrokerDynConfig(@PathVariable int brokerId, + @RequestBody Properties props) { + return kafkaAdminService.updateBrokerDynConf(brokerId, props); + } + + @DeleteMapping(value = "/brokers/{brokerId}/dynconf") + @ApiOperation(value = "Remove broker dynamic configs") + public void removeBrokerDynConfig(@PathVariable int brokerId, @RequestParam List configKeysToBeRemoved) { + kafkaAdminService.removeConfigInZk(Type.BROKER, String.valueOf(brokerId), configKeysToBeRemoved); + } + @GetMapping("/topics") @ApiOperation(value = "List topics") public List listTopics() { @@ -86,7 +156,7 @@ public boolean existTopic(@PathVariable String topic) { @ResponseStatus(HttpStatus.CREATED) @ApiOperation(value = "Write a message to the topic, for testing purpose") public GeneralResponse writeMessage(@PathVariable String topic, @RequestBody String message) { -// kafkaProducerService.send(topic, message); + kafkaProducerService.send(topic, message); return GeneralResponse.builder().state(GeneralResponseState.success) .msg(message + " has been sent").build(); } @@ -114,18 +184,23 @@ public Map deleteTopicList(@RequestParam List t @PutMapping(value = "/topics/{topic}/conf") @ApiOperation(value = "Update topic configs") - public Collection updateTopicConfig(@PathVariable String topic, + public Collection updateTopicConfig(@PathVariable String topic, @RequestBody Properties props) { return kafkaAdminService.updateTopicConf(topic, props); } @GetMapping(value = "/topics/{topic}/conf") @ApiOperation(value = "Get topic configs") - public Collection getTopicConfig(@PathVariable String topic) { - //Todo KIP-226 - Dynamic Broker Configuration dynamic confs are stored in zk /config/ + public Collection getTopicConfig(@PathVariable String topic) { return kafkaAdminService.getTopicConf(topic); } + @GetMapping(value = "/topics/{topic}/dynconf") + @ApiOperation(value = "Get topic dyn configs") + public Properties getTopicDynConfig(@PathVariable String topic) { + return kafkaAdminService.getConfigInZk(Type.TOPIC, topic); + } + @GetMapping(value = "/topics/{topic}/conf/{key}") @ApiOperation(value = "Get topic config by key") public Properties getTopicConfigByKey(@PathVariable String topic, @@ -135,7 +210,7 @@ public Properties getTopicConfigByKey(@PathVariable String topic, @PutMapping(value = "/topics/{topic}/conf/{key}={value}") @ApiOperation(value = "Update a topic config by key") - public Collection updateTopicConfigByKey(@PathVariable String topic, + public Collection updateTopicConfigByKey(@PathVariable String topic, @PathVariable String key, @PathVariable String value) { return kafkaAdminService.updateTopicConfByKey(topic, key, value); @@ -156,52 +231,23 @@ public List generateReassignPartitions(@RequestBody ReassignWrapper reas @PutMapping(value = "/partitions/reassign/execute") @ApiOperation(value = "Execute the partition reassignment") - public Map executeReassignPartitions( - @RequestBody String reassignStr) { - return kafkaAdminService.executeReassignPartition(reassignStr, -1l, -1l, 10000l); - } - - /* - @PostMapping(value = "/partitions/add") - @ApiOperation(value = "Add a partition to the topic") - public TopicMeta addPartition(@RequestBody AddPartition addPartition) { - String topic = addPartition.getTopic(); - isTopicExist(topic); - - if (addPartition.getReplicaAssignment() != null && !addPartition.getReplicaAssignment().equals("") && addPartition.getReplicaAssignment().split(",").length - != addPartition.getNumPartitionsAdded()) { - throw new InvalidTopicException("Topic " + topic + ": num of partitions added not equal to manual reassignment str!"); - } - - if (addPartition.getNumPartitionsAdded() == 0) { - throw new InvalidTopicException("Num of paritions added must be specified and should not be 0"); - } - return kafkaAdminService.addPartition(topic, addPartition); - } - - @PostMapping(value = "/partitions/reassign/generate") - @ApiOperation(value = "Generate plan for the partition reassignment") - public List generateReassignPartitions(@RequestBody ReassignWrapper reassignWrapper) { - return kafkaAdminService.generateReassignPartition(reassignWrapper); - - } - - @PutMapping(value = "/partitions/reassign/execute") - @ApiOperation(value = "Execute the partition reassignment") - public Map executeReassignPartitions( - @RequestBody String reassignStr) { - return kafkaAdminService.executeReassignPartition(reassignStr); + public Map executeReassignPartitions( + @RequestBody String reassignStr, long interBrokerThrottle, long replicaAlterLogDirsThrottle, + long timeoutMs) { + return kafkaAdminService + .executeReassignPartition(reassignStr, interBrokerThrottle, replicaAlterLogDirsThrottle, + timeoutMs); } @PutMapping(value = "/partitions/reassign/check") @ApiOperation(value = "Check the partition reassignment process") @ApiResponses(value = {@ApiResponse(code = 1, message = "Reassignment Completed"), - @ApiResponse(code = 0, message = "Reassignment In Progress"), - @ApiResponse(code = -1, message = "Reassignment Failed")}) - public Map checkReassignPartitions(@RequestBody String reassignStr) { - return kafkaAdminService.checkReassignStatus(reassignStr); + @ApiResponse(code = 0, message = "Reassignment In Progress"), + @ApiResponse(code = -1, message = "Reassignment Failed")}) + public Map checkReassignPartitions(@RequestBody String reassignStr) { + return kafkaAdminService.checkReassignStatusByStr(reassignStr); } - */ + @GetMapping(value = "/consumergroups") @ApiOperation(value = "List all consumer groups from zk and kafka") public Map> listAllConsumerGroups( @@ -268,11 +314,10 @@ public GeneralResponse resetOffset(@PathVariable String topic, @PathVariable String consumergroup, @PathVariable @ApiParam( value = "[earliest/latest/{long}/yyyy-MM-dd HH:mm:ss] can be supported. The date type is only valid for new consumer group.") String offset, - @PathVariable ConsumerType type) throws InterruptedException, ExecutionException { + @PathVariable ConsumerType type) { return kafkaAdminService.resetOffset(topic, partition, consumergroup, type, offset); } - @GetMapping(value = "/consumergroup/{consumergroup}/{type}/topic/{topic}/lastcommittime") public Map> getLastCommitTimestamp( @PathVariable String consumergroup, diff --git a/src/main/java/org/gnuhpc/bigdata/model/CustomConfigEntry.java b/src/main/java/org/gnuhpc/bigdata/model/CustomConfigEntry.java new file mode 100644 index 0000000..d855c1f --- /dev/null +++ b/src/main/java/org/gnuhpc/bigdata/model/CustomConfigEntry.java @@ -0,0 +1,15 @@ +package org.gnuhpc.bigdata.model; + +import lombok.AllArgsConstructor; +import lombok.Data; +import org.apache.kafka.clients.admin.ConfigEntry.ConfigSource; + +@Data +@AllArgsConstructor +public class CustomConfigEntry { + private String name; + private String value; + private boolean isSensitive; + private boolean isReadOnly; + private ConfigSource source; +} diff --git a/src/main/java/org/gnuhpc/bigdata/model/TwoTuple.java b/src/main/java/org/gnuhpc/bigdata/model/TwoTuple.java new file mode 100644 index 0000000..cf361bf --- /dev/null +++ b/src/main/java/org/gnuhpc/bigdata/model/TwoTuple.java @@ -0,0 +1,14 @@ +package org.gnuhpc.bigdata.model; + +import lombok.Data; + +@Data +public class TwoTuple { + private A first; + private B second; + + public TwoTuple(A a, B b) { + this.first = a; + this.second = b; + } +} diff --git a/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java b/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java index b6e61de..f334a4a 100644 --- a/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java +++ b/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java @@ -1,11 +1,9 @@ package org.gnuhpc.bigdata.service; import static java.lang.String.format; -import static java.sql.DriverManager.println; import static java.util.stream.Collectors.toList; import static java.util.stream.Collectors.toMap; import static java.util.stream.Collectors.toSet; -import static org.apache.coyote.http11.Constants.A; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; @@ -17,8 +15,6 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonDeserializer; -import io.swagger.annotations.Api; -import io.swagger.models.auth.In; import java.io.IOException; import java.text.ParseException; import java.text.SimpleDateFormat; @@ -38,9 +34,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; import java.util.function.Function; -import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; import javax.annotation.PostConstruct; @@ -52,7 +46,6 @@ import kafka.admin.RackAwareMode; import kafka.admin.ReassignPartitionsCommand; import kafka.admin.ReassignPartitionsCommand.Throttle; -import kafka.admin.ReassignmentStatus; import kafka.admin.TopicCommand; import kafka.api.PartitionOffsetRequestInfo; import kafka.cluster.Broker; @@ -62,6 +55,9 @@ import kafka.javaapi.OffsetRequest; import kafka.javaapi.OffsetResponse; import kafka.javaapi.consumer.SimpleConsumer; +import kafka.log.LogConfig; +import kafka.server.ConfigType; +import kafka.server.DynamicConfig; import kafka.utils.ZkUtils; import kafka.zk.AdminZkClient; import kafka.zk.KafkaZkClient; @@ -73,7 +69,14 @@ import org.apache.kafka.clients.admin.ConfigEntry; import org.apache.kafka.clients.admin.CreatePartitionsResult; import org.apache.kafka.clients.admin.DeleteTopicsResult; +import org.apache.kafka.clients.admin.DescribeClusterOptions; +import org.apache.kafka.clients.admin.DescribeClusterResult; import org.apache.kafka.clients.admin.DescribeConfigsResult; +import org.apache.kafka.clients.admin.DescribeLogDirsOptions; +import org.apache.kafka.clients.admin.DescribeLogDirsResult; +import org.apache.kafka.clients.admin.DescribeReplicaLogDirsOptions; +import org.apache.kafka.clients.admin.DescribeReplicaLogDirsResult; +import org.apache.kafka.clients.admin.DescribeReplicaLogDirsResult.ReplicaLogDirInfo; import org.apache.kafka.clients.admin.DescribeTopicsResult; import org.apache.kafka.clients.admin.KafkaAdminClient; import org.apache.kafka.clients.admin.ListTopicsOptions; @@ -83,7 +86,6 @@ import org.apache.kafka.clients.consumer.ConsumerRecord; import org.apache.kafka.clients.consumer.ConsumerRecords; import org.apache.kafka.clients.consumer.KafkaConsumer; -import org.apache.kafka.clients.consumer.OffsetAndMetadata; import org.apache.kafka.clients.consumer.OffsetAndTimestamp; import org.apache.kafka.clients.producer.KafkaProducer; import org.apache.kafka.clients.producer.ProducerRecord; @@ -95,20 +97,24 @@ import org.apache.kafka.common.TopicPartitionInfo; import org.apache.kafka.common.TopicPartitionReplica; import org.apache.kafka.common.config.ConfigResource; +import org.apache.kafka.common.config.ConfigResource.Type; import org.apache.kafka.common.errors.ApiException; import org.apache.kafka.common.errors.InvalidTopicException; import org.apache.kafka.common.internals.Topic; import org.apache.kafka.common.protocol.Errors; +import org.apache.kafka.common.requests.DescribeLogDirsResponse.LogDirInfo; import org.gnuhpc.bigdata.CollectionConvertor; import org.gnuhpc.bigdata.componet.OffsetStorage; import org.gnuhpc.bigdata.config.KafkaConfig; import org.gnuhpc.bigdata.constant.ConsumerGroupState; import org.gnuhpc.bigdata.constant.ConsumerType; import org.gnuhpc.bigdata.constant.GeneralResponseState; +import org.gnuhpc.bigdata.constant.ReassignmentStatus; import org.gnuhpc.bigdata.model.AddPartition; import org.gnuhpc.bigdata.model.BrokerInfo; import org.gnuhpc.bigdata.model.ConsumerGroupDesc; import org.gnuhpc.bigdata.model.ConsumerGroupMeta; +import org.gnuhpc.bigdata.model.CustomConfigEntry; import org.gnuhpc.bigdata.model.CustomTopicPartitionInfo; import org.gnuhpc.bigdata.model.GeneralResponse; import org.gnuhpc.bigdata.model.HealthCheckResult; @@ -119,6 +125,7 @@ import org.gnuhpc.bigdata.model.TopicBrief; import org.gnuhpc.bigdata.model.TopicDetail; import org.gnuhpc.bigdata.model.TopicMeta; +import org.gnuhpc.bigdata.model.TwoTuple; import org.gnuhpc.bigdata.utils.KafkaUtils; import org.gnuhpc.bigdata.utils.ZookeeperUtils; import org.gnuhpc.bigdata.validator.ConsumerGroupExistConstraint; @@ -127,14 +134,11 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; -import org.springframework.web.method.support.HandlerMethodReturnValueHandler; import scala.Function0; -import scala.Int; import scala.Tuple2; import scala.collection.JavaConverters; import scala.collection.Seq; import scala.collection.mutable.ListBuffer; -import scala.reflect.internal.Trees.New; import scala.runtime.AbstractFunction0; import scala.runtime.BoxedUnit; @@ -173,6 +177,8 @@ public class KafkaAdminService { private KafkaZkClient kafkaZkClient; + private AdminZkClient adminZkClient; + // private org.apache.kafka.clients.admin.AdminClient kafkaAdminClient; //For Json serialized private Gson gson; @@ -199,6 +205,7 @@ private void init() { this.zkUtils = zookeeperUtils.getZkUtils(); this.zkClient = zookeeperUtils.getCuratorClient(); this.kafkaZkClient = zookeeperUtils.getKafkaZkClient(); + this.adminZkClient = new AdminZkClient(kafkaZkClient); // Properties adminClientProp = new Properties(); // adminClientProp.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaConfig.getBrokers()); // //TODO eliminate the init connection @@ -336,6 +343,47 @@ public boolean existTopic(String topicName) { return kafkaZkClient.topicExists(topicName); } + public Map describeCluster() { + Map clusterDetail = new HashMap<>(); + + org.apache.kafka.clients.admin.AdminClient kafkaAdminClient = createKafkaAdminClient(); + DescribeClusterOptions describeClusterOptions = new DescribeClusterOptions() + .timeoutMs((int) kafkaAdminClientGetTimeoutMs); + + DescribeClusterResult describeClusterResult = kafkaAdminClient + .describeCluster(describeClusterOptions); + + KafkaFuture clusterIdFuture = describeClusterResult.clusterId(); + KafkaFuture controllerFuture = describeClusterResult.controller(); + KafkaFuture> nodesFuture = describeClusterResult.nodes(); + String clusterId = ""; + Node controller = null; + Collection nodes = new ArrayList<>(); + + try { + clusterId = clusterIdFuture.get(kafkaAdminClientGetTimeoutMs, TimeUnit.MILLISECONDS); + controller = controllerFuture.get(kafkaAdminClientGetTimeoutMs, TimeUnit.MILLISECONDS); + nodes = nodesFuture.get(kafkaAdminClientGetTimeoutMs, TimeUnit.MILLISECONDS); + } catch (Exception exception) { + log.warn("Describe cluster exception:" + exception); + throw new ApiException("Describe cluster exception:" + exception); + } finally { + if (clusterIdFuture.isDone() && !clusterIdFuture.isCompletedExceptionally()) { + clusterDetail.put("clusterId", clusterId); + } + if (controllerFuture.isDone() && !controllerFuture.isCompletedExceptionally()) { + clusterDetail.put("controllerId", controller); + } + if (nodesFuture.isDone() && !nodesFuture.isCompletedExceptionally()) { + clusterDetail.put("nodes", nodes); + } + } + + kafkaAdminClient.close(); + + return clusterDetail; + } + public List listBrokers() { List brokerList = CollectionConvertor .seqConvertJavaList(kafkaZkClient.getAllBrokersInCluster()); @@ -372,6 +420,182 @@ public int getControllerId() { return controllerId; } + public Map> listLogDirsByBroker(List brokerList) { + Map> logDirList = new HashMap<>(); + + Map> logDirInfosByBroker = describeLogDirsByBrokerAndTopic( + brokerList, null); + logDirInfosByBroker.entrySet().forEach(e -> { + List dirList = e.getValue().keySet().stream().collect(Collectors.toList()); + Collections.sort(dirList); + logDirList.put(e.getKey(), dirList); + }); + + return logDirList; + } + + public Map> describeLogDirsByBrokerAndTopic( + List brokerList, List topicList) { + org.apache.kafka.clients.admin.AdminClient kafkaAdminClient = createKafkaAdminClient(); + + List brokerIdsInCluster = listBrokers().stream().map(brokerInfo -> brokerInfo.getId()) + .collect(Collectors.toList()); + + if (brokerList == null || brokerList.isEmpty()) { + brokerList = brokerIdsInCluster; + } else { + for (int brokerId : brokerList) { + if (!brokerIdsInCluster.contains(brokerId)) { + throw new ApiException("Bad Request. Broker Id:" + brokerId + " non-exist."); + } + } + } + + if (topicList != null && !topicList.isEmpty()) { + for (String topic : topicList) { + if (!existTopic(topic)) { + throw new ApiException("Bad Request. Topic:" + topic + " non-exist."); + } + } + } + + DescribeLogDirsOptions describeLogDirsOptions = new DescribeLogDirsOptions() + .timeoutMs((int) kafkaAdminClientGetTimeoutMs); + DescribeLogDirsResult describeLogDirsResult = kafkaAdminClient + .describeLogDirs(brokerList, describeLogDirsOptions); + Map> logDirInfosByBroker = new HashMap<>(); + + try { + logDirInfosByBroker = describeLogDirsResult.all() + .get(kafkaAdminClientGetTimeoutMs, TimeUnit.MILLISECONDS); + } catch (Exception exception) { + log.warn("Describe log dirs exception:" + exception); + throw new ApiException("Describe log dirs exception:" + exception); + } finally { + if (topicList != null && !topicList.isEmpty()) { + logDirInfosByBroker.entrySet().forEach(e -> { + e.getValue().entrySet().forEach(m -> { + m.getValue().replicaInfos.entrySet().removeIf(t -> + !topicList.contains(t.getKey().topic())); + }); + }); + } + } + + kafkaAdminClient.close(); + + return logDirInfosByBroker; + } + + public Map describeReplicaLogDirs( + List replicas) { + org.apache.kafka.clients.admin.AdminClient kafkaAdminClient = createKafkaAdminClient(); + Map replicaLogDirInfoMap; + + DescribeReplicaLogDirsOptions describeReplicaLogDirsOptions = new DescribeReplicaLogDirsOptions() + .timeoutMs((int) kafkaAdminClientGetTimeoutMs); + DescribeReplicaLogDirsResult describeReplicaLogDirsResult = kafkaAdminClient + .describeReplicaLogDirs(replicas, describeReplicaLogDirsOptions); + + try { + replicaLogDirInfoMap = describeReplicaLogDirsResult.all() + .get(kafkaAdminClientGetTimeoutMs, TimeUnit.MILLISECONDS); + } catch (Exception exception) { + log.warn("Describe replica log dirs exception:" + exception); + throw new ApiException("Describe replica log dirs exception:" + exception); + } + + kafkaAdminClient.close(); + + return replicaLogDirInfoMap; + } + + public Collection getBrokerConf(int brokerId) { + String broker = String.valueOf(brokerId); + Properties dynamicProps = getConfigInZk(Type.BROKER, broker); + + Collection configs = describeConfig(Type.BROKER, String.valueOf(brokerId)); + + return mergeConfigs(configs, dynamicProps); + } + + public Properties getConfigInZk(ConfigResource.Type type, String name) { + Properties properties = new Properties(); + + if (type.equals(Type.BROKER)) { + properties = adminZkClient.fetchEntityConfig(ConfigType.Broker(), name); + } else if (type.equals(Type.TOPIC)) { + properties = adminZkClient.fetchEntityConfig(ConfigType.Topic(), name); + } + + return properties; + } + + private Collection mergeConfigs(Collection configs, + Properties dynamicProps) { + Collection mergedConfigs = new ArrayList<>(); + CustomConfigEntry newConfigEntry; + + for (ConfigEntry entry : configs) { + String key = entry.name(); + String value = entry.value(); + if (dynamicProps.containsKey(key)) { + value = dynamicProps.getProperty(key); + } + newConfigEntry = new CustomConfigEntry(key, value, entry.isSensitive(), + entry.isReadOnly(), entry.source()); + mergedConfigs.add(newConfigEntry); + } + + return mergedConfigs; + } + + private void validateConfigs(Type type, String name, Properties props, + List keysToBeChecked) { + for (Object configKey : keysToBeChecked) { + if (!props.containsKey(configKey)) { + throw new ApiException(type.name() + ":" + name + " has no such property:" + configKey); + } + } + } + + public Properties updateBrokerDynConf(int brokerId, Properties propsToBeUpdated) { + Properties props = getConfigInZk(Type.BROKER, String.valueOf(brokerId)); + + for (String key:propsToBeUpdated.stringPropertyNames()) { + if (props.containsKey(key)) { + props.setProperty(key, propsToBeUpdated.getProperty(key)); + } else { + props.put(key, propsToBeUpdated.getProperty(key)); + } + } + + adminZkClient.changeBrokerConfig( + JavaConverters.asScalaBufferConverter(Collections.singletonList((Object) brokerId)) + .asScala() + .toSeq(), props); + + return getConfigInZk(Type.BROKER, String.valueOf(brokerId)); + } + + public void removeConfigInZk(Type type, String name, List configKeysToBeRemoved) { + Properties props = getConfigInZk(type, name); + + validateConfigs(Type.BROKER, String.valueOf(name), props, configKeysToBeRemoved); + + props.entrySet().removeIf(entry -> configKeysToBeRemoved.contains(entry.getKey())); + + if (type.equals(Type.BROKER)) { + int brokerId = Integer.parseInt(name); + adminZkClient.changeBrokerConfig( + JavaConverters.asScalaBufferConverter(Collections.singletonList((Object) brokerId)) + .asScala() + .toSeq(), props); + } else if (type.equals(Type.TOPIC)) { + adminZkClient.changeTopicConfig(name, props); + } + } + public TopicMeta describeTopic(@TopicExistConstraint String topicName) { org.apache.kafka.clients.admin.AdminClient kafkaAdminClient = createKafkaAdminClient(); @@ -476,18 +700,22 @@ public boolean alterConfig(ConfigResource.Type type, String name, return true; } - public Collection updateTopicConf(@TopicExistConstraint String topic, + public Collection updateTopicConf(@TopicExistConstraint String topic, Properties props) { Collection configEntries = props.entrySet().stream().map( e -> new ConfigEntry(e.getKey().toString(), e.getValue().toString())) .collect(Collectors.toList()); alterConfig(ConfigResource.Type.TOPIC, topic, configEntries); - return describeConfig(ConfigResource.Type.TOPIC, topic); + return getTopicConf(topic); } - public Collection getTopicConf(@TopicExistConstraint String topic) { - return describeConfig(ConfigResource.Type.TOPIC, topic); + public Collection getTopicConf(@TopicExistConstraint String topic) { + Properties dynamicProps = getConfigInZk(Type.TOPIC, topic); + + Collection configs = describeConfig(Type.TOPIC, String.valueOf(topic)); + + return mergeConfigs(configs, dynamicProps); } public Properties getTopicConfByKey(@TopicExistConstraint String topic, String key) { @@ -503,12 +731,12 @@ public Properties getTopicConfByKey(@TopicExistConstraint String topic, String k return null; } - public Collection updateTopicConfByKey(@TopicExistConstraint String topic, + public Collection updateTopicConfByKey(@TopicExistConstraint String topic, String key, String value) { alterConfig(ConfigResource.Type.TOPIC, topic, Collections.singletonList(new ConfigEntry(key, value))); - return describeConfig(ConfigResource.Type.TOPIC, topic); + return getTopicConf(topic); } public Map> listAllConsumerGroups(ConsumerType type) { @@ -1020,7 +1248,7 @@ public List generateReassignPartition(ReassignWrapper reassignWrapper) { return result; } - public Map executeReassignPartition(String reassignStr, + public Map executeReassignPartition(String reassignStr, Long interBrokerThrottle, Long replicaAlterLogDirsThrottle, Long timeoutMs) { //Set default value interBrokerThrottle = (interBrokerThrottle == null) ? -1 : interBrokerThrottle; @@ -1028,30 +1256,21 @@ public Map executeReassignPartition(String reassignS (replicaAlterLogDirsThrottle == null) ? -1 : replicaAlterLogDirsThrottle; timeoutMs = (timeoutMs == null) ? 10000 : timeoutMs; - Tuple2 resultTuple2; - try { - //Parse and validate reassignment json string, return (partitionsToBeReassigned, replicaAssignment) - resultTuple2 = ReassignPartitionsCommand - .parseAndValidate(kafkaZkClient, reassignStr); - } catch (Exception exception) { - throw new ApiException("Bad Request. " + exception.getMessage()); - } - //Change list buffer to map - ListBuffer partitionsToBeReassignedList = (scala.collection.mutable.ListBuffer) resultTuple2 - ._1(); - scala.collection.mutable.HashMap> partitionsToBeReassignedMap = new scala.collection.mutable.HashMap<>(); - for (int i = 0; i < partitionsToBeReassignedList.size(); ++i) { - Tuple2 tup = (Tuple2) partitionsToBeReassignedList.apply(i); - partitionsToBeReassignedMap.put((TopicPartition) tup._1(), (Seq) tup._2()); - } - org.apache.kafka.clients.admin.AdminClient kafkaAdminClient = createKafkaAdminClient(); - AdminZkClient adminZkClient = new AdminZkClient(kafkaZkClient); + + TwoTuple>, + scala.collection.Map> reassignPlan = genReassignPlan( + reassignStr); + scala.collection.mutable.HashMap> partitionsToBeReassignedMap = reassignPlan + .getFirst(); + scala.collection.Map replicatAssignment = reassignPlan + .getSecond(); ReassignPartitionsCommand reassignPartitionsCommand = new ReassignPartitionsCommand( - kafkaZkClient, scala.Option.apply(null), + kafkaZkClient, scala.Option.apply(kafkaAdminClient), partitionsToBeReassignedMap, - (scala.collection.Map) resultTuple2._2(), adminZkClient); + replicatAssignment, adminZkClient); + Function0 postUpdateAction = new AbstractFunction0() { @Override public BoxedUnit apply() { @@ -1069,38 +1288,180 @@ public BoxedUnit apply() { if (reassignPartitionsCommand.reassignPartitions(throttle, timeoutMs)) { System.out.println("Successfully started reassignment of partitions."); } else { - throw new ApiException("Failed to reassign partitions " + resultTuple2._1()); + throw new ApiException("Failed to reassign partitions " + reassignPlan.getFirst()); } } -// return checkReassignStatus(reassignStr); - kafkaAdminClient.close(); - return new HashMap<>(); + return checkReassignStatus(partitionsToBeReassignedMap, replicatAssignment); } - private void checkReassignStatus( + private Map checkReassignStatus( scala.collection.Map> partitionsToBeReassigned, - scala.collection.Map replicaAssignement) { - Map reassignedPartitionsStatus = checkIfPartitionReassignmentSucceeded( + scala.collection.Map replicaAssignement) { + Map reassignStatus = new HashMap<>(); + Map reassignedPartitionsStatus = checkIfPartitionReassignmentSucceeded( partitionsToBeReassigned); - Map replicasReassignmentStatus = checkIfReplicaReassignmentSucceeded( - replicaAssignement); + Map replicasReassignmentStatus = checkIfReplicaReassignmentSucceeded( + CollectionConvertor.mapConvertJavaMap(replicaAssignement)); + + reassignStatus.put("partitionsReassignStatus", reassignedPartitionsStatus); + reassignStatus.put("replicasReassignStatus", replicasReassignmentStatus); + + if (removeThrottle(reassignedPartitionsStatus, replicasReassignmentStatus)) { + reassignStatus.put("removeThrottle", true); + } else { + reassignStatus.put("removeThrottle", false); + } + + return reassignStatus; + } + + private boolean removeThrottle(Map reassignedPartitionsStatus, Map replicasReassignmentStatus) { + for(Map.Entry entry: reassignedPartitionsStatus.entrySet()) { + //Partitions reassignemnt not all done + if (!entry.getValue().equals(ReassignmentStatus.ReassignmentCompleted.code())) { + return false; + } + } + + for(Map.Entry entry: replicasReassignmentStatus.entrySet()) { + //Replica reassignement not all done + if (!entry.getValue().equals(ReassignmentStatus.ReassignmentCompleted.code())) { + return false; + } + } + + List brokerList = CollectionConvertor + .seqConvertJavaList(kafkaZkClient.getAllBrokersInCluster()); + for (Broker broker:brokerList) { + int brokerId = broker.id(); + List keysToBeRemoved = new ArrayList<>(); +// keysToBeRemoved.add(DynamicConfig.Broker.LeaderReplicationThrottledRateProp()); +// keysToBeRemoved.add(DynamicConfig.Broker.FollowerReplicationThrottledRateProp()); +// keysToBeRemoved.add(DynamicConfig.Broker.ReplicaAlterLogDirsIoMaxBytesPerSecondProp()); + keysToBeRemoved.add("leader.replication.throttled.rate"); + keysToBeRemoved.add("follower.replication.throttled.rate"); + keysToBeRemoved.add("replica.alter.log.dirs.io.max.bytes.per.second"); + try { + removeConfigInZk(Type.BROKER, String.valueOf(brokerId), keysToBeRemoved); + } catch (ApiException apiException) { + log.info("Remove property on broker:" + brokerId + " failed since " + apiException.getMessage()); + } + } + + Set topics = reassignedPartitionsStatus.keySet().stream().map(tp->tp.topic()).collect(toSet()); + Set topicInReplicas = replicasReassignmentStatus.keySet().stream().map(tpr->tpr.topic()).collect(toSet()); + topics.addAll(topicInReplicas); + + for(String topic:topics) { + List keysToBeRemoved = new ArrayList<>(); + keysToBeRemoved.add(LogConfig.LeaderReplicationThrottledReplicasProp()); + keysToBeRemoved.add(LogConfig.FollowerReplicationThrottledReplicasProp()); + try { + removeConfigInZk(Type.TOPIC, topic, keysToBeRemoved); + } catch (ApiException apiException) { + log.info("Remove property on topic:" + topic + " failed since " + apiException.getMessage()); + } + } + + return true; } - private Map checkIfPartitionReassignmentSucceeded( + private Map checkIfPartitionReassignmentSucceeded( scala.collection.Map> partitionsToBeReassigned) { - Map reassignedPartitionsStatus = new HashMap<>(); + Map reassignedPartitionsStatus = new HashMap<>(); + + scala.collection.immutable.Map> partitionsBeingReassigned = kafkaZkClient + .getPartitionReassignment(); + scala.collection.Iterator topicPartitionIterator = partitionsToBeReassigned + .keysIterator(); + while (topicPartitionIterator.hasNext()) { + TopicPartition topicPartition = topicPartitionIterator.next(); + reassignedPartitionsStatus + .put(topicPartition, ReassignPartitionsCommand + .checkIfPartitionReassignmentSucceeded(kafkaZkClient, topicPartition, + partitionsToBeReassigned, partitionsBeingReassigned).status()); + } return reassignedPartitionsStatus; } - private Map checkIfReplicaReassignmentSucceeded( - scala.collection.Map replicaAssignement) { - Map replicasReassignmentStatus = new HashMap<>(); + private Map checkIfReplicaReassignmentSucceeded( + Map replicaAssignement) { + org.apache.kafka.clients.admin.AdminClient kafkaAdminClient = createKafkaAdminClient(); + Map replicasReassignmentStatus = new HashMap<>(); + + Map replicaLogDirInfos = new HashMap<>(); + + if (!replicaAssignement.isEmpty()) { + List replicaList = new ArrayList<>(replicaAssignement.keySet()); + replicaLogDirInfos = describeReplicaLogDirs(replicaList); + } + + for (Map.Entry newLogDirEntry : replicaAssignement.entrySet()) { + TopicPartitionReplica tpr = newLogDirEntry.getKey(); + String newLogDir = newLogDirEntry.getValue(); + ReplicaLogDirInfo replicaLogDirInfo = replicaLogDirInfos.get(tpr); + if (replicaLogDirInfo.getCurrentReplicaLogDir() == null) { + //tpr log dir not found + replicasReassignmentStatus.put(tpr, ReassignmentStatus.ReassignmentFailed.code()); + } else if (replicaLogDirInfo.getFutureReplicaLogDir() != null && replicaLogDirInfo + .getFutureReplicaLogDir().equals(newLogDir)) { + replicasReassignmentStatus.put(tpr, ReassignmentStatus.ReassignmentInProgress.code()); + } else if (replicaLogDirInfo.getFutureReplicaLogDir() != null && !replicaLogDirInfo + .getFutureReplicaLogDir().equals(newLogDir)) { + //tpr is being moved to another logdir instead of newLogDir + replicasReassignmentStatus.put(tpr, ReassignmentStatus.ReassignmentFailed.code()); + } else if (replicaLogDirInfo.getCurrentReplicaLogDir() != null && replicaLogDirInfo + .getCurrentReplicaLogDir().equals(newLogDir)) { + replicasReassignmentStatus.put(tpr, ReassignmentStatus.ReassignmentCompleted.code()); + } else { + replicasReassignmentStatus.put(tpr, ReassignmentStatus.ReassignmentFailed.code()); + } + } + kafkaAdminClient.close(); return replicasReassignmentStatus; } + public Map checkReassignStatusByStr(String reassignStr) { + TwoTuple>, + scala.collection.Map> reassignPlan = genReassignPlan( + reassignStr); + scala.collection.mutable.HashMap> partitionsToBeReassignedMap = reassignPlan + .getFirst(); + scala.collection.Map replicatAssignment = reassignPlan + .getSecond(); + + return checkReassignStatus(partitionsToBeReassignedMap, replicatAssignment); + } + + private TwoTuple>, + scala.collection.Map> genReassignPlan( + String reassignStr) { + Tuple2 resultTuple2; + try { + //Parse and validate reassignment json string, return (partitionsToBeReassigned, replicaAssignment) + resultTuple2 = ReassignPartitionsCommand + .parseAndValidate(kafkaZkClient, reassignStr); + } catch (Exception exception) { + throw new ApiException("Bad Request. " + exception.getMessage()); + } + //Change list buffer to map + ListBuffer partitionsToBeReassignedList = (scala.collection.mutable.ListBuffer) resultTuple2 + ._1(); + scala.collection.mutable.HashMap> partitionsToBeReassignedMap = new scala.collection.mutable.HashMap<>(); + for (int i = 0; i < partitionsToBeReassignedList.size(); ++i) { + Tuple2 tup = (Tuple2) partitionsToBeReassignedList.apply(i); + partitionsToBeReassignedMap.put((TopicPartition) tup._1(), (Seq) tup._2()); + } + + scala.collection.Map replicatAssignment = + (scala.collection.Map) resultTuple2._2(); + + return new TwoTuple<>(partitionsToBeReassignedMap, replicatAssignment); + } + /* public Map checkReassignStatus(String reassignStr) { Map> partitionsToBeReassigned = JavaConverters.mapAsJavaMapConverter( diff --git a/src/main/java/org/gnuhpc/bigdata/utils/ZookeeperUtils.java b/src/main/java/org/gnuhpc/bigdata/utils/ZookeeperUtils.java index 0a022fa..65c90d8 100644 --- a/src/main/java/org/gnuhpc/bigdata/utils/ZookeeperUtils.java +++ b/src/main/java/org/gnuhpc/bigdata/utils/ZookeeperUtils.java @@ -100,7 +100,7 @@ public void init() { e.printStackTrace(); log.error("Exception:", e); } - kafkaZkClient = KafkaZkClient.apply(zookeeperConfig.getUris(), false, SESSION_TIMEOUT, + kafkaZkClient = KafkaZkClient.apply(zookeeperConfig.getUris(), false, 10 * SESSION_TIMEOUT, CONNECTION_TIMEOUT, Integer.MAX_VALUE, Time.SYSTEM, "testMetricGroup", "testMetricType"); } From 96d0691d5bbe9d98ebbb7c1a6f5355e91cfe08b9 Mon Sep 17 00:00:00 2001 From: tinawenqiao <315524513@qq.com> Date: Sat, 29 Dec 2018 16:39:58 +0800 Subject: [PATCH 05/24] Modify code style refering to google code style. --- .travis.yml | 6 +- JMXFilterTemplate/KafkaBrokerFilter.yml | 364 +- JMXFilterTemplate/KafkaConsumerFilter.yml | 84 +- JMXFilterTemplate/KafkaProducerFilter.yml | 36 +- JMXFilterTemplate/KafkaTopicFilter.yml | 112 +- docs/index.html | 13212 +++++++++------- pom.xml | 934 +- src/docs/swagger/swagger.json | 2471 ++- .../KafkaRestSpringbootApplication.java | 9 +- .../bigdata/componet/OffsetStorage.java | 51 +- .../bigdata/componet/RestLogAspect.java | 61 +- .../org/gnuhpc/bigdata/config/JMXConfig.java | 7 +- .../gnuhpc/bigdata/config/KafkaConfig.java | 133 +- .../gnuhpc/bigdata/config/SwaggerConfig.java | 41 +- .../gnuhpc/bigdata/config/WebMvcConfig.java | 11 +- .../bigdata/config/WebSecurityConfig.java | 36 +- .../bigdata/config/ZookeeperConfig.java | 29 +- .../bigdata/constant/ConsumerGroupState.java | 7 +- .../bigdata/constant/ConsumerState.java | 4 +- .../gnuhpc/bigdata/constant/ConsumerType.java | 4 +- .../constant/GeneralResponseState.java | 4 +- .../bigdata/constant/ReassignmentStatus.java | 7 +- .../controller/CollectorController.java | 47 +- .../controller/CustomErrorController.java | 67 +- .../bigdata/controller/DebugController.java | 63 +- .../bigdata/controller/KafkaController.java | 121 +- .../bigdata/controller/SwaggerController.java | 13 +- .../bigdata/controller/UserController.java | 31 +- .../controller/ZookeeperController.java | 80 +- .../bigdata/exception/CollectorException.java | 15 +- .../gnuhpc/bigdata/exception/ErrorJson.java | 20 - .../bigdata/exception/ExceptionLogAspect.java | 23 +- .../exception/GlobalExceptionHandler.java | 165 +- .../bigdata/exception/KafkaErrorCode.java | 50 +- .../exception/KafkaExceptionHandler.java | 25 +- .../bigdata/exception/RestErrorResponse.java | 359 +- .../ServiceNotAvailableException.java | 35 +- .../gnuhpc/bigdata/model/AddPartition.java | 11 +- .../org/gnuhpc/bigdata/model/BrokerInfo.java | 44 +- .../bigdata/model/ConsumerGroupDesc.java | 59 +- .../model/ConsumerGroupDescFactory.java | 198 +- .../bigdata/model/ConsumerGroupMeta.java | 2 +- .../bigdata/model/CustomConfigEntry.java | 1 + .../model/CustomTopicPartitionInfo.java | 75 +- .../gnuhpc/bigdata/model/GeneralResponse.java | 7 +- .../bigdata/model/HealthCheckResult.java | 5 +- .../gnuhpc/bigdata/model/JMXAttribute.java | 156 +- .../org/gnuhpc/bigdata/model/JMXClient.java | 73 +- .../bigdata/model/JMXComplexAttribute.java | 64 +- .../bigdata/model/JMXConfiguration.java | 138 +- .../org/gnuhpc/bigdata/model/JMXFilter.java | 35 +- .../gnuhpc/bigdata/model/JMXMetricData.java | 8 +- .../gnuhpc/bigdata/model/JMXMetricDataV1.java | 6 +- .../org/gnuhpc/bigdata/model/JMXQuery.java | 5 +- .../bigdata/model/JMXSimpleAttribute.java | 56 +- .../bigdata/model/JMXTabularAttribute.java | 137 +- .../bigdata/model/MemberDescription.java | 1 + .../model/PartitionAssignmentState.java | 1 + .../bigdata/model/ReassignJsonWrapper.java | 14 +- .../gnuhpc/bigdata/model/ReassignWrapper.java | 39 +- .../java/org/gnuhpc/bigdata/model/Record.java | 1 + .../org/gnuhpc/bigdata/model/TopicBrief.java | 7 +- .../org/gnuhpc/bigdata/model/TopicDetail.java | 25 +- .../org/gnuhpc/bigdata/model/TopicMeta.java | 25 +- .../org/gnuhpc/bigdata/model/TwoTuple.java | 1 + .../java/org/gnuhpc/bigdata/model/User.java | 5 +- .../gnuhpc/bigdata/model/ZkServerClient.java | 14 +- .../bigdata/model/ZkServerEnvironment.java | 1 + .../gnuhpc/bigdata/model/ZkServerStat.java | 5 +- .../security/BasicAuthenticationPoint.java | 30 +- .../security/UserDetailsServiceImp.java | 44 +- .../JsonJodaDateTimeSerializer.java | 21 +- .../bigdata/service/CollectorService.java | 172 +- .../bigdata/service/KafkaAdminService.java | 1554 +- .../bigdata/service/KafkaConsumerService.java | 108 +- .../bigdata/service/KafkaProducerService.java | 12 +- .../gnuhpc/bigdata/service/UserService.java | 72 +- .../bigdata/service/ZookeeperService.java | 61 +- ...sult.java => FetchOffSetFromZkResult.java} | 9 +- .../bigdata/task/FetchOffsetFromZKTask.java | 41 - .../bigdata/task/FetchOffsetFromZkTask.java | 42 + .../org/gnuhpc/bigdata/utils/ByteUtils.java | 21 +- .../org/gnuhpc/bigdata/utils/CommonUtils.java | 24 +- .../org/gnuhpc/bigdata/utils/KafkaUtils.java | 67 +- .../gnuhpc/bigdata/utils/ZookeeperUtils.java | 515 +- .../ConsumerGroupExistConstraint.java | 13 +- .../ConsumerGroupExistValidator.java | 21 +- .../validator/TopicExistConstraint.java | 17 +- .../validator/TopicExistValidator.java | 19 +- ...nt.java => ZkNodePathExistConstraint.java} | 11 +- ...tor.java => ZkNodePathExistValidator.java} | 15 +- .../kafka-zk-springboot-distribution.xml | 108 +- src/main/resources/log4j.properties | 2 - .../KafkaRestSpringbootApplicationTests.java | 55 +- .../gnuhpc/bigdata/Swagger2MarkupTest.java | 52 +- 95 files changed, 14340 insertions(+), 8722 deletions(-) delete mode 100644 src/main/java/org/gnuhpc/bigdata/exception/ErrorJson.java rename src/main/java/org/gnuhpc/bigdata/task/{FetchOffSetFromZKResult.java => FetchOffSetFromZkResult.java} (56%) delete mode 100644 src/main/java/org/gnuhpc/bigdata/task/FetchOffsetFromZKTask.java create mode 100644 src/main/java/org/gnuhpc/bigdata/task/FetchOffsetFromZkTask.java rename src/main/java/org/gnuhpc/bigdata/validator/{ZKNodePathExistConstraint.java => ZkNodePathExistConstraint.java} (71%) rename src/main/java/org/gnuhpc/bigdata/validator/{ZKNodePathExistValidator.java => ZkNodePathExistValidator.java} (53%) diff --git a/.travis.yml b/.travis.yml index 97b3ea2..e283119 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,9 @@ language: java jdk: oraclejdk8 install: - - mvn -N io.takari:maven:wrapper - - ./mvnw install -DskipTests=true -Dmaven.javadoc.skip=true -B -V +- mvn -N io.takari:maven:wrapper +- ./mvnw install -DskipTests=true -Dmaven.javadoc.skip=true -B -V script: - echo "skipping tests" before_install: - - chmod +x mvnw +- chmod +x mvnw diff --git a/JMXFilterTemplate/KafkaBrokerFilter.yml b/JMXFilterTemplate/KafkaBrokerFilter.yml index aff723b..30f501f 100644 --- a/JMXFilterTemplate/KafkaBrokerFilter.yml +++ b/JMXFilterTemplate/KafkaBrokerFilter.yml @@ -1,184 +1,184 @@ filters: - # - # Kafka volume metrics. - # Notice: BytesInPerSec, BytesOutPerSec and MessagesInPerSec are performance metrics. - # - - include: - domain: kafka.server - bean: kafka.server:type=BrokerTopicMetrics,name=BytesInPerSec - attribute: - Count: - metric_type: KAFKA_BROKER_VOLUMN - alias: BytesIn - OneMinuteRate: - metric_type: KAFKA_BROKER_PERF - alias: BytesInPerSec - - include: - domain: kafka.server - bean: kafka.server:type=BrokerTopicMetrics,name=BytesOutPerSec - attribute: - Count: - metric_type: KAFKA_BROKER_VOLUMN - alias: BytesOut - OneMinuteRate: - metric_type: KAFKA_BROKER_PERF - alias: BytesOutPerSec - - include: - domain: kafka.server - bean: kafka.server:type=BrokerTopicMetrics,name=MessagesInPerSec - attribute: - Count: - metric_type: KAFKA_BROKER_VOLUMN - alias: MessagesIn - OneMinuteRate: - metric_type: KAFKA_BROKER_PERF - alias: MessagesInPerSec +# +# Kafka volume metrics. +# Notice: BytesInPerSec, BytesOutPerSec and MessagesInPerSec are performance metrics. +# +- include: + domain: kafka.server + bean: kafka.server:type=BrokerTopicMetrics,name=BytesInPerSec + attribute: + Count: + metric_type: KAFKA_BROKER_VOLUMN + alias: BytesIn + OneMinuteRate: + metric_type: KAFKA_BROKER_PERF + alias: BytesInPerSec +- include: + domain: kafka.server + bean: kafka.server:type=BrokerTopicMetrics,name=BytesOutPerSec + attribute: + Count: + metric_type: KAFKA_BROKER_VOLUMN + alias: BytesOut + OneMinuteRate: + metric_type: KAFKA_BROKER_PERF + alias: BytesOutPerSec +- include: + domain: kafka.server + bean: kafka.server:type=BrokerTopicMetrics,name=MessagesInPerSec + attribute: + Count: + metric_type: KAFKA_BROKER_VOLUMN + alias: MessagesIn + OneMinuteRate: + metric_type: KAFKA_BROKER_PERF + alias: MessagesInPerSec - # - # Kafka performance metrics - # - - include: - domain: kafka.network - bean: kafka.network:type=RequestMetrics,name=RequestsPerSec,request=Produce - attribute: - OneMinuteRate: - metric_type: KAFKA_BROKER_PERF - alias: RequestsPerSec_Produce - - include: - domain: kafka.network - bean: kafka.network:type=RequestMetrics,name=RequestsPerSec,request=FetchConsumer - attribute: - OneMinuteRate: - metric_type: KAFKA_BROKER_PERF - alias: RequestsPerSec_FetchConsumer - - include: - domain: kafka.network - bean: kafka.network:type=RequestMetrics,name=RequestsPerSec,request=FetchFollower - attribute: - OneMinuteRate: - metric_type: KAFKA_BROKER_PERF - alias: RequestsPerSec_FetchFollower - - include: - domain: kafka.network - bean: kafka.network:type=RequestMetrics,name=TotalTimeMs,request=Produce - attribute: - Mean: - metric_type: KAFKA_BROKER_PERF - alias: TotalTimeMs_Produce_Mean - Count: - metric_type: KAFKA_BROKER_PERF - alias: TotalTimeMs_Produce_Count - - include: - domain: kafka.network - bean: kafka.network:type=RequestMetrics,name=TotalTimeMs,request=FetchConsumer - attribute: - Mean: - metric_type: KAFKA_BROKER_PERF - alias: TotalTimeMs_FetchConsumer_Mean - Count: - metric_type: KAFKA_BROKER_PERF - alias: TotalTimeMs_FetchConsumer_Count - - include: - domain: kafka.network - bean: kafka.network:type=RequestMetrics,name=TotalTimeMs,request=FetchFollower - attribute: - Mean: - metric_type: KAFKA_BROKER_PERF - alias: TotalTimeMs_FetchFollower_Mean - Count: - metric_type: KAFKA_BROKER_PERF - alias: TotalTimeMs_FetchFollower_Count - - include: - domain: kafka.network - bean: kafka.network:type=SocketServer,name=NetworkProcessorAvgIdlePercent - attribute: - Value: - metric_type: KAFKA_BROKER_PERF - alias: NetworkProcessorAvgIdlePercent - - include: - domain: kafka.server - bean: kafka.server:type=KafkaRequestHandlerPool,name=RequestHandlerAvgIdlePercent - attribute: - FiveMinuteRate: - metric_type: KAFKA_BROKER_PERF - alias: RequestHandlerAvgIdlePercent - - include: - domain: kafka.server - bean: kafka.server:type=ReplicaFetcherManager,name=MaxLag,clientId=Replica - attribute: - Value: - metric_type: KAFKA_BROKER_PERF - alias: MaxLagBtwFollowerLeader - # - # Kafka availability metrics - # - - include: - domain: java.lang - bean: java.lang:type=Runtime - attribute: - Uptime: - metric_type: KAFKA_BROKER_AVAIL - alias: JVMUptime - - include: - domain: kafka.controller - bean: kafka.controller:type=KafkaController,name=OfflinePartitionsCount - attribute: - Value: - metric_type: KAFKA_BROKER_AVAIL - alias: OfflinePartitionsCount - - include: - domain: kafka.controller - bean: kafka.controller:type=KafkaController,name=ActiveControllerCount - attribute: - Value: - metric_type: KAFKA_BROKER_AVAIL - alias: ActiveControllerCount - - include: - domain: kafka.controller - bean: kafka.controller:type=ControllerStats,name=LeaderElectionRateAndTimeMs - attribute: - Count: - metric_type: KAFKA_BROKER_AVAIL - alias: LeaderElectionRateAndTimeMs - - include: - domain: kafka.controller - bean: kafka.controller:type=ControllerStats,name=UncleanLeaderElectionsPerSec - attribute: - Count: - metric_type: KAFKA_BROKER_AVAIL - alias: UncleanLeaderElectionsPerSec - - include: - domain: kafka.server - bean: kafka.server:type=ReplicaManager,name=UnderReplicatedPartitions - attribute: - Value: - metric_type: KAFKA_BROKER_AVAIL - alias: UnderReplicatedPartitions - - include: - domain: kafka.server - bean: kafka.server:type=ReplicaManager,name=PartitionCount - attribute: - Value: - metric_type: KAFKA_BROKER_AVAIL - alias: PartitionCount - - include: - domain: kafka.server - bean: kafka.server:type=ReplicaManager,name=IsrShrinksPerSec - attribute: - Count: - metric_type: KAFKA_BROKER_AVAIL - alias: IsrShrinksPerSec - - include: - domain: kafka.server - bean: kafka.server:type=ReplicaManager,name=IsrExpandsPerSec - attribute: - Count: - metric_type: KAFKA_BROKER_AVAIL - alias: IsrExpandsPerSec - - include: - domain: kafka.server - bean: kafka.server:type=ReplicaManager,name=LeaderCount - attribute: - Value: - metric_type: KAFKA_BROKER_AVAIL - alias: LeaderCount +# +# Kafka performance metrics +# +- include: + domain: kafka.network + bean: kafka.network:type=RequestMetrics,name=RequestsPerSec,request=Produce + attribute: + OneMinuteRate: + metric_type: KAFKA_BROKER_PERF + alias: RequestsPerSec_Produce +- include: + domain: kafka.network + bean: kafka.network:type=RequestMetrics,name=RequestsPerSec,request=FetchConsumer + attribute: + OneMinuteRate: + metric_type: KAFKA_BROKER_PERF + alias: RequestsPerSec_FetchConsumer +- include: + domain: kafka.network + bean: kafka.network:type=RequestMetrics,name=RequestsPerSec,request=FetchFollower + attribute: + OneMinuteRate: + metric_type: KAFKA_BROKER_PERF + alias: RequestsPerSec_FetchFollower +- include: + domain: kafka.network + bean: kafka.network:type=RequestMetrics,name=TotalTimeMs,request=Produce + attribute: + Mean: + metric_type: KAFKA_BROKER_PERF + alias: TotalTimeMs_Produce_Mean + Count: + metric_type: KAFKA_BROKER_PERF + alias: TotalTimeMs_Produce_Count +- include: + domain: kafka.network + bean: kafka.network:type=RequestMetrics,name=TotalTimeMs,request=FetchConsumer + attribute: + Mean: + metric_type: KAFKA_BROKER_PERF + alias: TotalTimeMs_FetchConsumer_Mean + Count: + metric_type: KAFKA_BROKER_PERF + alias: TotalTimeMs_FetchConsumer_Count +- include: + domain: kafka.network + bean: kafka.network:type=RequestMetrics,name=TotalTimeMs,request=FetchFollower + attribute: + Mean: + metric_type: KAFKA_BROKER_PERF + alias: TotalTimeMs_FetchFollower_Mean + Count: + metric_type: KAFKA_BROKER_PERF + alias: TotalTimeMs_FetchFollower_Count +- include: + domain: kafka.network + bean: kafka.network:type=SocketServer,name=NetworkProcessorAvgIdlePercent + attribute: + Value: + metric_type: KAFKA_BROKER_PERF + alias: NetworkProcessorAvgIdlePercent +- include: + domain: kafka.server + bean: kafka.server:type=KafkaRequestHandlerPool,name=RequestHandlerAvgIdlePercent + attribute: + FiveMinuteRate: + metric_type: KAFKA_BROKER_PERF + alias: RequestHandlerAvgIdlePercent +- include: + domain: kafka.server + bean: kafka.server:type=ReplicaFetcherManager,name=MaxLag,clientId=Replica + attribute: + Value: + metric_type: KAFKA_BROKER_PERF + alias: MaxLagBtwFollowerLeader +# +# Kafka availability metrics +# +- include: + domain: java.lang + bean: java.lang:type=Runtime + attribute: + Uptime: + metric_type: KAFKA_BROKER_AVAIL + alias: JVMUptime +- include: + domain: kafka.controller + bean: kafka.controller:type=KafkaController,name=OfflinePartitionsCount + attribute: + Value: + metric_type: KAFKA_BROKER_AVAIL + alias: OfflinePartitionsCount +- include: + domain: kafka.controller + bean: kafka.controller:type=KafkaController,name=ActiveControllerCount + attribute: + Value: + metric_type: KAFKA_BROKER_AVAIL + alias: ActiveControllerCount +- include: + domain: kafka.controller + bean: kafka.controller:type=ControllerStats,name=LeaderElectionRateAndTimeMs + attribute: + Count: + metric_type: KAFKA_BROKER_AVAIL + alias: LeaderElectionRateAndTimeMs +- include: + domain: kafka.controller + bean: kafka.controller:type=ControllerStats,name=UncleanLeaderElectionsPerSec + attribute: + Count: + metric_type: KAFKA_BROKER_AVAIL + alias: UncleanLeaderElectionsPerSec +- include: + domain: kafka.server + bean: kafka.server:type=ReplicaManager,name=UnderReplicatedPartitions + attribute: + Value: + metric_type: KAFKA_BROKER_AVAIL + alias: UnderReplicatedPartitions +- include: + domain: kafka.server + bean: kafka.server:type=ReplicaManager,name=PartitionCount + attribute: + Value: + metric_type: KAFKA_BROKER_AVAIL + alias: PartitionCount +- include: + domain: kafka.server + bean: kafka.server:type=ReplicaManager,name=IsrShrinksPerSec + attribute: + Count: + metric_type: KAFKA_BROKER_AVAIL + alias: IsrShrinksPerSec +- include: + domain: kafka.server + bean: kafka.server:type=ReplicaManager,name=IsrExpandsPerSec + attribute: + Count: + metric_type: KAFKA_BROKER_AVAIL + alias: IsrExpandsPerSec +- include: + domain: kafka.server + bean: kafka.server:type=ReplicaManager,name=LeaderCount + attribute: + Value: + metric_type: KAFKA_BROKER_AVAIL + alias: LeaderCount diff --git a/JMXFilterTemplate/KafkaConsumerFilter.yml b/JMXFilterTemplate/KafkaConsumerFilter.yml index 094ef7e..89456ed 100644 --- a/JMXFilterTemplate/KafkaConsumerFilter.yml +++ b/JMXFilterTemplate/KafkaConsumerFilter.yml @@ -1,43 +1,43 @@ filters: - - include: - domain: kafka.consumer - bean_regex: kafka.consumer:type=ConsumerFetcherManager,name=MaxLag,clientId=([-.\w]+) - attribute: - Value: - metric_type: KAFKA_CONSUMER_OLD_HIGH - alias: MaxLag - - include: - domain: kafka.consumer - bean_regex: kafka.consumer:type=ConsumerFetcherManager,name=MinFetchRate,clientId=([-.\w]+) - attribute: - Value: - metric_type: KAFKA_CONSUMER_OLD_HIGH - alias: MinFetchRate - - include: - domain: kafka.consumer - bean_regex: kafka.consumer:type=ConsumerTopicMetrics,name=MessagesPerSec,clientId=([-.\w]+) - attribute: - Count: - metric_type: KAFKA_CONSUMER_OLD_HIGH - alias: MessagesPerSec - - include: - domain: kafka.consumer - bean_regex: kafka.consumer:type=ConsumerTopicMetrics,name=BytesPerSec,clientId=([-.\w]+) - attribute: - Count: - metric_type: KAFKA_CONSUMER_OLD_HIGH - alias: BytesPerSec - - include: - domain: kafka.consumer - bean_regex: kafka.consumer:type=ZookeeperConsumerConnector,name=KafkaCommitsPerSec,clientId=([-.\w]+) - attribute: - Count: - metric_type: KAFKA_CONSUMER_OLD_HIGH - alias: KafkaCommitsPerSec - - include: - domain: kafka.consumer - bean_regex: kafka.consumer:type=ZookeeperConsumerConnector,name=OwnedPartitionsCount,clientId=([-.\w]+),groupId=([-.\w]+) - attribute: - Value: - metric_type: KAFKA_CONSUMER_OLD_HIGH - alias: OwnedPartitionsCount \ No newline at end of file +- include: + domain: kafka.consumer + bean_regex: kafka.consumer:type=ConsumerFetcherManager,name=MaxLag,clientId=([-.\w]+) + attribute: + Value: + metric_type: KAFKA_CONSUMER_OLD_HIGH + alias: MaxLag +- include: + domain: kafka.consumer + bean_regex: kafka.consumer:type=ConsumerFetcherManager,name=MinFetchRate,clientId=([-.\w]+) + attribute: + Value: + metric_type: KAFKA_CONSUMER_OLD_HIGH + alias: MinFetchRate +- include: + domain: kafka.consumer + bean_regex: kafka.consumer:type=ConsumerTopicMetrics,name=MessagesPerSec,clientId=([-.\w]+) + attribute: + Count: + metric_type: KAFKA_CONSUMER_OLD_HIGH + alias: MessagesPerSec +- include: + domain: kafka.consumer + bean_regex: kafka.consumer:type=ConsumerTopicMetrics,name=BytesPerSec,clientId=([-.\w]+) + attribute: + Count: + metric_type: KAFKA_CONSUMER_OLD_HIGH + alias: BytesPerSec +- include: + domain: kafka.consumer + bean_regex: kafka.consumer:type=ZookeeperConsumerConnector,name=KafkaCommitsPerSec,clientId=([-.\w]+) + attribute: + Count: + metric_type: KAFKA_CONSUMER_OLD_HIGH + alias: KafkaCommitsPerSec +- include: + domain: kafka.consumer + bean_regex: kafka.consumer:type=ZookeeperConsumerConnector,name=OwnedPartitionsCount,clientId=([-.\w]+),groupId=([-.\w]+) + attribute: + Value: + metric_type: KAFKA_CONSUMER_OLD_HIGH + alias: OwnedPartitionsCount \ No newline at end of file diff --git a/JMXFilterTemplate/KafkaProducerFilter.yml b/JMXFilterTemplate/KafkaProducerFilter.yml index 2d5de8a..292e4b7 100644 --- a/JMXFilterTemplate/KafkaProducerFilter.yml +++ b/JMXFilterTemplate/KafkaProducerFilter.yml @@ -1,20 +1,20 @@ filters: - - include: - domain: kafka.producer - bean_regex: kafka.producer:type=producer-metrics,client-id=([-.\w]+) - attribute: - request-rate: - metric_type: KAFKA_PRODUCER - alias: request-rate - request-size-avg: - metric_type: KAFKA_PRODUCER - alias: request-size-avg - # There is some problem. I can't find producer-topic-metrics in Kafka 0.10 - - include: - domain: kafka.producer - bean_regex: kafka.producer:type=producer-topic-metrics,client-id=([-.\w]+) - attribute: - byte-rate: - metric_type: KAFKA_PRODUCER - alias: byte-rate-topic +- include: + domain: kafka.producer + bean_regex: kafka.producer:type=producer-metrics,client-id=([-.\w]+) + attribute: + request-rate: + metric_type: KAFKA_PRODUCER + alias: request-rate + request-size-avg: + metric_type: KAFKA_PRODUCER + alias: request-size-avg +# There is some problem. I can't find producer-topic-metrics in Kafka 0.10 +- include: + domain: kafka.producer + bean_regex: kafka.producer:type=producer-topic-metrics,client-id=([-.\w]+) + attribute: + byte-rate: + metric_type: KAFKA_PRODUCER + alias: byte-rate-topic diff --git a/JMXFilterTemplate/KafkaTopicFilter.yml b/JMXFilterTemplate/KafkaTopicFilter.yml index 361f827..92fa45c 100644 --- a/JMXFilterTemplate/KafkaTopicFilter.yml +++ b/JMXFilterTemplate/KafkaTopicFilter.yml @@ -1,57 +1,57 @@ filters: - - include: - domain: kafka.server - bean: kafka.server:type=BrokerTopicMetrics,name=BytesInPerSec,topic={topicname} - attribute: - Count: - metric_type: KAFKA_TOPIC_PERF - alias: BytesInPerSec - - include: - domain: kafka.server - bean: kafka.server:type=BrokerTopicMetrics,name=BytesOutPerSec,topic={topicname} - attribute: - Count: - metric_type: KAFKA_TOPIC_PERF - alias: BytesOutPerSec - - include: - domain: kafka.server - bean: kafka.server:type=BrokerTopicMetrics,name=MessagesInPerSec,topic={topicname} - attribute: - Count: - metric_type: KAFKA_TOPIC_PERF - alias: MessagesInPerSec - - include: - domain: kafka.server - bean: kafka.server:type=BrokerTopicMetrics,name=BytesRejectedPerSec,topic={topicname} - attribute: - Count: - metric_type: KAFKA_TOPIC_PERF - alias: BytesRejectedPerSec - - include: - domain: kafka.server - bean: kafka.server:type=BrokerTopicMetrics,name=FailedFetchRequestsPerSec,topic={topicname} - attribute: - Count: - metric_type: KAFKA_TOPIC_PERF - alias: FailedFetchRequestsPerSec - - include: - domain: kafka.server - bean: kafka.server:type=BrokerTopicMetrics,name=FailedProduceRequestsPerSec,topic={topicname} - attribute: - Count: - metric_type: KAFKA_TOPIC_PERF - alias: FailedProduceRequestsPerSec - - include: - domain: kafka.server - bean: kafka.server:type=BrokerTopicMetrics,name=TotalFetchRequestsPerSec,topic={topicname} - attribute: - Count: - metric_type: KAFKA_TOPIC_PERF - alias: TotalFetchRequestsPerSec - - include: - domain: kafka.server - bean: kafka.server:type=BrokerTopicMetrics,name=TotalProduceRequestsPerSec,topic={topicname} - attribute: - Count: - metric_type: KAFKA_TOPIC_PERF - alias: TotalProduceRequestsPerSec \ No newline at end of file +- include: + domain: kafka.server + bean: kafka.server:type=BrokerTopicMetrics,name=BytesInPerSec,topic={topicname} + attribute: + Count: + metric_type: KAFKA_TOPIC_PERF + alias: BytesInPerSec +- include: + domain: kafka.server + bean: kafka.server:type=BrokerTopicMetrics,name=BytesOutPerSec,topic={topicname} + attribute: + Count: + metric_type: KAFKA_TOPIC_PERF + alias: BytesOutPerSec +- include: + domain: kafka.server + bean: kafka.server:type=BrokerTopicMetrics,name=MessagesInPerSec,topic={topicname} + attribute: + Count: + metric_type: KAFKA_TOPIC_PERF + alias: MessagesInPerSec +- include: + domain: kafka.server + bean: kafka.server:type=BrokerTopicMetrics,name=BytesRejectedPerSec,topic={topicname} + attribute: + Count: + metric_type: KAFKA_TOPIC_PERF + alias: BytesRejectedPerSec +- include: + domain: kafka.server + bean: kafka.server:type=BrokerTopicMetrics,name=FailedFetchRequestsPerSec,topic={topicname} + attribute: + Count: + metric_type: KAFKA_TOPIC_PERF + alias: FailedFetchRequestsPerSec +- include: + domain: kafka.server + bean: kafka.server:type=BrokerTopicMetrics,name=FailedProduceRequestsPerSec,topic={topicname} + attribute: + Count: + metric_type: KAFKA_TOPIC_PERF + alias: FailedProduceRequestsPerSec +- include: + domain: kafka.server + bean: kafka.server:type=BrokerTopicMetrics,name=TotalFetchRequestsPerSec,topic={topicname} + attribute: + Count: + metric_type: KAFKA_TOPIC_PERF + alias: TotalFetchRequestsPerSec +- include: + domain: kafka.server + bean: kafka.server:type=BrokerTopicMetrics,name=TotalProduceRequestsPerSec,topic={topicname} + attribute: + Count: + metric_type: KAFKA_TOPIC_PERF + alias: TotalProduceRequestsPerSec \ No newline at end of file diff --git a/docs/index.html b/docs/index.html index b1cb3b3..d5fe9b4 100644 --- a/docs/index.html +++ b/docs/index.html @@ -1,5344 +1,7894 @@ - - - - -Kafka REST API SwaggerUI - - + + + + + Kafka REST API SwaggerUI + +
-
-

1. Overview

-
-
-

Kafka REST API SwaggerUI

-
-
-

1.1. Version information

-
-

Version : 0.1.0

-
-
-
-

1.2. Contact information

-
-

Contact : gnuhpc
-Contact Email : gnuhpc@gmail.com

-
-
-
-

1.3. URI scheme

-
-

Host : localhost:8080
-BasePath : /

-
-
-
-

1.4. Tags

-
-
    -
  • -

    collector-controller : Rest API for Collecting JMX Metric Data

    -
  • -
  • -

    kafka-controller : Kafka Controller

    -
  • -
  • -

    user-controller : Security User Management Controller.

    -
  • -
  • -

    zookeeper-controller : Zookeeper Controller

    -
  • -
-
-
-
-
-
-

2. Chapter of manual content 1

-
-
-

This is some dummy text

-
-
-

2.1. Sub chapter

-
-

Dummy text of sub chapter

-
-
-
-
-
-

3. Chapter of manual content 2

-
-
-

This is some dummy text

-
-
-
-
-

4. Resources

-
-
-

4.1. Collector-controller

-
-

Rest API for Collecting JMX Metric Data

-
-
-

4.1.1. Fetch all JMX metric data

-
-
-
GET /jmx/v1
-
-
-
-
Parameters
- ------ - - - - - - - - - - - - - - - - -
TypeNameDescriptionSchema

Query

jmxurl
-optional

Parameter jmxurl should be a comma-separated list of {IP:Port} or set to 'default'

string

-
-
-
Responses
- ----- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

200

OK

< JMXMetricDataV1 > array

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.1.2. Fetch JMX metric data with query filter. You can get the query filter template through the API /jmx/v2/filters.

-
-
-
POST /jmx/v2
-
-
-
-
Parameters
- ------ - - - - - - - - - - - - - - - - - - - - - - -
TypeNameDescriptionSchema

Query

jmxurl
-optional

Parameter jmxurl should be a comma-separated list of {IP:Port} or set to 'default'

string

Body

jmxQuery
-required

jmxQuery

JMXQuery

-
-
-
Responses
- ----- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

200

OK

< JMXMetricData > array

201

Created

No Content

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.1.3. List the query filter templates with the filterKey. If filterKey is set to empty, it will return all the templates.

-
-
-
GET /jmx/v2/filters
-
-
-
-
Parameters
- ------ - - - - - - - - - - - - - - - - -
TypeNameDescriptionSchema

Query

filterKey
-required

filterKey

string

-
-
-
Responses
- ----- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

200

OK

< string, object > map

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-
-

4.2. Kafka-controller

-
-

Kafka Controller

-
-
-

4.2.1. List brokers in this cluster

-
-
-
GET /kafka/brokers
-
-
-
-
Responses
- ----- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

200

OK

< BrokerInfo > array

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.2.2. Get the message from the offset of the partition in the topic, decoder is not supported yet

-
-
-
GET /kafka/consumer/{topic}/{partition}/{offset}
-
-
-
-
Parameters
- ------ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TypeNameDescriptionSchema

Path

offset
-required

offset

integer(int64)

Path

partition
-required

partition

integer(int32)

Path

topic
-required

topic

string

Query

decoder
-optional

decoder

string

-
-
-
Responses
- ----- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

200

OK

string

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.2.3. Delete old Consumer Group

-
-
-
DELETE /kafka/consumergroup/{consumergroup}
-
-
-
-
Parameters
- ------ - - - - - - - - - - - - - - - - -
TypeNameDescriptionSchema

Path

consumergroup
-required

consumergroup

string

-
-
-
Responses
- ----- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

200

OK

GeneralResponse

204

No Content

No Content

401

Unauthorized

No Content

403

Forbidden

No Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.2.4. getLastCommitTimestamp

-
-
-
GET /kafka/consumergroup/{consumergroup}/{type}/topic/{topic}/lastcommittime
-
-
-
-
Parameters
- ------ - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TypeNameDescriptionSchema

Path

consumergroup
-required

consumergroup

string

Path

topic
-required

topic

string

Path

type
-required

type

enum (NEW, OLD)

-
-
-
Responses
- ----- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

200

OK

< string, < string, integer(int64) > map > map

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.2.5. Reset consumer group offset, earliest/latest can be used

-
-
-
PUT /kafka/consumergroup/{consumergroup}/{type}/topic/{topic}/{partition}/{offset}
-
-
-
-
Parameters
- ------ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TypeNameDescriptionSchema

Path

consumergroup
-required

consumergroup

string

Path

offset
-required

offset

string

Path

partition
-required

partition

integer(int32)

Path

topic
-required

topic

string

Path

type
-required

type

enum (NEW, OLD)

-
-
-
Responses
- ----- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

200

OK

GeneralResponse

201

Created

No Content

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.2.6. List all consumer groups from zk and kafka

-
-
-
GET /kafka/consumergroups
-
-
-
-
Parameters
- ------ - - - - - - - - - - - - - - - - - - - - - - -
TypeNameDescriptionSchema

Query

topic
-optional

topic

string

Query

type
-optional

type

enum (NEW, OLD)

-
-
-
Responses
- ----- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

200

OK

< string, < string > array > map

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.2.7. Describe consumer groups, showing lag and offset, may be slow if multi topic are listened

-
-
-
GET /kafka/consumergroups/{consumerGroup}/{type}
-
-
-
-
Parameters
- ------ - - - - - - - - - - - - - - - - - - - - - - -
TypeNameDescriptionSchema

Path

consumerGroup
-required

consumerGroup

string

Path

type
-required

type

enum (NEW, OLD)

-
-
-
Responses
- ----- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

200

OK

< string, < ConsumerGroupDesc > array > map

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.2.8. Get the topics involved of the specify consumer group

-
-
-
GET /kafka/consumergroups/{consumerGroup}/{type}/topic
-
-
-
-
Parameters
- ------ - - - - - - - - - - - - - - - - - - - - - - -
TypeNameDescriptionSchema

Path

consumerGroup
-required

consumerGroup

string

Path

type
-required

type

enum (NEW, OLD)

-
-
-
Responses
- ----- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

200

OK

< string > array

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.2.9. Describe consumer groups by topic, showing lag and offset

-
-
-
GET /kafka/consumergroups/{consumerGroup}/{type}/topic/{topic}
-
-
-
-
Parameters
- ------ - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TypeNameDescriptionSchema

Path

consumerGroup
-required

consumerGroup

string

Path

topic
-required

topic

string

Path

type
-required

type

enum (NEW, OLD)

-
-
-
Responses
- ----- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

200

OK

< ConsumerGroupDesc > array

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.2.10. Check the cluster health.

-
-
-
GET /kafka/health
-
-
-
-
Responses
- ----- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

200

OK

HealthCheckResult

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.2.11. Add a partition to the topic

-
-
-
POST /kafka/partitions/add
-
-
-
-
Parameters
- ------ - - - - - - - - - - - - - - - - -
TypeNameDescriptionSchema

Body

addPartition
-required

addPartition

AddPartition

-
-
-
Responses
- ----- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

200

OK

TopicMeta

201

Created

No Content

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.2.12. Check the partition reassignment process

-
-
-
PUT /kafka/partitions/reassign/check
-
-
-
-
Parameters
- ------ - - - - - - - - - - - - - - - - -
TypeNameDescriptionSchema

Body

reassignStr
-required

reassignStr

string

-
-
-
Responses
- ----- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

-1

Reassignment Failed

No Content

0

Reassignment In Progress

No Content

1

Reassignment Completed

No Content

200

OK

< string, integer(int32) > map

201

Created

No Content

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.2.13. Execute the partition reassignment

-
-
-
PUT /kafka/partitions/reassign/execute
-
-
-
-
Parameters
- ------ - - - - - - - - - - - - - - - - -
TypeNameDescriptionSchema

Body

reassignStr
-required

reassignStr

string

-
-
-
Responses
- ----- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

200

OK

< string, integer(int32) > map

201

Created

No Content

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.2.14. Generate plan for the partition reassignment

-
-
-
POST /kafka/partitions/reassign/generate
-
-
-
-
Parameters
- ------ - - - - - - - - - - - - - - - - -
TypeNameDescriptionSchema

Body

reassignWrapper
-required

reassignWrapper

ReassignWrapper

-
-
-
Responses
- ----- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

200

OK

< string > array

201

Created

No Content

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.2.15. List topics

-
-
-
GET /kafka/topics
-
-
-
-
Responses
- ----- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

200

OK

< string > array

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.2.16. Create a topic

-
-
-
POST /kafka/topics/create
-
-
-
-
Parameters
- ------ - - - - - - - - - - - - - - - - - - - - - - -
TypeNameDescriptionSchema

Query

reassignStr
-optional

reassignStr

string

Body

topic
-required

topic

TopicDetail

-
-
-
Responses
- ----- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

201

Created

TopicMeta

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.2.17. Describe a topic by fetching the metadata and config

-
-
-
GET /kafka/topics/{topic}
-
-
-
-
Parameters
- ------ - - - - - - - - - - - - - - - - -
TypeNameDescriptionSchema

Path

topic
-required

topic

string

-
-
-
Responses
- ----- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

200

OK

TopicMeta

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.2.18. Delete a topic (you should enable topic deletion

-
-
-
DELETE /kafka/topics/{topic}
-
-
-
-
Parameters
- ------ - - - - - - - - - - - - - - - - -
TypeNameDescriptionSchema

Path

topic
-required

topic

string

-
-
-
Responses
- ----- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

200

OK

GeneralResponse

204

No Content

No Content

401

Unauthorized

No Content

403

Forbidden

No Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.2.19. Create topic configs

-
-
-
POST /kafka/topics/{topic}/conf
-
-
-
-
Parameters
- ------ - - - - - - - - - - - - - - - - - - - - - - -
TypeNameDescriptionSchema

Path

topic
-required

topic

string

Body

prop
-required

prop

< string, object > map

-
-
-
Responses
- ----- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

200

OK

< string, object > map

201

Created

No Content

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.2.20. Get topic configs

-
-
-
GET /kafka/topics/{topic}/conf
-
-
-
-
Parameters
- ------ - - - - - - - - - - - - - - - - -
TypeNameDescriptionSchema

Path

topic
-required

topic

string

-
-
-
Responses
- ----- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

200

OK

< string, object > map

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.2.21. Update topic configs

-
-
-
PUT /kafka/topics/{topic}/conf
-
-
-
-
Parameters
- ------ - - - - - - - - - - - - - - - - - - - - - - -
TypeNameDescriptionSchema

Path

topic
-required

topic

string

Body

prop
-required

prop

< string, object > map

-
-
-
Responses
- ----- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

200

OK

< string, object > map

201

Created

No Content

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.2.22. Delete topic configs

-
-
-
DELETE /kafka/topics/{topic}/conf
-
-
-
-
Parameters
- ------ - - - - - - - - - - - - - - - - - - - - - - -
TypeNameDescriptionSchema

Path

topic
-required

topic

string

Body

delProps
-required

delProps

< string > array

-
-
-
Responses
- ----- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

200

OK

< string, object > map

204

No Content

No Content

401

Unauthorized

No Content

403

Forbidden

No Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.2.23. Get topic config by key

-
-
-
GET /kafka/topics/{topic}/conf/{key}
-
-
-
-
Parameters
- ------ - - - - - - - - - - - - - - - - - - - - - - -
TypeNameDescriptionSchema

Path

key
-required

key

string

Path

topic
-required

topic

string

-
-
-
Responses
- ----- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

200

OK

< string, object > map

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.2.24. Delete a topic config by key

-
-
-
DELETE /kafka/topics/{topic}/conf/{key}
-
-
-
-
Parameters
- ------ - - - - - - - - - - - - - - - - - - - - - - -
TypeNameDescriptionSchema

Path

key
-required

key

string

Path

topic
-required

topic

string

-
-
-
Responses
- ----- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

200

OK

boolean

204

No Content

No Content

401

Unauthorized

No Content

403

Forbidden

No Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.2.25. Create a topic config by key

-
-
-
POST /kafka/topics/{topic}/conf/{key}={value}
-
-
-
-
Parameters
- ------ - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TypeNameDescriptionSchema

Path

key
-required

key

string

Path

topic
-required

topic

string

Path

value
-required

value

string

-
-
-
Responses
- ----- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

200

OK

< string, object > map

201

Created

No Content

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.2.26. Update a topic config by key

-
-
-
PUT /kafka/topics/{topic}/conf/{key}={value}
-
-
-
-
Parameters
- ------ - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TypeNameDescriptionSchema

Path

key
-required

key

string

Path

topic
-required

topic

string

Path

value
-required

value

string

-
-
-
Responses
- ----- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

200

OK

< string, object > map

201

Created

No Content

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.2.27. Tell if a topic exists

-
-
-
GET /kafka/topics/{topic}/exist
-
-
-
-
Parameters
- ------ - - - - - - - - - - - - - - - - -
TypeNameDescriptionSchema

Path

topic
-required

topic

string

-
-
-
Responses
- ----- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

200

OK

boolean

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.2.28. Write a message to the topic, for testing purpose

-
-
-
POST /kafka/topics/{topic}/write
-
-
-
-
Parameters
- ------ - - - - - - - - - - - - - - - - - - - - - - -
TypeNameDescriptionSchema

Path

topic
-required

topic

string

Body

message
-required

message

string

-
-
-
Responses
- ----- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

201

Created

GeneralResponse

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

-
-
-
Consumes
-
-
    -
  • -

    text/plain

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.2.29. List topics Brief

-
-
-
GET /kafka/topicsbrief
-
-
-
-
Responses
- ----- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

200

OK

< TopicBrief > array

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-
-

4.3. User-controller

-
-

Security User Management Controller.

-
-
-

4.3.1. Add user.

-
-
-
POST /users
-
-
-
-
Parameters
- ------ - - - - - - - - - - - - - - - - -
TypeNameDescriptionSchema

Body

user
-required

user

User

-
-
-
Responses
- ----- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

200

OK

GeneralResponse

201

Created

No Content

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.3.2. Get user list.

-
-
-
GET /users
-
-
-
-
Responses
- ----- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

200

OK

< string > array

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.3.3. Modify user information.

-
-
-
PUT /users
-
-
-
-
Parameters
- ------ - - - - - - - - - - - - - - - - -
TypeNameDescriptionSchema

Body

user
-required

user

User

-
-
-
Responses
- ----- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

200

OK

GeneralResponse

201

Created

No Content

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.3.4. Delete user.

-
-
-
DELETE /users/{username}
-
-
-
-
Parameters
- ------ - - - - - - - - - - - - - - - - -
TypeNameDescriptionSchema

Path

username
-required

username

string

-
-
-
Responses
- ----- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

200

OK

GeneralResponse

204

No Content

No Content

401

Unauthorized

No Content

403

Forbidden

No Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-
-

4.4. Zookeeper-controller

-
-

Zookeeper Controller

-
-
-

4.4.1. Get the connection state of zookeeper

-
-
-
GET /zk/connstate
-
-
-
-
Responses
- ----- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

200

OK

string

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.4.2. Get the environment information of zookeeper

-
-
-
GET /zk/env
-
-
-
-
Responses
- ----- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

200

OK

< string, ZkServerEnvironment > map

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.4.3. List a zookeeper path

-
-
-
GET /zk/ls/{path}
-
-
-
-
Parameters
- ------ - - - - - - - - - - - - - - - - -
TypeNameDescriptionSchema

Path

path
-required

path

string

-
-
-
Responses
- ----- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

200

OK

< string > array

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.4.4. Get the service state of zookeeper

-
-
-
GET /zk/stat
-
-
-
-
Responses
- ----- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

200

OK

< string, ZkServerStat > map

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-
-
-
-

5. Definitions

-
-
-

5.1. AddPartition

- ---- - - - - - - - - - - - - - - - - - - - - -
NameSchema

numPartitionsAdded
-optional

integer(int32)

replicaAssignment
-optional

string

topic
-optional

string

-
-
-

5.2. BrokerInfo

- ---- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameSchema

endPoints
-optional

< string > array

host
-optional

string

id
-optional

integer(int32)

jmxPort
-optional

integer(int32)

port
-optional

integer(int32)

rack
-optional

string

securityProtocol
-optional

object

startTime
-optional

string(date-time)

version
-optional

integer(int32)

-
-
-

5.3. ConsumerGroupDesc

- ---- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameSchema

consumerId
-optional

string

currentOffset
-optional

integer(int64)

groupName
-optional

string

host
-optional

string

lag
-optional

integer(int64)

logEndOffset
-optional

integer(int64)

partitionId
-optional

integer(int32)

state
-optional

enum (RUNNING, PENDING)

topic
-optional

string

type
-optional

enum (NEW, OLD)

-
-
-

5.4. GeneralResponse

- ---- - - - - - - - - - - - - - - - - -
NameSchema

msg
-optional

string

state
-optional

enum (success, failure)

-
-
-

5.5. HashMap«string,object»

-
-

Type : < string, object > map

-
-
-
-

5.6. HealthCheckResult

- ----- - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionSchema

msg
-optional

string

status
-optional

string

timestamp
-optional

Example : "yyyy-MM-dd HH:mm:ss"

string

-
-
-

5.7. HostAndPort

- ---- - - - - - - - - - - - - - - - - -
NameSchema

hostText
-optional

string

port
-optional

integer(int32)

-
-
-

5.8. JMXConfiguration

- ---- - - - - - - - - - - - - - - - - -
NameSchema

exclude
-optional

JMXFilter

include
-optional

JMXFilter

-
-
-

5.9. JMXFilter

- ---- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameSchema

attribute
-optional

object

beanNames
-optional

< string > array

beanRegexes
-optional

< Pattern > array

domain
-optional

string

domainRegex
-optional

Pattern

emptyBeanName
-optional

boolean

filter
-optional

< string, object > map

-
-
-

5.10. JMXMetricData

- ----- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionSchema

collected
-optional

boolean

host
-optional

string

metrics
-optional

< HashMap«string,object» > array

msg
-optional

string

timestamp
-optional

Example : "yyyy-MM-dd HH:mm:ss"

string

-
-
-

5.11. JMXMetricDataV1

- ----- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionSchema

collected
-optional

boolean

host
-optional

string

mbeanInfo
-optional

object

msg
-optional

string

timestamp
-optional

Example : "yyyy-MM-dd HH:mm:ss"

string

-
-
-

5.12. JMXQuery

- ---- - - - - - - - - - - - - -
NameSchema

filters
-optional

< JMXConfiguration > array

-
-
-

5.13. Map«int,long»

-
-

Type : < string, integer(int64) > map

-
-
-
-

5.14. Pattern

- ---- - - - - - - - - - - - - -
NameSchema

cursor
-optional

integer(int32)

-
-
-

5.15. ReassignWrapper

- ---- - - - - - - - - - - - - - - - - -
NameSchema

brokers
-optional

< integer(int32) > array

topics
-optional

< string > array

-
-
-

5.16. TopicAndPartition

-
-

Type : object

-
-
-
-

5.17. TopicBrief

- ---- - - - - - - - - - - - - - - - - - - - - -
NameSchema

isrRate
-optional

number(double)

numPartition
-optional

integer(int32)

topic
-optional

string

-
-
-

5.18. TopicDetail

- ---- - - - - - - - - - - - - - - - - - - - - - - - - -
NameSchema

factor
-optional

integer(int32)

name
-optional

string

partitions
-optional

integer(int32)

prop
-optional

< string, object > map

-
-
-

5.19. TopicMeta

- ---- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameSchema

partitionCount
-optional

integer(int32)

replicationFactor
-optional

integer(int32)

topicCustomConfigs
-optional

< string, object > map

topicName
-optional

string

topicPartitionInfos
-optional

< TopicPartitionInfo > array

-
-
-

5.20. TopicPartitionInfo

- ---- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameSchema

endOffset
-optional

integer(int64)

in_sync
-optional

boolean

isr
-optional

< string > array

leader
-optional

string

messageAvailable
-optional

integer(int64)

partitionId
-optional

integer(int32)

replicas
-optional

< string > array

startOffset
-optional

integer(int64)

-
-
-

5.21. User

- ---- - - - - - - - - - - - - - - - - - - - - -
NameSchema

password
-optional

string

role
-optional

string

username
-optional

string

-
-
-

5.22. ZkServerClient

- ---- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameSchema

host
-optional

string

ops
-optional

integer(int32)

port
-optional

integer(int32)

queued
-optional

integer(int32)

received
-optional

integer(int32)

sent
-optional

integer(int32)

-
-
-

5.23. ZkServerEnvironment

- ---- - - - - - - - - - - - - -
NameSchema

attributes
-optional

< string, string > map

-
-
-

5.24. ZkServerStat

- ---- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameSchema

avgLatency
-optional

integer(int32)

buildDate
-optional

string

clients
-optional

< ZkServerClient > array

connections
-optional

integer(int32)

maxLatency
-optional

integer(int32)

minLatency
-optional

integer(int32)

mode
-optional

enum (Leader, Follower, Observer)

nodes
-optional

integer(int32)

outstanding
-optional

integer(int32)

received
-optional

integer(int32)

sent
-optional

integer(int32)

version
-optional

string

zxId
-optional

string

-
-
-
+
+

1. Overview

+
+
+

Kafka REST API SwaggerUI

+
+
+

1.1. + Version information

+
+

Version : 0.1.0

+
+
+
+

1.2. + Contact information

+
+

Contact : gnuhpc
+ Contact Email : gnuhpc@gmail.com

+
+
+
+

1.3. URI scheme

+
+

Host : localhost:8080
+ BasePath : /

+
+
+
+

1.4. Tags

+
+
    +
  • +

    collector-controller : Rest API for Collecting JMX Metric Data

    +
  • +
  • +

    kafka-controller : Kafka Controller

    +
  • +
  • +

    user-controller : Security User Management Controller.

    +
  • +
  • +

    zookeeper-controller : Zookeeper Controller

    +
  • +
+
+
+
+
+
+

2. Chapter of + manual content 1

+
+
+

This is some dummy text

+
+
+

2.1. Sub chapter

+
+

Dummy text of sub chapter

+
+
+
+
+
+

3. Chapter of + manual content 2

+
+
+

This is some dummy text

+
+
+
+
+

4. Resources

+
+
+

4.1. + Collector-controller

+
+

Rest API for Collecting JMX Metric Data

+
+
+

4.1.1. Fetch + all JMX metric data

+
+
+
GET /jmx/v1
+
+
+
+
Parameters
+ + + + + + + + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

+ Query

jmxurl
+ optional

Parameter + jmxurl should be a comma-separated list of {IP:Port} or set to 'default'

string

+
+
+
+
Responses
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

+ 200

OK

< JMXMetricDataV1 > array

+ 401

+ Unauthorized

No + Content

+ 403

Forbidden

+

No + Content

+ 404

Not Found

+

No + Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.1.2. Fetch + JMX metric data with query filter. You can get the query filter template through the API + /jmx/v2/filters.

+
+
+
POST /jmx/v2
+
+
+
+
Parameters
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

+ Query

jmxurl
+ optional

Parameter + jmxurl should be a comma-separated list of {IP:Port} or set to 'default'

string

+

+ Body

jmxQuery
+ required

jmxQuery

+

JMXQuery

+
+
+
Responses
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

+ 200

OK

< JMXMetricData > array

+ 201

Created

+

No + Content

+ 401

+ Unauthorized

No + Content

+ 403

Forbidden

+

No + Content

+ 404

Not Found

+

No + Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.1.3. + List the query filter templates with the filterKey. If filterKey is set to empty, it + will return all the templates.

+
+
+
GET /jmx/v2/filters
+
+
+
+
Parameters
+ + + + + + + + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

+ Query

filterKey
+ required

filterKey

+

string

+
+
+
+
Responses
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

+ 200

OK

< string, + object > map

+ 401

+ Unauthorized

No + Content

+ 403

Forbidden

+

No + Content

+ 404

Not Found

+

No + Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+
+

4.2. + Kafka-controller

+
+

Kafka Controller

+
+
+

4.2.1. + List brokers in this cluster

+
+
+
GET /kafka/brokers
+
+
+
+
Responses
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

+ 200

OK

< BrokerInfo > array

+ 401

+ Unauthorized

No + Content

+ 403

Forbidden

+

No + Content

+ 404

Not Found

+

No + Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.2.2. Get + the message from the offset of the partition in the topic, decoder is not supported yet +

+
+
+
GET /kafka/consumer/{topic}/{partition}/{offset}
+
+
+
+
Parameters
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

+ Path

offset
+ required

offset

+

+ integer(int64)

+ Path

partition
+ required

partition

+

+ integer(int32)

+ Path

+ topic
+ required

topic

+

string

+

+ Query

decoder
+ optional

decoder

+

string

+
+
+
+
Responses
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

+ 200

OK

string

+

+ 401

+ Unauthorized

No + Content

+ 403

Forbidden

+

No + Content

+ 404

Not Found

+

No + Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.2.3. + Delete old Consumer Group

+
+
+
DELETE /kafka/consumergroup/{consumergroup}
+
+
+
+
Parameters
+ + + + + + + + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

+ Path

consumergroup
+ required

+ consumergroup

string

+
+
+
+
Responses
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

+ 200

OK

GeneralResponse

+ 204

No + Content

No + Content

+ 401

+ Unauthorized

No + Content

+ 403

Forbidden

+

No + Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.2.4. + getLastCommitTimestamp

+
+
+
GET /kafka/consumergroup/{consumergroup}/{type}/topic/{topic}/lastcommittime
+
+
+
+
Parameters
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

+ Path

consumergroup
+ required

+ consumergroup

string

+

+ Path

+ topic
+ required

topic

+

string

+

+ Path

+ type
+ required

type

enum (NEW, + OLD)

+
+
+
Responses
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

+ 200

OK

< string, + < string, integer(int64) > map > map

+ 401

+ Unauthorized

No + Content

+ 403

Forbidden

+

No + Content

+ 404

Not Found

+

No + Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.2.5. + Reset consumer group offset, earliest/latest can be used

+
+
+
PUT /kafka/consumergroup/{consumergroup}/{type}/topic/{topic}/{partition}/{offset}
+
+
+
+
Parameters
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

+ Path

consumergroup
+ required

+ consumergroup

string

+

+ Path

offset
+ required

offset

+

string

+

+ Path

partition
+ required

partition

+

+ integer(int32)

+ Path

+ topic
+ required

topic

+

string

+

+ Path

+ type
+ required

type

enum (NEW, + OLD)

+
+
+
Responses
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

+ 200

OK

GeneralResponse

+ 201

Created

+

No + Content

+ 401

+ Unauthorized

No + Content

+ 403

Forbidden

+

No + Content

+ 404

Not Found

+

No + Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.2.6. + List all consumer groups from zk and kafka

+
+
+
GET /kafka/consumergroups
+
+
+
+
Parameters
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

+ Query

+ topic
+ optional

topic

+

string

+

+ Query

+ type
+ optional

type

enum (NEW, + OLD)

+
+
+
Responses
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

+ 200

OK

< string, + < string > array > map

+ 401

+ Unauthorized

No + Content

+ 403

Forbidden

+

No + Content

+ 404

Not Found

+

No + Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.2.7. + Describe consumer groups, showing lag and offset, may be slow if multi topic are + listened

+
+
+
GET /kafka/consumergroups/{consumerGroup}/{type}
+
+
+
+
Parameters
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

+ Path

consumerGroup
+ required

+ consumerGroup

string

+

+ Path

+ type
+ required

type

enum (NEW, + OLD)

+
+
+
Responses
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

+ 200

OK

< string, + < ConsumerGroupDesc > array > map

+

+ 401

+ Unauthorized

No + Content

+ 403

Forbidden

+

No + Content

+ 404

Not Found

+

No + Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.2.8. + Get the topics involved of the specify consumer group

+
+
+
GET /kafka/consumergroups/{consumerGroup}/{type}/topic
+
+
+
+
Parameters
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

+ Path

consumerGroup
+ required

+ consumerGroup

string

+

+ Path

+ type
+ required

type

enum (NEW, + OLD)

+
+
+
Responses
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

+ 200

OK

< string + > array

+ 401

+ Unauthorized

No + Content

+ 403

Forbidden

+

No + Content

+ 404

Not Found

+

No + Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.2.9. + Describe consumer groups by topic, showing lag and offset

+
+
+
GET /kafka/consumergroups/{consumerGroup}/{type}/topic/{topic}
+
+
+
+
Parameters
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

+ Path

consumerGroup
+ required

+ consumerGroup

string

+

+ Path

+ topic
+ required

topic

+

string

+

+ Path

+ type
+ required

type

enum (NEW, + OLD)

+
+
+
Responses
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

+ 200

OK

< ConsumerGroupDesc > array

+ 401

+ Unauthorized

No + Content

+ 403

Forbidden

+

No + Content

+ 404

Not Found

+

No + Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.2.10. + Check the cluster health.

+
+
+
GET /kafka/health
+
+
+
+
Responses
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

+ 200

OK

HealthCheckResult

+ 401

+ Unauthorized

No + Content

+ 403

Forbidden

+

No + Content

+ 404

Not Found

+

No + Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.2.11. + Add a partition to the topic

+
+
+
POST /kafka/partitions/add
+
+
+
+
Parameters
+ + + + + + + + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

+ Body

addPartition
+ required

+ addPartition

AddPartition

+
+
+
Responses
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

+ 200

OK

TopicMeta

+ 201

Created

+

No + Content

+ 401

+ Unauthorized

No + Content

+ 403

Forbidden

+

No + Content

+ 404

Not Found

+

No + Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.2.12. + Check the partition reassignment process

+
+
+
PUT /kafka/partitions/reassign/check
+
+
+
+
Parameters
+ + + + + + + + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

+ Body

reassignStr
+ required

+ reassignStr

string

+
+
+
+
Responses
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

+ -1

Reassignment + Failed

No + Content

+ 0

Reassignment + In Progress

No + Content

+ 1

Reassignment + Completed

No + Content

+ 200

OK

< string, + integer(int32) > map

+ 201

Created

+

No + Content

+ 401

+ Unauthorized

No + Content

+ 403

Forbidden

+

No + Content

+ 404

Not Found

+

No + Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.2.13. + Execute the partition reassignment

+
+
+
PUT /kafka/partitions/reassign/execute
+
+
+
+
Parameters
+ + + + + + + + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

+ Body

reassignStr
+ required

+ reassignStr

string

+
+
+
+
Responses
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

+ 200

OK

< string, + integer(int32) > map

+ 201

Created

+

No + Content

+ 401

+ Unauthorized

No + Content

+ 403

Forbidden

+

No + Content

+ 404

Not Found

+

No + Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.2.14. + Generate plan for the partition reassignment

+
+
+
POST /kafka/partitions/reassign/generate
+
+
+
+
Parameters
+ + + + + + + + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

+ Body

reassignWrapper
+ required

+ reassignWrapper

ReassignWrapper

+
+
+
Responses
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

+ 200

OK

< string + > array

+ 201

Created

+

No + Content

+ 401

+ Unauthorized

No + Content

+ 403

Forbidden

+

No + Content

+ 404

Not Found

+

No + Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.2.15. + List topics

+
+
+
GET /kafka/topics
+
+
+
+
Responses
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

+ 200

OK

< string + > array

+ 401

+ Unauthorized

No + Content

+ 403

Forbidden

+

No + Content

+ 404

Not Found

+

No + Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.2.16. + Create a topic

+
+
+
POST /kafka/topics/create
+
+
+
+
Parameters
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

+ Query

reassignStr
+ optional

+ reassignStr

string

+

+ Body

+ topic
+ required

topic

+

TopicDetail

+
+
+
Responses
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

+ 201

Created

+

TopicMeta

+ 401

+ Unauthorized

No + Content

+ 403

Forbidden

+

No + Content

+ 404

Not Found

+

No + Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.2.17. + Describe a topic by fetching the metadata and config

+
+
+
GET /kafka/topics/{topic}
+
+
+
+
Parameters
+ + + + + + + + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

+ Path

+ topic
+ required

topic

+

string

+
+
+
+
Responses
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

+ 200

OK

TopicMeta

+ 401

+ Unauthorized

No + Content

+ 403

Forbidden

+

No + Content

+ 404

Not Found

+

No + Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.2.18. + Delete a topic (you should enable topic deletion

+
+
+
DELETE /kafka/topics/{topic}
+
+
+
+
Parameters
+ + + + + + + + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

+ Path

+ topic
+ required

topic

+

string

+
+
+
+
Responses
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

+ 200

OK

GeneralResponse

+ 204

No + Content

No + Content

+ 401

+ Unauthorized

No + Content

+ 403

Forbidden

+

No + Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.2.19. + Create topic configs

+
+
+
POST /kafka/topics/{topic}/conf
+
+
+
+
Parameters
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

+ Path

+ topic
+ required

topic

+

string

+

+ Body

+ prop
+ required

prop

< string, + object > map

+
+
+
Responses
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

+ 200

OK

< string, + object > map

+ 201

Created

+

No + Content

+ 401

+ Unauthorized

No + Content

+ 403

Forbidden

+

No + Content

+ 404

Not Found

+

No + Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.2.20. + Get topic configs

+
+
+
GET /kafka/topics/{topic}/conf
+
+
+
+
Parameters
+ + + + + + + + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

+ Path

+ topic
+ required

topic

+

string

+
+
+
+
Responses
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

+ 200

OK

< string, + object > map

+ 401

+ Unauthorized

No + Content

+ 403

Forbidden

+

No + Content

+ 404

Not Found

+

No + Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.2.21. + Update topic configs

+
+
+
PUT /kafka/topics/{topic}/conf
+
+
+
+
Parameters
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

+ Path

+ topic
+ required

topic

+

string

+

+ Body

+ prop
+ required

prop

< string, + object > map

+
+
+
Responses
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

+ 200

OK

< string, + object > map

+ 201

Created

+

No + Content

+ 401

+ Unauthorized

No + Content

+ 403

Forbidden

+

No + Content

+ 404

Not Found

+

No + Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.2.22. + Delete topic configs

+
+
+
DELETE /kafka/topics/{topic}/conf
+
+
+
+
Parameters
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

+ Path

+ topic
+ required

topic

+

string

+

+ Body

delProps
+ required

delProps

+

< string + > array

+
+
+
Responses
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

+ 200

OK

< string, + object > map

+ 204

No + Content

No + Content

+ 401

+ Unauthorized

No + Content

+ 403

Forbidden

+

No + Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.2.23. + Get topic config by key

+
+
+
GET /kafka/topics/{topic}/conf/{key}
+
+
+
+
Parameters
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

+ Path

+ key
+ required

key

string

+

+ Path

+ topic
+ required

topic

+

string

+
+
+
+
Responses
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

+ 200

OK

< string, + object > map

+ 401

+ Unauthorized

No + Content

+ 403

Forbidden

+

No + Content

+ 404

Not Found

+

No + Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.2.24. + Delete a topic config by key

+
+
+
DELETE /kafka/topics/{topic}/conf/{key}
+
+
+
+
Parameters
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

+ Path

+ key
+ required

key

string

+

+ Path

+ topic
+ required

topic

+

string

+
+
+
+
Responses
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

+ 200

OK

boolean

+

+ 204

No + Content

No + Content

+ 401

+ Unauthorized

No + Content

+ 403

Forbidden

+

No + Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.2.25. + Create a topic config by key

+
+
+
POST /kafka/topics/{topic}/conf/{key}={value}
+
+
+
+
Parameters
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

+ Path

+ key
+ required

key

string

+

+ Path

+ topic
+ required

topic

+

string

+

+ Path

+ value
+ required

value

+

string

+
+
+
+
Responses
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

+ 200

OK

< string, + object > map

+ 201

Created

+

No + Content

+ 401

+ Unauthorized

No + Content

+ 403

Forbidden

+

No + Content

+ 404

Not Found

+

No + Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.2.26. + Update a topic config by key

+
+
+
PUT /kafka/topics/{topic}/conf/{key}={value}
+
+
+
+
Parameters
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

+ Path

+ key
+ required

key

string

+

+ Path

+ topic
+ required

topic

+

string

+

+ Path

+ value
+ required

value

+

string

+
+
+
+
Responses
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

+ 200

OK

< string, + object > map

+ 201

Created

+

No + Content

+ 401

+ Unauthorized

No + Content

+ 403

Forbidden

+

No + Content

+ 404

Not Found

+

No + Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.2.27. + Tell if a topic exists

+
+
+
GET /kafka/topics/{topic}/exist
+
+
+
+
Parameters
+ + + + + + + + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

+ Path

+ topic
+ required

topic

+

string

+
+
+
+
Responses
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

+ 200

OK

boolean

+

+ 401

+ Unauthorized

No + Content

+ 403

Forbidden

+

No + Content

+ 404

Not Found

+

No + Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.2.28. + Write a message to the topic, for testing purpose

+
+
+
POST /kafka/topics/{topic}/write
+
+
+
+
Parameters
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

+ Path

+ topic
+ required

topic

+

string

+

+ Body

message
+ required

message

+

string

+
+
+
+
Responses
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

+ 201

Created

+

GeneralResponse

+ 401

+ Unauthorized

No + Content

+ 403

Forbidden

+

No + Content

+ 404

Not Found

+

No + Content

+
+
+
Consumes
+
+
    +
  • +

    text/plain

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.2.29. + List topics Brief

+
+
+
GET /kafka/topicsbrief
+
+
+
+
Responses
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

+ 200

OK

< TopicBrief > array

+ 401

+ Unauthorized

No + Content

+ 403

Forbidden

+

No + Content

+ 404

Not Found

+

No + Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+
+

4.3. + User-controller

+
+

Security User Management Controller.

+
+
+

4.3.1. Add + user.

+
+
+
POST /users
+
+
+
+
Parameters
+ + + + + + + + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

+ Body

+ user
+ required

user

User

+
+
+
Responses
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

+ 200

OK

GeneralResponse

+ 201

Created

+

No + Content

+ 401

+ Unauthorized

No + Content

+ 403

Forbidden

+

No + Content

+ 404

Not Found

+

No + Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.3.2. Get user + list.

+
+
+
GET /users
+
+
+
+
Responses
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

+ 200

OK

< string + > array

+ 401

+ Unauthorized

No + Content

+ 403

Forbidden

+

No + Content

+ 404

Not Found

+

No + Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.3.3. + Modify user information.

+
+
+
PUT /users
+
+
+
+
Parameters
+ + + + + + + + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

+ Body

+ user
+ required

user

User

+
+
+
Responses
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

+ 200

OK

GeneralResponse

+ 201

Created

+

No + Content

+ 401

+ Unauthorized

No + Content

+ 403

Forbidden

+

No + Content

+ 404

Not Found

+

No + Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.3.4. + Delete user.

+
+
+
DELETE /users/{username}
+
+
+
+
Parameters
+ + + + + + + + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

+ Path

username
+ required

username

+

string

+
+
+
+
Responses
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

+ 200

OK

GeneralResponse

+ 204

No + Content

No + Content

+ 401

+ Unauthorized

No + Content

+ 403

Forbidden

+

No + Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+
+

4.4. + Zookeeper-controller

+
+

Zookeeper Controller

+
+
+

4.4.1. + Get the connection state of zookeeper

+
+
+
GET /zk/connstate
+
+
+
+
Responses
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

+ 200

OK

string

+

+ 401

+ Unauthorized

No + Content

+ 403

Forbidden

+

No + Content

+ 404

Not Found

+

No + Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.4.2. Get the + environment information of zookeeper

+
+
+
GET /zk/env
+
+
+
+
Responses
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

+ 200

OK

< string, + ZkServerEnvironment > map

+ 401

+ Unauthorized

No + Content

+ 403

Forbidden

+

No + Content

+ 404

Not Found

+

No + Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.4.3. List a zookeeper + path

+
+
+
GET /zk/ls/{path}
+
+
+
+
Parameters
+ + + + + + + + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

+ Path

+ path
+ required

path

string

+
+
+
+
Responses
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

+ 200

OK

< string + > array

+ 401

+ Unauthorized

No + Content

+ 403

Forbidden

+

No + Content

+ 404

Not Found

+

No + Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.4.4. Get the + service state of zookeeper

+
+
+
GET /zk/stat
+
+
+
+
Responses
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

+ 200

OK

< string, + ZkServerStat > map

+ 401

+ Unauthorized

No + Content

+ 403

Forbidden

+

No + Content

+ 404

Not Found

+

No + Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+
+
+
+

5. Definitions

+
+
+

5.1. AddPartition

+ + + + + + + + + + + + + + + + + + + + + + + + + +
NameSchema

numPartitionsAdded
+ optional

+ integer(int32)

replicaAssignment
+ optional

string

+ topic
+ optional

string

+
+
+

5.2. BrokerInfo

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameSchema

+ endPoints
+ optional

< string > + array

+ host
+ optional

string

+ id
+ optional

+ integer(int32)

+ jmxPort
+ optional

+ integer(int32)

+ port
+ optional

+ integer(int32)

+ rack
+ optional

string

securityProtocol
+ optional

object

+ startTime
+ optional

+ string(date-time)

+ version
+ optional

+ integer(int32)

+
+
+

5.3. + ConsumerGroupDesc

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameSchema

consumerId
+ optional

string

currentOffset
+ optional

+ integer(int64)

+ groupName
+ optional

string

+ host
+ optional

string

+ lag
+ optional

+ integer(int64)

logEndOffset
+ optional

+ integer(int64)

partitionId
+ optional

+ integer(int32)

+ state
+ optional

enum (RUNNING, + PENDING)

+ topic
+ optional

string

+ type
+ optional

enum (NEW, + OLD)

+
+
+

5.4. + GeneralResponse

+ + + + + + + + + + + + + + + + + + + + + +
NameSchema

+ msg
+ optional

string

+ state
+ optional

enum (success, + failure)

+
+
+

5.5. + HashMap«string,object»

+
+

Type : < string, object > map

+
+
+
+

5.6. + HealthCheckResult

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameDescriptionSchema

+ msg
+ optional

string

+ status
+ optional

string

+ timestamp
+ optional

+ Example : "yyyy-MM-dd HH:mm:ss"

string

+
+
+

5.7. HostAndPort

+ + + + + + + + + + + + + + + + + + + + + +
NameSchema

+ hostText
+ optional

string

+ port
+ optional

+ integer(int32)

+
+
+

5.8. + JMXConfiguration

+ + + + + + + + + + + + + + + + + + + + + +
NameSchema

+ exclude
+ optional

JMXFilter

+ include
+ optional

JMXFilter

+
+
+

5.9. JMXFilter

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameSchema

+ attribute
+ optional

object

+ beanNames
+ optional

< string > + array

beanRegexes
+ optional

< Pattern > array

+ domain
+ optional

string

domainRegex
+ optional

Pattern

emptyBeanName
+ optional

boolean

+ filter
+ optional

< string, + object > map

+
+
+

5.10. JMXMetricData +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameDescriptionSchema

+ collected
+ optional

boolean

+ host
+ optional

string

+ metrics
+ optional

< HashMap«string,object» > array

+

+ msg
+ optional

string

+ timestamp
+ optional

+ Example : "yyyy-MM-dd HH:mm:ss"

string

+
+
+

5.11. + JMXMetricDataV1

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameDescriptionSchema

+ collected
+ optional

boolean

+ host
+ optional

string

+ mbeanInfo
+ optional

object

+ msg
+ optional

string

+ timestamp
+ optional

+ Example : "yyyy-MM-dd HH:mm:ss"

string

+
+
+

5.12. JMXQuery

+ + + + + + + + + + + + + + + + + +
NameSchema

+ filters
+ optional

< JMXConfiguration > array

+
+
+

5.13. + Map«int,long»

+
+

Type : < string, integer(int64) > map

+
+
+
+

5.14. Pattern

+ + + + + + + + + + + + + + + + + +
NameSchema

+ cursor
+ optional

+ integer(int32)

+
+
+

5.15. + ReassignWrapper

+ + + + + + + + + + + + + + + + + + + + + +
NameSchema

+ brokers
+ optional

< + integer(int32) > array

+ topics
+ optional

< string > + array

+
+
+

5.16. + TopicAndPartition

+
+

Type : object

+
+
+
+

5.17. TopicBrief

+ + + + + + + + + + + + + + + + + + + + + + + + + +
NameSchema

+ isrRate
+ optional

+ number(double)

numPartition
+ optional

+ integer(int32)

+ topic
+ optional

string

+
+
+

5.18. TopicDetail

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameSchema

+ factor
+ optional

+ integer(int32)

+ name
+ optional

string

partitions
+ optional

+ integer(int32)

+ prop
+ optional

< string, + object > map

+
+
+

5.19. TopicMeta

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameSchema

partitionCount
+ optional

+ integer(int32)

replicationFactor
+ optional

+ integer(int32)

topicCustomConfigs
+ optional

< string, + object > map

+ topicName
+ optional

string

topicPartitionInfos
+ optional

< TopicPartitionInfo > array

+
+
+

5.20. + TopicPartitionInfo

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameSchema

+ endOffset
+ optional

+ integer(int64)

+ in_sync
+ optional

boolean

+ isr
+ optional

< string > + array

+ leader
+ optional

string

messageAvailable
+ optional

+ integer(int64)

partitionId
+ optional

+ integer(int32)

+ replicas
+ optional

< string > + array

startOffset
+ optional

+ integer(int64)

+
+
+

5.21. User

+ + + + + + + + + + + + + + + + + + + + + + + + + +
NameSchema

+ password
+ optional

string

+ role
+ optional

string

+ username
+ optional

string

+
+
+

5.22. ZkServerClient +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameSchema

+ host
+ optional

string

+ ops
+ optional

+ integer(int32)

+ port
+ optional

+ integer(int32)

+ queued
+ optional

+ integer(int32)

+ received
+ optional

+ integer(int32)

+ sent
+ optional

+ integer(int32)

+
+
+

5.23. + ZkServerEnvironment

+ + + + + + + + + + + + + + + + + +
NameSchema

attributes
+ optional

< string, + string > map

+
+
+

5.24. ZkServerStat

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameSchema

avgLatency
+ optional

+ integer(int32)

+ buildDate
+ optional

string

+ clients
+ optional

< ZkServerClient > array

connections
+ optional

+ integer(int32)

maxLatency
+ optional

+ integer(int32)

minLatency
+ optional

+ integer(int32)

+ mode
+ optional

enum (Leader, + Follower, Observer)

+ nodes
+ optional

+ integer(int32)

outstanding
+ optional

+ integer(int32)

+ received
+ optional

+ integer(int32)

+ sent
+ optional

+ integer(int32)

+ version
+ optional

string

+ zxId
+ optional

string

+
+
+
\ No newline at end of file diff --git a/pom.xml b/pom.xml index b46e5f2..7f2b042 100644 --- a/pom.xml +++ b/pom.xml @@ -1,468 +1,476 @@ - - 4.0.0 - - org.gnuhpc.bigdata - kafka-rest-springboot - 0.10.x-release - jar - - kafka-zk-rest-springboot - Kafka/Zookeeper Rest API services - - - + + 4.0.0 + + org.gnuhpc.bigdata + kafka-rest-springboot + 0.10.x-release + jar + + kafka-zk-rest-springboot + Kafka/Zookeeper Rest API services + + + + org.springframework.boot + spring-boot-starter-parent + 1.5.4.RELEASE + + + + + UTF-8 + UTF-8 + 1.8 + + 1.2.0 + ${project.basedir}/src/docs/asciidoc + ${project.basedir}/src/docs/swagger + ${project.build.directory}/asciidoc/snippets + + ${project.build.directory}/asciidoc/md + + ${project.build.directory}/asciidoc/html + + ${project.build.directory}/asciidoc/pdf + + + ${swagger.output.dir}/swagger.json + 2.9.1 + 2.9.5 + 1.19 + + + + jcenter-snapshots + jcenter + http://oss.jfrog.org/artifactory/oss-snapshot-local/ + + + jcenter-releases + jcenter + http://jcenter.bintray.com + + false + + + + + + + jcentral + bintray + http://jcenter.bintray.com + + false + + + + jcenter-snapshots + jcenter + http://oss.jfrog.org/artifactory/oss-snapshot-local/ + + + Spring + Spring-milestone + http://repo.spring.io/milestone/ + + + + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-security + + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-logging + + + + + org.springframework.boot + spring-boot-starter-log4j + 1.3.8.RELEASE + + + + org.springframework.boot + spring-boot-devtools + runtime + + + org.springframework.boot + spring-boot-configuration-processor + true + + + org.springframework.boot + spring-boot-starter-aop + + + + + + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.projectlombok + lombok + true + + + + + + com.101tec + zkclient + 0.10 + + + + org.apache.curator + curator-client + 2.11.0 + + + + + io.springfox + springfox-swagger2 + 2.7.0 + + + io.swagger + swagger-annotations + 1.5.16 + + + io.springfox + springfox-swagger-ui + 2.7.0 + + + + org.apache.kafka + kafka-clients + 1.1.1 + + + + + org.springframework.kafka + spring-kafka + 1.2.2.RELEASE + + + + org.apache.kafka + kafka_2.11 + 1.1.1 + + + com.101tec + zkclient + + + + + + + joda-time + joda-time + 2.9.9 + + + com.google.code.gson + gson + 2.6.2 + + + + com.fasterxml.jackson.datatype + jackson-datatype-joda + ${jackson.version} + + + com.fasterxml.jackson.core + jackson-databind + ${jackson.version} + + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + + + com.fasterxml.jackson.dataformat + jackson-dataformat-yaml + ${jackson.version} + + + com.fasterxml.jackson.module + jackson-module-scala_2.11 + ${jackson.module.scala.version} + + + + org.apache.commons + commons-collections4 + 4.0 + + + + + org.springframework.restdocs + spring-restdocs-mockmvc + test + + + + commons-io + commons-io + 2.5 + + + org.apache.commons + commons-lang3 + 3.5 + + + org.apache.curator + curator-framework + 2.11.0 + + + + + + + net.alchim31.maven + scala-maven-plugin + 3.3.1 + + + scala-compile + process-resources + + add-source + compile + + + + scala-test-compile + process-test-resources + + testCompile + + + + + org.springframework.boot - spring-boot-starter-parent - 1.5.4.RELEASE - - - - - UTF-8 - UTF-8 - 1.8 - - 1.2.0 - ${project.basedir}/src/docs/asciidoc - ${project.basedir}/src/docs/swagger - ${project.build.directory}/asciidoc/snippets - ${project.build.directory}/asciidoc/md - ${project.build.directory}/asciidoc/html - ${project.build.directory}/asciidoc/pdf - - ${swagger.output.dir}/swagger.json - 2.9.1 - 2.9.5 - 1.19 - - - - jcenter-snapshots - jcenter - http://oss.jfrog.org/artifactory/oss-snapshot-local/ - - - jcenter-releases - jcenter - http://jcenter.bintray.com - - false - - - - - - - jcentral - bintray - http://jcenter.bintray.com - - false - - - - jcenter-snapshots - jcenter - http://oss.jfrog.org/artifactory/oss-snapshot-local/ - - - Spring - Spring-milestone - http://repo.spring.io/milestone/ - - - - - - - org.springframework.boot - spring-boot-starter-web - - - - org.springframework.boot - spring-boot-starter-security - - - - org.springframework.boot - spring-boot-starter - - - org.springframework.boot - spring-boot-starter-logging - - - - - org.springframework.boot - spring-boot-starter-log4j - 1.3.8.RELEASE - - - - org.springframework.boot - spring-boot-devtools - runtime - - - org.springframework.boot - spring-boot-configuration-processor - true - - - org.springframework.boot - spring-boot-starter-aop - - - - - - - - org.springframework.boot - spring-boot-starter-test - test - - - - org.projectlombok - lombok - true - - - - - - com.101tec - zkclient - 0.10 - - - - org.apache.curator - curator-client - 2.11.0 - - - - - io.springfox - springfox-swagger2 - 2.7.0 - - - io.swagger - swagger-annotations - 1.5.16 - - - io.springfox - springfox-swagger-ui - 2.7.0 - - - - org.apache.kafka - kafka-clients - 1.1.1 - - - - - org.springframework.kafka - spring-kafka - 1.2.2.RELEASE - - - - org.apache.kafka - kafka_2.11 - 1.1.1 - - - com.101tec - zkclient - - - - - - - joda-time - joda-time - 2.9.9 - - - com.google.code.gson - gson - 2.6.2 - - - - com.fasterxml.jackson.datatype - jackson-datatype-joda - ${jackson.version} - - - com.fasterxml.jackson.core - jackson-databind - ${jackson.version} - - - - com.fasterxml.jackson.datatype - jackson-datatype-jsr310 - - - com.fasterxml.jackson.dataformat - jackson-dataformat-yaml - ${jackson.version} - - - com.fasterxml.jackson.module - jackson-module-scala_2.11 - ${jackson.module.scala.version} - - - - org.apache.commons - commons-collections4 - 4.0 - - - - - org.springframework.restdocs - spring-restdocs-mockmvc - test - - - - commons-io - commons-io - 2.5 - - - org.apache.commons - commons-lang3 - 3.5 - - - org.apache.curator - curator-framework - 2.11.0 - - - - - - - net.alchim31.maven - scala-maven-plugin - 3.3.1 - - - scala-compile - process-resources - - add-source - compile - - - - scala-test-compile - process-test-resources - - testCompile - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - - org.apache.maven.plugins - maven-surefire-plugin - - - ${swagger.output.dir} - ${swagger.snippetOutput.dir} - - - - - - - io.github.swagger2markup - swagger2markup-maven-plugin - ${swagger2markup.version} - - - io.github.swagger2markup - swagger2markup-import-files-ext - ${swagger2markup.version} - - - io.github.swagger2markup - swagger2markup-spring-restdocs-ext - ${swagger2markup.version} - - - - ${swagger.input} - ${generated.asciidoc.directory} - - ASCIIDOC - TAGS - - - ${project.basedir}/src/docs/asciidoc/extensions/overview - - - ${project.basedir}/src/docs/asciidoc/extensions/definitions - - - ${project.basedir}/src/docs/asciidoc/extensions/paths - - - ${project.basedir}src/docs/asciidoc/extensions/security/ - - - ${swagger.snippetOutput.dir} - - true - - - - - - test - - convertSwagger2markup - - - - - - - - org.asciidoctor - asciidoctor-maven-plugin - 1.5.3 - - - - org.asciidoctor - asciidoctorj-pdf - 1.5.0-alpha.10.1 - - - org.jruby - jruby-complete - 1.7.21 - - - - - ${asciidoctor.input.directory} - index.adoc - - book - left - 3 - - - - - ${generated.asciidoc.directory} - - - - - - output-html - test - - process-asciidoc - - - html5 - ${asciidoctor.html.output.directory} - - - - - output-pdf - test - - process-asciidoc - - - pdf - ${asciidoctor.pdf.output.directory} - - - - - - - - - maven-resources-plugin - 2.7 - - - copy-resources - prepare-package - - copy-resources - - - ${project.basedir}/docs - - - ${asciidoctor.html.output.directory} - - - ${asciidoctor.pdf.output.directory} - - - ${generated.asciidoc.directory} - - - - - - - - - org.apache.maven.plugins - maven-assembly-plugin - 2.4.1 - - src/main/resources/distribution/kafka-zk-springboot-distribution.xml - kafka-zk-api-${project.version}-dist - false - - - - package - - single - - - - - - + spring-boot-maven-plugin + + + + org.apache.maven.plugins + maven-surefire-plugin + + + ${swagger.output.dir} + + ${swagger.snippetOutput.dir} + + + + + + + + io.github.swagger2markup + swagger2markup-maven-plugin + ${swagger2markup.version} + + + io.github.swagger2markup + swagger2markup-import-files-ext + ${swagger2markup.version} + + + io.github.swagger2markup + swagger2markup-spring-restdocs-ext + ${swagger2markup.version} + + + + ${swagger.input} + ${generated.asciidoc.directory} + + ASCIIDOC + TAGS + + + ${project.basedir}/src/docs/asciidoc/extensions/overview + + + ${project.basedir}/src/docs/asciidoc/extensions/definitions + + + ${project.basedir}/src/docs/asciidoc/extensions/paths + + + ${project.basedir}src/docs/asciidoc/extensions/security/ + + + ${swagger.snippetOutput.dir} + + true + + + + + + test + + convertSwagger2markup + + + + + + + + org.asciidoctor + asciidoctor-maven-plugin + 1.5.3 + + + + org.asciidoctor + asciidoctorj-pdf + 1.5.0-alpha.10.1 + + + org.jruby + jruby-complete + 1.7.21 + + + + + ${asciidoctor.input.directory} + index.adoc + + book + left + 3 + + + + + ${generated.asciidoc.directory} + + + + + + output-html + test + + process-asciidoc + + + html5 + ${asciidoctor.html.output.directory} + + + + + output-pdf + test + + process-asciidoc + + + pdf + ${asciidoctor.pdf.output.directory} + + + + + + + + + maven-resources-plugin + 2.7 + + + copy-resources + prepare-package + + copy-resources + + + ${project.basedir}/docs + + + ${asciidoctor.html.output.directory} + + + ${asciidoctor.pdf.output.directory} + + + ${generated.asciidoc.directory} + + + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + 2.4.1 + + src/main/resources/distribution/kafka-zk-springboot-distribution.xml + + kafka-zk-api-${project.version}-dist + false + + + + package + + single + + + + + + diff --git a/src/docs/swagger/swagger.json b/src/docs/swagger/swagger.json index 9fda04d..601e028 100644 --- a/src/docs/swagger/swagger.json +++ b/src/docs/swagger/swagger.json @@ -1 +1,2470 @@ -{"swagger":"2.0","info":{"description":"Kafka REST API SwaggerUI","version":"0.1.0","title":"Kafka REST API SwaggerUI","contact":{"name":"gnuhpc","url":"https://github.com/gnuhpc","email":"gnuhpc@gmail.com"}},"host":"localhost:8080","basePath":"/","tags":[{"name":"collector-controller","description":"Rest API for Collecting JMX Metric Data"},{"name":"user-controller","description":"Security User Management Controller."},{"name":"zookeeper-controller","description":"Zookeeper Controller"},{"name":"kafka-controller","description":"Kafka Controller"}],"paths":{"/jmx/v1":{"get":{"tags":["collector-controller"],"summary":"Fetch all JMX metric data","operationId":"collectJMXMetricUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"jmxurl","in":"query","description":"Parameter jmxurl should be a comma-separated list of {IP:Port} or set to 'default'","required":false,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/JMXMetricDataV1"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/jmx/v2":{"post":{"tags":["collector-controller"],"summary":"Fetch JMX metric data with query filter. You can get the query filter template through the API /jmx/v2/filters.","operationId":"collectJMXMetricUsingPOST","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"jmxurl","in":"query","description":"Parameter jmxurl should be a comma-separated list of {IP:Port} or set to 'default'","required":false,"type":"string"},{"in":"body","name":"jmxQuery","description":"jmxQuery","required":true,"schema":{"$ref":"#/definitions/JMXQuery"}}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/JMXMetricData"}}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/jmx/v2/filters":{"get":{"tags":["collector-controller"],"summary":"List the query filter templates with the filterKey. If filterKey is set to empty, it will return all the templates.","operationId":"listJMXFilterTemplateUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"filterKey","in":"query","description":"filterKey","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"object"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/brokers":{"get":{"tags":["kafka-controller"],"summary":"List brokers in this cluster","operationId":"listBrokersUsingGET","consumes":["application/json"],"produces":["*/*"],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/BrokerInfo"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/consumer/{topic}/{partition}/{offset}":{"get":{"tags":["kafka-controller"],"summary":"Get the message from the offset of the partition in the topic, decoder is not supported yet","operationId":"getMessageUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"topic","in":"path","description":"topic","required":true,"type":"string"},{"name":"partition","in":"path","description":"partition","required":true,"type":"integer","format":"int32"},{"name":"offset","in":"path","description":"offset","required":true,"type":"integer","format":"int64"},{"name":"decoder","in":"query","description":"decoder","required":false,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"string"}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/consumergroup/{consumergroup}":{"delete":{"tags":["kafka-controller"],"summary":"Delete old Consumer Group","operationId":"deleteOldConsumerGroupUsingDELETE","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"consumergroup","in":"path","description":"consumergroup","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/GeneralResponse"}},"204":{"description":"No Content"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"}}}},"/kafka/consumergroup/{consumergroup}/{type}/topic/{topic}/lastcommittime":{"get":{"tags":["kafka-controller"],"summary":"getLastCommitTimestamp","operationId":"getLastCommitTimestampUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"consumergroup","in":"path","description":"consumergroup","required":true,"type":"string"},{"name":"topic","in":"path","description":"topic","required":true,"type":"string"},{"name":"type","in":"path","description":"type","required":true,"type":"string","enum":["NEW","OLD"]}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"object","additionalProperties":{"type":"integer","format":"int64"}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/consumergroup/{consumergroup}/{type}/topic/{topic}/{partition}/{offset}":{"put":{"tags":["kafka-controller"],"summary":"Reset consumer group offset, earliest/latest can be used","operationId":"resetOffsetUsingPUT","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"topic","in":"path","description":"topic","required":true,"type":"string"},{"name":"partition","in":"path","description":"partition","required":true,"type":"integer","format":"int32"},{"name":"consumergroup","in":"path","description":"consumergroup","required":true,"type":"string"},{"name":"offset","in":"path","description":"offset","required":true,"type":"string"},{"name":"type","in":"path","description":"type","required":true,"type":"string","enum":["NEW","OLD"]}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/GeneralResponse"}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/consumergroups":{"get":{"tags":["kafka-controller"],"summary":"List all consumer groups from zk and kafka","operationId":"listAllConsumerGroupsUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"type","in":"query","description":"type","required":false,"type":"string","enum":["NEW","OLD"]},{"name":"topic","in":"query","description":"topic","required":false,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/consumergroups/{consumerGroup}/{type}":{"get":{"tags":["kafka-controller"],"summary":"Describe consumer groups, showing lag and offset, may be slow if multi topic are listened","operationId":"describeCGUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"consumerGroup","in":"path","description":"consumerGroup","required":true,"type":"string"},{"name":"type","in":"path","description":"type","required":true,"type":"string","enum":["NEW","OLD"]}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"array","items":{"$ref":"#/definitions/ConsumerGroupDesc"}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/consumergroups/{consumerGroup}/{type}/topic":{"get":{"tags":["kafka-controller"],"summary":"Get the topics involved of the specify consumer group","operationId":"listTopicByCGUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"consumerGroup","in":"path","description":"consumerGroup","required":true,"type":"string"},{"name":"type","in":"path","description":"type","required":true,"type":"string","enum":["NEW","OLD"]}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"type":"string"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/consumergroups/{consumerGroup}/{type}/topic/{topic}":{"get":{"tags":["kafka-controller"],"summary":"Describe consumer groups by topic, showing lag and offset","operationId":"describeCGByTopicUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"consumerGroup","in":"path","description":"consumerGroup","required":true,"type":"string"},{"name":"type","in":"path","description":"type","required":true,"type":"string","enum":["NEW","OLD"]},{"name":"topic","in":"path","description":"topic","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/ConsumerGroupDesc"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/health":{"get":{"tags":["kafka-controller"],"summary":"Check the cluster health.","operationId":"healthCheckUsingGET","consumes":["application/json"],"produces":["*/*"],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/HealthCheckResult"}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/partitions/add":{"post":{"tags":["kafka-controller"],"summary":"Add a partition to the topic","operationId":"addPartitionUsingPOST","consumes":["application/json"],"produces":["*/*"],"parameters":[{"in":"body","name":"addPartition","description":"addPartition","required":true,"schema":{"$ref":"#/definitions/AddPartition"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/TopicMeta"}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/partitions/reassign/check":{"put":{"tags":["kafka-controller"],"summary":"Check the partition reassignment process","operationId":"checkReassignPartitionsUsingPUT","consumes":["application/json"],"produces":["*/*"],"parameters":[{"in":"body","name":"reassignStr","description":"reassignStr","required":true,"schema":{"type":"string"}}],"responses":{"-1":{"description":"Reassignment Failed"},"0":{"description":"Reassignment In Progress"},"1":{"description":"Reassignment Completed"},"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"integer","format":"int32"}}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/partitions/reassign/execute":{"put":{"tags":["kafka-controller"],"summary":"Execute the partition reassignment","operationId":"executeReassignPartitionsUsingPUT","consumes":["application/json"],"produces":["*/*"],"parameters":[{"in":"body","name":"reassignStr","description":"reassignStr","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"integer","format":"int32"}}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/partitions/reassign/generate":{"post":{"tags":["kafka-controller"],"summary":"Generate plan for the partition reassignment","operationId":"generateReassignPartitionsUsingPOST","consumes":["application/json"],"produces":["*/*"],"parameters":[{"in":"body","name":"reassignWrapper","description":"reassignWrapper","required":true,"schema":{"$ref":"#/definitions/ReassignWrapper"}}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"type":"string"}}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/topics":{"get":{"tags":["kafka-controller"],"summary":"List topics","operationId":"listTopicsUsingGET","consumes":["application/json"],"produces":["*/*"],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"type":"string"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/topics/create":{"post":{"tags":["kafka-controller"],"summary":"Create a topic","operationId":"createTopicUsingPOST","consumes":["application/json"],"produces":["*/*"],"parameters":[{"in":"body","name":"topic","description":"topic","required":true,"schema":{"$ref":"#/definitions/TopicDetail"}},{"name":"reassignStr","in":"query","description":"reassignStr","required":false,"type":"string"}],"responses":{"201":{"description":"Created","schema":{"$ref":"#/definitions/TopicMeta"}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/topics/{topic}":{"get":{"tags":["kafka-controller"],"summary":"Describe a topic by fetching the metadata and config","operationId":"describeTopicUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"topic","in":"path","description":"topic","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/TopicMeta"}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}},"delete":{"tags":["kafka-controller"],"summary":"Delete a topic (you should enable topic deletion","operationId":"deleteTopicUsingDELETE","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"topic","in":"path","description":"topic","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/GeneralResponse"}},"204":{"description":"No Content"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"}}}},"/kafka/topics/{topic}/conf":{"get":{"tags":["kafka-controller"],"summary":"Get topic configs","operationId":"getTopicConfigUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"topic","in":"path","description":"topic","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"object"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}},"post":{"tags":["kafka-controller"],"summary":"Create topic configs","operationId":"createTopicConfigUsingPOST","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"topic","in":"path","description":"topic","required":true,"type":"string"},{"in":"body","name":"prop","description":"prop","required":true,"schema":{"type":"object","additionalProperties":{"type":"object"}}}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"object"}}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}},"put":{"tags":["kafka-controller"],"summary":"Update topic configs","operationId":"updateTopicConfigUsingPUT","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"topic","in":"path","description":"topic","required":true,"type":"string"},{"in":"body","name":"prop","description":"prop","required":true,"schema":{"type":"object","additionalProperties":{"type":"object"}}}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"object"}}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}},"delete":{"tags":["kafka-controller"],"summary":"Delete topic configs","operationId":"deleteTopicConfigUsingDELETE","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"topic","in":"path","description":"topic","required":true,"type":"string"},{"in":"body","name":"delProps","description":"delProps","required":true,"schema":{"type":"array","items":{"type":"string"}}}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"object"}}},"204":{"description":"No Content"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"}}}},"/kafka/topics/{topic}/conf/{key}":{"get":{"tags":["kafka-controller"],"summary":"Get topic config by key","operationId":"getTopicConfigByKeyUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"topic","in":"path","description":"topic","required":true,"type":"string"},{"name":"key","in":"path","description":"key","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"object"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}},"delete":{"tags":["kafka-controller"],"summary":"Delete a topic config by key","operationId":"deleteTopicConfigByKeyUsingDELETE","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"topic","in":"path","description":"topic","required":true,"type":"string"},{"name":"key","in":"path","description":"key","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"boolean"}},"204":{"description":"No Content"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"}}}},"/kafka/topics/{topic}/conf/{key}={value}":{"post":{"tags":["kafka-controller"],"summary":"Create a topic config by key","operationId":"createTopicConfigByKeyUsingPOST","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"topic","in":"path","description":"topic","required":true,"type":"string"},{"name":"key","in":"path","description":"key","required":true,"type":"string"},{"name":"value","in":"path","description":"value","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"object"}}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}},"put":{"tags":["kafka-controller"],"summary":"Update a topic config by key","operationId":"updateTopicConfigByKeyUsingPUT","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"topic","in":"path","description":"topic","required":true,"type":"string"},{"name":"key","in":"path","description":"key","required":true,"type":"string"},{"name":"value","in":"path","description":"value","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"object"}}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/topics/{topic}/exist":{"get":{"tags":["kafka-controller"],"summary":"Tell if a topic exists","operationId":"existTopicUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"topic","in":"path","description":"topic","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"boolean"}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/topics/{topic}/write":{"post":{"tags":["kafka-controller"],"summary":"Write a message to the topic, for testing purpose","operationId":"writeMessageUsingPOST","consumes":["text/plain"],"produces":["*/*"],"parameters":[{"name":"topic","in":"path","description":"topic","required":true,"type":"string"},{"in":"body","name":"message","description":"message","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"Created","schema":{"$ref":"#/definitions/GeneralResponse"}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/topicsbrief":{"get":{"tags":["kafka-controller"],"summary":"List topics Brief","operationId":"listTopicBriefUsingGET","consumes":["application/json"],"produces":["*/*"],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/TopicBrief"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/users":{"get":{"tags":["user-controller"],"summary":"Get user list.","operationId":"listUserUsingGET","consumes":["application/json"],"produces":["*/*"],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"type":"string"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}},"post":{"tags":["user-controller"],"summary":"Add user.","operationId":"addUserUsingPOST","consumes":["application/json"],"produces":["*/*"],"parameters":[{"in":"body","name":"user","description":"user","required":true,"schema":{"$ref":"#/definitions/User"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/GeneralResponse"}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}},"put":{"tags":["user-controller"],"summary":"Modify user information.","operationId":"modifyUserUsingPUT","consumes":["application/json"],"produces":["*/*"],"parameters":[{"in":"body","name":"user","description":"user","required":true,"schema":{"$ref":"#/definitions/User"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/GeneralResponse"}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/users/{username}":{"delete":{"tags":["user-controller"],"summary":"Delete user.","operationId":"delUserUsingDELETE","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"username","in":"path","description":"username","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/GeneralResponse"}},"204":{"description":"No Content"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"}}}},"/zk/connstate":{"get":{"tags":["zookeeper-controller"],"summary":"Get the connection state of zookeeper","operationId":"zkConnStateUsingGET","consumes":["application/json"],"produces":["*/*"],"responses":{"200":{"description":"OK","schema":{"type":"string"}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/zk/env":{"get":{"tags":["zookeeper-controller"],"summary":"Get the environment information of zookeeper","operationId":"getEnvUsingGET","consumes":["application/json"],"produces":["*/*"],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"$ref":"#/definitions/ZkServerEnvironment"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/zk/ls/{path}":{"get":{"tags":["zookeeper-controller"],"summary":"List a zookeeper path","operationId":"lsUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"path","in":"path","description":"path","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"type":"string"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/zk/stat":{"get":{"tags":["zookeeper-controller"],"summary":"Get the service state of zookeeper","operationId":"getStatUsingGET","consumes":["application/json"],"produces":["*/*"],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"$ref":"#/definitions/ZkServerStat"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}}},"definitions":{"AddPartition":{"type":"object","properties":{"numPartitionsAdded":{"type":"integer","format":"int32"},"replicaAssignment":{"type":"string"},"topic":{"type":"string"}}},"BrokerInfo":{"type":"object","properties":{"endPoints":{"type":"array","items":{"type":"string"}},"host":{"type":"string"},"id":{"type":"integer","format":"int32"},"jmxPort":{"type":"integer","format":"int32"},"port":{"type":"integer","format":"int32"},"rack":{"type":"string"},"securityProtocol":{"type":"object"},"startTime":{"type":"string","format":"date-time"},"version":{"type":"integer","format":"int32"}}},"ConsumerGroupDesc":{"type":"object","properties":{"consumerId":{"type":"string"},"currentOffset":{"type":"integer","format":"int64"},"groupName":{"type":"string"},"host":{"type":"string"},"lag":{"type":"integer","format":"int64"},"logEndOffset":{"type":"integer","format":"int64"},"partitionId":{"type":"integer","format":"int32"},"state":{"type":"string","enum":["RUNNING","PENDING"]},"topic":{"type":"string"},"type":{"type":"string","enum":["NEW","OLD"]}}},"GeneralResponse":{"type":"object","properties":{"msg":{"type":"string"},"state":{"type":"string","enum":["success","failure"]}}},"HashMap«string,object»":{"type":"object","additionalProperties":{"type":"object"}},"HealthCheckResult":{"type":"object","properties":{"msg":{"type":"string"},"status":{"type":"string"},"timestamp":{"type":"string","example":"yyyy-MM-dd HH:mm:ss"}}},"HostAndPort":{"type":"object","properties":{"hostText":{"type":"string"},"port":{"type":"integer","format":"int32"}}},"JMXConfiguration":{"type":"object","properties":{"include":{"$ref":"#/definitions/JMXFilter"},"exclude":{"$ref":"#/definitions/JMXFilter"}}},"JMXFilter":{"type":"object","properties":{"attribute":{"type":"object"},"beanNames":{"type":"array","items":{"type":"string"}},"beanRegexes":{"type":"array","items":{"$ref":"#/definitions/Pattern"}},"domain":{"type":"string"},"domainRegex":{"$ref":"#/definitions/Pattern"},"emptyBeanName":{"type":"boolean"},"filter":{"type":"object","additionalProperties":{"type":"object"}}}},"JMXMetricData":{"type":"object","properties":{"collected":{"type":"boolean"},"host":{"type":"string"},"metrics":{"type":"array","items":{"$ref":"#/definitions/HashMap«string,object»"}},"msg":{"type":"string"},"timestamp":{"type":"string","example":"yyyy-MM-dd HH:mm:ss"}}},"JMXMetricDataV1":{"type":"object","properties":{"collected":{"type":"boolean"},"host":{"type":"string"},"mbeanInfo":{"type":"object"},"msg":{"type":"string"},"timestamp":{"type":"string","example":"yyyy-MM-dd HH:mm:ss"}}},"JMXQuery":{"type":"object","properties":{"filters":{"type":"array","items":{"$ref":"#/definitions/JMXConfiguration"}}}},"Map«int,long»":{"type":"object","additionalProperties":{"type":"integer","format":"int64"}},"Pattern":{"type":"object","properties":{"cursor":{"type":"integer","format":"int32"}}},"ReassignWrapper":{"type":"object","properties":{"brokers":{"type":"array","items":{"type":"integer","format":"int32"}},"topics":{"type":"array","items":{"type":"string"}}}},"TopicAndPartition":{"type":"object"},"TopicBrief":{"type":"object","properties":{"isrRate":{"type":"number","format":"double"},"numPartition":{"type":"integer","format":"int32"},"topic":{"type":"string"}}},"TopicDetail":{"type":"object","properties":{"factor":{"type":"integer","format":"int32"},"name":{"type":"string"},"partitions":{"type":"integer","format":"int32"},"prop":{"type":"object","additionalProperties":{"type":"object"}}}},"TopicMeta":{"type":"object","properties":{"partitionCount":{"type":"integer","format":"int32"},"replicationFactor":{"type":"integer","format":"int32"},"topicCustomConfigs":{"type":"object","additionalProperties":{"type":"object"}},"topicName":{"type":"string"},"topicPartitionInfos":{"type":"array","items":{"$ref":"#/definitions/TopicPartitionInfo"}}}},"TopicPartitionInfo":{"type":"object","properties":{"endOffset":{"type":"integer","format":"int64"},"in_sync":{"type":"boolean"},"isr":{"type":"array","items":{"type":"string"}},"leader":{"type":"string"},"messageAvailable":{"type":"integer","format":"int64"},"partitionId":{"type":"integer","format":"int32"},"replicas":{"type":"array","items":{"type":"string"}},"startOffset":{"type":"integer","format":"int64"}}},"User":{"type":"object","properties":{"username":{"type":"string"},"password":{"type":"string"},"role":{"type":"string"}}},"ZkServerClient":{"type":"object","properties":{"host":{"type":"string"},"ops":{"type":"integer","format":"int32"},"port":{"type":"integer","format":"int32"},"queued":{"type":"integer","format":"int32"},"received":{"type":"integer","format":"int32"},"sent":{"type":"integer","format":"int32"}}},"ZkServerEnvironment":{"type":"object","properties":{"attributes":{"type":"object","additionalProperties":{"type":"string"}}}},"ZkServerStat":{"type":"object","properties":{"avgLatency":{"type":"integer","format":"int32"},"buildDate":{"type":"string"},"clients":{"type":"array","items":{"$ref":"#/definitions/ZkServerClient"}},"connections":{"type":"integer","format":"int32"},"maxLatency":{"type":"integer","format":"int32"},"minLatency":{"type":"integer","format":"int32"},"mode":{"type":"string","enum":["Leader","Follower","Observer"]},"nodes":{"type":"integer","format":"int32"},"outstanding":{"type":"integer","format":"int32"},"received":{"type":"integer","format":"int32"},"sent":{"type":"integer","format":"int32"},"version":{"type":"string"},"zxId":{"type":"string"}}}}} \ No newline at end of file +{ + "swagger": "2.0", + "info": { + "description": "Kafka REST API SwaggerUI", + "version": "0.1.0", + "title": "Kafka REST API SwaggerUI", + "contact": { + "name": "gnuhpc", + "url": "https://github.com/gnuhpc", + "email": "gnuhpc@gmail.com" + } + }, + "host": "localhost:8080", + "basePath": "/", + "tags": [ + { + "name": "collector-controller", + "description": "Rest API for Collecting JMX Metric Data" + }, + { + "name": "user-controller", + "description": "Security User Management Controller." + }, + { + "name": "zookeeper-controller", + "description": "Zookeeper Controller" + }, + { + "name": "kafka-controller", + "description": "Kafka Controller" + } + ], + "paths": { + "/jmx/v1": { + "get": { + "tags": [ + "collector-controller" + ], + "summary": "Fetch all JMX metric data", + "operationId": "collectJMXMetricUsingGET", + "consumes": [ + "application/json" + ], + "produces": [ + "*/*" + ], + "parameters": [ + { + "name": "jmxurl", + "in": "query", + "description": "Parameter jmxurl should be a comma-separated list of {IP:Port} or set to 'default'", + "required": false, + "type": "string" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/JMXMetricDataV1" + } + } + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "404": { + "description": "Not Found" + } + } + } + }, + "/jmx/v2": { + "post": { + "tags": [ + "collector-controller" + ], + "summary": "Fetch JMX metric data with query filter. You can get the query filter template through the API /jmx/v2/filters.", + "operationId": "collectJMXMetricUsingPOST", + "consumes": [ + "application/json" + ], + "produces": [ + "*/*" + ], + "parameters": [ + { + "name": "jmxurl", + "in": "query", + "description": "Parameter jmxurl should be a comma-separated list of {IP:Port} or set to 'default'", + "required": false, + "type": "string" + }, + { + "in": "body", + "name": "jmxQuery", + "description": "jmxQuery", + "required": true, + "schema": { + "$ref": "#/definitions/JMXQuery" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/JMXMetricData" + } + } + }, + "201": { + "description": "Created" + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "404": { + "description": "Not Found" + } + } + } + }, + "/jmx/v2/filters": { + "get": { + "tags": [ + "collector-controller" + ], + "summary": "List the query filter templates with the filterKey. If filterKey is set to empty, it will return all the templates.", + "operationId": "listJMXFilterTemplateUsingGET", + "consumes": [ + "application/json" + ], + "produces": [ + "*/*" + ], + "parameters": [ + { + "name": "filterKey", + "in": "query", + "description": "filterKey", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "object" + } + } + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "404": { + "description": "Not Found" + } + } + } + }, + "/kafka/brokers": { + "get": { + "tags": [ + "kafka-controller" + ], + "summary": "List brokers in this cluster", + "operationId": "listBrokersUsingGET", + "consumes": [ + "application/json" + ], + "produces": [ + "*/*" + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/BrokerInfo" + } + } + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "404": { + "description": "Not Found" + } + } + } + }, + "/kafka/consumer/{topic}/{partition}/{offset}": { + "get": { + "tags": [ + "kafka-controller" + ], + "summary": "Get the message from the offset of the partition in the topic, decoder is not supported yet", + "operationId": "getMessageUsingGET", + "consumes": [ + "application/json" + ], + "produces": [ + "*/*" + ], + "parameters": [ + { + "name": "topic", + "in": "path", + "description": "topic", + "required": true, + "type": "string" + }, + { + "name": "partition", + "in": "path", + "description": "partition", + "required": true, + "type": "integer", + "format": "int32" + }, + { + "name": "offset", + "in": "path", + "description": "offset", + "required": true, + "type": "integer", + "format": "int64" + }, + { + "name": "decoder", + "in": "query", + "description": "decoder", + "required": false, + "type": "string" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "404": { + "description": "Not Found" + } + } + } + }, + "/kafka/consumergroup/{consumergroup}": { + "delete": { + "tags": [ + "kafka-controller" + ], + "summary": "Delete old Consumer Group", + "operationId": "deleteOldConsumerGroupUsingDELETE", + "consumes": [ + "application/json" + ], + "produces": [ + "*/*" + ], + "parameters": [ + { + "name": "consumergroup", + "in": "path", + "description": "consumergroup", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/GeneralResponse" + } + }, + "204": { + "description": "No Content" + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + } + } + } + }, + "/kafka/consumergroup/{consumergroup}/{type}/topic/{topic}/lastcommittime": { + "get": { + "tags": [ + "kafka-controller" + ], + "summary": "getLastCommitTimestamp", + "operationId": "getLastCommitTimestampUsingGET", + "consumes": [ + "application/json" + ], + "produces": [ + "*/*" + ], + "parameters": [ + { + "name": "consumergroup", + "in": "path", + "description": "consumergroup", + "required": true, + "type": "string" + }, + { + "name": "topic", + "in": "path", + "description": "topic", + "required": true, + "type": "string" + }, + { + "name": "type", + "in": "path", + "description": "type", + "required": true, + "type": "string", + "enum": [ + "NEW", + "OLD" + ] + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "object", + "additionalProperties": { + "type": "integer", + "format": "int64" + } + } + } + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "404": { + "description": "Not Found" + } + } + } + }, + "/kafka/consumergroup/{consumergroup}/{type}/topic/{topic}/{partition}/{offset}": { + "put": { + "tags": [ + "kafka-controller" + ], + "summary": "Reset consumer group offset, earliest/latest can be used", + "operationId": "resetOffsetUsingPUT", + "consumes": [ + "application/json" + ], + "produces": [ + "*/*" + ], + "parameters": [ + { + "name": "topic", + "in": "path", + "description": "topic", + "required": true, + "type": "string" + }, + { + "name": "partition", + "in": "path", + "description": "partition", + "required": true, + "type": "integer", + "format": "int32" + }, + { + "name": "consumergroup", + "in": "path", + "description": "consumergroup", + "required": true, + "type": "string" + }, + { + "name": "offset", + "in": "path", + "description": "offset", + "required": true, + "type": "string" + }, + { + "name": "type", + "in": "path", + "description": "type", + "required": true, + "type": "string", + "enum": [ + "NEW", + "OLD" + ] + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/GeneralResponse" + } + }, + "201": { + "description": "Created" + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "404": { + "description": "Not Found" + } + } + } + }, + "/kafka/consumergroups": { + "get": { + "tags": [ + "kafka-controller" + ], + "summary": "List all consumer groups from zk and kafka", + "operationId": "listAllConsumerGroupsUsingGET", + "consumes": [ + "application/json" + ], + "produces": [ + "*/*" + ], + "parameters": [ + { + "name": "type", + "in": "query", + "description": "type", + "required": false, + "type": "string", + "enum": [ + "NEW", + "OLD" + ] + }, + { + "name": "topic", + "in": "query", + "description": "topic", + "required": false, + "type": "string" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "404": { + "description": "Not Found" + } + } + } + }, + "/kafka/consumergroups/{consumerGroup}/{type}": { + "get": { + "tags": [ + "kafka-controller" + ], + "summary": "Describe consumer groups, showing lag and offset, may be slow if multi topic are listened", + "operationId": "describeCGUsingGET", + "consumes": [ + "application/json" + ], + "produces": [ + "*/*" + ], + "parameters": [ + { + "name": "consumerGroup", + "in": "path", + "description": "consumerGroup", + "required": true, + "type": "string" + }, + { + "name": "type", + "in": "path", + "description": "type", + "required": true, + "type": "string", + "enum": [ + "NEW", + "OLD" + ] + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "$ref": "#/definitions/ConsumerGroupDesc" + } + } + } + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "404": { + "description": "Not Found" + } + } + } + }, + "/kafka/consumergroups/{consumerGroup}/{type}/topic": { + "get": { + "tags": [ + "kafka-controller" + ], + "summary": "Get the topics involved of the specify consumer group", + "operationId": "listTopicByCGUsingGET", + "consumes": [ + "application/json" + ], + "produces": [ + "*/*" + ], + "parameters": [ + { + "name": "consumerGroup", + "in": "path", + "description": "consumerGroup", + "required": true, + "type": "string" + }, + { + "name": "type", + "in": "path", + "description": "type", + "required": true, + "type": "string", + "enum": [ + "NEW", + "OLD" + ] + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "404": { + "description": "Not Found" + } + } + } + }, + "/kafka/consumergroups/{consumerGroup}/{type}/topic/{topic}": { + "get": { + "tags": [ + "kafka-controller" + ], + "summary": "Describe consumer groups by topic, showing lag and offset", + "operationId": "describeCGByTopicUsingGET", + "consumes": [ + "application/json" + ], + "produces": [ + "*/*" + ], + "parameters": [ + { + "name": "consumerGroup", + "in": "path", + "description": "consumerGroup", + "required": true, + "type": "string" + }, + { + "name": "type", + "in": "path", + "description": "type", + "required": true, + "type": "string", + "enum": [ + "NEW", + "OLD" + ] + }, + { + "name": "topic", + "in": "path", + "description": "topic", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/ConsumerGroupDesc" + } + } + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "404": { + "description": "Not Found" + } + } + } + }, + "/kafka/health": { + "get": { + "tags": [ + "kafka-controller" + ], + "summary": "Check the cluster health.", + "operationId": "healthCheckUsingGET", + "consumes": [ + "application/json" + ], + "produces": [ + "*/*" + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/HealthCheckResult" + } + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "404": { + "description": "Not Found" + } + } + } + }, + "/kafka/partitions/add": { + "post": { + "tags": [ + "kafka-controller" + ], + "summary": "Add a partition to the topic", + "operationId": "addPartitionUsingPOST", + "consumes": [ + "application/json" + ], + "produces": [ + "*/*" + ], + "parameters": [ + { + "in": "body", + "name": "addPartition", + "description": "addPartition", + "required": true, + "schema": { + "$ref": "#/definitions/AddPartition" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/TopicMeta" + } + }, + "201": { + "description": "Created" + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "404": { + "description": "Not Found" + } + } + } + }, + "/kafka/partitions/reassign/check": { + "put": { + "tags": [ + "kafka-controller" + ], + "summary": "Check the partition reassignment process", + "operationId": "checkReassignPartitionsUsingPUT", + "consumes": [ + "application/json" + ], + "produces": [ + "*/*" + ], + "parameters": [ + { + "in": "body", + "name": "reassignStr", + "description": "reassignStr", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "-1": { + "description": "Reassignment Failed" + }, + "0": { + "description": "Reassignment In Progress" + }, + "1": { + "description": "Reassignment Completed" + }, + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "integer", + "format": "int32" + } + } + }, + "201": { + "description": "Created" + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "404": { + "description": "Not Found" + } + } + } + }, + "/kafka/partitions/reassign/execute": { + "put": { + "tags": [ + "kafka-controller" + ], + "summary": "Execute the partition reassignment", + "operationId": "executeReassignPartitionsUsingPUT", + "consumes": [ + "application/json" + ], + "produces": [ + "*/*" + ], + "parameters": [ + { + "in": "body", + "name": "reassignStr", + "description": "reassignStr", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "integer", + "format": "int32" + } + } + }, + "201": { + "description": "Created" + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "404": { + "description": "Not Found" + } + } + } + }, + "/kafka/partitions/reassign/generate": { + "post": { + "tags": [ + "kafka-controller" + ], + "summary": "Generate plan for the partition reassignment", + "operationId": "generateReassignPartitionsUsingPOST", + "consumes": [ + "application/json" + ], + "produces": [ + "*/*" + ], + "parameters": [ + { + "in": "body", + "name": "reassignWrapper", + "description": "reassignWrapper", + "required": true, + "schema": { + "$ref": "#/definitions/ReassignWrapper" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "201": { + "description": "Created" + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "404": { + "description": "Not Found" + } + } + } + }, + "/kafka/topics": { + "get": { + "tags": [ + "kafka-controller" + ], + "summary": "List topics", + "operationId": "listTopicsUsingGET", + "consumes": [ + "application/json" + ], + "produces": [ + "*/*" + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "404": { + "description": "Not Found" + } + } + } + }, + "/kafka/topics/create": { + "post": { + "tags": [ + "kafka-controller" + ], + "summary": "Create a topic", + "operationId": "createTopicUsingPOST", + "consumes": [ + "application/json" + ], + "produces": [ + "*/*" + ], + "parameters": [ + { + "in": "body", + "name": "topic", + "description": "topic", + "required": true, + "schema": { + "$ref": "#/definitions/TopicDetail" + } + }, + { + "name": "reassignStr", + "in": "query", + "description": "reassignStr", + "required": false, + "type": "string" + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/TopicMeta" + } + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "404": { + "description": "Not Found" + } + } + } + }, + "/kafka/topics/{topic}": { + "get": { + "tags": [ + "kafka-controller" + ], + "summary": "Describe a topic by fetching the metadata and config", + "operationId": "describeTopicUsingGET", + "consumes": [ + "application/json" + ], + "produces": [ + "*/*" + ], + "parameters": [ + { + "name": "topic", + "in": "path", + "description": "topic", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/TopicMeta" + } + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "404": { + "description": "Not Found" + } + } + }, + "delete": { + "tags": [ + "kafka-controller" + ], + "summary": "Delete a topic (you should enable topic deletion", + "operationId": "deleteTopicUsingDELETE", + "consumes": [ + "application/json" + ], + "produces": [ + "*/*" + ], + "parameters": [ + { + "name": "topic", + "in": "path", + "description": "topic", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/GeneralResponse" + } + }, + "204": { + "description": "No Content" + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + } + } + } + }, + "/kafka/topics/{topic}/conf": { + "get": { + "tags": [ + "kafka-controller" + ], + "summary": "Get topic configs", + "operationId": "getTopicConfigUsingGET", + "consumes": [ + "application/json" + ], + "produces": [ + "*/*" + ], + "parameters": [ + { + "name": "topic", + "in": "path", + "description": "topic", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "object" + } + } + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "404": { + "description": "Not Found" + } + } + }, + "post": { + "tags": [ + "kafka-controller" + ], + "summary": "Create topic configs", + "operationId": "createTopicConfigUsingPOST", + "consumes": [ + "application/json" + ], + "produces": [ + "*/*" + ], + "parameters": [ + { + "name": "topic", + "in": "path", + "description": "topic", + "required": true, + "type": "string" + }, + { + "in": "body", + "name": "prop", + "description": "prop", + "required": true, + "schema": { + "type": "object", + "additionalProperties": { + "type": "object" + } + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "object" + } + } + }, + "201": { + "description": "Created" + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "404": { + "description": "Not Found" + } + } + }, + "put": { + "tags": [ + "kafka-controller" + ], + "summary": "Update topic configs", + "operationId": "updateTopicConfigUsingPUT", + "consumes": [ + "application/json" + ], + "produces": [ + "*/*" + ], + "parameters": [ + { + "name": "topic", + "in": "path", + "description": "topic", + "required": true, + "type": "string" + }, + { + "in": "body", + "name": "prop", + "description": "prop", + "required": true, + "schema": { + "type": "object", + "additionalProperties": { + "type": "object" + } + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "object" + } + } + }, + "201": { + "description": "Created" + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "404": { + "description": "Not Found" + } + } + }, + "delete": { + "tags": [ + "kafka-controller" + ], + "summary": "Delete topic configs", + "operationId": "deleteTopicConfigUsingDELETE", + "consumes": [ + "application/json" + ], + "produces": [ + "*/*" + ], + "parameters": [ + { + "name": "topic", + "in": "path", + "description": "topic", + "required": true, + "type": "string" + }, + { + "in": "body", + "name": "delProps", + "description": "delProps", + "required": true, + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "object" + } + } + }, + "204": { + "description": "No Content" + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + } + } + } + }, + "/kafka/topics/{topic}/conf/{key}": { + "get": { + "tags": [ + "kafka-controller" + ], + "summary": "Get topic config by key", + "operationId": "getTopicConfigByKeyUsingGET", + "consumes": [ + "application/json" + ], + "produces": [ + "*/*" + ], + "parameters": [ + { + "name": "topic", + "in": "path", + "description": "topic", + "required": true, + "type": "string" + }, + { + "name": "key", + "in": "path", + "description": "key", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "object" + } + } + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "404": { + "description": "Not Found" + } + } + }, + "delete": { + "tags": [ + "kafka-controller" + ], + "summary": "Delete a topic config by key", + "operationId": "deleteTopicConfigByKeyUsingDELETE", + "consumes": [ + "application/json" + ], + "produces": [ + "*/*" + ], + "parameters": [ + { + "name": "topic", + "in": "path", + "description": "topic", + "required": true, + "type": "string" + }, + { + "name": "key", + "in": "path", + "description": "key", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "boolean" + } + }, + "204": { + "description": "No Content" + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + } + } + } + }, + "/kafka/topics/{topic}/conf/{key}={value}": { + "post": { + "tags": [ + "kafka-controller" + ], + "summary": "Create a topic config by key", + "operationId": "createTopicConfigByKeyUsingPOST", + "consumes": [ + "application/json" + ], + "produces": [ + "*/*" + ], + "parameters": [ + { + "name": "topic", + "in": "path", + "description": "topic", + "required": true, + "type": "string" + }, + { + "name": "key", + "in": "path", + "description": "key", + "required": true, + "type": "string" + }, + { + "name": "value", + "in": "path", + "description": "value", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "object" + } + } + }, + "201": { + "description": "Created" + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "404": { + "description": "Not Found" + } + } + }, + "put": { + "tags": [ + "kafka-controller" + ], + "summary": "Update a topic config by key", + "operationId": "updateTopicConfigByKeyUsingPUT", + "consumes": [ + "application/json" + ], + "produces": [ + "*/*" + ], + "parameters": [ + { + "name": "topic", + "in": "path", + "description": "topic", + "required": true, + "type": "string" + }, + { + "name": "key", + "in": "path", + "description": "key", + "required": true, + "type": "string" + }, + { + "name": "value", + "in": "path", + "description": "value", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "object" + } + } + }, + "201": { + "description": "Created" + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "404": { + "description": "Not Found" + } + } + } + }, + "/kafka/topics/{topic}/exist": { + "get": { + "tags": [ + "kafka-controller" + ], + "summary": "Tell if a topic exists", + "operationId": "existTopicUsingGET", + "consumes": [ + "application/json" + ], + "produces": [ + "*/*" + ], + "parameters": [ + { + "name": "topic", + "in": "path", + "description": "topic", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "boolean" + } + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "404": { + "description": "Not Found" + } + } + } + }, + "/kafka/topics/{topic}/write": { + "post": { + "tags": [ + "kafka-controller" + ], + "summary": "Write a message to the topic, for testing purpose", + "operationId": "writeMessageUsingPOST", + "consumes": [ + "text/plain" + ], + "produces": [ + "*/*" + ], + "parameters": [ + { + "name": "topic", + "in": "path", + "description": "topic", + "required": true, + "type": "string" + }, + { + "in": "body", + "name": "message", + "description": "message", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/GeneralResponse" + } + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "404": { + "description": "Not Found" + } + } + } + }, + "/kafka/topicsbrief": { + "get": { + "tags": [ + "kafka-controller" + ], + "summary": "List topics Brief", + "operationId": "listTopicBriefUsingGET", + "consumes": [ + "application/json" + ], + "produces": [ + "*/*" + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/TopicBrief" + } + } + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "404": { + "description": "Not Found" + } + } + } + }, + "/users": { + "get": { + "tags": [ + "user-controller" + ], + "summary": "Get user list.", + "operationId": "listUserUsingGET", + "consumes": [ + "application/json" + ], + "produces": [ + "*/*" + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "404": { + "description": "Not Found" + } + } + }, + "post": { + "tags": [ + "user-controller" + ], + "summary": "Add user.", + "operationId": "addUserUsingPOST", + "consumes": [ + "application/json" + ], + "produces": [ + "*/*" + ], + "parameters": [ + { + "in": "body", + "name": "user", + "description": "user", + "required": true, + "schema": { + "$ref": "#/definitions/User" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/GeneralResponse" + } + }, + "201": { + "description": "Created" + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "404": { + "description": "Not Found" + } + } + }, + "put": { + "tags": [ + "user-controller" + ], + "summary": "Modify user information.", + "operationId": "modifyUserUsingPUT", + "consumes": [ + "application/json" + ], + "produces": [ + "*/*" + ], + "parameters": [ + { + "in": "body", + "name": "user", + "description": "user", + "required": true, + "schema": { + "$ref": "#/definitions/User" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/GeneralResponse" + } + }, + "201": { + "description": "Created" + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "404": { + "description": "Not Found" + } + } + } + }, + "/users/{username}": { + "delete": { + "tags": [ + "user-controller" + ], + "summary": "Delete user.", + "operationId": "delUserUsingDELETE", + "consumes": [ + "application/json" + ], + "produces": [ + "*/*" + ], + "parameters": [ + { + "name": "username", + "in": "path", + "description": "username", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/GeneralResponse" + } + }, + "204": { + "description": "No Content" + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + } + } + } + }, + "/zk/connstate": { + "get": { + "tags": [ + "zookeeper-controller" + ], + "summary": "Get the connection state of zookeeper", + "operationId": "zkConnStateUsingGET", + "consumes": [ + "application/json" + ], + "produces": [ + "*/*" + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "404": { + "description": "Not Found" + } + } + } + }, + "/zk/env": { + "get": { + "tags": [ + "zookeeper-controller" + ], + "summary": "Get the environment information of zookeeper", + "operationId": "getEnvUsingGET", + "consumes": [ + "application/json" + ], + "produces": [ + "*/*" + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/ZkServerEnvironment" + } + } + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "404": { + "description": "Not Found" + } + } + } + }, + "/zk/ls/{path}": { + "get": { + "tags": [ + "zookeeper-controller" + ], + "summary": "List a zookeeper path", + "operationId": "lsUsingGET", + "consumes": [ + "application/json" + ], + "produces": [ + "*/*" + ], + "parameters": [ + { + "name": "path", + "in": "path", + "description": "path", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "404": { + "description": "Not Found" + } + } + } + }, + "/zk/stat": { + "get": { + "tags": [ + "zookeeper-controller" + ], + "summary": "Get the service state of zookeeper", + "operationId": "getStatUsingGET", + "consumes": [ + "application/json" + ], + "produces": [ + "*/*" + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/ZkServerStat" + } + } + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "404": { + "description": "Not Found" + } + } + } + } + }, + "definitions": { + "AddPartition": { + "type": "object", + "properties": { + "numPartitionsAdded": { + "type": "integer", + "format": "int32" + }, + "replicaAssignment": { + "type": "string" + }, + "topic": { + "type": "string" + } + } + }, + "BrokerInfo": { + "type": "object", + "properties": { + "endPoints": { + "type": "array", + "items": { + "type": "string" + } + }, + "host": { + "type": "string" + }, + "id": { + "type": "integer", + "format": "int32" + }, + "jmxPort": { + "type": "integer", + "format": "int32" + }, + "port": { + "type": "integer", + "format": "int32" + }, + "rack": { + "type": "string" + }, + "securityProtocol": { + "type": "object" + }, + "startTime": { + "type": "string", + "format": "date-time" + }, + "version": { + "type": "integer", + "format": "int32" + } + } + }, + "ConsumerGroupDesc": { + "type": "object", + "properties": { + "consumerId": { + "type": "string" + }, + "currentOffset": { + "type": "integer", + "format": "int64" + }, + "groupName": { + "type": "string" + }, + "host": { + "type": "string" + }, + "lag": { + "type": "integer", + "format": "int64" + }, + "logEndOffset": { + "type": "integer", + "format": "int64" + }, + "partitionId": { + "type": "integer", + "format": "int32" + }, + "state": { + "type": "string", + "enum": [ + "RUNNING", + "PENDING" + ] + }, + "topic": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "NEW", + "OLD" + ] + } + } + }, + "GeneralResponse": { + "type": "object", + "properties": { + "msg": { + "type": "string" + }, + "state": { + "type": "string", + "enum": [ + "success", + "failure" + ] + } + } + }, + "HashMap«string,object»": { + "type": "object", + "additionalProperties": { + "type": "object" + } + }, + "HealthCheckResult": { + "type": "object", + "properties": { + "msg": { + "type": "string" + }, + "status": { + "type": "string" + }, + "timestamp": { + "type": "string", + "example": "yyyy-MM-dd HH:mm:ss" + } + } + }, + "HostAndPort": { + "type": "object", + "properties": { + "hostText": { + "type": "string" + }, + "port": { + "type": "integer", + "format": "int32" + } + } + }, + "JMXConfiguration": { + "type": "object", + "properties": { + "include": { + "$ref": "#/definitions/JMXFilter" + }, + "exclude": { + "$ref": "#/definitions/JMXFilter" + } + } + }, + "JMXFilter": { + "type": "object", + "properties": { + "attribute": { + "type": "object" + }, + "beanNames": { + "type": "array", + "items": { + "type": "string" + } + }, + "beanRegexes": { + "type": "array", + "items": { + "$ref": "#/definitions/Pattern" + } + }, + "domain": { + "type": "string" + }, + "domainRegex": { + "$ref": "#/definitions/Pattern" + }, + "emptyBeanName": { + "type": "boolean" + }, + "filter": { + "type": "object", + "additionalProperties": { + "type": "object" + } + } + } + }, + "JMXMetricData": { + "type": "object", + "properties": { + "collected": { + "type": "boolean" + }, + "host": { + "type": "string" + }, + "metrics": { + "type": "array", + "items": { + "$ref": "#/definitions/HashMap«string,object»" + } + }, + "msg": { + "type": "string" + }, + "timestamp": { + "type": "string", + "example": "yyyy-MM-dd HH:mm:ss" + } + } + }, + "JMXMetricDataV1": { + "type": "object", + "properties": { + "collected": { + "type": "boolean" + }, + "host": { + "type": "string" + }, + "mbeanInfo": { + "type": "object" + }, + "msg": { + "type": "string" + }, + "timestamp": { + "type": "string", + "example": "yyyy-MM-dd HH:mm:ss" + } + } + }, + "JMXQuery": { + "type": "object", + "properties": { + "filters": { + "type": "array", + "items": { + "$ref": "#/definitions/JMXConfiguration" + } + } + } + }, + "Map«int,long»": { + "type": "object", + "additionalProperties": { + "type": "integer", + "format": "int64" + } + }, + "Pattern": { + "type": "object", + "properties": { + "cursor": { + "type": "integer", + "format": "int32" + } + } + }, + "ReassignWrapper": { + "type": "object", + "properties": { + "brokers": { + "type": "array", + "items": { + "type": "integer", + "format": "int32" + } + }, + "topics": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "TopicAndPartition": { + "type": "object" + }, + "TopicBrief": { + "type": "object", + "properties": { + "isrRate": { + "type": "number", + "format": "double" + }, + "numPartition": { + "type": "integer", + "format": "int32" + }, + "topic": { + "type": "string" + } + } + }, + "TopicDetail": { + "type": "object", + "properties": { + "factor": { + "type": "integer", + "format": "int32" + }, + "name": { + "type": "string" + }, + "partitions": { + "type": "integer", + "format": "int32" + }, + "prop": { + "type": "object", + "additionalProperties": { + "type": "object" + } + } + } + }, + "TopicMeta": { + "type": "object", + "properties": { + "partitionCount": { + "type": "integer", + "format": "int32" + }, + "replicationFactor": { + "type": "integer", + "format": "int32" + }, + "topicCustomConfigs": { + "type": "object", + "additionalProperties": { + "type": "object" + } + }, + "topicName": { + "type": "string" + }, + "topicPartitionInfos": { + "type": "array", + "items": { + "$ref": "#/definitions/TopicPartitionInfo" + } + } + } + }, + "TopicPartitionInfo": { + "type": "object", + "properties": { + "endOffset": { + "type": "integer", + "format": "int64" + }, + "in_sync": { + "type": "boolean" + }, + "isr": { + "type": "array", + "items": { + "type": "string" + } + }, + "leader": { + "type": "string" + }, + "messageAvailable": { + "type": "integer", + "format": "int64" + }, + "partitionId": { + "type": "integer", + "format": "int32" + }, + "replicas": { + "type": "array", + "items": { + "type": "string" + } + }, + "startOffset": { + "type": "integer", + "format": "int64" + } + } + }, + "User": { + "type": "object", + "properties": { + "username": { + "type": "string" + }, + "password": { + "type": "string" + }, + "role": { + "type": "string" + } + } + }, + "ZkServerClient": { + "type": "object", + "properties": { + "host": { + "type": "string" + }, + "ops": { + "type": "integer", + "format": "int32" + }, + "port": { + "type": "integer", + "format": "int32" + }, + "queued": { + "type": "integer", + "format": "int32" + }, + "received": { + "type": "integer", + "format": "int32" + }, + "sent": { + "type": "integer", + "format": "int32" + } + } + }, + "ZkServerEnvironment": { + "type": "object", + "properties": { + "attributes": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + }, + "ZkServerStat": { + "type": "object", + "properties": { + "avgLatency": { + "type": "integer", + "format": "int32" + }, + "buildDate": { + "type": "string" + }, + "clients": { + "type": "array", + "items": { + "$ref": "#/definitions/ZkServerClient" + } + }, + "connections": { + "type": "integer", + "format": "int32" + }, + "maxLatency": { + "type": "integer", + "format": "int32" + }, + "minLatency": { + "type": "integer", + "format": "int32" + }, + "mode": { + "type": "string", + "enum": [ + "Leader", + "Follower", + "Observer" + ] + }, + "nodes": { + "type": "integer", + "format": "int32" + }, + "outstanding": { + "type": "integer", + "format": "int32" + }, + "received": { + "type": "integer", + "format": "int32" + }, + "sent": { + "type": "integer", + "format": "int32" + }, + "version": { + "type": "string" + }, + "zxId": { + "type": "string" + } + } + } + } +} \ No newline at end of file diff --git a/src/main/java/org/gnuhpc/bigdata/KafkaRestSpringbootApplication.java b/src/main/java/org/gnuhpc/bigdata/KafkaRestSpringbootApplication.java index b3e4e77..f0d10b5 100644 --- a/src/main/java/org/gnuhpc/bigdata/KafkaRestSpringbootApplication.java +++ b/src/main/java/org/gnuhpc/bigdata/KafkaRestSpringbootApplication.java @@ -9,8 +9,9 @@ @EnableScheduling @Log4j public class KafkaRestSpringbootApplication { - public static void main(String[] args) { - log.info("+++++++++Kafka-zk Rest Application starting++++++++++"); - SpringApplication.run(KafkaRestSpringbootApplication.class, args); - } + + public static void main(String[] args) { + log.info("+++++++++Kafka-zk Rest Application starting++++++++++"); + SpringApplication.run(KafkaRestSpringbootApplication.class, args); + } } diff --git a/src/main/java/org/gnuhpc/bigdata/componet/OffsetStorage.java b/src/main/java/org/gnuhpc/bigdata/componet/OffsetStorage.java index 41927da..b44b1a5 100644 --- a/src/main/java/org/gnuhpc/bigdata/componet/OffsetStorage.java +++ b/src/main/java/org/gnuhpc/bigdata/componet/OffsetStorage.java @@ -1,44 +1,43 @@ package org.gnuhpc.bigdata.componet; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import kafka.common.OffsetAndMetadata; import kafka.coordinator.group.GroupTopicPartition; import lombok.Data; import org.springframework.stereotype.Component; -import java.util.Collections; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - @Component @Data public class OffsetStorage { - private static Map> consumerOffsets = new ConcurrentHashMap<>(); - public Map> getMap() { - return consumerOffsets; - } + private static Map> consumerOffsets = + new ConcurrentHashMap<>(); - public void put(String consumerGroup, - Map offsetMap){ - if (offsetMap != null) { - consumerOffsets.put(consumerGroup, offsetMap); - } - } + public Map> getMap() { + return consumerOffsets; + } - public void clear(){ - consumerOffsets.clear(); + public void put(String consumerGroup, Map offsetMap) { + if (offsetMap != null) { + consumerOffsets.put(consumerGroup, offsetMap); } + } - public Map get(String consumerGroup){ - return consumerOffsets.get(consumerGroup); - } + public void clear() { + consumerOffsets.clear(); + } - public void remove(String consumerGroup){ - consumerOffsets.remove(consumerGroup); - } + public Map get(String consumerGroup) { + return consumerOffsets.get(consumerGroup); + } - @Override - public String toString() { - return consumerOffsets.toString(); - } + public void remove(String consumerGroup) { + consumerOffsets.remove(consumerGroup); + } + + @Override + public String toString() { + return consumerOffsets.toString(); + } } diff --git a/src/main/java/org/gnuhpc/bigdata/componet/RestLogAspect.java b/src/main/java/org/gnuhpc/bigdata/componet/RestLogAspect.java index da5ad72..746e367 100644 --- a/src/main/java/org/gnuhpc/bigdata/componet/RestLogAspect.java +++ b/src/main/java/org/gnuhpc/bigdata/componet/RestLogAspect.java @@ -1,6 +1,7 @@ package org.gnuhpc.bigdata.componet; - +import java.util.Arrays; +import javax.servlet.http.HttpServletRequest; import lombok.extern.log4j.Log4j; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.AfterReturning; @@ -11,37 +12,41 @@ import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; -import javax.servlet.http.HttpServletRequest; -import java.util.Arrays; - @Aspect @Component @Log4j public class RestLogAspect { - ThreadLocal startTime = new ThreadLocal<>(); - @Pointcut("execution(public * org.gnuhpc.bigdata.controller..*.*(..))") - public void restServiceLog(){} - @Before("restServiceLog()") - public void doBefore(JoinPoint joinPoint) throws Throwable { - startTime.set(System.currentTimeMillis()); - // 接收到请求,记录请求内容 - ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); - HttpServletRequest request = attributes.getRequest(); - // 记录下请求内容 - log.info("===================== Controller Request ===================="); - log.info("URL : " + request.getRequestURL().toString()); - log.info("HTTP_METHOD : " + request.getMethod()); - log.info("IP : " + request.getRemoteAddr()); - log.info("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName()); - log.info("ARGS : " + Arrays.toString(joinPoint.getArgs())); - } + ThreadLocal startTime = new ThreadLocal<>(); + + @Pointcut("execution(public * org.gnuhpc.bigdata.controller..*.*(..))") + public void restServiceLog() {} + + @Before("restServiceLog()") + public void doBefore(JoinPoint joinPoint) throws Throwable { + startTime.set(System.currentTimeMillis()); + // 接收到请求,记录请求内容 + ServletRequestAttributes attributes = + (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + HttpServletRequest request = attributes.getRequest(); + // 记录下请求内容 + log.info("===================== Controller Request ===================="); + log.info("URL : " + request.getRequestURL().toString()); + log.info("HTTP_METHOD : " + request.getMethod()); + log.info("IP : " + request.getRemoteAddr()); + log.info( + "CLASS_METHOD : " + + joinPoint.getSignature().getDeclaringTypeName() + + "." + + joinPoint.getSignature().getName()); + log.info("ARGS : " + Arrays.toString(joinPoint.getArgs())); + } - @AfterReturning(returning = "ret", pointcut = "restServiceLog()") - public void doAfterReturning(Object ret) throws Throwable { - // 处理完请求,返回内容 - log.info("===================== Controller Response ================="); - log.info("SPEND TIME : " + (System.currentTimeMillis() - startTime.get())); - log.info("RESPONSE : " + ret); - } + @AfterReturning(returning = "ret", pointcut = "restServiceLog()") + public void doAfterReturning(Object ret) throws Throwable { + // 处理完请求,返回内容 + log.info("===================== Controller Response ================="); + log.info("SPEND TIME : " + (System.currentTimeMillis() - startTime.get())); + log.info("RESPONSE : " + ret); + } } diff --git a/src/main/java/org/gnuhpc/bigdata/config/JMXConfig.java b/src/main/java/org/gnuhpc/bigdata/config/JMXConfig.java index 27c0258..64bc381 100644 --- a/src/main/java/org/gnuhpc/bigdata/config/JMXConfig.java +++ b/src/main/java/org/gnuhpc/bigdata/config/JMXConfig.java @@ -1,11 +1,12 @@ package org.gnuhpc.bigdata.config; -import org.gnuhpc.bigdata.utils.CommonUtils; - import java.io.File; +import org.gnuhpc.bigdata.utils.CommonUtils; public class JMXConfig { + public static final String JMX_CONNECT_TIMEOUT = "attribute.remote.x.request.waiting.timeout"; public static final String JMX_PROTOCOL = "service:jmx:rmi:///jndi/rmi://"; - public static final String JMX_FILTER_DIR = CommonUtils.PROJECT_ROOT_FOLDER + File.separator + "JMXFilterTemplate"; + public static final String JMX_FILTER_DIR = + CommonUtils.PROJECT_ROOT_FOLDER + File.separator + "JMXFilterTemplate"; } diff --git a/src/main/java/org/gnuhpc/bigdata/config/KafkaConfig.java b/src/main/java/org/gnuhpc/bigdata/config/KafkaConfig.java index e4b1b26..09e8018 100644 --- a/src/main/java/org/gnuhpc/bigdata/config/KafkaConfig.java +++ b/src/main/java/org/gnuhpc/bigdata/config/KafkaConfig.java @@ -4,18 +4,17 @@ import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; +import java.nio.ByteBuffer; +import java.util.HashMap; +import java.util.Map; import lombok.Data; import lombok.Getter; import lombok.extern.log4j.Log4j; import org.apache.kafka.clients.consumer.ConsumerConfig; -import org.apache.kafka.clients.consumer.ConsumerRecord; import org.apache.kafka.common.serialization.ByteBufferDeserializer; -import org.apache.kafka.common.serialization.StringDeserializer; import org.gnuhpc.bigdata.componet.OffsetStorage; -import org.gnuhpc.bigdata.service.KafkaConsumerService; import org.gnuhpc.bigdata.utils.KafkaUtils; import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; @@ -26,87 +25,81 @@ import org.springframework.kafka.listener.AbstractMessageListenerContainer; import org.springframework.kafka.listener.ConcurrentMessageListenerContainer; -import java.nio.ByteBuffer; -import java.util.HashMap; -import java.util.Map; - -/** - * Created by gnuhpc on 2017/7/12. - */ - +/** Created by gnuhpc on 2017/7/12. */ @Log4j @Data @EnableKafka @Configuration @Getter public class KafkaConfig { - @Value("${kafka.brokers}") - private String brokers; - @Value("${kafka.offset.topic}") - private String internalTopic; + @Value("${kafka.brokers}") + private String brokers; + + @Value("${kafka.offset.topic}") + private String internalTopic; - @Value("${kafka.offset.partitions}") - private int internalTopicPartitions; + @Value("${kafka.offset.partitions}") + private int internalTopicPartitions; - @Value("${spring.kafka.consumer.group-id}") - private String groupId; + @Value("${spring.kafka.consumer.group-id}") + private String groupId; - @Value("${kafka.healthcheck.topic}") - private String healthCheckTopic; + @Value("${kafka.healthcheck.topic}") + private String healthCheckTopic; - @Bean(initMethod = "init", destroyMethod = "destroy") - public KafkaUtils kafkaUtils() { - return new KafkaUtils(); - } + @Bean(initMethod = "init", destroyMethod = "destroy") + public KafkaUtils kafkaUtils() { + return new KafkaUtils(); + } - @Bean - public OffsetStorage offsetStorage() { - return new OffsetStorage(); - } + @Bean + public OffsetStorage offsetStorage() { + return new OffsetStorage(); + } -// @Bean -// public KafkaConsumerService kafkaConsumerService() { -// return new KafkaConsumerService(internalTopicPartitions); -// } + // @Bean + // public KafkaConsumerService kafkaConsumerService() { + // return new KafkaConsumerService(internalTopicPartitions); + // } - @Bean - public KafkaListenerContainerFactory> - kafkaListenerContainerFactory() { - ConcurrentKafkaListenerContainerFactory factory = - new ConcurrentKafkaListenerContainerFactory<>(); - factory.setConsumerFactory(consumerFactory()); - factory.getContainerProperties().setAckMode(AbstractMessageListenerContainer.AckMode.MANUAL); - return factory; - } + @Bean + public KafkaListenerContainerFactory> + kafkaListenerContainerFactory() { + ConcurrentKafkaListenerContainerFactory factory = + new ConcurrentKafkaListenerContainerFactory<>(); + factory.setConsumerFactory(consumerFactory()); + factory.getContainerProperties().setAckMode(AbstractMessageListenerContainer.AckMode.MANUAL); + return factory; + } - @Bean - public DefaultKafkaConsumerFactory consumerFactory() { - return new DefaultKafkaConsumerFactory<>(consumerConfigs()); - } + @Bean + public DefaultKafkaConsumerFactory consumerFactory() { + return new DefaultKafkaConsumerFactory<>(consumerConfigs()); + } - @Bean - public Map consumerConfigs() { - Map props = new HashMap<>(); - props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, brokers); - props.put(ConsumerConfig.GROUP_ID_CONFIG, groupId); - props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, false); - props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest"); - props.put(ConsumerConfig.EXCLUDE_INTERNAL_TOPICS_CONFIG, "false"); - props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, ByteBufferDeserializer.class); - props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, ByteBufferDeserializer.class); - return props; - } + @Bean + public Map consumerConfigs() { + Map props = new HashMap<>(); + props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, brokers); + props.put(ConsumerConfig.GROUP_ID_CONFIG, groupId); + props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, false); + props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest"); + props.put(ConsumerConfig.EXCLUDE_INTERNAL_TOPICS_CONFIG, "false"); + props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, ByteBufferDeserializer.class); + props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, ByteBufferDeserializer.class); + return props; + } - @Bean - public Jackson2ObjectMapperBuilder objectMapperBuilder() { - return new Jackson2ObjectMapperBuilder() { - @Override - public void configure(ObjectMapper objectMapper) { - super.configure(objectMapper); - objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY); - objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); - } - }; - } + @Bean + public Jackson2ObjectMapperBuilder objectMapperBuilder() { + return new Jackson2ObjectMapperBuilder() { + @Override + public void configure(ObjectMapper objectMapper) { + super.configure(objectMapper); + objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY); + objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); + } + }; + } } diff --git a/src/main/java/org/gnuhpc/bigdata/config/SwaggerConfig.java b/src/main/java/org/gnuhpc/bigdata/config/SwaggerConfig.java index e4017ca..23f145e 100644 --- a/src/main/java/org/gnuhpc/bigdata/config/SwaggerConfig.java +++ b/src/main/java/org/gnuhpc/bigdata/config/SwaggerConfig.java @@ -3,7 +3,6 @@ import com.google.common.base.Predicates; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.web.bind.annotation.RequestMapping; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; @@ -13,30 +12,28 @@ import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.swagger2.annotations.EnableSwagger2; -/** - * Created by gnuhpc on 2017/7/16. - */ +/** Created by gnuhpc on 2017/7/16. */ @Configuration @EnableSwagger2 public class SwaggerConfig { - @Bean - public Docket api() { - return new Docket(DocumentationType.SWAGGER_2) - .apiInfo(apiInfo()) - .select() - .apis(RequestHandlerSelectors.any()) - .apis(Predicates.not(RequestHandlerSelectors.basePackage("org.springframework.boot"))) - .paths(PathSelectors.any()) - .build(); - } + @Bean + public Docket api() { + return new Docket(DocumentationType.SWAGGER_2) + .apiInfo(apiInfo()) + .select() + .apis(RequestHandlerSelectors.any()) + .apis(Predicates.not(RequestHandlerSelectors.basePackage("org.springframework.boot"))) + .paths(PathSelectors.any()) + .build(); + } - private ApiInfo apiInfo() { - return new ApiInfoBuilder() - .title("Kafka REST API SwaggerUI") - .description("Kafka REST API SwaggerUI") - .contact(new Contact("gnuhpc", "https://github.com/gnuhpc", "gnuhpc@gmail.com")) - .version("0.1.0") - .build(); - } + private ApiInfo apiInfo() { + return new ApiInfoBuilder() + .title("Kafka REST API SwaggerUI") + .description("Kafka REST API SwaggerUI") + .contact(new Contact("gnuhpc", "https://github.com/gnuhpc", "gnuhpc@gmail.com")) + .version("0.1.0") + .build(); + } } diff --git a/src/main/java/org/gnuhpc/bigdata/config/WebMvcConfig.java b/src/main/java/org/gnuhpc/bigdata/config/WebMvcConfig.java index 56bac65..213974e 100644 --- a/src/main/java/org/gnuhpc/bigdata/config/WebMvcConfig.java +++ b/src/main/java/org/gnuhpc/bigdata/config/WebMvcConfig.java @@ -4,12 +4,11 @@ import org.springframework.web.servlet.config.annotation.PathMatchConfigurer; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; -/** - * Created by gnuhpc on 2017/7/22. - */ +/** Created by gnuhpc on 2017/7/22. */ @Configuration public class WebMvcConfig extends WebMvcConfigurerAdapter { - public void configurePathMatch(PathMatchConfigurer configurer) { - configurer.setUseSuffixPatternMatch(false); - } + + public void configurePathMatch(PathMatchConfigurer configurer) { + configurer.setUseSuffixPatternMatch(false); + } } diff --git a/src/main/java/org/gnuhpc/bigdata/config/WebSecurityConfig.java b/src/main/java/org/gnuhpc/bigdata/config/WebSecurityConfig.java index 5b95330..8330b57 100644 --- a/src/main/java/org/gnuhpc/bigdata/config/WebSecurityConfig.java +++ b/src/main/java/org/gnuhpc/bigdata/config/WebSecurityConfig.java @@ -1,5 +1,6 @@ package org.gnuhpc.bigdata.config; +import java.io.File; import org.gnuhpc.bigdata.security.BasicAuthenticationPoint; import org.gnuhpc.bigdata.security.UserDetailsServiceImp; import org.gnuhpc.bigdata.utils.CommonUtils; @@ -16,46 +17,53 @@ import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; -import java.io.File; - @Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { - public static final String SECURITY_FILE_PATH = CommonUtils.PROJECT_ROOT_FOLDER + File.separator + - "security" + File.separator + "security.yml"; - @Autowired - private BasicAuthenticationPoint basicAuthenticationPoint; + public static final String SECURITY_FILE_PATH = + CommonUtils.PROJECT_ROOT_FOLDER + + File.separator + + "security" + + File.separator + + "security.yml"; + + @Autowired private BasicAuthenticationPoint basicAuthenticationPoint; @Value("${server.security.check}") private boolean securityCheck; + @Value("${server.security.checkInitDelay}") private int checkInitDelay; + @Value("${server.security.checkSecurityInterval}") private int checkSecurityInterval; @Bean public UserDetailsService userDetailsService() { return new UserDetailsServiceImp(securityCheck, checkInitDelay, checkSecurityInterval); - }; + } @Bean public BCryptPasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); - }; + } @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable(); if (securityCheck) { - http.authorizeRequests().antMatchers("/api", "/swagger-ui.html", "/webjars/**", "/swagger-resources/**", "/v2/**").permitAll() - .antMatchers(HttpMethod.GET, "/**").permitAll() - .anyRequest().authenticated(); + http.authorizeRequests() + .antMatchers("/api", "/swagger-ui.html", "/webjars/**", "/swagger-resources/**", "/v2/**") + .permitAll() + .antMatchers(HttpMethod.GET, "/**") + .permitAll() + .anyRequest() + .authenticated(); http.httpBasic().authenticationEntryPoint(basicAuthenticationPoint); http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); } else { - http.authorizeRequests().antMatchers("/**").permitAll() - .anyRequest().authenticated(); + http.authorizeRequests().antMatchers("/**").permitAll().anyRequest().authenticated(); } } @@ -65,6 +73,6 @@ public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception } public static void main(String[] args) { - //System.out.println(new BCryptPasswordEncoder().encode("admin1234")); + // System.out.println(new BCryptPasswordEncoder().encode("admin1234")); } } diff --git a/src/main/java/org/gnuhpc/bigdata/config/ZookeeperConfig.java b/src/main/java/org/gnuhpc/bigdata/config/ZookeeperConfig.java index e218611..cd61336 100644 --- a/src/main/java/org/gnuhpc/bigdata/config/ZookeeperConfig.java +++ b/src/main/java/org/gnuhpc/bigdata/config/ZookeeperConfig.java @@ -1,6 +1,9 @@ package org.gnuhpc.bigdata.config; import com.google.common.net.HostAndPort; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; import lombok.Getter; import lombok.Setter; import lombok.extern.log4j.Log4j; @@ -10,15 +13,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.stereotype.Component; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.stream.Collectors; - -/** - * Created by gnuhpc on 2017/7/16. - */ - +/** Created by gnuhpc on 2017/7/16. */ @Log4j @Setter @Getter @@ -26,15 +21,15 @@ @Component @Configuration public class ZookeeperConfig { - private String uris; - @Bean(initMethod = "init", destroyMethod = "destroy") - public ZookeeperUtils zookeeperUtils(){ - return new ZookeeperUtils(); - } + private String uris; - public List getHostAndPort(){ - return Arrays.stream(uris.split(",")).map(HostAndPort::fromString).collect(Collectors.toList()); - } + @Bean(initMethod = "init", destroyMethod = "destroy") + public ZookeeperUtils zookeeperUtils() { + return new ZookeeperUtils(); + } + public List getHostAndPort() { + return Arrays.stream(uris.split(",")).map(HostAndPort::fromString).collect(Collectors.toList()); + } } diff --git a/src/main/java/org/gnuhpc/bigdata/constant/ConsumerGroupState.java b/src/main/java/org/gnuhpc/bigdata/constant/ConsumerGroupState.java index a1a7123..0c9062b 100644 --- a/src/main/java/org/gnuhpc/bigdata/constant/ConsumerGroupState.java +++ b/src/main/java/org/gnuhpc/bigdata/constant/ConsumerGroupState.java @@ -10,7 +10,7 @@ public enum ConsumerGroupState { DEAD("Dead"), EMPTY("Empty"); - private final static HashMap NAME_TO_ENUM; + private static final HashMap NAME_TO_ENUM; static { NAME_TO_ENUM = new HashMap<>(); @@ -25,10 +25,7 @@ public enum ConsumerGroupState { this.name = name; } - - /** - * Parse a string into a consumer group state. - */ + /** Parse a string into a consumer group state. */ public static ConsumerGroupState parse(String name) { ConsumerGroupState state = NAME_TO_ENUM.get(name); return state == null ? UNKNOWN : state; diff --git a/src/main/java/org/gnuhpc/bigdata/constant/ConsumerState.java b/src/main/java/org/gnuhpc/bigdata/constant/ConsumerState.java index d4ebcf0..2a23a1f 100644 --- a/src/main/java/org/gnuhpc/bigdata/constant/ConsumerState.java +++ b/src/main/java/org/gnuhpc/bigdata/constant/ConsumerState.java @@ -1,6 +1,6 @@ package org.gnuhpc.bigdata.constant; public enum ConsumerState { - RUNNING, - PENDING + RUNNING, + PENDING } diff --git a/src/main/java/org/gnuhpc/bigdata/constant/ConsumerType.java b/src/main/java/org/gnuhpc/bigdata/constant/ConsumerType.java index 322058a..c385292 100644 --- a/src/main/java/org/gnuhpc/bigdata/constant/ConsumerType.java +++ b/src/main/java/org/gnuhpc/bigdata/constant/ConsumerType.java @@ -1,6 +1,6 @@ package org.gnuhpc.bigdata.constant; public enum ConsumerType { - NEW, - OLD, + NEW, + OLD, } diff --git a/src/main/java/org/gnuhpc/bigdata/constant/GeneralResponseState.java b/src/main/java/org/gnuhpc/bigdata/constant/GeneralResponseState.java index df1a69b..c0878e4 100644 --- a/src/main/java/org/gnuhpc/bigdata/constant/GeneralResponseState.java +++ b/src/main/java/org/gnuhpc/bigdata/constant/GeneralResponseState.java @@ -1,6 +1,6 @@ package org.gnuhpc.bigdata.constant; public enum GeneralResponseState { - success, - failure + success, + failure } diff --git a/src/main/java/org/gnuhpc/bigdata/constant/ReassignmentStatus.java b/src/main/java/org/gnuhpc/bigdata/constant/ReassignmentStatus.java index e2d464a..3462949 100644 --- a/src/main/java/org/gnuhpc/bigdata/constant/ReassignmentStatus.java +++ b/src/main/java/org/gnuhpc/bigdata/constant/ReassignmentStatus.java @@ -1,8 +1,9 @@ package org.gnuhpc.bigdata.constant; public enum ReassignmentStatus { - ReassignmentFailed(-1, "Reassignment Failed"), ReassignmentInProgress(0, - "Reassignment In Progress"), ReassignmentCompleted(1, "Reassignment Completed"); + ReassignmentFailed(-1, "Reassignment Failed"), + ReassignmentInProgress(0, "Reassignment In Progress"), + ReassignmentCompleted(1, "Reassignment Completed"); private int status; private String msg; @@ -14,7 +15,7 @@ private ReassignmentStatus(int status, String msg) { public static ReassignmentStatus valueOf(int status) { ReassignmentStatus[] reassignmentStatusList = values(); - for (int i = 0; i collectJMXMetric( - @Pattern(regexp = IP_AND_PORT_LIST_REGEX)@RequestParam @ApiParam( - value = "Parameter jmxurl should be a comma-separated list of {IP:Port} or set to \'default\'")String jmxurl) { - if(jmxurl.equals("default")) { + @Pattern(regexp = IP_AND_PORT_LIST_REGEX) + @RequestParam + @ApiParam(value = "Parameter jmxurl should be a comma-separated list of {IP:Port} or set" + + " to \'default\'") + String jmxurl) { + if (jmxurl.equals("default")) { jmxurl = jmxKafkaURL; } @@ -42,11 +50,15 @@ public List collectJMXMetric( } @PostMapping("/jmx/v2") - @ApiOperation(value = "Fetch JMX metric data with query filter. You can get the query filter template through the API /jmx/v2/filters.") - public List collectJMXMetric(@Pattern(regexp = IP_AND_PORT_LIST_REGEX)@RequestParam - @ApiParam(value = "Parameter jmxurl should be a comma-separated list of {IP:Port} or set to \'default\'")String jmxurl, - @RequestBody JMXQuery jmxQuery) { - if(jmxurl.equals("default")) { + @ApiOperation(value = "Fetch JMX metric data with query filter. You can get the query filter " + + "template through the API /jmx/v2/filters.") + public List collectJMXMetric( + @Pattern(regexp = IP_AND_PORT_LIST_REGEX) + @RequestParam @ApiParam(value = "Parameter jmxurl should be a comma-separated list of " + + "{IP:Port} or set to \'default\'") + String jmxurl, + @RequestBody JMXQuery jmxQuery) { + if (jmxurl.equals("default")) { jmxurl = jmxKafkaURL; } @@ -56,7 +68,8 @@ public List collectJMXMetric(@Pattern(regexp = IP_AND_PORT_LIST_R } @GetMapping("/jmx/v2/filters") - @ApiOperation(value = "List the query filter templates with the filterKey. If filterKey is set to empty, it will return all the templates.") + @ApiOperation(value = "List the query filter templates with the filterKey. If filterKey is set " + + "to empty, it will return all the templates.") public HashMap listJMXFilterTemplate(@RequestParam String filterKey) { return collectorService.listJMXFilterTemplate(filterKey); } diff --git a/src/main/java/org/gnuhpc/bigdata/controller/CustomErrorController.java b/src/main/java/org/gnuhpc/bigdata/controller/CustomErrorController.java index ac6fc0e..b4359d2 100644 --- a/src/main/java/org/gnuhpc/bigdata/controller/CustomErrorController.java +++ b/src/main/java/org/gnuhpc/bigdata/controller/CustomErrorController.java @@ -1,6 +1,8 @@ package org.gnuhpc.bigdata.controller; -import org.gnuhpc.bigdata.exception.ErrorJson; +import java.util.Map; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import org.gnuhpc.bigdata.exception.RestErrorResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; @@ -13,39 +15,38 @@ import org.springframework.web.context.request.ServletRequestAttributes; import springfox.documentation.annotations.ApiIgnore; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.util.Map; - @RestController @ApiIgnore public class CustomErrorController implements ErrorController { - private static final String PATH = "/error"; - - @Value("${server.debug}") - private boolean debug; - - @Autowired - private ErrorAttributes errorAttributes; - - @RequestMapping(value = PATH) - public RestErrorResponse error(HttpServletRequest request, HttpServletResponse response) { - // Appropriate HTTP response code (e.g. 404 or 500) is automatically set by Spring. - // Here we just define response body. - //return new ErrorJson(response.getStatus(), getErrorAttributes(request, debug)); - return new RestErrorResponse(HttpStatus.valueOf(response.getStatus()), response.getStatus(), - (String)getErrorAttributes(request, debug).get("message"), - (String)getErrorAttributes(request, debug).get("trace"), - ""); - } - - @Override - public String getErrorPath() { - return PATH; - } - - private Map getErrorAttributes(HttpServletRequest request, boolean includeStackTrace) { - RequestAttributes requestAttributes = new ServletRequestAttributes(request); - return errorAttributes.getErrorAttributes(requestAttributes, includeStackTrace); - } + + private static final String PATH = "/error"; + + @Value("${server.debug}") + private boolean debug; + + @Autowired private ErrorAttributes errorAttributes; + + @RequestMapping(value = PATH) + public RestErrorResponse error(HttpServletRequest request, HttpServletResponse response) { + // Appropriate HTTP response code (e.g. 404 or 500) is automatically set by Spring. + // Here we just define response body. + // return new ErrorJson(response.getStatus(), getErrorAttributes(request, debug)); + return new RestErrorResponse( + HttpStatus.valueOf(response.getStatus()), + response.getStatus(), + (String) getErrorAttributes(request, debug).get("message"), + (String) getErrorAttributes(request, debug).get("trace"), + ""); + } + + @Override + public String getErrorPath() { + return PATH; + } + + private Map getErrorAttributes( + HttpServletRequest request, boolean includeStackTrace) { + RequestAttributes requestAttributes = new ServletRequestAttributes(request); + return errorAttributes.getErrorAttributes(requestAttributes, includeStackTrace); + } } diff --git a/src/main/java/org/gnuhpc/bigdata/controller/DebugController.java b/src/main/java/org/gnuhpc/bigdata/controller/DebugController.java index e6eee8d..c5738e9 100644 --- a/src/main/java/org/gnuhpc/bigdata/controller/DebugController.java +++ b/src/main/java/org/gnuhpc/bigdata/controller/DebugController.java @@ -1,6 +1,8 @@ package org.gnuhpc.bigdata.controller; -import io.swagger.annotations.Api; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; import kafka.common.OffsetAndMetadata; import kafka.coordinator.group.GroupTopicPartition; import org.gnuhpc.bigdata.componet.OffsetStorage; @@ -11,41 +13,34 @@ import org.springframework.web.bind.annotation.RestController; import springfox.documentation.annotations.ApiIgnore; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; - -/** - * Created by gnuhpc on 2017/7/16. - */ - +/** Created by gnuhpc on 2017/7/16. */ @RequestMapping("/debug") @RestController @ApiIgnore public class DebugController { - @Autowired - ApplicationContext appContext; - - @Autowired - OffsetStorage offsetStorage; - - @RequestMapping("/beans") - public Map beans(@RequestParam(required = false) String q) { - Map retMap = new HashMap<>(); - - String[] retArray = Arrays.stream(appContext.getBeanDefinitionNames()) - .filter(beanName -> - (q == null || q.length() == 0) || - beanName.toLowerCase().contains(q.trim().toLowerCase()) - ) - .toArray(String[]::new); - - retMap.put("beans", retArray); - return retMap; - } - - @RequestMapping("/offsets") - public Map> offsets(){ - return offsetStorage.getMap(); - } + + @Autowired ApplicationContext appContext; + + @Autowired OffsetStorage offsetStorage; + + @RequestMapping("/beans") + public Map beans(@RequestParam(required = false) String q) { + Map retMap = new HashMap<>(); + + String[] retArray = + Arrays.stream(appContext.getBeanDefinitionNames()) + .filter( + beanName -> + (q == null || q.length() == 0) + || beanName.toLowerCase().contains(q.trim().toLowerCase())) + .toArray(String[]::new); + + retMap.put("beans", retArray); + return retMap; + } + + @RequestMapping("/offsets") + public Map> offsets() { + return offsetStorage.getMap(); + } } diff --git a/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java b/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java index 0fea98f..2c38bfc 100644 --- a/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java +++ b/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java @@ -9,7 +9,6 @@ import java.util.Map; import java.util.Properties; import java.util.Set; -import java.util.concurrent.ExecutionException; import joptsimple.internal.Strings; import lombok.extern.log4j.Log4j; import org.apache.kafka.clients.admin.DescribeReplicaLogDirsResult.ReplicaLogDirInfo; @@ -46,19 +45,15 @@ import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; -/** - * Created by gnuhpc on 2017/7/16. - */ +/** Created by gnuhpc on 2017/7/16. */ @Log4j @RequestMapping("/kafka") @RestController public class KafkaController { - @Autowired - private KafkaAdminService kafkaAdminService; + @Autowired private KafkaAdminService kafkaAdminService; - @Autowired - private KafkaProducerService kafkaProducerService; + @Autowired private KafkaProducerService kafkaProducerService; @GetMapping(value = "/cluster") @ApiOperation(value = "Describe cluster, nodes, controller info.") @@ -114,15 +109,17 @@ public Properties getBrokerDynConfig(@PathVariable int brokerId) { @PutMapping(value = "/brokers/{brokerId}/dynconf") @ApiOperation(value = "Update broker configs") - public Properties updateBrokerDynConfig(@PathVariable int brokerId, - @RequestBody Properties props) { + public Properties updateBrokerDynConfig( + @PathVariable int brokerId, @RequestBody Properties props) { return kafkaAdminService.updateBrokerDynConf(brokerId, props); } @DeleteMapping(value = "/brokers/{brokerId}/dynconf") @ApiOperation(value = "Remove broker dynamic configs") - public void removeBrokerDynConfig(@PathVariable int brokerId, @RequestParam List configKeysToBeRemoved) { - kafkaAdminService.removeConfigInZk(Type.BROKER, String.valueOf(brokerId), configKeysToBeRemoved); + public void removeBrokerDynConfig( + @PathVariable int brokerId, @RequestParam List configKeysToBeRemoved) { + kafkaAdminService.removeConfigInZk( + Type.BROKER, String.valueOf(brokerId), configKeysToBeRemoved); } @GetMapping("/topics") @@ -141,8 +138,8 @@ public List listTopicBrief() { @ResponseStatus(HttpStatus.CREATED) @ApiOperation(value = "Create a topic") @ApiParam(value = "if reassignStr set, partitions and repli-factor will be ignored.") - public TopicMeta createTopic(@RequestBody TopicDetail topic, - @RequestParam(required = false) String reassignStr) { + public TopicMeta createTopic( + @RequestBody TopicDetail topic, @RequestParam(required = false) String reassignStr) { return kafkaAdminService.createTopic(topic, reassignStr); } @@ -157,16 +154,22 @@ public boolean existTopic(@PathVariable String topic) { @ApiOperation(value = "Write a message to the topic, for testing purpose") public GeneralResponse writeMessage(@PathVariable String topic, @RequestBody String message) { kafkaProducerService.send(topic, message); - return GeneralResponse.builder().state(GeneralResponseState.success) - .msg(message + " has been sent").build(); + return GeneralResponse.builder() + .state(GeneralResponseState.success) + .msg(message + " has been sent") + .build(); } @GetMapping(value = "/consumer/{topic}/{partition}/{offset}") - @ApiOperation(value = "Get the message from the offset of the partition in the topic" + - ", decoder is not supported yet") - public String getMessage(@PathVariable String topic, + @ApiOperation( + value = + "Get the message from the offset of the partition in the topic" + + ", decoder is not supported yet") + public String getMessage( + @PathVariable String topic, @PathVariable int partition, - @PathVariable long offset, @RequestParam(required = false) String decoder) { + @PathVariable long offset, + @RequestParam(required = false) String decoder) { return kafkaAdminService.getRecordByOffset(topic, partition, offset, decoder, "").getValue(); } @@ -184,8 +187,8 @@ public Map deleteTopicList(@RequestParam List t @PutMapping(value = "/topics/{topic}/conf") @ApiOperation(value = "Update topic configs") - public Collection updateTopicConfig(@PathVariable String topic, - @RequestBody Properties props) { + public Collection updateTopicConfig( + @PathVariable String topic, @RequestBody Properties props) { return kafkaAdminService.updateTopicConf(topic, props); } @@ -203,16 +206,14 @@ public Properties getTopicDynConfig(@PathVariable String topic) { @GetMapping(value = "/topics/{topic}/conf/{key}") @ApiOperation(value = "Get topic config by key") - public Properties getTopicConfigByKey(@PathVariable String topic, - @PathVariable String key) { + public Properties getTopicConfigByKey(@PathVariable String topic, @PathVariable String key) { return kafkaAdminService.getTopicConfByKey(topic, key); } @PutMapping(value = "/topics/{topic}/conf/{key}={value}") @ApiOperation(value = "Update a topic config by key") - public Collection updateTopicConfigByKey(@PathVariable String topic, - @PathVariable String key, - @PathVariable String value) { + public Collection updateTopicConfigByKey( + @PathVariable String topic, @PathVariable String key, @PathVariable String value) { return kafkaAdminService.updateTopicConfByKey(topic, key, value); } @@ -226,24 +227,27 @@ public Map addPartition(@RequestBody List @ApiOperation(value = "Generate plan for the partition reassignment") public List generateReassignPartitions(@RequestBody ReassignWrapper reassignWrapper) { return kafkaAdminService.generateReassignPartition(reassignWrapper); - } @PutMapping(value = "/partitions/reassign/execute") @ApiOperation(value = "Execute the partition reassignment") public Map executeReassignPartitions( - @RequestBody String reassignStr, long interBrokerThrottle, long replicaAlterLogDirsThrottle, + @RequestBody String reassignStr, + long interBrokerThrottle, + long replicaAlterLogDirsThrottle, long timeoutMs) { - return kafkaAdminService - .executeReassignPartition(reassignStr, interBrokerThrottle, replicaAlterLogDirsThrottle, - timeoutMs); + return kafkaAdminService.executeReassignPartition( + reassignStr, interBrokerThrottle, replicaAlterLogDirsThrottle, timeoutMs); } @PutMapping(value = "/partitions/reassign/check") @ApiOperation(value = "Check the partition reassignment process") - @ApiResponses(value = {@ApiResponse(code = 1, message = "Reassignment Completed"), - @ApiResponse(code = 0, message = "Reassignment In Progress"), - @ApiResponse(code = -1, message = "Reassignment Failed")}) + @ApiResponses( + value = { + @ApiResponse(code = 1, message = "Reassignment Completed"), + @ApiResponse(code = 0, message = "Reassignment In Progress"), + @ApiResponse(code = -1, message = "Reassignment Failed") + }) public Map checkReassignPartitions(@RequestBody String reassignStr) { return kafkaAdminService.checkReassignStatusByStr(reassignStr); } @@ -262,13 +266,16 @@ public Map> listAllConsumerGroups( @GetMapping(value = "/consumergroups/{consumerGroup}/{type}/topic") @ApiOperation(value = "Get the topics involved of the specify consumer group") - public Set listTopicByCG(@PathVariable String consumerGroup, - @PathVariable ConsumerType type) { - return kafkaAdminService.listTopicsByCG(consumerGroup, type); + public Set listTopicByConsumerGroup( + @PathVariable String consumerGroup, @PathVariable ConsumerType type) { + return kafkaAdminService.listTopicsByConsumerGroup(consumerGroup, type); } @GetMapping(value = "/consumergroups/{consumerGroup}/meta") - @ApiOperation(value = "Get the meta data of the specify new consumer group, including state, coordinator, assignmentStrategy, members") + @ApiOperation( + value = + "Get the meta data of the specify new consumer group, including state, coordinator," + + " assignmentStrategy, members") public ConsumerGroupMeta getConsumerGroupMeta(@PathVariable String consumerGroup) { if (kafkaAdminService.isNewConsumerGroup(consumerGroup)) { return kafkaAdminService.getConsumerGroupMeta(consumerGroup); @@ -279,41 +286,53 @@ public ConsumerGroupMeta getConsumerGroupMeta(@PathVariable String consumerGroup @GetMapping(value = "/consumergroups/{consumerGroup}/{type}/topic/{topic}") @ApiOperation(value = "Describe consumer groups by topic, showing lag and offset") - public List describeCGByTopic( + public List describeConsumerGroupByTopic( @ConsumerGroupExistConstraint @PathVariable String consumerGroup, @PathVariable ConsumerType type, - @PathVariable String topic) throws InterruptedException, ExecutionException { + @PathVariable String topic) { if (!Strings.isNullOrEmpty(topic)) { existTopic(topic); } else { throw new ApiException("Topic must be set!"); } if (type != null && type == ConsumerType.NEW) { - return kafkaAdminService.describeNewCGByTopic(consumerGroup, topic); + return kafkaAdminService.describeNewConsumerGroupByTopic(consumerGroup, topic); } if (type != null && type == ConsumerType.OLD) { - return kafkaAdminService.describeOldCGByTopic(consumerGroup, topic); + return kafkaAdminService.describeOldConsumerGroupByTopic(consumerGroup, topic); } throw new ApiException("Unknown type specified!"); } @GetMapping(value = "/consumergroups/{consumerGroup}/{type}") - @ApiOperation(value = "Describe consumer groups, showing lag and offset, may be slow if multi topic are listened") - public Map> describeCG( + @ApiOperation( + value = + "Describe consumer group, showing lag and offset, may be slow if multi" + + " topics are listened") + public Map> describeConsumerGroup( @ConsumerGroupExistConstraint @PathVariable String consumerGroup, @PathVariable ConsumerType type) { return kafkaAdminService.describeConsumerGroup(consumerGroup, type); } @PutMapping(value = "/consumergroup/{consumergroup}/{type}/topic/{topic}/{partition}/{offset}") - @ApiOperation(value = "Reset consumer group offset, earliest/latest can be used. Support reset by time for new consumer group, pass a parameter that satisfies yyyy-MM-dd HH:mm:ss to offset.") - public GeneralResponse resetOffset(@PathVariable String topic, + @ApiOperation( + value = + "Reset consumer group offset, earliest/latest can be used. Support reset by time for " + + "new consumer group, pass a parameter that satisfies yyyy-MM-dd HH:mm:ss " + + "to offset.") + public GeneralResponse resetOffset( + @PathVariable String topic, @PathVariable int partition, @PathVariable String consumergroup, - @PathVariable @ApiParam( - value = "[earliest/latest/{long}/yyyy-MM-dd HH:mm:ss] can be supported. The date type is only valid for new consumer group.") String offset, + @PathVariable + @ApiParam( + value = + "[earliest/latest/{long}/yyyy-MM-dd HH:mm:ss] can be supported. " + + "The date type is only valid for new consumer group.") + String offset, @PathVariable ConsumerType type) { return kafkaAdminService.resetOffset(topic, partition, consumergroup, type, offset); } @@ -328,8 +347,8 @@ public Map> getLastCommitTimestamp( @DeleteMapping(value = "/consumergroup/{consumergroup}/{type}") @ApiOperation(value = "Delete Consumer Group") - public GeneralResponse deleteOldConsumerGroup(@PathVariable String consumergroup, - @PathVariable ConsumerType type) { + public GeneralResponse deleteOldConsumerGroup( + @PathVariable String consumergroup, @PathVariable ConsumerType type) { return kafkaAdminService.deleteConsumerGroup(consumergroup, type); } diff --git a/src/main/java/org/gnuhpc/bigdata/controller/SwaggerController.java b/src/main/java/org/gnuhpc/bigdata/controller/SwaggerController.java index b149055..1bb38c0 100644 --- a/src/main/java/org/gnuhpc/bigdata/controller/SwaggerController.java +++ b/src/main/java/org/gnuhpc/bigdata/controller/SwaggerController.java @@ -1,18 +1,15 @@ package org.gnuhpc.bigdata.controller; -import io.swagger.annotations.Api; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RestController; import springfox.documentation.annotations.ApiIgnore; @Controller @ApiIgnore public class SwaggerController { - @GetMapping(value = "/api") - public String swagger() { - return "redirect:swagger-ui.html"; - } + + @GetMapping(value = "/api") + public String swagger() { + return "redirect:swagger-ui.html"; + } } diff --git a/src/main/java/org/gnuhpc/bigdata/controller/UserController.java b/src/main/java/org/gnuhpc/bigdata/controller/UserController.java index fba7f50..13879b2 100644 --- a/src/main/java/org/gnuhpc/bigdata/controller/UserController.java +++ b/src/main/java/org/gnuhpc/bigdata/controller/UserController.java @@ -2,6 +2,8 @@ import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; +import java.util.List; +import javax.validation.Valid; import lombok.extern.log4j.Log4j; import org.gnuhpc.bigdata.constant.GeneralResponseState; import org.gnuhpc.bigdata.model.GeneralResponse; @@ -9,17 +11,20 @@ import org.gnuhpc.bigdata.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.validation.BindingResult; -import org.springframework.web.bind.annotation.*; - -import javax.validation.Valid; -import java.util.List; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; @Log4j @RestController @Api(value = "/users", description = "Security User Management Controller.") public class UserController { - @Autowired - private UserService userService; + + @Autowired private UserService userService; @GetMapping("/users") @ApiOperation(value = "Get user list.") @@ -29,9 +34,12 @@ public List listUser() { @PostMapping("/users") @ApiOperation(value = "Add user.") - public GeneralResponse addUser(@RequestBody@Valid User user, BindingResult results) { + public GeneralResponse addUser(@RequestBody @Valid User user, BindingResult results) { if (results.hasErrors()) { - return GeneralResponse.builder().state(GeneralResponseState.failure).msg(results.getFieldError().getDefaultMessage()).build(); + return GeneralResponse.builder() + .state(GeneralResponseState.failure) + .msg(results.getFieldError().getDefaultMessage()) + .build(); } log.info("Receive add user request: username:" + user.getUsername()); return userService.addUser(user); @@ -39,9 +47,12 @@ public GeneralResponse addUser(@RequestBody@Valid User user, BindingResult resul @PutMapping("/users") @ApiOperation(value = "Modify user information.") - public GeneralResponse modifyUser(@RequestBody@Valid User user, BindingResult results) { + public GeneralResponse modifyUser(@RequestBody @Valid User user, BindingResult results) { if (results.hasErrors()) { - return GeneralResponse.builder().state(GeneralResponseState.failure).msg(results.getFieldError().getDefaultMessage()).build(); + return GeneralResponse.builder() + .state(GeneralResponseState.failure) + .msg(results.getFieldError().getDefaultMessage()) + .build(); } log.info("Receive modify user request: username:" + user.getUsername()); return userService.modifyUser(user); diff --git a/src/main/java/org/gnuhpc/bigdata/controller/ZookeeperController.java b/src/main/java/org/gnuhpc/bigdata/controller/ZookeeperController.java index 05e51b3..592c077 100644 --- a/src/main/java/org/gnuhpc/bigdata/controller/ZookeeperController.java +++ b/src/main/java/org/gnuhpc/bigdata/controller/ZookeeperController.java @@ -3,70 +3,64 @@ import com.google.common.net.HostAndPort; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; +import java.util.List; +import java.util.Map; import org.gnuhpc.bigdata.model.ZkServerEnvironment; import org.gnuhpc.bigdata.model.ZkServerStat; import org.gnuhpc.bigdata.service.ZookeeperService; import org.gnuhpc.bigdata.utils.ZookeeperUtils; -import org.gnuhpc.bigdata.validator.ZKNodePathExistConstraint; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import java.util.List; -import java.util.Map; - -/** - * Created by gnuhpc on 2017/7/16. - */ +/** Created by gnuhpc on 2017/7/16. */ @RestController @RequestMapping("/zk") @Api(value = "Control Zookeeper with Rest API") public class ZookeeperController { - @Autowired - private ZookeeperUtils zookeeperUtils; - @Autowired - private ZookeeperService zookeeperService; + @Autowired private ZookeeperUtils zookeeperUtils; - @GetMapping("/ls/path") - @ApiOperation(value = "List a zookeeper path") - public List ls(@RequestParam String path){ - return zookeeperUtils.lsPath(path); - /* - try { - return zookeeperUtils.getCuratorClient().getChildren().forPath(path); - } catch (Exception e) { - e.printStackTrace(); - } + @Autowired private ZookeeperService zookeeperService; - return null; - */ + @GetMapping("/ls/path") + @ApiOperation(value = "List a zookeeper path") + public List ls(@RequestParam String path) { + return zookeeperUtils.lsPath(path); + /* + try { + return zookeeperUtils.getCuratorClient().getChildren().forPath(path); + } catch (Exception e) { + e.printStackTrace(); } - @GetMapping("/get/path") - @ApiOperation(value = "Get data of a zookeeper path") - public Map get(@RequestParam String path){ - return zookeeperUtils.getNodeData(path); - } + return null; + */ + } - @GetMapping("/connstate") - @ApiOperation(value = "Get the connection state of zookeeper") - public String zkConnState(){ - return zookeeperUtils.getCuratorClient().getState().toString(); - } + @GetMapping("/get/path") + @ApiOperation(value = "Get data of a zookeeper path") + public Map get(@RequestParam String path) { + return zookeeperUtils.getNodeData(path); + } - @GetMapping("/stat") - @ApiOperation(value = "Get the service state of zookeeper") - public Map getStat(){ - return zookeeperService.stat(); - } + @GetMapping("/connstate") + @ApiOperation(value = "Get the connection state of zookeeper") + public String zkConnState() { + return zookeeperUtils.getCuratorClient().getState().toString(); + } - @GetMapping("/env") - @ApiOperation(value = "Get the environment information of zookeeper") - public Map getEnv(){ - return zookeeperService.environment(); - } + @GetMapping("/stat") + @ApiOperation(value = "Get the service state of zookeeper") + public Map getStat() { + return zookeeperService.stat(); + } + @GetMapping("/env") + @ApiOperation(value = "Get the environment information of zookeeper") + public Map getEnv() { + return zookeeperService.environment(); + } } diff --git a/src/main/java/org/gnuhpc/bigdata/exception/CollectorException.java b/src/main/java/org/gnuhpc/bigdata/exception/CollectorException.java index b68974f..dd9d9ca 100644 --- a/src/main/java/org/gnuhpc/bigdata/exception/CollectorException.java +++ b/src/main/java/org/gnuhpc/bigdata/exception/CollectorException.java @@ -1,6 +1,7 @@ package org.gnuhpc.bigdata.exception; public class CollectorException extends Exception { + public CollectorException(String message, Throwable cause) { super(message, cause); } @@ -16,18 +17,4 @@ public CollectorException(Throwable cause) { public CollectorException() { super(); } - - public String catchStackTrace() { - String stackTraceString = ""; - StackTraceElement[] stackElements = this.getStackTrace(); - if (stackElements != null) { - for (int i = 0; i < stackElements.length; i++) { - stackTraceString = stackTraceString + stackElements[i].getClassName()+"\\/t"; - stackTraceString = stackTraceString + stackElements[i].getFileName()+"\\/t"; - stackTraceString = stackTraceString + stackElements[i].getLineNumber()+"\\/t"; - stackTraceString = stackTraceString + stackElements[i].getMethodName()+"\\/t"; - } - } - return stackTraceString; - } } diff --git a/src/main/java/org/gnuhpc/bigdata/exception/ErrorJson.java b/src/main/java/org/gnuhpc/bigdata/exception/ErrorJson.java deleted file mode 100644 index 89e0361..0000000 --- a/src/main/java/org/gnuhpc/bigdata/exception/ErrorJson.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.gnuhpc.bigdata.exception; - -import java.util.Map; - -public class ErrorJson { - public Integer status; - public String error; - public String message; - public String timeStamp; - public String trace; - - public ErrorJson(int status, Map errorAttributes) { - this.status = status; - this.error = (String) errorAttributes.get("error"); - this.message = (String) errorAttributes.get("message"); - this.timeStamp = errorAttributes.get("timestamp").toString(); - this.trace = (String) errorAttributes.get("trace"); - } - -} diff --git a/src/main/java/org/gnuhpc/bigdata/exception/ExceptionLogAspect.java b/src/main/java/org/gnuhpc/bigdata/exception/ExceptionLogAspect.java index ac54f7c..8dee3de 100644 --- a/src/main/java/org/gnuhpc/bigdata/exception/ExceptionLogAspect.java +++ b/src/main/java/org/gnuhpc/bigdata/exception/ExceptionLogAspect.java @@ -1,30 +1,23 @@ package org.gnuhpc.bigdata.exception; - import lombok.extern.log4j.Log4j; import org.aspectj.lang.JoinPoint; -import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; -import org.springframework.web.context.request.RequestContextHolder; -import org.springframework.web.context.request.ServletRequestAttributes; - -import javax.servlet.http.HttpServletRequest; -import java.util.Arrays; @Aspect @Component @Log4j public class ExceptionLogAspect { - @Pointcut("execution(public * org.gnuhpc.bigdata.exception..*.*(..))") - public void exceptionLog() { - } - @Before("exceptionLog()") - public void doBefore(JoinPoint joinPoint) throws Throwable { - // 接收到请求,记录请求内容 - log.info("Exception happened!", (Exception) joinPoint.getArgs()[0]); - } + @Pointcut("execution(public * org.gnuhpc.bigdata.exception..*.*(..))") + public void exceptionLog() {} + + @Before("exceptionLog()") + public void doBefore(JoinPoint joinPoint) throws Throwable { + // 接收到请求,记录请求内容 + log.info("Exception happened!", (Exception) joinPoint.getArgs()[0]); + } } diff --git a/src/main/java/org/gnuhpc/bigdata/exception/GlobalExceptionHandler.java b/src/main/java/org/gnuhpc/bigdata/exception/GlobalExceptionHandler.java index b9f06c8..4037976 100644 --- a/src/main/java/org/gnuhpc/bigdata/exception/GlobalExceptionHandler.java +++ b/src/main/java/org/gnuhpc/bigdata/exception/GlobalExceptionHandler.java @@ -1,9 +1,18 @@ package org.gnuhpc.bigdata.exception; +import java.util.List; +import java.util.Set; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.validation.ConstraintViolationException; import lombok.extern.log4j.Log4j; import org.springframework.beans.ConversionNotSupportedException; import org.springframework.beans.TypeMismatchException; -import org.springframework.http.*; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.http.converter.HttpMessageNotWritableException; import org.springframework.util.CollectionUtils; @@ -25,20 +34,16 @@ import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; import org.springframework.web.util.WebUtils; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.validation.ConstraintViolationException; -import java.util.List; -import java.util.Set; - @Log4j @ControllerAdvice public class GlobalExceptionHandler extends ResponseEntityExceptionHandler { + /** * A single place to customize the response body of all Exception types. - *

The default implementation sets the {@link WebUtils#ERROR_EXCEPTION_ATTRIBUTE} - * request attribute and creates a {@link ResponseEntity} from the given - * body, headers, and status. + * + *

The default implementation sets the {@link WebUtils#ERROR_EXCEPTION_ATTRIBUTE} request + * attribute and creates a {@link ResponseEntity} from the given body, headers, and status. + * * @param ex the exception * @param body the body for the response * @param headers the headers for the response @@ -46,19 +51,21 @@ public class GlobalExceptionHandler extends ResponseEntityExceptionHandler { * @param request the current request */ @Override - protected ResponseEntity handleExceptionInternal(Exception ex, Object body, - HttpHeaders headers, HttpStatus status, WebRequest request) { + protected ResponseEntity handleExceptionInternal( + Exception ex, Object body, HttpHeaders headers, HttpStatus status, WebRequest request) { if (HttpStatus.INTERNAL_SERVER_ERROR.equals(status)) { request.setAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE, ex, WebRequest.SCOPE_REQUEST); } String error = "Internal Server Error"; - return buildResponseEntity(new RestErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, error ,ex)); + return buildResponseEntity(new RestErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, error, ex)); } /** * Customize the response for HttpRequestMethodNotSupportedException. + * *

This method logs a warning, sets the "Allow" header. + * * @param ex the exception * @param headers the headers to be written to the response * @param status the selected response status @@ -66,26 +73,34 @@ protected ResponseEntity handleExceptionInternal(Exception ex, Object bo * @return the RestErrorResponse Object */ @Override - protected ResponseEntity handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException ex, - HttpHeaders headers, HttpStatus status, WebRequest webRequest) { + protected ResponseEntity handleHttpRequestMethodNotSupported( + HttpRequestMethodNotSupportedException ex, + HttpHeaders headers, + HttpStatus status, + WebRequest webRequest) { pageNotFoundLogger.warn(ex.getMessage()); ServletWebRequest servletRequest = (ServletWebRequest) webRequest; HttpServletRequest request = servletRequest.getNativeRequest(HttpServletRequest.class); StringBuilder builder = new StringBuilder(); - builder.append("Request method: " + request.getMethod()+ " is not supported. Supported Methods: "); + builder.append( + "Request method: " + request.getMethod() + " is not supported. Supported Methods: "); Set supportedMethods = ex.getSupportedHttpMethods(); supportedMethods.forEach(m -> builder.append(m).append(", ")); if (!CollectionUtils.isEmpty(supportedMethods)) { headers.setAllow(supportedMethods); } - return buildResponseEntity(new RestErrorResponse(HttpStatus.METHOD_NOT_ALLOWED, builder.substring(0, builder.length() - 2), ex)); + return buildResponseEntity( + new RestErrorResponse( + HttpStatus.METHOD_NOT_ALLOWED, builder.substring(0, builder.length() - 2), ex)); } /** * Customize the response for HttpMediaTypeNotSupportedException. + * *

This method sets the "Accept" header. + * * @param ex the exception * @param headers the headers to be written to the response * @param status the selected response status @@ -93,8 +108,11 @@ protected ResponseEntity handleHttpRequestMethodNotSupported(HttpRequest * @return the RestErrorResponse Object */ @Override - protected ResponseEntity handleHttpMediaTypeNotSupported(HttpMediaTypeNotSupportedException ex, - HttpHeaders headers, HttpStatus status, WebRequest request) { + protected ResponseEntity handleHttpMediaTypeNotSupported( + HttpMediaTypeNotSupportedException ex, + HttpHeaders headers, + HttpStatus status, + WebRequest request) { StringBuilder builder = new StringBuilder(); builder.append(ex.getContentType()); builder.append(" media type is not supported. Supported media types: "); @@ -105,12 +123,14 @@ protected ResponseEntity handleHttpMediaTypeNotSupported(HttpMediaTypeNo headers.setAccept(mediaTypes); } - return buildResponseEntity(new RestErrorResponse(HttpStatus.UNSUPPORTED_MEDIA_TYPE, - builder.substring(0, builder.length() - 2), ex)); + return buildResponseEntity( + new RestErrorResponse( + HttpStatus.UNSUPPORTED_MEDIA_TYPE, builder.substring(0, builder.length() - 2), ex)); } /** * Customize the response for HttpMediaTypeNotAcceptableException. + * * @param ex the exception * @param headers the headers to be written to the response * @param status the selected response status @@ -118,14 +138,18 @@ protected ResponseEntity handleHttpMediaTypeNotSupported(HttpMediaTypeNo * @return the RestErrorResponse Object */ @Override - protected ResponseEntity handleHttpMediaTypeNotAcceptable(HttpMediaTypeNotAcceptableException ex, - HttpHeaders headers, HttpStatus status, WebRequest request) { + protected ResponseEntity handleHttpMediaTypeNotAcceptable( + HttpMediaTypeNotAcceptableException ex, + HttpHeaders headers, + HttpStatus status, + WebRequest request) { String error = "Media Type not Acceptable"; - return buildResponseEntity(new RestErrorResponse(HttpStatus.NOT_ACCEPTABLE, error ,ex)); + return buildResponseEntity(new RestErrorResponse(HttpStatus.NOT_ACCEPTABLE, error, ex)); } /** * Customize the response for MissingPathVariableException. + * * @param ex the exception * @param headers the headers to be written to the response * @param status the selected response status @@ -134,14 +158,15 @@ protected ResponseEntity handleHttpMediaTypeNotAcceptable(HttpMediaTypeN * @since 4.2 */ @Override - protected ResponseEntity handleMissingPathVariable(MissingPathVariableException ex, - HttpHeaders headers, HttpStatus status, WebRequest request) { + protected ResponseEntity handleMissingPathVariable( + MissingPathVariableException ex, HttpHeaders headers, HttpStatus status, WebRequest request) { String error = "Path Variable : " + ex.getVariableName() + " is missing"; return buildResponseEntity(new RestErrorResponse(HttpStatus.BAD_REQUEST, error, ex)); } /** * Customize the response for MissingServletRequestParameterException. + * * @param ex the exception * @param headers the headers to be written to the response * @param status the selected response status @@ -149,14 +174,18 @@ protected ResponseEntity handleMissingPathVariable(MissingPathVariableEx * @return the RestErrorResponse Object */ @Override - protected ResponseEntity handleMissingServletRequestParameter(MissingServletRequestParameterException ex, - HttpHeaders headers, HttpStatus status, WebRequest request) { + protected ResponseEntity handleMissingServletRequestParameter( + MissingServletRequestParameterException ex, + HttpHeaders headers, + HttpStatus status, + WebRequest request) { String error = ex.getParameterName() + " parameter is missing"; return buildResponseEntity(new RestErrorResponse(HttpStatus.BAD_REQUEST, error, ex)); } /** * Customize the response for ServletRequestBindingException. + * * @param ex the exception * @param headers the headers to be written to the response * @param status the selected response status @@ -164,15 +193,20 @@ protected ResponseEntity handleMissingServletRequestParameter(MissingSer * @return the RestErrorResponse Object */ @Override - protected ResponseEntity handleServletRequestBindingException(ServletRequestBindingException ex, - HttpHeaders headers, HttpStatus status, WebRequest request) { + protected ResponseEntity handleServletRequestBindingException( + ServletRequestBindingException ex, + HttpHeaders headers, + HttpStatus status, + WebRequest request) { String error = "ServletRequest Bind Error"; - return buildResponseEntity(new RestErrorResponse(HttpStatus.BAD_REQUEST, error ,ex)); + return buildResponseEntity(new RestErrorResponse(HttpStatus.BAD_REQUEST, error, ex)); } /** * Customize the response for ConversionNotSupportedException. + * *

This method delegates to {@link #handleExceptionInternal}. + * * @param ex the exception * @param headers the headers to be written to the response * @param status the selected response status @@ -180,13 +214,17 @@ protected ResponseEntity handleServletRequestBindingException(ServletReq * @return a {@code ResponseEntity} instance */ @Override - protected ResponseEntity handleConversionNotSupported(ConversionNotSupportedException ex, - HttpHeaders headers, HttpStatus status, WebRequest request) { + protected ResponseEntity handleConversionNotSupported( + ConversionNotSupportedException ex, + HttpHeaders headers, + HttpStatus status, + WebRequest request) { return handleExceptionInternal(ex, null, headers, status, request); } /** * Customize the response for TypeMismatchException. + * * @param ex the exception * @param headers the headers to be written to the response * @param status the selected response status @@ -194,14 +232,15 @@ protected ResponseEntity handleConversionNotSupported(ConversionNotSuppo * @return the RestErrorResponse Object */ @Override - protected ResponseEntity handleTypeMismatch(TypeMismatchException ex, HttpHeaders headers, - HttpStatus status, WebRequest request) { + protected ResponseEntity handleTypeMismatch( + TypeMismatchException ex, HttpHeaders headers, HttpStatus status, WebRequest request) { String error = "Request parameter value type mismatch error. "; - return buildResponseEntity(new RestErrorResponse(HttpStatus.BAD_REQUEST, error ,ex)); + return buildResponseEntity(new RestErrorResponse(HttpStatus.BAD_REQUEST, error, ex)); } /** * Customize the response for HttpMessageNotReadableException. + * * @param ex the exception * @param headers the headers to be written to the response * @param status the selected response status @@ -209,14 +248,18 @@ protected ResponseEntity handleTypeMismatch(TypeMismatchException ex, Ht * @return the RestErrorResponse Object */ @Override - protected ResponseEntity handleHttpMessageNotReadable(HttpMessageNotReadableException ex, - HttpHeaders headers, HttpStatus status, WebRequest request) { + protected ResponseEntity handleHttpMessageNotReadable( + HttpMessageNotReadableException ex, + HttpHeaders headers, + HttpStatus status, + WebRequest request) { String error = "Malformed JSON request"; return buildResponseEntity(new RestErrorResponse(HttpStatus.BAD_REQUEST, error, ex)); } /** * Customize the response for HttpMessageNotWritableException. + * * @param ex the exception * @param headers the headers to be written to the response * @param status the selected response status @@ -224,8 +267,11 @@ protected ResponseEntity handleHttpMessageNotReadable(HttpMessageNotRead * @return the RestErrorResponse Object */ @Override - protected ResponseEntity handleHttpMessageNotWritable(HttpMessageNotWritableException ex, - HttpHeaders headers, HttpStatus status, WebRequest request) { + protected ResponseEntity handleHttpMessageNotWritable( + HttpMessageNotWritableException ex, + HttpHeaders headers, + HttpStatus status, + WebRequest request) { String error = "Error writing JSON output"; return buildResponseEntity(new RestErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, error, ex)); @@ -233,6 +279,7 @@ protected ResponseEntity handleHttpMessageNotWritable(HttpMessageNotWrit /** * Customize the response for MethodArgumentNotValidException. + * * @param ex the exception * @param headers the headers to be written to the response * @param status the selected response status @@ -240,8 +287,11 @@ protected ResponseEntity handleHttpMessageNotWritable(HttpMessageNotWrit * @return the RestErrorResponse Object */ @Override - protected ResponseEntity handleMethodArgumentNotValid(MethodArgumentNotValidException ex, - HttpHeaders headers, HttpStatus status, WebRequest request) { + protected ResponseEntity handleMethodArgumentNotValid( + MethodArgumentNotValidException ex, + HttpHeaders headers, + HttpStatus status, + WebRequest request) { String error = "Method Argument Validation Error."; RestErrorResponse restErrorResponse = new RestErrorResponse(HttpStatus.BAD_REQUEST, error, ex); @@ -251,7 +301,7 @@ protected ResponseEntity handleMethodArgumentNotValid(MethodArgumentNotV } @ExceptionHandler(ConstraintViolationException.class) - public ResponseEntity handleConstraintViolation(ConstraintViolationException ex){ + public ResponseEntity handleConstraintViolation(ConstraintViolationException ex) { String error = "Constraint Violation Error."; RestErrorResponse restErrorResponse = new RestErrorResponse(HttpStatus.BAD_REQUEST, error, ex); restErrorResponse.addValidationErrors(ex.getConstraintViolations()); @@ -260,7 +310,9 @@ public ResponseEntity handleConstraintViolation(ConstraintViolationExcep /** * Customize the response for MissingServletRequestPartException. + * *

This method delegates to {@link #handleExceptionInternal}. + * * @param ex the exception * @param headers the headers to be written to the response * @param status the selected response status @@ -268,15 +320,20 @@ public ResponseEntity handleConstraintViolation(ConstraintViolationExcep * @return the RestErrorResponse Object */ @Override - protected ResponseEntity handleMissingServletRequestPart(MissingServletRequestPartException ex, - HttpHeaders headers, HttpStatus status, WebRequest request) { + protected ResponseEntity handleMissingServletRequestPart( + MissingServletRequestPartException ex, + HttpHeaders headers, + HttpStatus status, + WebRequest request) { return handleExceptionInternal(ex, null, headers, status, request); } /** * Customize the response for BindException. + * *

This method delegates to {@link #handleExceptionInternal}. + * * @param ex the exception * @param headers the headers to be written to the response * @param status the selected response status @@ -284,15 +341,17 @@ protected ResponseEntity handleMissingServletRequestPart(MissingServletR * @return a {@code ResponseEntity} instance */ @Override - protected ResponseEntity handleBindException(BindException ex, HttpHeaders headers, - HttpStatus status, WebRequest request) { + protected ResponseEntity handleBindException( + BindException ex, HttpHeaders headers, HttpStatus status, WebRequest request) { return handleExceptionInternal(ex, null, headers, status, request); } /** * Customize the response for NoHandlerFoundException. + * *

This method delegates to {@link #handleExceptionInternal}. + * * @param ex the exception * @param headers the headers to be written to the response * @param status the selected response status @@ -302,14 +361,16 @@ protected ResponseEntity handleBindException(BindException ex, HttpHeade */ @Override protected ResponseEntity handleNoHandlerFoundException( - NoHandlerFoundException ex, HttpHeaders headers, HttpStatus status, WebRequest request) { + NoHandlerFoundException ex, HttpHeaders headers, HttpStatus status, WebRequest request) { return handleExceptionInternal(ex, null, headers, status, request); } /** * Customize the response for NoHandlerFoundException. + * *

This method delegates to {@link #handleExceptionInternal}. + * * @param ex the exception * @param headers the headers to be written to the response * @param status the selected response status @@ -319,7 +380,10 @@ protected ResponseEntity handleNoHandlerFoundException( */ @Override protected ResponseEntity handleAsyncRequestTimeoutException( - AsyncRequestTimeoutException ex, HttpHeaders headers, HttpStatus status, WebRequest webRequest) { + AsyncRequestTimeoutException ex, + HttpHeaders headers, + HttpStatus status, + WebRequest webRequest) { if (webRequest instanceof ServletWebRequest) { ServletWebRequest servletRequest = (ServletWebRequest) webRequest; @@ -327,7 +391,8 @@ protected ResponseEntity handleAsyncRequestTimeoutException( HttpServletResponse response = servletRequest.getNativeResponse(HttpServletResponse.class); if (response.isCommitted()) { if (logger.isErrorEnabled()) { - logger.error("Async timeout for " + request.getMethod() + " [" + request.getRequestURI() + "]"); + logger.error( + "Async timeout for " + request.getMethod() + " [" + request.getRequestURI() + "]"); } return null; } diff --git a/src/main/java/org/gnuhpc/bigdata/exception/KafkaErrorCode.java b/src/main/java/org/gnuhpc/bigdata/exception/KafkaErrorCode.java index 91e16aa..c9fde96 100644 --- a/src/main/java/org/gnuhpc/bigdata/exception/KafkaErrorCode.java +++ b/src/main/java/org/gnuhpc/bigdata/exception/KafkaErrorCode.java @@ -1,30 +1,30 @@ package org.gnuhpc.bigdata.exception; public enum KafkaErrorCode { - NO_ERROR, - OFFSET_OUT_OF_RANGE, - INVALID_MESSAGE, - UNKNOWN_TOPIC_OR_PARTITION, - INVALID_FETCH_SIZE, - LEADER_NOT_AVAILABLE, - NOT_LEADER_FOR_PARTITION, - REQUEST_TIMED_OUT, - BROKER_NOT_AVAILABLE, - REPLICA_NOT_AVAILABLE, - MESSAGE_SIZE_TOO_LARGE, - STALE_CONTROLLER_EPOCH, - OFFSET_METADATA_TOO_LARGE, - OFFSETS_LOAD_IN_PROGRESS, - CONSUMER_COORDINATOR_NOT_AVAILABLE, - NOT_COORDINATOR_FOR_CONSUMER, - SERVICE_DOWN, - UNKNOWN; + NO_ERROR, + OFFSET_OUT_OF_RANGE, + INVALID_MESSAGE, + UNKNOWN_TOPIC_OR_PARTITION, + INVALID_FETCH_SIZE, + LEADER_NOT_AVAILABLE, + NOT_LEADER_FOR_PARTITION, + REQUEST_TIMED_OUT, + BROKER_NOT_AVAILABLE, + REPLICA_NOT_AVAILABLE, + MESSAGE_SIZE_TOO_LARGE, + STALE_CONTROLLER_EPOCH, + OFFSET_METADATA_TOO_LARGE, + OFFSETS_LOAD_IN_PROGRESS, + CONSUMER_COORDINATOR_NOT_AVAILABLE, + NOT_COORDINATOR_FOR_CONSUMER, + SERVICE_DOWN, + UNKNOWN; - public static KafkaErrorCode getError(int errorCode) { - if (errorCode < 0 || errorCode >= UNKNOWN.ordinal()) { - return UNKNOWN; - } else { - return values()[errorCode]; - } + public static KafkaErrorCode getError(int errorCode) { + if (errorCode < 0 || errorCode >= UNKNOWN.ordinal()) { + return UNKNOWN; + } else { + return values()[errorCode]; } -} \ No newline at end of file + } +} diff --git a/src/main/java/org/gnuhpc/bigdata/exception/KafkaExceptionHandler.java b/src/main/java/org/gnuhpc/bigdata/exception/KafkaExceptionHandler.java index 2e1972f..4f1b784 100644 --- a/src/main/java/org/gnuhpc/bigdata/exception/KafkaExceptionHandler.java +++ b/src/main/java/org/gnuhpc/bigdata/exception/KafkaExceptionHandler.java @@ -2,25 +2,22 @@ import lombok.extern.log4j.Log4j; import org.apache.kafka.common.errors.ApiException; -import org.springframework.boot.context.config.ResourceNotFoundException; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; -import javax.validation.ConstraintViolation; -import javax.validation.ConstraintViolationException; -import java.util.Set; - @Log4j @RestControllerAdvice public class KafkaExceptionHandler { - @ExceptionHandler(ApiException.class) - public RestErrorResponse kafkaApiException(ApiException ex) { - RestErrorResponse.Builder responseBuilder = new RestErrorResponse.Builder(); - return responseBuilder.setStatus(HttpStatus.INTERNAL_SERVER_ERROR) - .setCode(KafkaErrorCode.UNKNOWN.ordinal()) - .setMessage("Api Exception happened!") - .setDeveloperMessage(ex.getMessage()) - .build(); - } + + @ExceptionHandler(ApiException.class) + public RestErrorResponse kafkaApiException(ApiException ex) { + RestErrorResponse.Builder responseBuilder = new RestErrorResponse.Builder(); + return responseBuilder + .setStatus(HttpStatus.INTERNAL_SERVER_ERROR) + .setCode(KafkaErrorCode.UNKNOWN.ordinal()) + .setMessage("Api Exception happened!") + .setDeveloperMessage(ex.getMessage()) + .build(); + } } diff --git a/src/main/java/org/gnuhpc/bigdata/exception/RestErrorResponse.java b/src/main/java/org/gnuhpc/bigdata/exception/RestErrorResponse.java index 72e35bf..83280ad 100644 --- a/src/main/java/org/gnuhpc/bigdata/exception/RestErrorResponse.java +++ b/src/main/java/org/gnuhpc/bigdata/exception/RestErrorResponse.java @@ -1,7 +1,11 @@ package org.gnuhpc.bigdata.exception; - import com.fasterxml.jackson.annotation.JsonFormat; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import javax.validation.ConstraintViolation; import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; @@ -11,208 +15,209 @@ import org.springframework.validation.FieldError; import org.springframework.validation.ObjectError; -import javax.validation.ConstraintViolation; -import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; - @Data public class RestErrorResponse { + private HttpStatus status; + + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss") + private LocalDateTime timestamp; + + private int code; + private String message; + private String developerMessage; + private String moreInfoUrl; + private List subErrorList; + + public RestErrorResponse() { + // this.timestamp = new Date(); + this.timestamp = LocalDateTime.now(); + } + + public RestErrorResponse(HttpStatus status, String message, Throwable ex) { + this(); + this.status = status; + this.code = status.value(); + this.message = message; + this.developerMessage = ex.getLocalizedMessage(); + } + + public RestErrorResponse(HttpStatus status, String message, String moreInfoUrl, Throwable ex) { + this(); + this.status = status; + this.code = status.value(); + this.message = message; + this.developerMessage = ex.getLocalizedMessage(); + this.moreInfoUrl = moreInfoUrl; + } + + public RestErrorResponse( + HttpStatus status, int code, String message, String developerMessage, String moreInfoUrl) { + this(); + if (status == null) { + throw new NullPointerException("HttpStatus argument cannot be null."); + } + this.status = status; + this.code = code; + this.message = message; + this.developerMessage = developerMessage; + this.moreInfoUrl = moreInfoUrl; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o instanceof RestErrorResponse) { + RestErrorResponse re = (RestErrorResponse) o; + return ObjectUtils.nullSafeEquals(getStatus(), re.getStatus()) + && getCode() == re.getCode() + && ObjectUtils.nullSafeEquals(getMessage(), re.getMessage()) + && ObjectUtils.nullSafeEquals(getDeveloperMessage(), re.getDeveloperMessage()) + && ObjectUtils.nullSafeEquals(getMoreInfoUrl(), re.getMoreInfoUrl()); + } + + return false; + } + + @Override + public int hashCode() { + //noinspection ThrowableResultOfMethodCallIgnored + return ObjectUtils.nullSafeHashCode( + new Object[] { + getStatus(), getCode(), getMessage(), getDeveloperMessage(), getMoreInfoUrl() + }); + } + + public String toString() { + //noinspection StringBufferReplaceableByString + return new StringBuilder() + .append(getStatus().value()) + .append(" (") + .append(getStatus().getReasonPhrase()) + .append(" )") + .toString(); + } + + private void addSubError(RestSubError subError) { + if (subErrorList == null) { + subErrorList = new ArrayList<>(); + } + subErrorList.add(subError); + } + + private void addValidationError( + String object, String field, Object rejectedValue, String message) { + addSubError(new RestValidationError(object, field, rejectedValue, message)); + } + + private void addValidationError(String object, String message) { + addSubError(new RestValidationError(object, message)); + } + + private void addValidationError(FieldError fieldError) { + this.addValidationError( + fieldError.getObjectName(), + fieldError.getField(), + fieldError.getRejectedValue(), + fieldError.getDefaultMessage()); + } + + private void addValidationError(ObjectError objectError) { + this.addValidationError(objectError.getObjectName(), objectError.getDefaultMessage()); + } + + void addValidationError(List globalErrors) { + globalErrors.forEach(this::addValidationError); + } + + /** + * Utility method for adding error of ConstraintViolation. Usually when a @Validated validation + * fails. + * + * @param cv the ConstraintViolation + */ + private void addValidationError(ConstraintViolation cv) { + this.addValidationError( + cv.getRootBeanClass().getSimpleName(), + ((PathImpl) cv.getPropertyPath()).getLeafNode().asString(), + cv.getInvalidValue(), + cv.getMessage()); + } + + void addValidationErrors(List fieldErrors) { + fieldErrors.forEach(this::addValidationError); + } + + void addValidationErrors(Set> constraintViolations) { + constraintViolations.forEach(this::addValidationError); + } + + public static class Builder { + private HttpStatus status; - @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss") - private LocalDateTime timestamp; private int code; private String message; private String developerMessage; private String moreInfoUrl; - private List subErrorList; - public RestErrorResponse() { - //this.timestamp = new Date(); - this.timestamp = LocalDateTime.now(); - } + public Builder() {} - public RestErrorResponse(HttpStatus status, String message, Throwable ex) { - this(); - this.status = status; - this.code = status.value(); - this.message = message; - this.developerMessage = ex.getLocalizedMessage(); + public Builder setStatus(int statusCode) { + this.status = HttpStatus.valueOf(statusCode); + return this; } - public RestErrorResponse(HttpStatus status, String message, String moreInfoUrl, Throwable ex) { - this(); - this.status = status; - this.code = status.value(); - this.message = message; - this.developerMessage = ex.getLocalizedMessage(); - this.moreInfoUrl = moreInfoUrl; + public Builder setStatus(HttpStatus status) { + this.status = status; + return this; } - public RestErrorResponse(HttpStatus status, int code, String message, String developerMessage, String moreInfoUrl) { - this(); - if (status == null) { - throw new NullPointerException("HttpStatus argument cannot be null."); - } - this.status = status; - this.code = code; - this.message = message; - this.developerMessage = developerMessage; - this.moreInfoUrl = moreInfoUrl; + public Builder setCode(int code) { + this.code = code; + return this; } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o instanceof RestErrorResponse) { - RestErrorResponse re = (RestErrorResponse) o; - return ObjectUtils.nullSafeEquals(getStatus(), re.getStatus()) && - getCode() == re.getCode() && - ObjectUtils.nullSafeEquals(getMessage(), re.getMessage()) && - ObjectUtils.nullSafeEquals(getDeveloperMessage(), re.getDeveloperMessage()) && - ObjectUtils.nullSafeEquals(getMoreInfoUrl(), re.getMoreInfoUrl()); - } - - return false; + public Builder setMessage(String message) { + this.message = message; + return this; } - @Override - public int hashCode() { - //noinspection ThrowableResultOfMethodCallIgnored - return ObjectUtils.nullSafeHashCode(new Object[]{ - getStatus(), getCode(), getMessage(), getDeveloperMessage(), getMoreInfoUrl() - }); + public Builder setDeveloperMessage(String developerMessage) { + this.developerMessage = developerMessage; + return this; } - public String toString() { - //noinspection StringBufferReplaceableByString - return new StringBuilder().append(getStatus().value()) - .append(" (").append(getStatus().getReasonPhrase()).append(" )") - .toString(); + public Builder setMoreInfoUrl(String moreInfoUrl) { + this.moreInfoUrl = moreInfoUrl; + return this; } - private void addSubError(RestSubError subError) { - if (subErrorList == null) { - subErrorList = new ArrayList<>(); - } - subErrorList.add(subError); + public RestErrorResponse build() { + if (this.status == null) { + this.status = HttpStatus.INTERNAL_SERVER_ERROR; + } + return new RestErrorResponse( + this.status, this.code, this.message, this.developerMessage, this.moreInfoUrl); } + } - private void addValidationError(String object, String field, Object rejectedValue, String message) { - addSubError(new RestValidationError(object, field, rejectedValue, message)); - } + abstract class RestSubError {} - private void addValidationError(String object, String message) { - addSubError(new RestValidationError(object, message)); - } + @Data + @EqualsAndHashCode(callSuper = false) + @AllArgsConstructor + class RestValidationError extends RestSubError { - private void addValidationError(FieldError fieldError) { - this.addValidationError( - fieldError.getObjectName(), - fieldError.getField(), - fieldError.getRejectedValue(), - fieldError.getDefaultMessage()); - } - - void addValidationErrors(List fieldErrors) { - fieldErrors.forEach(this::addValidationError); - } - - private void addValidationError(ObjectError objectError) { - this.addValidationError( - objectError.getObjectName(), - objectError.getDefaultMessage()); - } - - void addValidationError(List globalErrors) { - globalErrors.forEach(this::addValidationError); - } - - /** - * Utility method for adding error of ConstraintViolation. Usually when a @Validated validation fails. - * @param cv the ConstraintViolation - */ - private void addValidationError(ConstraintViolation cv) { - this.addValidationError( - cv.getRootBeanClass().getSimpleName(), - ((PathImpl) cv.getPropertyPath()).getLeafNode().asString(), - cv.getInvalidValue(), - cv.getMessage()); - } - - void addValidationErrors(Set> constraintViolations) { - constraintViolations.forEach(this::addValidationError); - } - - public static class Builder { - private HttpStatus status; - private int code; - private String message; - private String developerMessage; - private String moreInfoUrl; - - public Builder() { - } - - public Builder setStatus(int statusCode) { - this.status = HttpStatus.valueOf(statusCode); - return this; - } - - public Builder setStatus(HttpStatus status) { - this.status = status; - return this; - } - - public Builder setCode(int code) { - this.code = code; - return this; - } - - public Builder setMessage(String message) { - this.message = message; - return this; - } - - public Builder setDeveloperMessage(String developerMessage) { - this.developerMessage = developerMessage; - return this; - } - - public Builder setMoreInfoUrl(String moreInfoUrl) { - this.moreInfoUrl = moreInfoUrl; - return this; - } - - - public RestErrorResponse build() { - if (this.status == null) { - this.status = HttpStatus.INTERNAL_SERVER_ERROR; - } - return new RestErrorResponse(this.status, this.code, this.message, this.developerMessage, this.moreInfoUrl); - } - } - - abstract class RestSubError { - - } + private String object; + private String field; + private Object rejectedValue; + private String message; - @Data - @EqualsAndHashCode(callSuper = false) - @AllArgsConstructor - class RestValidationError extends RestSubError { - private String object; - private String field; - private Object rejectedValue; - private String message; - - RestValidationError(String object, String message) { - this.object = object; - this.message = message; - } + RestValidationError(String object, String message) { + this.object = object; + this.message = message; } + } } diff --git a/src/main/java/org/gnuhpc/bigdata/exception/ServiceNotAvailableException.java b/src/main/java/org/gnuhpc/bigdata/exception/ServiceNotAvailableException.java index 6da0245..b992a16 100644 --- a/src/main/java/org/gnuhpc/bigdata/exception/ServiceNotAvailableException.java +++ b/src/main/java/org/gnuhpc/bigdata/exception/ServiceNotAvailableException.java @@ -5,21 +5,26 @@ @Data @EqualsAndHashCode -public class ServiceNotAvailableException extends RuntimeException{ - private String serviceType; - private String serviceState; +public class ServiceNotAvailableException extends RuntimeException { - public ServiceNotAvailableException(String serviceType, String serviceState, String message){ - super(message); - this.serviceType = serviceType; - this.serviceState = serviceState; - } + private String serviceType; + private String serviceState; - @Override - public String toString() { - return "ServiceNotAvailableException{" + - "serviceType='" + serviceType + '\'' + - ", serviceState='" + serviceState + '\'' + - '}'; - } + public ServiceNotAvailableException(String serviceType, String serviceState, String message) { + super(message); + this.serviceType = serviceType; + this.serviceState = serviceState; + } + + @Override + public String toString() { + return "ServiceNotAvailableException{" + + "serviceType='" + + serviceType + + '\'' + + ", serviceState='" + + serviceState + + '\'' + + '}'; + } } diff --git a/src/main/java/org/gnuhpc/bigdata/model/AddPartition.java b/src/main/java/org/gnuhpc/bigdata/model/AddPartition.java index 352a689..a46960b 100644 --- a/src/main/java/org/gnuhpc/bigdata/model/AddPartition.java +++ b/src/main/java/org/gnuhpc/bigdata/model/AddPartition.java @@ -5,15 +5,14 @@ import lombok.ToString; import lombok.extern.log4j.Log4j; -/** - * Created by gnuhpc on 2017/7/23. - */ +/** Created by gnuhpc on 2017/7/23. */ @Getter @Setter @Log4j @ToString public class AddPartition { - String topic; - int numPartitionsAdded; - String replicaAssignment; + + String topic; + int numPartitionsAdded; + String replicaAssignment; } diff --git a/src/main/java/org/gnuhpc/bigdata/model/BrokerInfo.java b/src/main/java/org/gnuhpc/bigdata/model/BrokerInfo.java index d6b48bf..5373350 100644 --- a/src/main/java/org/gnuhpc/bigdata/model/BrokerInfo.java +++ b/src/main/java/org/gnuhpc/bigdata/model/BrokerInfo.java @@ -2,6 +2,8 @@ import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.google.gson.annotations.SerializedName; +import java.util.List; +import java.util.Map; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.Setter; @@ -9,29 +11,29 @@ import org.gnuhpc.bigdata.serializer.JsonJodaDateTimeSerializer; import org.joda.time.DateTime; -import java.util.List; -import java.util.Map; - @Getter @Setter @AllArgsConstructor @ToString -public class BrokerInfo -{ - @SerializedName("listener_security_protocol_map") - private Map securityProtocol; - @SerializedName("endpoints") - private List endPoints; - @SerializedName("jmx_port") - private int jmxPort; - private String host; - - @JsonSerialize(using = JsonJodaDateTimeSerializer.class) - @SerializedName("timestamp") - private DateTime startTime; - - private int port; - private int version; - private String rack = ""; - private int id= -1; +public class BrokerInfo { + + @SerializedName("listener_security_protocol_map") + private Map securityProtocol; + + @SerializedName("endpoints") + private List endPoints; + + @SerializedName("jmx_port") + private int jmxPort; + + private String host; + + @JsonSerialize(using = JsonJodaDateTimeSerializer.class) + @SerializedName("timestamp") + private DateTime startTime; + + private int port; + private int version; + private String rack = ""; + private int id = -1; } diff --git a/src/main/java/org/gnuhpc/bigdata/model/ConsumerGroupDesc.java b/src/main/java/org/gnuhpc/bigdata/model/ConsumerGroupDesc.java index a5b62c2..0b4158f 100644 --- a/src/main/java/org/gnuhpc/bigdata/model/ConsumerGroupDesc.java +++ b/src/main/java/org/gnuhpc/bigdata/model/ConsumerGroupDesc.java @@ -11,9 +11,7 @@ import org.gnuhpc.bigdata.constant.ConsumerGroupState; import org.gnuhpc.bigdata.constant.ConsumerType; -/** - * Created by gnuhpc on 2017/7/27. - */ +/** Created by gnuhpc on 2017/7/27. */ @Setter @Getter @Log4j @@ -21,32 +19,35 @@ @EqualsAndHashCode @Builder(builderClassName = "ConsumerGroupDescBuilder") public class ConsumerGroupDesc implements Comparable { - private String groupName; - private ConsumerGroupState state; - private String assignmentStrategy; - private Node coordinator; - private String topic; - private int partitionId; - private long currentOffset; - private long logEndOffset; - @Setter(AccessLevel.NONE) - private long lag; - private String consumerId; - private String host="-"; - private ConsumerType type; - @Override - public int compareTo(ConsumerGroupDesc o) { - if (this.topic.equals(o.topic)) { - if (this.partitionId == o.partitionId) { - return 0; - } else if (this.partitionId < o.partitionId) { - return -1; - } else { - return 1; - } - } else { - return this.topic.compareTo(this.topic); - } + private String groupName; + private ConsumerGroupState state; + private String assignmentStrategy; + private Node coordinator; + private String topic; + private int partitionId; + private long currentOffset; + private long logEndOffset; + + @Setter(AccessLevel.NONE) + private long lag; + + private String consumerId; + private String host = "-"; + private ConsumerType type; + + @Override + public int compareTo(ConsumerGroupDesc o) { + if (this.topic.equals(o.topic)) { + if (this.partitionId == o.partitionId) { + return 0; + } else if (this.partitionId < o.partitionId) { + return -1; + } else { + return 1; + } + } else { + return this.topic.compareTo(o.topic); } + } } diff --git a/src/main/java/org/gnuhpc/bigdata/model/ConsumerGroupDescFactory.java b/src/main/java/org/gnuhpc/bigdata/model/ConsumerGroupDescFactory.java index 9f97bb3..7d729e5 100644 --- a/src/main/java/org/gnuhpc/bigdata/model/ConsumerGroupDescFactory.java +++ b/src/main/java/org/gnuhpc/bigdata/model/ConsumerGroupDescFactory.java @@ -1,5 +1,6 @@ package org.gnuhpc.bigdata.model; +import java.util.Map; import kafka.admin.AdminClient; import kafka.common.OffsetAndMetadata; import kafka.coordinator.group.GroupTopicPartition; @@ -7,106 +8,115 @@ import org.apache.kafka.clients.consumer.KafkaConsumer; import org.apache.kafka.common.TopicPartition; import org.gnuhpc.bigdata.constant.ConsumerGroupState; -import org.gnuhpc.bigdata.constant.ConsumerState; import org.gnuhpc.bigdata.constant.ConsumerType; import org.gnuhpc.bigdata.utils.KafkaUtils; -import java.util.Arrays; -import java.util.Map; -import java.util.stream.Collectors; - @AllArgsConstructor public class ConsumerGroupDescFactory { - private KafkaUtils kafkaUtils; - - public ConsumerGroupDesc makeOldConsumerGroupDesc( - Map.Entry op, - Map fetchOffSetFromZKResultList, - String topic, String consumerGroup, TopicMeta topicMeta) { - - ConsumerGroupDesc.ConsumerGroupDescBuilder cgdBuilder = ConsumerGroupDesc.builder() - .groupName(consumerGroup) - .topic(topic) - .partitionId(op.getKey()) - .currentOffset(fetchOffSetFromZKResultList.get(op.getKey())) - .logEndOffset( - topicMeta.getTopicPartitionInfos().stream() - .filter(tpi -> tpi.getTopicPartitionInfo().partition() == op.getKey()).findFirst().get().getEndOffset()); - - - if (op.getValue().equals("none")) { - cgdBuilder.consumerId("-"); - cgdBuilder.host("-"); - cgdBuilder.state(ConsumerGroupState.EMPTY); - } else { - cgdBuilder.consumerId(op.getValue()); - cgdBuilder.host(op.getValue().replace(consumerGroup + "_", "")); - cgdBuilder.state(ConsumerGroupState.STABLE); - } - cgdBuilder.type(ConsumerType.OLD); - return cgdBuilder.build(); - } - public ConsumerGroupDesc makeNewRunningConsumerGroupDesc( - TopicPartition tp, - String consumerGroup, - Map partitionEndOffsetMap, - AdminClient.ConsumerSummary cs) { - KafkaConsumer consumer = kafkaUtils.createNewConsumer(consumerGroup); - ConsumerGroupDesc.ConsumerGroupDescBuilder cgdBuilder = ConsumerGroupDesc.builder() - .groupName(consumerGroup) - .topic(tp.topic()) - .partitionId(tp.partition()) - .consumerId(cs.clientId()) - .host(cs.host()) - .state(ConsumerGroupState.STABLE) - .type(ConsumerType.NEW); - - long currentOffset = -1L; - - org.apache.kafka.clients.consumer.OffsetAndMetadata offset = consumer.committed(new TopicPartition(tp.topic(), tp.partition())); - if (offset != null) { - currentOffset = offset.offset(); - } - cgdBuilder.currentOffset(currentOffset); - - Long endOffset = partitionEndOffsetMap.get(tp.partition()); - if (endOffset == null) { //if endOffset is null ,the partition of this topic has no leader replication - cgdBuilder.logEndOffset(-1l); - } else { - cgdBuilder.logEndOffset(endOffset); - } - consumer.close(); - - return cgdBuilder.build(); + private KafkaUtils kafkaUtils; + + public ConsumerGroupDesc makeOldConsumerGroupDesc( + Map.Entry op, + Map fetchOffSetFromZkResultList, + String topic, + String consumerGroup, + TopicMeta topicMeta) { + + ConsumerGroupDesc.ConsumerGroupDescBuilder cgdBuilder = + ConsumerGroupDesc.builder() + .groupName(consumerGroup) + .topic(topic) + .partitionId(op.getKey()) + .currentOffset(fetchOffSetFromZkResultList.get(op.getKey())) + .logEndOffset( + topicMeta + .getTopicPartitionInfos() + .stream() + .filter(tpi -> tpi.getTopicPartitionInfo().partition() == op.getKey()) + .findFirst() + .get() + .getEndOffset()); + + if (op.getValue().equals("none")) { + cgdBuilder.consumerId("-"); + cgdBuilder.host("-"); + cgdBuilder.state(ConsumerGroupState.EMPTY); + } else { + cgdBuilder.consumerId(op.getValue()); + cgdBuilder.host(op.getValue().replace(consumerGroup + "_", "")); + cgdBuilder.state(ConsumerGroupState.STABLE); } - - public ConsumerGroupDesc makeNewPendingConsumerGroupDesc( - String consumerGroup, - Map partitionEndOffsetMap, - Map.Entry topicStorage, - String topic) { - Long partitionCurrentOffset = (topicStorage.getValue() == null) ? -1l: topicStorage.getValue().offset(); - - int partitionId = topicStorage.getKey().topicPartition().partition(); - ConsumerGroupDesc.ConsumerGroupDescBuilder cgdBuilder = ConsumerGroupDesc.builder() - .groupName(consumerGroup) - .topic(topic) - .consumerId("-") - .partitionId(partitionId) - .currentOffset(partitionCurrentOffset) - .host("-") - .state(ConsumerGroupState.EMPTY) - .type(ConsumerType.NEW); - - Long endOffset = partitionEndOffsetMap.get(partitionId); - - if (endOffset == null) { //if endOffset is null ,the partition of this topic has no leader replication - cgdBuilder.logEndOffset(-1l); - } else { - cgdBuilder.logEndOffset(endOffset); - } - - return cgdBuilder.build(); + cgdBuilder.type(ConsumerType.OLD); + return cgdBuilder.build(); + } + + public ConsumerGroupDesc makeNewRunningConsumerGroupDesc( + TopicPartition tp, + String consumerGroup, + Map partitionEndOffsetMap, + AdminClient.ConsumerSummary cs) { + KafkaConsumer consumer = kafkaUtils.createNewConsumer(consumerGroup); + ConsumerGroupDesc.ConsumerGroupDescBuilder cgdBuilder = + ConsumerGroupDesc.builder() + .groupName(consumerGroup) + .topic(tp.topic()) + .partitionId(tp.partition()) + .consumerId(cs.clientId()) + .host(cs.host()) + .state(ConsumerGroupState.STABLE) + .type(ConsumerType.NEW); + + long currentOffset = -1L; + + org.apache.kafka.clients.consumer.OffsetAndMetadata offset = + consumer.committed(new TopicPartition(tp.topic(), tp.partition())); + if (offset != null) { + currentOffset = offset.offset(); + } + cgdBuilder.currentOffset(currentOffset); + + Long endOffset = partitionEndOffsetMap.get(tp.partition()); + if (endOffset + == null) { // if endOffset is null ,the partition of this topic has no leader replication + cgdBuilder.logEndOffset(-1L); + } else { + cgdBuilder.logEndOffset(endOffset); } + consumer.close(); + + return cgdBuilder.build(); + } + + public ConsumerGroupDesc makeNewPendingConsumerGroupDesc( + String consumerGroup, + Map partitionEndOffsetMap, + Map.Entry topicStorage, + String topic) { + Long partitionCurrentOffset = + (topicStorage.getValue() == null) ? -1L : topicStorage.getValue().offset(); + + int partitionId = topicStorage.getKey().topicPartition().partition(); + ConsumerGroupDesc.ConsumerGroupDescBuilder cgdBuilder = + ConsumerGroupDesc.builder() + .groupName(consumerGroup) + .topic(topic) + .consumerId("-") + .partitionId(partitionId) + .currentOffset(partitionCurrentOffset) + .host("-") + .state(ConsumerGroupState.EMPTY) + .type(ConsumerType.NEW); + + Long endOffset = partitionEndOffsetMap.get(partitionId); + + if (endOffset + == null) { // if endOffset is null ,the partition of this topic has no leader replication + cgdBuilder.logEndOffset(-1L); + } else { + cgdBuilder.logEndOffset(endOffset); + } + + return cgdBuilder.build(); + } } diff --git a/src/main/java/org/gnuhpc/bigdata/model/ConsumerGroupMeta.java b/src/main/java/org/gnuhpc/bigdata/model/ConsumerGroupMeta.java index e117aeb..d6349f7 100644 --- a/src/main/java/org/gnuhpc/bigdata/model/ConsumerGroupMeta.java +++ b/src/main/java/org/gnuhpc/bigdata/model/ConsumerGroupMeta.java @@ -1,7 +1,6 @@ package org.gnuhpc.bigdata.model; import java.util.List; -import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import org.apache.kafka.common.Node; @@ -10,6 +9,7 @@ @Data @Builder public class ConsumerGroupMeta { + private String groupId; private ConsumerGroupState state; private String assignmentStrategy; diff --git a/src/main/java/org/gnuhpc/bigdata/model/CustomConfigEntry.java b/src/main/java/org/gnuhpc/bigdata/model/CustomConfigEntry.java index d855c1f..fc5e833 100644 --- a/src/main/java/org/gnuhpc/bigdata/model/CustomConfigEntry.java +++ b/src/main/java/org/gnuhpc/bigdata/model/CustomConfigEntry.java @@ -7,6 +7,7 @@ @Data @AllArgsConstructor public class CustomConfigEntry { + private String name; private String value; private boolean isSensitive; diff --git a/src/main/java/org/gnuhpc/bigdata/model/CustomTopicPartitionInfo.java b/src/main/java/org/gnuhpc/bigdata/model/CustomTopicPartitionInfo.java index 1c51250..d4e1d8e 100644 --- a/src/main/java/org/gnuhpc/bigdata/model/CustomTopicPartitionInfo.java +++ b/src/main/java/org/gnuhpc/bigdata/model/CustomTopicPartitionInfo.java @@ -1,47 +1,58 @@ package org.gnuhpc.bigdata.model; -import com.fasterxml.jackson.annotation.JsonAutoDetect; -import lombok.*; +import java.io.Serializable; +import lombok.AccessLevel; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; import org.apache.commons.collections4.CollectionUtils; import org.apache.kafka.common.TopicPartitionInfo; -import java.io.Serializable; - @Getter @Setter @ToString @EqualsAndHashCode -public class CustomTopicPartitionInfo implements Comparable, Serializable -{ - private TopicPartitionInfo topicPartitionInfo; - private boolean in_sync; - private long startOffset; - private long endOffset; - @Setter(AccessLevel.NONE) - private long messageAvailable; - - public void setIn_sync(){ - if(topicPartitionInfo.isr()!=null && topicPartitionInfo.replicas() !=null && - topicPartitionInfo.isr().size() == topicPartitionInfo.replicas().size()){ - in_sync = CollectionUtils.isEqualCollection(topicPartitionInfo.isr(), topicPartitionInfo.replicas()); - } - else{ - in_sync = false; - } - } +public class CustomTopicPartitionInfo + implements Comparable, Serializable { - public void setMessageAvailable() { - this.messageAvailable = this.endOffset - this.startOffset; - } + private TopicPartitionInfo topicPartitionInfo; + private boolean in_sync; + private long startOffset; + private long endOffset; + + @Setter(AccessLevel.NONE) + private long messageAvailable; - @Override - public int compareTo(CustomTopicPartitionInfo topicPartitionInfo) { - if (this.topicPartitionInfo.partition() < topicPartitionInfo.topicPartitionInfo.partition()) return -1; - else if (this.topicPartitionInfo.partition() == topicPartitionInfo.topicPartitionInfo.partition()) return 0; - else return 1; + public void setIn_sync() { + if (topicPartitionInfo.isr() != null + && topicPartitionInfo.replicas() != null + && topicPartitionInfo.isr().size() == topicPartitionInfo.replicas().size()) { + in_sync = + CollectionUtils.isEqualCollection( + topicPartitionInfo.isr(), topicPartitionInfo.replicas()); + } else { + in_sync = false; } + } - public TopicPartitionInfo getTopicPartitionInfo() { - return this.topicPartitionInfo; + public void setMessageAvailable() { + this.messageAvailable = this.endOffset - this.startOffset; + } + + @Override + public int compareTo(CustomTopicPartitionInfo topicPartitionInfo) { + if (this.topicPartitionInfo.partition() < topicPartitionInfo.topicPartitionInfo.partition()) { + return -1; + } else if (this.topicPartitionInfo.partition() + == topicPartitionInfo.topicPartitionInfo.partition()) { + return 0; + } else { + return 1; } + } + + public TopicPartitionInfo getTopicPartitionInfo() { + return this.topicPartitionInfo; + } } diff --git a/src/main/java/org/gnuhpc/bigdata/model/GeneralResponse.java b/src/main/java/org/gnuhpc/bigdata/model/GeneralResponse.java index e86606c..b04119d 100644 --- a/src/main/java/org/gnuhpc/bigdata/model/GeneralResponse.java +++ b/src/main/java/org/gnuhpc/bigdata/model/GeneralResponse.java @@ -11,7 +11,8 @@ @AllArgsConstructor @Builder public class GeneralResponse { - private GeneralResponseState state; - private String msg; - private Object data; + + private GeneralResponseState state; + private String msg; + private Object data; } diff --git a/src/main/java/org/gnuhpc/bigdata/model/HealthCheckResult.java b/src/main/java/org/gnuhpc/bigdata/model/HealthCheckResult.java index 910f7bd..77a0563 100644 --- a/src/main/java/org/gnuhpc/bigdata/model/HealthCheckResult.java +++ b/src/main/java/org/gnuhpc/bigdata/model/HealthCheckResult.java @@ -1,16 +1,17 @@ package org.gnuhpc.bigdata.model; import com.fasterxml.jackson.annotation.JsonFormat; +import java.time.LocalDateTime; import lombok.Getter; import lombok.Setter; -import java.time.LocalDateTime; - @Getter @Setter public class HealthCheckResult { + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime timestamp; + public String status; public String msg; diff --git a/src/main/java/org/gnuhpc/bigdata/model/JMXAttribute.java b/src/main/java/org/gnuhpc/bigdata/model/JMXAttribute.java index 3419edc..084358a 100644 --- a/src/main/java/org/gnuhpc/bigdata/model/JMXAttribute.java +++ b/src/main/java/org/gnuhpc/bigdata/model/JMXAttribute.java @@ -1,19 +1,31 @@ package org.gnuhpc.bigdata.model; -import lombok.Getter; -import lombok.Setter; - -import javax.management.*; import java.io.IOException; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.regex.Matcher; import java.util.regex.Pattern; +import javax.management.AttributeNotFoundException; +import javax.management.InstanceNotFoundException; +import javax.management.MBeanAttributeInfo; +import javax.management.MBeanException; +import javax.management.MBeanServerConnection; +import javax.management.ObjectName; +import javax.management.ReflectionException; +import lombok.Getter; +import lombok.Setter; @Getter @Setter public abstract class JMXAttribute { + private MBeanAttributeInfo attribute; private ObjectName beanName; private MBeanServerConnection connection; @@ -22,9 +34,18 @@ public abstract class JMXAttribute { private String domain; private HashMap beanParameters; private JMXConfiguration matchingConf; - private LinkedHashMap> valueConversions = new LinkedHashMap>(); - private static final List EXCLUDED_BEAN_PARAMS = Arrays.asList("domain", "domain_regex", "bean_name", "bean", - "bean_regex", "attribute", "exclude_tags", "tags"); + private LinkedHashMap> valueConversions = + new LinkedHashMap>(); + private static final List EXCLUDED_BEAN_PARAMS = + Arrays.asList( + "domain", + "domain_regex", + "bean_name", + "bean", + "bean_regex", + "attribute", + "exclude_tags", + "tags"); protected static final String METRIC_TYPE = "metric_type"; protected static final String ALIAS = "alias"; private static final String FIRST_CAP_PATTERN = "(.)([A-Z][a-z]+)"; @@ -32,33 +53,40 @@ public abstract class JMXAttribute { private static final String METRIC_REPLACEMENT = "([^a-zA-Z0-9_.]+)|(^[^a-zA-Z]+)"; private static final String DOT_UNDERSCORE = "_*\\._*"; - public JMXAttribute(MBeanAttributeInfo attribute, ObjectName beanName, MBeanServerConnection connection) { + public JMXAttribute( + MBeanAttributeInfo attribute, ObjectName beanName, MBeanServerConnection connection) { this.attribute = attribute; this.attributeName = attribute.getName(); this.beanName = beanName; this.beanStringName = beanName.toString(); this.connection = connection; - // A bean name is formatted like that: org.apache.cassandra.db:type=Caches,keyspace=system,cache=HintsColumnFamilyKeyCache + // A bean name is formatted like that: + // org.apache.cassandra.db:type=Caches,keyspace=system,cache=HintsColumnFamilyKeyCache // i.e. : domain:bean_parameter1,bean_parameter2 - //Note: some beans have a ':' in the name. Example: some.domain:name="some.bean.0.0.0.0:80.some-metric" + // Note: some beans have a ':' in the name. Example: + // some.domain:name="some.bean.0.0.0.0:80.some-metric" int splitPosition = beanStringName.indexOf(':'); String domain = beanStringName.substring(0, splitPosition); - String beanParametersString = beanStringName.substring(splitPosition+1); + String beanParametersString = beanStringName.substring(splitPosition + 1); this.domain = domain; this.matchingConf = null; HashMap beanParametersHash = getBeanParametersHash(beanParametersString); - //LinkedList beanParametersList = getBeanParametersList(instanceName, beanParametersHash, instanceTags); + // LinkedList beanParametersList = getBeanParametersList(instanceName, + // beanParametersHash, instanceTags); this.beanParameters = beanParametersHash; } - - public abstract LinkedList> getMetrics() throws AttributeNotFoundException, InstanceNotFoundException, MBeanException, ReflectionException, IOException; + public abstract LinkedList> getMetrics() + throws AttributeNotFoundException, InstanceNotFoundException, MBeanException, + ReflectionException, IOException; /** - * An abstract function implemented in the inherited classes JMXSimpleAttribute and JMXComplexAttribute + * An abstract function implemented in the inherited classes JMXSimpleAttribute and + * JMXComplexAttribute * - * @param conf Configuration a Configuration object that will be used to check if the JMX Attribute match this configuration + * @param conf Configuration a Configuration object that will be used to check if the JMX + * Attribute match this configuration * @return a boolean that tells if the attribute matches the configuration or not */ public abstract boolean match(JMXConfiguration conf); @@ -68,10 +96,12 @@ public static HashMap getBeanParametersHash(String beanParameter HashMap beanParamsMap = new HashMap(beanParameters.length); for (String param : beanParameters) { String[] paramSplit = param.split("="); + String first = paramSplit[0]; if (paramSplit.length > 1) { - beanParamsMap.put(new String(paramSplit[0]), new String(paramSplit[1])); + String sec = paramSplit[1]; + beanParamsMap.put(first, sec); } else { - beanParamsMap.put(new String(paramSplit[0]), ""); + beanParamsMap.put(first, ""); } } @@ -83,7 +113,7 @@ boolean matchDomain(JMXConfiguration conf) { Pattern includeDomainRegex = conf.getInclude().getDomainRegex(); return (includeDomain == null || includeDomain.equals(this.getDomain())) - && (includeDomainRegex == null || includeDomainRegex.matcher(this.getDomain()).matches()); + && (includeDomainRegex == null || includeDomainRegex.matcher(this.getDomain()).matches()); } boolean matchBean(JMXConfiguration configuration) { @@ -104,7 +134,8 @@ private boolean matchBeanName(JMXConfiguration configuration) { ArrayList beanValues = include.getParameterValues(bean_attr); - if (beanParameters.get(bean_attr) == null || !(beanValues.contains(beanParameters.get(bean_attr)))){ + if (beanParameters.get(bean_attr) == null + || !(beanValues.contains(beanParameters.get(bean_attr)))) { return false; } } @@ -112,7 +143,9 @@ private boolean matchBeanName(JMXConfiguration configuration) { } private boolean matchBeanRegex(JMXFilter filter, boolean matchIfNoRegex) { - if (filter == null) return matchIfNoRegex; + if (filter == null) { + return matchIfNoRegex; + } ArrayList beanRegexes = filter.getBeanRegexes(); if (beanRegexes.isEmpty()) { return matchIfNoRegex; @@ -121,8 +154,8 @@ private boolean matchBeanRegex(JMXFilter filter, boolean matchIfNoRegex) { for (Pattern beanRegex : beanRegexes) { Matcher m = beanRegex.matcher(beanStringName); - if(m.matches()) { - for (int i = 0; i<= m.groupCount(); i++) { + if (m.matches()) { + for (int i = 0; i <= m.groupCount(); i++) { this.beanParameters.put(Integer.toString(i), m.group(i)); } return true; @@ -132,12 +165,14 @@ private boolean matchBeanRegex(JMXFilter filter, boolean matchIfNoRegex) { } boolean excludeMatchDomain(JMXConfiguration conf) { - if (conf.getExclude() == null) return false; + if (conf.getExclude() == null) { + return false; + } String excludeDomain = conf.getExclude().getDomain(); Pattern excludeDomainRegex = conf.getExclude().getDomainRegex(); - return excludeDomain != null && excludeDomain.equals(domain) - || excludeDomainRegex != null && excludeDomainRegex.matcher(domain).matches(); + return excludeDomain != null && excludeDomain.equals(domain) + || excludeDomainRegex != null && excludeDomainRegex.matcher(domain).matches(); } boolean excludeMatchBean(JMXConfiguration configuration) { @@ -146,10 +181,12 @@ boolean excludeMatchBean(JMXConfiguration configuration) { private boolean excludeMatchBeanName(JMXConfiguration conf) { JMXFilter exclude = conf.getExclude(); - if (exclude == null) return false; + if (exclude == null) { + return false; + } ArrayList beanNames = exclude.getBeanNames(); - if(beanNames.contains(beanStringName)){ + if (beanNames.contains(beanStringName)) { return true; } @@ -172,11 +209,13 @@ private boolean excludeMatchBeanName(JMXConfiguration conf) { return false; } - Object getJmxValue() throws AttributeNotFoundException, InstanceNotFoundException, MBeanException, ReflectionException, IOException { + Object getJmxValue() + throws AttributeNotFoundException, InstanceNotFoundException, MBeanException, + ReflectionException, IOException { return this.getConnection().getAttribute(this.getBeanName(), this.getAttribute().getName()); } - public static List getExcludedBeanParams(){ + public static List getExcludedBeanParams() { return EXCLUDED_BEAN_PARAMS; } @@ -186,9 +225,9 @@ public static List getExcludedBeanParams(){ if (value instanceof String) { return Double.parseDouble((String) value); } else if (value instanceof Integer) { - return new Double((Integer) (value)); + return Double.parseDouble(value.toString()); } else if (value instanceof AtomicInteger) { - return new Double(((AtomicInteger) (value)).get()); + return Double.parseDouble(value.toString()); } else if (value instanceof AtomicLong) { Long l = ((AtomicLong) (value)).get(); return l.doubleValue(); @@ -197,13 +236,13 @@ public static List getExcludedBeanParams(){ } else if (value instanceof Boolean) { return ((Boolean) value ? 1.0 : 0.0); } else if (value instanceof Long) { - Long l = new Long((Long) value); + Long l = Long.valueOf(value.toString()); return l.doubleValue(); } else if (value instanceof Number) { return ((Number) value).doubleValue(); } else { try { - return new Double((Double) value); + return Double.parseDouble(value.toString()); } catch (Exception e) { throw new NumberFormatException(); } @@ -225,12 +264,15 @@ Object convertMetricValue(Object metricValue, String field) { @SuppressWarnings("unchecked") HashMap getValueConversions(String field) { - String fullAttributeName =(field!=null)?(getAttribute().getName() + "." + field):(getAttribute().getName()); + String fullAttributeName = + (field != null) ? (getAttribute().getName() + "." + field) : (getAttribute().getName()); if (valueConversions.get(fullAttributeName) == null) { Object includedAttribute = matchingConf.getInclude().getAttribute(); if (includedAttribute instanceof LinkedHashMap) { LinkedHashMap> attribute = - ((LinkedHashMap>>) includedAttribute).get(fullAttributeName); + ((LinkedHashMap>>) + includedAttribute) + .get(fullAttributeName); if (attribute != null) { valueConversions.put(fullAttributeName, attribute.get("values")); @@ -247,37 +289,36 @@ HashMap getValueConversions(String field) { /** * Overload `getAlias` method. * - * Note: used for `JMXSimpleAttribute` only, as `field` is null. + *

Note: used for `JMXSimpleAttribute` only, as `field` is null. */ - protected String getAlias(){ + protected String getAlias() { return getAlias(null); } /** * Get attribute alias. * - * In order, tries to: - * * Use `alias_match` to generate an alias with a regular expression - * * Use `alias` directly - * * Create an generic alias prefixed with user's `metric_prefix` preference or default to `jmx` + *

In order, tries to: * Use `alias_match` to generate an alias with a regular expression * Use + * `alias` directly * Create an generic alias prefixed with user's `metric_prefix` preference or + * default to `jmx` * - * Argument(s): - * * (Optional) `field` - * `Null` for `JMXSimpleAttribute`. + *

Argument(s): * (Optional) `field` `Null` for `JMXSimpleAttribute`. */ protected String getAlias(String field) { String alias = null; JMXFilter include = getMatchingConf().getInclude(); - String fullAttributeName =(field!=null)?(getAttribute().getName() + "." + field):(getAttribute().getName()); + String fullAttributeName = + (field != null) ? (getAttribute().getName() + "." + field) : (getAttribute().getName()); if (include.getAttribute() instanceof LinkedHashMap) { - LinkedHashMap> attribute = (LinkedHashMap>) (include.getAttribute()); + LinkedHashMap> attribute = + (LinkedHashMap>) (include.getAttribute()); alias = getUserAlias(attribute, fullAttributeName); } - //If still null - generate generic alias + // If still null - generate generic alias if (alias == null) { alias = "jmx." + getDomain() + "." + fullAttributeName; } @@ -288,16 +329,11 @@ protected String getAlias(String field) { /** * Retrieve user defined alias. Substitute regular expression named groups. * - * Example: - * ``` - * bean: org.datadog.jmxfetch.test:foo=Bar,qux=Baz - * attribute: - * toto: - * alias: my.metric.$foo.$attribute - * ``` - * returns a metric name `my.metric.bar.toto` + *

Example: ``` bean: org.datadog.jmxfetch.test:foo=Bar,qux=Baz attribute: toto: alias: + * my.metric.$foo.$attribute ``` returns a metric name `my.metric.bar.toto` */ - private String getUserAlias(LinkedHashMap> attribute, String fullAttributeName){ + private String getUserAlias( + LinkedHashMap> attribute, String fullAttributeName) { String alias = attribute.get(fullAttributeName).get(ALIAS); if (alias == null) { return null; @@ -312,7 +348,7 @@ private String getUserAlias(LinkedHashMap> return alias; } - private String replaceByAlias(String alias){ + private String replaceByAlias(String alias) { // Bean parameters for (Map.Entry param : beanParameters.entrySet()) { alias = alias.replace("$" + param.getKey(), param.getValue()); @@ -327,4 +363,4 @@ static String convertMetricName(String metricName) { metricName = metricName.replaceAll(DOT_UNDERSCORE, ".").trim(); return metricName; } -} \ No newline at end of file +} diff --git a/src/main/java/org/gnuhpc/bigdata/model/JMXClient.java b/src/main/java/org/gnuhpc/bigdata/model/JMXClient.java index 2c30e94..8fa00f1 100644 --- a/src/main/java/org/gnuhpc/bigdata/model/JMXClient.java +++ b/src/main/java/org/gnuhpc/bigdata/model/JMXClient.java @@ -1,27 +1,32 @@ package org.gnuhpc.bigdata.model; -import lombok.Getter; -import lombok.Setter; -import lombok.extern.log4j.Log4j; -import org.gnuhpc.bigdata.config.JMXConfig; -import org.gnuhpc.bigdata.exception.CollectorException; +import static com.google.common.base.Preconditions.checkNotNull; -import javax.management.remote.JMXConnector; -import javax.management.remote.JMXConnectorFactory; -import javax.management.remote.JMXServiceURL; import java.io.IOException; import java.net.MalformedURLException; import java.net.SocketTimeoutException; import java.util.HashMap; import java.util.Map; -import java.util.concurrent.*; - -import static com.google.common.base.Preconditions.checkNotNull; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorFactory; +import javax.management.remote.JMXServiceURL; +import lombok.Getter; +import lombok.Setter; +import lombok.extern.log4j.Log4j; +import org.gnuhpc.bigdata.config.JMXConfig; +import org.gnuhpc.bigdata.exception.CollectorException; @Getter @Setter @Log4j public class JMXClient { + private String ip; private String port; private JMXConnector jmxConnector = null; @@ -41,11 +46,14 @@ public JMXClient(String host) { String[] ipAndPort = host.split(":"); this.ip = ipAndPort[0]; this.port = ipAndPort[1]; - this.jmxServiceURL = new StringBuilder().append(JMXConfig.JMX_PROTOCOL) + this.jmxServiceURL = + new StringBuilder() + .append(JMXConfig.JMX_PROTOCOL) .append(this.ip) .append(":") .append(this.port) - .append("/jmxrmi").toString(); + .append("/jmxrmi") + .toString(); } public JMXConnector connect() throws CollectorException { @@ -53,11 +61,17 @@ public JMXConnector connect() throws CollectorException { JMXServiceURL jmxServiceURL = new JMXServiceURL(this.jmxServiceURL); jmxConnector = JMXConnectorFactory.connect(jmxServiceURL, jmxEnv); } catch (MalformedURLException e) { - throw new CollectorException(String.format("%s occurred. URL: %s. Reason: %s", - e.getClass().getCanonicalName(), this.jmxServiceURL, e.getCause()), e); + throw new CollectorException( + String.format( + "%s occurred. URL: %s. Reason: %s", + e.getClass().getCanonicalName(), this.jmxServiceURL, e.getCause()), + e); } catch (IOException e) { - throw new CollectorException(String.format("%s occurred. URL: %s. Reason: %s", - e.getClass().getCanonicalName(), this.jmxServiceURL, e.getCause()), e); + throw new CollectorException( + String.format( + "%s occurred. URL: %s. Reason: %s", + e.getClass().getCanonicalName(), this.jmxServiceURL, e.getCause()), + e); } return jmxConnector; } @@ -72,22 +86,24 @@ public JMXConnector connectWithTimeout() throws IOException, InterruptedExceptio BlockingQueue mailbox = new ArrayBlockingQueue(1); ExecutorService executor = Executors.newSingleThreadExecutor(daemonThreadFactory); - executor.submit(() -> { - try { - JMXConnector connector = JMXConnectorFactory.connect(url, jmxEnv); - if (!mailbox.offer(connector)) { - connector.close(); - } - } catch (Throwable t) { - mailbox.offer(t); - } - }); + executor.submit( + () -> { + try { + JMXConnector connector = JMXConnectorFactory.connect(url, jmxEnv); + if (!mailbox.offer(connector)) { + connector.close(); + } + } catch (Throwable t) { + mailbox.offer(t); + } + }); Object result; try { result = mailbox.poll(JMX_TIMEOUT, TimeUnit.SECONDS); if (result == null) { - if (!mailbox.offer("")) + if (!mailbox.offer("")) { result = mailbox.take(); + } } } catch (InterruptedException e) { throw e; @@ -119,6 +135,7 @@ public void close() throws CollectorException { } private static class DaemonThreadFactory implements ThreadFactory { + public Thread newThread(Runnable r) { Thread t = Executors.defaultThreadFactory().newThread(r); t.setDaemon(true); diff --git a/src/main/java/org/gnuhpc/bigdata/model/JMXComplexAttribute.java b/src/main/java/org/gnuhpc/bigdata/model/JMXComplexAttribute.java index 6107797..3626200 100644 --- a/src/main/java/org/gnuhpc/bigdata/model/JMXComplexAttribute.java +++ b/src/main/java/org/gnuhpc/bigdata/model/JMXComplexAttribute.java @@ -1,22 +1,35 @@ package org.gnuhpc.bigdata.model; -import javax.management.*; -import javax.management.openmbean.CompositeData; import java.io.IOException; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.Map; +import javax.management.AttributeNotFoundException; +import javax.management.InstanceNotFoundException; +import javax.management.MBeanAttributeInfo; +import javax.management.MBeanException; +import javax.management.MBeanServerConnection; +import javax.management.ObjectName; +import javax.management.ReflectionException; +import javax.management.openmbean.CompositeData; public class JMXComplexAttribute extends JMXAttribute { + private HashMap> subAttributeList; - public JMXComplexAttribute(MBeanAttributeInfo attribute, ObjectName beanName, MBeanServerConnection connection) { + public JMXComplexAttribute( + MBeanAttributeInfo attribute, ObjectName beanName, MBeanServerConnection connection) { super(attribute, beanName, connection); this.subAttributeList = new HashMap<>(); } @Override public LinkedList> getMetrics() - throws AttributeNotFoundException, InstanceNotFoundException, - MBeanException, ReflectionException, IOException { + throws AttributeNotFoundException, InstanceNotFoundException, MBeanException, + ReflectionException, IOException { LinkedList> metrics = new LinkedList>(); @@ -41,10 +54,8 @@ public LinkedList> getMetrics() metric.put("value", castToDouble(getValue(subAttribute), subAttribute)); metrics.add(metric); - } return metrics; - } private Object getMetricType(String subAttribute) { @@ -53,7 +64,8 @@ private Object getMetricType(String subAttribute) { JMXFilter include = getMatchingConf().getInclude(); if (include.getAttribute() instanceof LinkedHashMap) { - LinkedHashMap> attribute = (LinkedHashMap>) (include.getAttribute()); + LinkedHashMap> attribute = + (LinkedHashMap>) (include.getAttribute()); metricType = attribute.get(subAttributeName).get(METRIC_TYPE); if (metricType == null) { metricType = attribute.get(subAttributeName).get("type"); @@ -67,8 +79,9 @@ private Object getMetricType(String subAttribute) { return metricType; } - private Object getValue(String subAttribute) throws AttributeNotFoundException, InstanceNotFoundException, - MBeanException, ReflectionException, IOException { + private Object getValue(String subAttribute) + throws AttributeNotFoundException, InstanceNotFoundException, MBeanException, + ReflectionException, IOException { Object value = this.getJmxValue(); String attributeType = getAttribute().getType(); @@ -76,7 +89,8 @@ private Object getValue(String subAttribute) throws AttributeNotFoundException, if ("javax.management.openmbean.CompositeData".equals(attributeType)) { CompositeData data = (CompositeData) value; return data.get(subAttribute); - } else if (("java.util.HashMap".equals(attributeType)) || ("java.util.Map".equals(attributeType))) { + } else if (("java.util.HashMap".equals(attributeType)) + || ("java.util.Map".equals(attributeType))) { Map data = (Map) value; return data.get(subAttribute); } @@ -86,9 +100,9 @@ private Object getValue(String subAttribute) throws AttributeNotFoundException, @Override public boolean match(JMXConfiguration configuration) { if (!matchDomain(configuration) - || !matchBean(configuration) - || excludeMatchDomain(configuration) - || excludeMatchBean(configuration)) { + || !matchBean(configuration) + || excludeMatchDomain(configuration) + || excludeMatchBean(configuration)) { return false; } @@ -108,7 +122,8 @@ private void populateSubAttributeList(Object attributeValue) { for (String key : data.getCompositeType().keySet()) { this.subAttributeList.put(key, new HashMap()); } - } else if (("java.util.HashMap".equals(attributeType)) || ("java.util.Map".equals(attributeType))) { + } else if (("java.util.HashMap".equals(attributeType)) + || ("java.util.Map".equals(attributeType))) { Map data = (Map) attributeValue; for (String key : data.keySet()) { this.subAttributeList.put(key, new HashMap()); @@ -118,7 +133,9 @@ private void populateSubAttributeList(Object attributeValue) { private boolean excludeMatchAttribute(JMXConfiguration configuration) { JMXFilter exclude = configuration.getExclude(); - if (exclude == null) return false; + if (exclude == null) { + return false; + } if (exclude.getAttribute() != null && matchSubAttribute(exclude, getAttributeName(), false)) { return true; } @@ -142,7 +159,8 @@ private boolean matchAttribute(JMXConfiguration configuration) { while (it.hasNext()) { String subAttribute = it.next(); - if (!matchSubAttribute(configuration.getInclude(), getAttributeName() + "." + subAttribute, true)) { + if (!matchSubAttribute( + configuration.getInclude(), getAttributeName() + "." + subAttribute, true)) { it.remove(); } } @@ -150,18 +168,18 @@ private boolean matchAttribute(JMXConfiguration configuration) { return subAttributeList.size() > 0; } - private boolean matchSubAttribute(JMXFilter params, String subAttributeName, boolean matchOnEmpty) { + private boolean matchSubAttribute( + JMXFilter params, String subAttributeName, boolean matchOnEmpty) { if ((params.getAttribute() instanceof LinkedHashMap) - && ((LinkedHashMap) (params.getAttribute())).containsKey(subAttributeName)) { + && ((LinkedHashMap) (params.getAttribute())) + .containsKey(subAttributeName)) { return true; } else if ((params.getAttribute() instanceof ArrayList - && ((ArrayList) (params.getAttribute())).contains(subAttributeName))) { + && ((ArrayList) (params.getAttribute())).contains(subAttributeName))) { return true; } else if (params.getAttribute() == null) { return matchOnEmpty; } return false; - } - } diff --git a/src/main/java/org/gnuhpc/bigdata/model/JMXConfiguration.java b/src/main/java/org/gnuhpc/bigdata/model/JMXConfiguration.java index ee47f14..48528ce 100644 --- a/src/main/java/org/gnuhpc/bigdata/model/JMXConfiguration.java +++ b/src/main/java/org/gnuhpc/bigdata/model/JMXConfiguration.java @@ -1,45 +1,54 @@ package org.gnuhpc.bigdata.model; +import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.Map; +import java.util.Set; import lombok.Getter; import lombok.Setter; -import com.fasterxml.jackson.annotation.JsonCreator; - -import java.util.*; @Getter @Setter public class JMXConfiguration { + private JMXFilter include; private JMXFilter exclude; /** * Access JMXConfiguration elements more easily * - * Also provides helper methods to extract common information among JMXFilters. + *

Also provides helper methods to extract common information among JMXFilters. */ @JsonCreator - public JMXConfiguration(@JsonProperty("include") JMXFilter include, @JsonProperty("exclude") JMXFilter exclude) { + public JMXConfiguration( + @JsonProperty("include") JMXFilter include, @JsonProperty("exclude") JMXFilter exclude) { this.include = include; this.exclude = exclude; } - private Boolean hasInclude(){ + private Boolean hasInclude() { return getInclude() != null; } /** * JMXFilter a configuration list to keep the ones with `include` JMXFilters. * - * @param configurationList the configuration list to JMXFilter - * - * @return a configuration list + * @param configurationList the configuration list to JMXFilter + * @return a configuration list */ - private static LinkedList getIncludeConfigurationList(LinkedList configurationList){ - LinkedList includeConfigList = new LinkedList(configurationList); + private static LinkedList getIncludeConfigurationList( + LinkedList configurationList) { + LinkedList includeConfigList = + new LinkedList(configurationList); Iterator confItr = includeConfigList.iterator(); - while(confItr.hasNext()) { + while (confItr.hasNext()) { JMXConfiguration conf = confItr.next(); if (!conf.hasInclude()) { confItr.remove(); @@ -51,12 +60,13 @@ private static LinkedList getIncludeConfigurationList(LinkedLi /** * Extract `include` JMXFilters from the configuration list and index then by domain name. * - * @param configurationList the configuration list to process - * - * @return JMXFilters by domain name + * @param configurationList the configuration list to process + * @return JMXFilters by domain name */ - private static HashMap> getIncludeJMXFiltersByDomain(LinkedList configurationList){ - HashMap> includeJMXFiltersByDomain = new HashMap>(); + private static HashMap> getIncludeJMXFiltersByDomain( + LinkedList configurationList) { + HashMap> includeJMXFiltersByDomain = + new HashMap>(); for (JMXConfiguration conf : configurationList) { JMXFilter JMXFilter = conf.getInclude(); @@ -70,7 +80,8 @@ private static HashMap> getIncludeJMXFiltersByDoma String[] splitBeanName = beanName.split(":"); String domain = splitBeanName[0]; String rawBeanParameters = splitBeanName[1]; - HashMap beanParametersHash = JMXAttribute.getBeanParametersHash(rawBeanParameters); + HashMap beanParametersHash = + JMXAttribute.getBeanParametersHash(rawBeanParameters); beanParametersHash.put("domain", domain); JMXFilters.add(new JMXFilter(beanParametersHash)); } @@ -78,7 +89,7 @@ private static HashMap> getIncludeJMXFiltersByDoma JMXFilters.add(JMXFilter); } - for (JMXFilter f: JMXFilters) { + for (JMXFilter f : JMXFilters) { // Retrieve the existing JMXFilters for the domain, add the new JMXFilters LinkedList domainJMXFilters; String domainName = f.getDomain(); @@ -99,26 +110,26 @@ private static HashMap> getIncludeJMXFiltersByDoma /** * Extract, among JMXFilters, bean key parameters in common. * - * @param JMXFiltersByDomain JMXFilters by domain name - * - * @return common bean key parameters by domain name + * @param JMXFiltersByDomain JMXFilters by domain name + * @return common bean key parameters by domain name */ - private static HashMap> getCommonBeanKeysByDomain(HashMap> JMXFiltersByDomain){ - HashMap> beanKeysIntersectionByDomain = new HashMap>(); + private static HashMap> getCommonBeanKeysByDomain( + HashMap> JMXFiltersByDomain) { + HashMap> beanKeysIntersectionByDomain = new HashMap>(); for (Map.Entry> JMXFiltersEntry : JMXFiltersByDomain.entrySet()) { String domainName = JMXFiltersEntry.getKey(); - LinkedList mJMXFilters= JMXFiltersEntry.getValue(); + LinkedList mJMXFilters = JMXFiltersEntry.getValue(); // Compute keys intersection Set keysIntersection = new HashSet(mJMXFilters.getFirst().keySet()); - for (JMXFilter f: mJMXFilters) { + for (JMXFilter f : mJMXFilters) { keysIntersection.retainAll(f.keySet()); } // Remove special parameters - for(String param : JMXAttribute.getExcludedBeanParams()){ + for (String param : JMXAttribute.getExcludedBeanParams()) { keysIntersection.remove(param); } beanKeysIntersectionByDomain.put(domainName, keysIntersection); @@ -128,18 +139,22 @@ private static HashMap> getCommonBeanKeysByDomain(HashMapvalues, with the specified bean keys, among the given JMXFilters. + * Build a map of common bean keys->values, with the specified bean keys, among the given + * JMXFilters. * - * @param beanKeysByDomain bean keys by domain name - * @param JMXFiltersByDomain JMXFilters by domain name - * - * @return bean pattern (keys->values) by domain name + * @param beanKeysByDomain bean keys by domain name + * @param JMXFiltersByDomain JMXFilters by domain name + * @return bean pattern (keys->values) by domain name */ - private static HashMap> getCommonScopeByDomain(HashMap> beanKeysByDomain, HashMap> JMXFiltersByDomain){ + private static HashMap> getCommonScopeByDomain( + HashMap> beanKeysByDomain, + HashMap> JMXFiltersByDomain) { // Compute a common scope a among JMXFilters by domain name - HashMap> commonScopeByDomain = new HashMap>(); + HashMap> commonScopeByDomain = + new HashMap>(); - for (Map.Entry> commonParametersByDomainEntry : beanKeysByDomain.entrySet()) { + for (Map.Entry> commonParametersByDomainEntry : + beanKeysByDomain.entrySet()) { String domainName = commonParametersByDomainEntry.getKey(); Set commonParameters = commonParametersByDomainEntry.getValue(); LinkedList JMXFilters = JMXFiltersByDomain.get(domainName); @@ -153,12 +168,12 @@ private static HashMap> getCommonScopeByDo for (JMXFilter f : JMXFilters) { ArrayList parameterValues = f.getParameterValues(parameter); - if (parameterValues.size() != 1 || (commonValue != null && !commonValue.equals(parameterValues.get(0)))) { + if (parameterValues.size() != 1 + || (commonValue != null && !commonValue.equals(parameterValues.get(0)))) { hasCommonValue = false; break; } commonValue = parameterValues.get(0); - } if (hasCommonValue) { commonScope.put(parameter, commonValue); @@ -173,48 +188,57 @@ private static HashMap> getCommonScopeByDo /** * Stringify a bean pattern. * - * @param domain domain name - * @param beanScope map of bean keys-> values - * - * @return string pattern identifying the bean scope + * @param domain domain name + * @param beanScope map of bean keys-> values + * @return string pattern identifying the bean scope */ - private static String beanScopeToString(String domain, LinkedHashMap beanScope){ - String result = ""; + private static String beanScopeToString(String domain, LinkedHashMap beanScope) { + StringBuffer resultBuf = new StringBuffer(); // Domain domain = (domain != null) ? domain : "*"; - result += domain + ":"; + resultBuf.append(domain); + resultBuf.append(":"); // Scope parameters for (Map.Entry beanScopeEntry : beanScope.entrySet()) { String param = beanScopeEntry.getKey(); String value = beanScopeEntry.getValue(); - result += param + "=" + value + ","; + resultBuf.append(param); + resultBuf.append("="); + resultBuf.append(value); + resultBuf.append(","); } - result += "*"; - return result; + resultBuf.append("*"); + + return resultBuf.toString(); } /** * Find, among the JMXConfiguration list, a potential common bean pattern by domain name. * - * @param JMXConfigurationList the JMXConfiguration list to process - * - * @return common bean pattern strings + * @param JMXConfigurationList the JMXConfiguration list to process + * @return common bean pattern strings */ - public static LinkedList getGreatestCommonScopes(LinkedList JMXConfigurationList){ + public static LinkedList getGreatestCommonScopes( + LinkedList JMXConfigurationList) { LinkedList result = new LinkedList(); if (JMXConfigurationList == null || JMXConfigurationList.isEmpty()) { return result; } - LinkedList includeConfigList = getIncludeConfigurationList(JMXConfigurationList); - HashMap> includeJMXFiltersByDomain = getIncludeJMXFiltersByDomain(includeConfigList); - HashMap> parametersIntersectionByDomain = getCommonBeanKeysByDomain(includeJMXFiltersByDomain); - HashMap> commonBeanScopeByDomain = getCommonScopeByDomain(parametersIntersectionByDomain, includeJMXFiltersByDomain); - - for (Map.Entry> beanScopeEntry: commonBeanScopeByDomain.entrySet()) { + LinkedList includeConfigList = + getIncludeConfigurationList(JMXConfigurationList); + HashMap> includeJMXFiltersByDomain = + getIncludeJMXFiltersByDomain(includeConfigList); + HashMap> parametersIntersectionByDomain = + getCommonBeanKeysByDomain(includeJMXFiltersByDomain); + HashMap> commonBeanScopeByDomain = + getCommonScopeByDomain(parametersIntersectionByDomain, includeJMXFiltersByDomain); + + for (Map.Entry> beanScopeEntry : + commonBeanScopeByDomain.entrySet()) { String domain = beanScopeEntry.getKey(); LinkedHashMap beanScope = beanScopeEntry.getValue(); diff --git a/src/main/java/org/gnuhpc/bigdata/model/JMXFilter.java b/src/main/java/org/gnuhpc/bigdata/model/JMXFilter.java index 79c7e25..a80b6cd 100644 --- a/src/main/java/org/gnuhpc/bigdata/model/JMXFilter.java +++ b/src/main/java/org/gnuhpc/bigdata/model/JMXFilter.java @@ -1,28 +1,28 @@ package org.gnuhpc.bigdata.model; import com.fasterxml.jackson.annotation.JsonCreator; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Set; +import java.util.regex.Pattern; import lombok.Getter; import lombok.Setter; -import java.util.*; -import java.util.regex.Pattern; - @Getter @Setter public class JMXFilter { + HashMap filter; Pattern domainRegex; ArrayList beanRegexes = null; - //ArrayList excludeTags = null; - //HashMap additionalTags = null; + // ArrayList excludeTags = null; + // HashMap additionalTags = null; /** - * A simple class to manipulate include/exclude filter elements more easily - * A filter may contain: - * - A domain (key: 'domain') or a domain regex (key: 'domain_regex') - * - Bean names (key: 'bean' or 'bean_name') or bean regexes (key: 'bean_regex') - * - Attributes (key: 'attribute') - * - Additional bean parameters (other keys) + * A simple class to manipulate include/exclude filter elements more easily A filter may contain: + * - A domain (key: 'domain') or a domain regex (key: 'domain_regex') - Bean names (key: 'bean' or + * 'bean_name') or bean regexes (key: 'bean_regex') - Attributes (key: 'attribute') - Additional + * bean parameters (other keys) */ @JsonCreator @SuppressWarnings("unchecked") @@ -44,7 +44,7 @@ public Set keySet() { return filter.keySet(); } - @SuppressWarnings({ "unchecked", "serial" }) + @SuppressWarnings({"unchecked", "serial"}) private static ArrayList toStringArrayList(final Object toCast) { // Return object as an ArrayList wherever it's defined as // list or not @@ -64,18 +64,19 @@ private static ArrayList toStringArrayList(final Object toCast) { return (ArrayList) toCast; } - public ArrayList getBeanNames() { - if (isEmptyBeanName()){ + if (isEmptyBeanName()) { return new ArrayList(); } - final Object beanNames = (filter.get("bean") != null) ? filter.get("bean") : filter.get("bean_name"); + final Object beanNames = + (filter.get("bean") != null) ? filter.get("bean") : filter.get("bean_name"); // Return bean names as an ArrayList wherever it's defined as // list or not // // ### Example // bean: - // - org.apache.cassandra.db:type=Caches,keyspace=system,cache=HintsColumnFamilyKeyCache + // - + // org.apache.cassandra.db:type=Caches,keyspace=system,cache=HintsColumnFamilyKeyCache // - org.datadog.jmxfetch.test:type=type=SimpleTestJavaApp // ### OR // bean: org.datadog.jmxfetch.test:type=type=SimpleTestJavaApp @@ -98,7 +99,7 @@ public ArrayList getBeanRegexes() { // a list or not if (this.beanRegexes == null) { - if (filter.get("bean_regex") == null){ + if (filter.get("bean_regex") == null) { this.beanRegexes = new ArrayList(); } else { final Object beanRegexNames = filter.get("bean_regex"); diff --git a/src/main/java/org/gnuhpc/bigdata/model/JMXMetricData.java b/src/main/java/org/gnuhpc/bigdata/model/JMXMetricData.java index 5ee3876..b41edbc 100644 --- a/src/main/java/org/gnuhpc/bigdata/model/JMXMetricData.java +++ b/src/main/java/org/gnuhpc/bigdata/model/JMXMetricData.java @@ -1,19 +1,21 @@ package org.gnuhpc.bigdata.model; import com.fasterxml.jackson.annotation.JsonFormat; -import lombok.Getter; -import lombok.Setter; - import java.time.LocalDateTime; import java.util.HashMap; import java.util.LinkedList; +import lombok.Getter; +import lombok.Setter; @Getter @Setter public class JMXMetricData { + private String host; + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime timestamp; + private Boolean collected; private LinkedList> metrics; private String msg; diff --git a/src/main/java/org/gnuhpc/bigdata/model/JMXMetricDataV1.java b/src/main/java/org/gnuhpc/bigdata/model/JMXMetricDataV1.java index 8ee573e..7237151 100644 --- a/src/main/java/org/gnuhpc/bigdata/model/JMXMetricDataV1.java +++ b/src/main/java/org/gnuhpc/bigdata/model/JMXMetricDataV1.java @@ -1,18 +1,20 @@ package org.gnuhpc.bigdata.model; +import com.fasterxml.jackson.annotation.JsonFormat; import java.time.LocalDateTime; import java.util.Map; - -import com.fasterxml.jackson.annotation.JsonFormat; import lombok.Getter; import lombok.Setter; @Getter @Setter public class JMXMetricDataV1 { + private String host; + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime timestamp; + private Boolean collected; private Map mbeanInfo; private String msg; diff --git a/src/main/java/org/gnuhpc/bigdata/model/JMXQuery.java b/src/main/java/org/gnuhpc/bigdata/model/JMXQuery.java index bf03ad2..920a6ae 100644 --- a/src/main/java/org/gnuhpc/bigdata/model/JMXQuery.java +++ b/src/main/java/org/gnuhpc/bigdata/model/JMXQuery.java @@ -1,13 +1,12 @@ package org.gnuhpc.bigdata.model; +import java.util.LinkedList; import lombok.Getter; import lombok.Setter; -import java.util.LinkedList; - - @Getter @Setter public class JMXQuery { + private LinkedList filters; } diff --git a/src/main/java/org/gnuhpc/bigdata/model/JMXSimpleAttribute.java b/src/main/java/org/gnuhpc/bigdata/model/JMXSimpleAttribute.java index 0ddd4f4..bc89d6f 100644 --- a/src/main/java/org/gnuhpc/bigdata/model/JMXSimpleAttribute.java +++ b/src/main/java/org/gnuhpc/bigdata/model/JMXSimpleAttribute.java @@ -1,27 +1,35 @@ package org.gnuhpc.bigdata.model; -import lombok.Getter; -import lombok.Setter; - -import javax.management.*; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.LinkedList; +import javax.management.AttributeNotFoundException; +import javax.management.InstanceNotFoundException; +import javax.management.MBeanAttributeInfo; +import javax.management.MBeanException; +import javax.management.MBeanServerConnection; +import javax.management.ObjectName; +import javax.management.ReflectionException; +import lombok.Getter; +import lombok.Setter; @Getter @Setter public class JMXSimpleAttribute extends JMXAttribute { + private String metricType; - public JMXSimpleAttribute(MBeanAttributeInfo attribute, ObjectName beanName, MBeanServerConnection connection) { + public JMXSimpleAttribute( + MBeanAttributeInfo attribute, ObjectName beanName, MBeanServerConnection connection) { super(attribute, beanName, connection); } @Override - public LinkedList> getMetrics() throws AttributeNotFoundException, - InstanceNotFoundException, MBeanException, ReflectionException, IOException { + public LinkedList> getMetrics() + throws AttributeNotFoundException, InstanceNotFoundException, MBeanException, + ReflectionException, IOException { HashMap metric = new HashMap(); metric.put("domain", getBeanName().getDomain()); @@ -29,7 +37,7 @@ public LinkedList> getMetrics() throws AttributeNotFound metric.put("attributeName", getAttributeName()); metric.put("alias", getAlias()); metric.put("value", castToDouble(getValue(), null)); - //metric.put("tags", getTags()); + // metric.put("tags", getTags()); metric.put("metric_type", getMetricType()); LinkedList> metrics = new LinkedList>(); metrics.add(metric); @@ -38,13 +46,11 @@ public LinkedList> getMetrics() throws AttributeNotFound public boolean match(JMXConfiguration configuration) { return matchDomain(configuration) - && matchBean(configuration) - && matchAttribute(configuration) - && !( - excludeMatchDomain(configuration) - || excludeMatchBean(configuration) - || excludeMatchAttribute(configuration)); - + && matchBean(configuration) + && matchAttribute(configuration) + && !(excludeMatchDomain(configuration) + || excludeMatchBean(configuration) + || excludeMatchAttribute(configuration)); } private boolean matchAttribute(JMXConfiguration configuration) { @@ -52,11 +58,12 @@ private boolean matchAttribute(JMXConfiguration configuration) { if (include.getAttribute() == null) { return true; } else if ((include.getAttribute() instanceof LinkedHashMap) - && ((LinkedHashMap) (include.getAttribute())).containsKey(getAttributeName())) { + && ((LinkedHashMap) (include.getAttribute())) + .containsKey(getAttributeName())) { return true; } else if ((include.getAttribute() instanceof ArrayList - && ((ArrayList) (include.getAttribute())).contains(getAttributeName()))) { + && ((ArrayList) (include.getAttribute())).contains(getAttributeName()))) { return true; } @@ -65,21 +72,25 @@ private boolean matchAttribute(JMXConfiguration configuration) { private boolean excludeMatchAttribute(JMXConfiguration configuration) { JMXFilter exclude = configuration.getExclude(); - if (exclude == null) return false; + if (exclude == null) { + return false; + } if (exclude.getAttribute() == null) { return false; } else if ((exclude.getAttribute() instanceof LinkedHashMap) - && ((LinkedHashMap) (exclude.getAttribute())).containsKey(getAttributeName())) { + && ((LinkedHashMap) (exclude.getAttribute())) + .containsKey(getAttributeName())) { return true; } else if ((exclude.getAttribute() instanceof ArrayList - && ((ArrayList) (exclude.getAttribute())).contains(getAttributeName()))) { + && ((ArrayList) (exclude.getAttribute())).contains(getAttributeName()))) { return true; } return false; } - private Object getValue() throws AttributeNotFoundException, InstanceNotFoundException, MBeanException, + private Object getValue() + throws AttributeNotFoundException, InstanceNotFoundException, MBeanException, ReflectionException, IOException, NumberFormatException { return this.getJmxValue(); } @@ -89,7 +100,8 @@ private String getMetricType() { if (metricType != null) { return metricType; } else if (include.getAttribute() instanceof LinkedHashMap) { - LinkedHashMap> attribute = (LinkedHashMap>) (include.getAttribute()); + LinkedHashMap> attribute = + (LinkedHashMap>) (include.getAttribute()); metricType = attribute.get(getAttributeName()).get(METRIC_TYPE); if (metricType == null) { metricType = attribute.get(getAttributeName()).get("type"); diff --git a/src/main/java/org/gnuhpc/bigdata/model/JMXTabularAttribute.java b/src/main/java/org/gnuhpc/bigdata/model/JMXTabularAttribute.java index 916adc3..7370823 100644 --- a/src/main/java/org/gnuhpc/bigdata/model/JMXTabularAttribute.java +++ b/src/main/java/org/gnuhpc/bigdata/model/JMXTabularAttribute.java @@ -1,15 +1,31 @@ package org.gnuhpc.bigdata.model; -import lombok.Getter; -import lombok.Setter; -import lombok.extern.log4j.Log4j; - -import javax.management.*; +import java.io.IOException; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import javax.management.AttributeNotFoundException; +import javax.management.InstanceNotFoundException; +import javax.management.MBeanAttributeInfo; +import javax.management.MBeanException; +import javax.management.MBeanServerConnection; +import javax.management.ObjectName; +import javax.management.ReflectionException; import javax.management.openmbean.CompositeData; import javax.management.openmbean.InvalidKeyException; import javax.management.openmbean.TabularData; -import java.io.IOException; -import java.util.*; +import lombok.Getter; +import lombok.Setter; +import lombok.extern.log4j.Log4j; @Getter @Setter @@ -18,21 +34,25 @@ public class JMXTabularAttribute extends JMXAttribute { private HashMap>> subAttributeList; - public JMXTabularAttribute(MBeanAttributeInfo attribute, ObjectName beanName, MBeanServerConnection connection) { + public JMXTabularAttribute( + MBeanAttributeInfo attribute, ObjectName beanName, MBeanServerConnection connection) { super(attribute, beanName, connection); - subAttributeList = new HashMap>>(); + subAttributeList = new HashMap<>(); } @Override - public LinkedList> getMetrics() throws AttributeNotFoundException, - InstanceNotFoundException, MBeanException, ReflectionException, IOException { + public LinkedList> getMetrics() + throws AttributeNotFoundException, InstanceNotFoundException, MBeanException, + ReflectionException, IOException { LinkedList> metrics = new LinkedList>(); - HashMap>> subMetrics = new HashMap>>(); - - for (String dataKey : subAttributeList.keySet()) { - HashMap> subSub = subAttributeList.get(dataKey); - for (String metricKey : subSub.keySet()) { + HashMap>> subMetrics = + new HashMap>>(); + + for (Map.Entry>> dataEntry : + subAttributeList.entrySet()) { + HashMap> subSub = dataEntry.getValue(); + for (Map.Entry> metricEntry : subSub.entrySet()) { + String metricKey = metricEntry.getKey(); String fullMetricKey = getAttributeName() + "." + metricKey; HashMap metric = subSub.get(metricKey); @@ -52,19 +72,19 @@ public LinkedList> getMetrics() throws AttributeNotFound metric.put("tags", getTags(dataKey, metricKey)); }*/ - metric.put("value", castToDouble(getValue(dataKey, metricKey), null)); + metric.put("value", castToDouble(getValue(dataEntry.getKey(), metricKey), null)); - if(!subMetrics.containsKey(fullMetricKey)) { + if (!subMetrics.containsKey(fullMetricKey)) { subMetrics.put(fullMetricKey, new LinkedList>()); } subMetrics.get(fullMetricKey).add(metric); } } - for (String key : subMetrics.keySet()) { + for (Map.Entry>> keyEntry : subMetrics.entrySet()) { // only add explicitly included metrics - if (getAttributesFor(key) != null) { - metrics.addAll(sortAndFilter(key, subMetrics.get(key))); + if (getAttributesFor(keyEntry.getKey()) != null) { + metrics.addAll(sortAndFilter(keyEntry.getKey(), keyEntry.getValue())); } } @@ -77,8 +97,8 @@ private Object getMetricType(String subAttribute) { JMXFilter include = getMatchingConf().getInclude(); if (include.getAttribute() instanceof LinkedHashMap) { - LinkedHashMap> attribute = (LinkedHashMap>) (include.getAttribute()); + LinkedHashMap> attribute = + (LinkedHashMap>) (include.getAttribute()); metricType = attribute.get(subAttributeName).get(METRIC_TYPE); if (metricType == null) { metricType = attribute.get(subAttributeName).get("type"); @@ -92,13 +112,12 @@ private Object getMetricType(String subAttribute) { return metricType; } - private Object getValue(String key, String subAttribute) throws AttributeNotFoundException, - InstanceNotFoundException, - MBeanException, ReflectionException, IOException { + private Object getValue(String key, String subAttribute) + throws AttributeNotFoundException, InstanceNotFoundException, MBeanException, + ReflectionException, IOException { - try{ + try { Object value = this.getJmxValue(); - String attributeType = getAttribute().getType(); TabularData data = (TabularData) value; for (Object rowKey : data.keySet()) { @@ -122,9 +141,13 @@ private Object getValue(String key, String subAttribute) throws AttributeNotFoun } } } - } - catch (InvalidKeyException e) { - log.warn("`"+getAttribute().getName()+"` attribute does not have a `"+subAttribute+"` key."); + } catch (InvalidKeyException e) { + log.warn( + "`" + + getAttribute().getName() + + "` attribute does not have a `" + + subAttribute + + "` key."); return null; } @@ -136,14 +159,14 @@ private Object getValue(String key, String subAttribute) throws AttributeNotFoun if (include != null) { Object includeAttribute = include.getAttribute(); if (includeAttribute instanceof LinkedHashMap) { - return (Map) ((Map)includeAttribute).get(key); + return (Map) ((Map) includeAttribute).get(key); } } return null; } - private List> sortAndFilter(String metricKey, LinkedList> - metrics) { + private List> sortAndFilter( + String metricKey, LinkedList> metrics) { Map attributes = getAttributesFor(metricKey); if (!attributes.containsKey("limit")) { return metrics; @@ -163,7 +186,9 @@ private List> sortAndFilter(String metricKey, LinkedList return metrics; } - private class MetricComparator implements Comparator> { + private static class MetricComparator + implements Comparator>, Serializable { + public int compare(HashMap o1, HashMap o2) { Double v1 = (Double) o1.get("value"); Double v2 = (Double) o2.get("value"); @@ -174,9 +199,9 @@ public int compare(HashMap o1, HashMap o2) { @Override public boolean match(JMXConfiguration configuration) { if (!matchDomain(configuration) - || !matchBean(configuration) - || excludeMatchDomain(configuration) - || excludeMatchBean(configuration)) { + || !matchBean(configuration) + || excludeMatchDomain(configuration) + || excludeMatchBean(configuration)) { return false; } @@ -186,7 +211,7 @@ public boolean match(JMXConfiguration configuration) { return false; } - return matchAttribute(configuration);//TODO && !excludeMatchAttribute(configuration); + return matchAttribute(configuration); // TODO && !excludeMatchAttribute(configuration); } private void populateSubAttributeList(Object value) { @@ -195,10 +220,12 @@ private void populateSubAttributeList(Object value) { Collection keys = (Collection) rowKey; CompositeData compositeData = data.get(keys.toArray()); String pathKey = getMultiKey(keys); - HashMap> subAttributes = new HashMap>(); + HashMap> subAttributes = + new HashMap>(); for (String key : compositeData.getCompositeType().keySet()) { if (compositeData.get(key) instanceof CompositeData) { - for (String subKey : ((CompositeData) compositeData.get(key)).getCompositeType().keySet()) { + for (String subKey : + ((CompositeData) compositeData.get(key)).getCompositeType().keySet()) { subAttributes.put(key + "." + subKey, new HashMap()); } } else { @@ -214,14 +241,16 @@ private boolean matchAttribute(JMXConfiguration configuration) { return true; } - Iterator it1 = subAttributeList.keySet().iterator(); + Iterator>>> it1 = + subAttributeList.entrySet().iterator(); while (it1.hasNext()) { - String key = it1.next(); - HashMap> subSub = subAttributeList.get(key); - Iterator it2 = subSub.keySet().iterator(); + Entry>> entry = it1.next(); + HashMap> subSub = entry.getValue(); + Iterator>> it2 = subSub.entrySet().iterator(); while (it2.hasNext()) { - String subKey = it2.next(); - if (!matchSubAttribute(configuration.getInclude(), getAttributeName() + "." + subKey, true)) { + String subKey = it2.next().getKey(); + if (!matchSubAttribute( + configuration.getInclude(), getAttributeName() + "." + subKey, true)) { it2.remove(); } } @@ -237,7 +266,9 @@ private String getMultiKey(Collection keys) { StringBuilder sb = new StringBuilder(); boolean first = true; for (Object key : keys) { - if (!first) { sb.append(","); } + if (!first) { + sb.append(","); + } // I hope these have sane toString() methods sb.append(key.toString()); first = false; @@ -245,12 +276,14 @@ private String getMultiKey(Collection keys) { return sb.toString(); } - private boolean matchSubAttribute(JMXFilter params, String subAttributeName, boolean matchOnEmpty) { + private boolean matchSubAttribute( + JMXFilter params, String subAttributeName, boolean matchOnEmpty) { if ((params.getAttribute() instanceof LinkedHashMap) - && ((LinkedHashMap) (params.getAttribute())).containsKey(subAttributeName)) { + && ((LinkedHashMap) (params.getAttribute())) + .containsKey(subAttributeName)) { return true; } else if ((params.getAttribute() instanceof ArrayList - && ((ArrayList) (params.getAttribute())).contains(subAttributeName))) { + && ((ArrayList) (params.getAttribute())).contains(subAttributeName))) { return true; } else if (params.getAttribute() == null) { return matchOnEmpty; diff --git a/src/main/java/org/gnuhpc/bigdata/model/MemberDescription.java b/src/main/java/org/gnuhpc/bigdata/model/MemberDescription.java index ddb5331..eff70ad 100644 --- a/src/main/java/org/gnuhpc/bigdata/model/MemberDescription.java +++ b/src/main/java/org/gnuhpc/bigdata/model/MemberDescription.java @@ -10,6 +10,7 @@ @Getter @Setter public class MemberDescription { + private String memberId; private String clientId; private String host; diff --git a/src/main/java/org/gnuhpc/bigdata/model/PartitionAssignmentState.java b/src/main/java/org/gnuhpc/bigdata/model/PartitionAssignmentState.java index 16cd5b7..2fd5a2d 100644 --- a/src/main/java/org/gnuhpc/bigdata/model/PartitionAssignmentState.java +++ b/src/main/java/org/gnuhpc/bigdata/model/PartitionAssignmentState.java @@ -9,6 +9,7 @@ @Builder @AllArgsConstructor public class PartitionAssignmentState { + private String group; private Node coordinator; private String topic; diff --git a/src/main/java/org/gnuhpc/bigdata/model/ReassignJsonWrapper.java b/src/main/java/org/gnuhpc/bigdata/model/ReassignJsonWrapper.java index ae39a3e..78693ae 100644 --- a/src/main/java/org/gnuhpc/bigdata/model/ReassignJsonWrapper.java +++ b/src/main/java/org/gnuhpc/bigdata/model/ReassignJsonWrapper.java @@ -1,19 +1,17 @@ package org.gnuhpc.bigdata.model; +import java.util.List; +import java.util.Map; import lombok.Getter; import lombok.Setter; import lombok.ToString; -import java.util.List; -import java.util.Map; - -/** - * Created by gnuhpc on 2017/7/25. - */ +/** Created by gnuhpc on 2017/7/25. */ @Setter @Getter @ToString public class ReassignJsonWrapper { - private List> topics; - private int version = 1; + + private List> topics; + private int version = 1; } diff --git a/src/main/java/org/gnuhpc/bigdata/model/ReassignWrapper.java b/src/main/java/org/gnuhpc/bigdata/model/ReassignWrapper.java index 0315554..84042f0 100644 --- a/src/main/java/org/gnuhpc/bigdata/model/ReassignWrapper.java +++ b/src/main/java/org/gnuhpc/bigdata/model/ReassignWrapper.java @@ -1,34 +1,33 @@ package org.gnuhpc.bigdata.model; import com.google.gson.Gson; -import lombok.Getter; -import lombok.Setter; -import lombok.ToString; - import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; -/** - * Created by gnuhpc on 2017/7/25. - */ +/** Created by gnuhpc on 2017/7/25. */ @Getter @Setter @ToString public class ReassignWrapper { - private List topics; - private List brokers; - public String generateReassignJsonString(){ - ReassignJsonWrapper reassignJsonWrapper = new ReassignJsonWrapper(); - List> topicList = new ArrayList<>(); - for(String topic:topics){ - Map topicMap = new HashMap<>(); - topicMap.put("topic",topic); - topicList.add(topicMap); - } - reassignJsonWrapper.setTopics(topicList); - Gson gson = new Gson(); - return gson.toJson(reassignJsonWrapper); + + private List topics; + private List brokers; + + public String generateReassignJsonString() { + ReassignJsonWrapper reassignJsonWrapper = new ReassignJsonWrapper(); + List> topicList = new ArrayList<>(); + for (String topic : topics) { + Map topicMap = new HashMap<>(); + topicMap.put("topic", topic); + topicList.add(topicMap); } + reassignJsonWrapper.setTopics(topicList); + Gson gson = new Gson(); + return gson.toJson(reassignJsonWrapper); + } } diff --git a/src/main/java/org/gnuhpc/bigdata/model/Record.java b/src/main/java/org/gnuhpc/bigdata/model/Record.java index cd5f3af..352ecfe 100644 --- a/src/main/java/org/gnuhpc/bigdata/model/Record.java +++ b/src/main/java/org/gnuhpc/bigdata/model/Record.java @@ -4,6 +4,7 @@ @Data public class Record { + long offset; String key; String value; diff --git a/src/main/java/org/gnuhpc/bigdata/model/TopicBrief.java b/src/main/java/org/gnuhpc/bigdata/model/TopicBrief.java index 7906231..eade921 100644 --- a/src/main/java/org/gnuhpc/bigdata/model/TopicBrief.java +++ b/src/main/java/org/gnuhpc/bigdata/model/TopicBrief.java @@ -8,7 +8,8 @@ @AllArgsConstructor @NoArgsConstructor public class TopicBrief { - private String topic; - private int numPartition; - private double isrRate; + + private String topic; + private int numPartition; + private double isrRate; } diff --git a/src/main/java/org/gnuhpc/bigdata/model/TopicDetail.java b/src/main/java/org/gnuhpc/bigdata/model/TopicDetail.java index c5107b2..935f5f1 100644 --- a/src/main/java/org/gnuhpc/bigdata/model/TopicDetail.java +++ b/src/main/java/org/gnuhpc/bigdata/model/TopicDetail.java @@ -16,28 +16,27 @@ package org.gnuhpc.bigdata.model; +import java.util.Properties; import lombok.Getter; import lombok.Setter; import lombok.ToString; -import java.util.Properties; - @Setter @Getter @ToString public class TopicDetail { - public static final int DEFAULT_PARTITION_NUMBER = 2; - public static final int DEFAULT_REPLICATION_FACTOR = 1; + public static final int DEFAULT_PARTITION_NUMBER = 2; + public static final int DEFAULT_REPLICATION_FACTOR = 1; - private int partitions; - private int factor; - private String name; - private Properties prop; + private int partitions; + private int factor; + private String name; + private Properties prop; - public TopicDetail() { - this.partitions = DEFAULT_PARTITION_NUMBER; - this.factor = DEFAULT_REPLICATION_FACTOR; - this.prop = new Properties(); - } + public TopicDetail() { + this.partitions = DEFAULT_PARTITION_NUMBER; + this.factor = DEFAULT_REPLICATION_FACTOR; + this.prop = new Properties(); + } } diff --git a/src/main/java/org/gnuhpc/bigdata/model/TopicMeta.java b/src/main/java/org/gnuhpc/bigdata/model/TopicMeta.java index d744550..17e5469 100644 --- a/src/main/java/org/gnuhpc/bigdata/model/TopicMeta.java +++ b/src/main/java/org/gnuhpc/bigdata/model/TopicMeta.java @@ -1,26 +1,23 @@ package org.gnuhpc.bigdata.model; +import java.util.List; import lombok.Getter; import lombok.Setter; import lombok.extern.log4j.Log4j; -import java.util.List; -import java.util.Properties; - -/** - * Created by gnuhpc on 2017/7/21. - */ +/** Created by gnuhpc on 2017/7/21. */ @Log4j @Getter @Setter public class TopicMeta { - private String topicName; - private boolean internal; - private int partitionCount; - private int replicationFactor; - private List topicPartitionInfos; - public TopicMeta(String topicName){ - this.topicName = topicName; - } + private String topicName; + private boolean internal; + private int partitionCount; + private int replicationFactor; + private List topicPartitionInfos; + + public TopicMeta(String topicName) { + this.topicName = topicName; + } } diff --git a/src/main/java/org/gnuhpc/bigdata/model/TwoTuple.java b/src/main/java/org/gnuhpc/bigdata/model/TwoTuple.java index cf361bf..378c72a 100644 --- a/src/main/java/org/gnuhpc/bigdata/model/TwoTuple.java +++ b/src/main/java/org/gnuhpc/bigdata/model/TwoTuple.java @@ -4,6 +4,7 @@ @Data public class TwoTuple { + private A first; private B second; diff --git a/src/main/java/org/gnuhpc/bigdata/model/User.java b/src/main/java/org/gnuhpc/bigdata/model/User.java index be06e96..a64cba6 100644 --- a/src/main/java/org/gnuhpc/bigdata/model/User.java +++ b/src/main/java/org/gnuhpc/bigdata/model/User.java @@ -1,17 +1,16 @@ package org.gnuhpc.bigdata.model; +import javax.validation.constraints.NotNull; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.Setter; import org.hibernate.validator.constraints.NotBlank; -import org.hibernate.validator.constraints.NotEmpty; - -import javax.validation.constraints.NotNull; @Getter @Setter @AllArgsConstructor public class User { + @NotNull(message = "Username can not be null.") @NotBlank(message = "Username can not be blank.") private String username; diff --git a/src/main/java/org/gnuhpc/bigdata/model/ZkServerClient.java b/src/main/java/org/gnuhpc/bigdata/model/ZkServerClient.java index 343245a..1aaace1 100644 --- a/src/main/java/org/gnuhpc/bigdata/model/ZkServerClient.java +++ b/src/main/java/org/gnuhpc/bigdata/model/ZkServerClient.java @@ -1,16 +1,16 @@ package org.gnuhpc.bigdata.model; - import lombok.AllArgsConstructor; import lombok.Data; @Data @AllArgsConstructor public class ZkServerClient { - private final String host; - private final Integer port; - private final Integer ops; - private final Integer queued; - private final Integer received; - private final Integer sent; + + private final String host; + private final Integer port; + private final Integer ops; + private final Integer queued; + private final Integer received; + private final Integer sent; } diff --git a/src/main/java/org/gnuhpc/bigdata/model/ZkServerEnvironment.java b/src/main/java/org/gnuhpc/bigdata/model/ZkServerEnvironment.java index c748578..870b3fc 100644 --- a/src/main/java/org/gnuhpc/bigdata/model/ZkServerEnvironment.java +++ b/src/main/java/org/gnuhpc/bigdata/model/ZkServerEnvironment.java @@ -4,6 +4,7 @@ import java.util.Map; public class ZkServerEnvironment { + private final Map attributes = new HashMap(); public void add(final String attribute, final String value) { diff --git a/src/main/java/org/gnuhpc/bigdata/model/ZkServerStat.java b/src/main/java/org/gnuhpc/bigdata/model/ZkServerStat.java index f100282..48d1f77 100644 --- a/src/main/java/org/gnuhpc/bigdata/model/ZkServerStat.java +++ b/src/main/java/org/gnuhpc/bigdata/model/ZkServerStat.java @@ -1,15 +1,14 @@ package org.gnuhpc.bigdata.model; +import java.util.List; import lombok.AllArgsConstructor; import lombok.Data; import org.gnuhpc.bigdata.constant.ZkServerMode; -import java.util.List; - - @Data @AllArgsConstructor public class ZkServerStat { + private final String version; private final String buildDate; private final List clients; diff --git a/src/main/java/org/gnuhpc/bigdata/security/BasicAuthenticationPoint.java b/src/main/java/org/gnuhpc/bigdata/security/BasicAuthenticationPoint.java index 3c2d795..c97c2da 100644 --- a/src/main/java/org/gnuhpc/bigdata/security/BasicAuthenticationPoint.java +++ b/src/main/java/org/gnuhpc/bigdata/security/BasicAuthenticationPoint.java @@ -5,6 +5,12 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import java.io.IOException; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import lombok.NoArgsConstructor; import org.gnuhpc.bigdata.exception.RestErrorResponse; import org.springframework.boot.jackson.JsonComponent; @@ -13,22 +19,18 @@ import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint; import org.springframework.stereotype.Component; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; - @Component public class BasicAuthenticationPoint extends BasicAuthenticationEntryPoint { + @Override - public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authEx) - throws IOException, ServletException { - response.addHeader("WWW-Authenticate", "Basic realm=" +getRealmName()); + public void commence( + HttpServletRequest request, HttpServletResponse response, AuthenticationException authEx) + throws IOException, ServletException { + response.addHeader("WWW-Authenticate", "Basic realm=" + getRealmName()); response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); String error = "Authenciation Error:" + authEx.getClass().getCanonicalName(); - RestErrorResponse restAuthenticationError = new RestErrorResponse(HttpStatus.UNAUTHORIZED, error, authEx); + RestErrorResponse restAuthenticationError = + new RestErrorResponse(HttpStatus.UNAUTHORIZED, error, authEx); ObjectMapper mapper = new ObjectMapper(); JavaTimeModule javaTimeModule = new JavaTimeModule(); javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer()); @@ -44,9 +46,11 @@ public void afterPropertiesSet() throws Exception { @JsonComponent @NoArgsConstructor - private class LocalDateTimeSerializer extends JsonSerializer { + private static class LocalDateTimeSerializer extends JsonSerializer { + @Override - public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider sp) throws IOException{ + public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider sp) + throws IOException { gen.writeString(value.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); } } diff --git a/src/main/java/org/gnuhpc/bigdata/security/UserDetailsServiceImp.java b/src/main/java/org/gnuhpc/bigdata/security/UserDetailsServiceImp.java index 2bb09e7..262fcbf 100644 --- a/src/main/java/org/gnuhpc/bigdata/security/UserDetailsServiceImp.java +++ b/src/main/java/org/gnuhpc/bigdata/security/UserDetailsServiceImp.java @@ -1,6 +1,13 @@ package org.gnuhpc.bigdata.security; import com.google.common.util.concurrent.ThreadFactoryBuilder; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; import lombok.extern.log4j.Log4j; import org.gnuhpc.bigdata.config.WebSecurityConfig; import org.gnuhpc.bigdata.model.User; @@ -10,26 +17,23 @@ import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; - @Log4j public class UserDetailsServiceImp implements UserDetailsService { + private ScheduledExecutorService securityFileChecker; private ArrayList userList = new ArrayList<>(); - public UserDetailsServiceImp(boolean checkSecurity, int checkInitDelay, int checkSecurityInterval) { + public UserDetailsServiceImp( + boolean checkSecurity, int checkInitDelay, int checkSecurityInterval) { if (checkSecurity) { - securityFileChecker = Executors.newSingleThreadScheduledExecutor( + securityFileChecker = + Executors.newSingleThreadScheduledExecutor( new ThreadFactoryBuilder().setNameFormat("securityFileChecker").build()); - securityFileChecker.scheduleWithFixedDelay(new SecurityFileCheckerRunnable(), - checkInitDelay, checkSecurityInterval, TimeUnit.SECONDS); + securityFileChecker.scheduleWithFixedDelay( + new SecurityFileCheckerRunnable(), + checkInitDelay, + checkSecurityInterval, + TimeUnit.SECONDS); userList = fetchUserListFromSecurtiyFile(); } } @@ -51,7 +55,7 @@ public UserDetails loadUserByUsername(String username) throws UsernameNotFoundEx } private User findUserByUsername(String username) { - for (User user:userList) { + for (User user : userList) { if (username.equals(user.getUsername())) { return user; } @@ -64,11 +68,12 @@ private ArrayList fetchUserListFromSecurtiyFile() { try { HashMap accounts = CommonUtils.yamlParse(securityFilePath); userList.clear(); - accounts.forEach((key, value)->{ - String username = (String)key; - Map userInfo = (Map)value; - userList.add(new User(username, userInfo.get("password"), userInfo.get("role"))); - }); + accounts.forEach( + (key, value) -> { + String username = (String) key; + Map userInfo = (Map) value; + userList.add(new User(username, userInfo.get("password"), userInfo.get("role"))); + }); } catch (IOException ioException) { log.error("Security file process exception.", ioException); } @@ -77,6 +82,7 @@ private ArrayList fetchUserListFromSecurtiyFile() { } private class SecurityFileCheckerRunnable implements Runnable { + @Override public void run() { try { diff --git a/src/main/java/org/gnuhpc/bigdata/serializer/JsonJodaDateTimeSerializer.java b/src/main/java/org/gnuhpc/bigdata/serializer/JsonJodaDateTimeSerializer.java index 8702c08..7783fd2 100644 --- a/src/main/java/org/gnuhpc/bigdata/serializer/JsonJodaDateTimeSerializer.java +++ b/src/main/java/org/gnuhpc/bigdata/serializer/JsonJodaDateTimeSerializer.java @@ -1,23 +1,22 @@ package org.gnuhpc.bigdata.serializer; import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; +import java.io.IOException; import org.joda.time.DateTime; import org.joda.time.format.DateTimeFormatter; import org.joda.time.format.ISODateTimeFormat; -import java.io.IOException; +/** Created by gnuhpc on 2017/7/19. */ +public class JsonJodaDateTimeSerializer extends JsonSerializer { -/** - * Created by gnuhpc on 2017/7/19. - */ -public class JsonJodaDateTimeSerializer extends JsonSerializer{ - private static DateTimeFormatter formatter = ISODateTimeFormat.dateTime(); + private static DateTimeFormatter formatter = ISODateTimeFormat.dateTime(); - @Override - public void serialize(DateTime value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { - jsonGenerator.writeString(formatter.print(value)); - } + @Override + public void serialize( + DateTime value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) + throws IOException { + jsonGenerator.writeString(formatter.print(value)); + } } diff --git a/src/main/java/org/gnuhpc/bigdata/service/CollectorService.java b/src/main/java/org/gnuhpc/bigdata/service/CollectorService.java index e3bcade..be8a4b9 100644 --- a/src/main/java/org/gnuhpc/bigdata/service/CollectorService.java +++ b/src/main/java/org/gnuhpc/bigdata/service/CollectorService.java @@ -1,34 +1,66 @@ package org.gnuhpc.bigdata.service; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.management.MBeanAttributeInfo; +import javax.management.MBeanInfo; +import javax.management.MBeanServerConnection; +import javax.management.ObjectName; import lombok.extern.log4j.Log4j; import org.gnuhpc.bigdata.config.JMXConfig; import org.gnuhpc.bigdata.exception.CollectorException; -import org.gnuhpc.bigdata.model.*; +import org.gnuhpc.bigdata.model.JMXAttribute; +import org.gnuhpc.bigdata.model.JMXClient; +import org.gnuhpc.bigdata.model.JMXComplexAttribute; +import org.gnuhpc.bigdata.model.JMXConfiguration; +import org.gnuhpc.bigdata.model.JMXMetricData; +import org.gnuhpc.bigdata.model.JMXMetricDataV1; +import org.gnuhpc.bigdata.model.JMXQuery; +import org.gnuhpc.bigdata.model.JMXSimpleAttribute; +import org.gnuhpc.bigdata.model.JMXTabularAttribute; import org.gnuhpc.bigdata.utils.CommonUtils; import org.json.JSONObject; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; -import javax.management.MBeanAttributeInfo; -import javax.management.MBeanInfo; -import javax.management.MBeanServerConnection; -import javax.management.ObjectName; -import java.io.File; -import java.io.IOException; -import java.util.*; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - @Service @Log4j @Validated public class CollectorService { - private final static List SIMPLE_TYPES = Arrays.asList("long", - "java.lang.String", "int", "float", "double", "java.lang.Double","java.lang.Float", "java.lang.Integer", "java.lang.Long", - "java.util.concurrent.atomic.AtomicInteger", "java.util.concurrent.atomic.AtomicLong", - "java.lang.Object", "java.lang.Boolean", "boolean", "java.lang.Number"); - private final static List COMPOSED_TYPES = Arrays.asList("javax.management.openmbean.CompositeData", "java.util.HashMap", "java.util.Map"); - private final static List MULTI_TYPES = Arrays.asList("javax.management.openmbean.TabularData"); + + private static final List SIMPLE_TYPES = + Arrays.asList( + "long", + "java.lang.String", + "int", + "float", + "double", + "java.lang.Double", + "java.lang.Float", + "java.lang.Integer", + "java.lang.Long", + "java.util.concurrent.atomic.AtomicInteger", + "java.util.concurrent.atomic.AtomicLong", + "java.lang.Object", + "java.lang.Boolean", + "boolean", + "java.lang.Number"); + private static final List COMPOSED_TYPES = + Arrays.asList( + "javax.management.openmbean.CompositeData", "java.util.HashMap", "java.util.Map"); + private static final List MULTI_TYPES = + Arrays.asList("javax.management.openmbean.TabularData"); public List collectJMXData(String jmxurl) { LinkedList jmxMetricDataList = new LinkedList<>(); @@ -40,17 +72,23 @@ public List collectJMXData(String jmxurl) { try { log.info("Start to collect JMXServiceURL:" + jmxClient.getJmxServiceURL()); jmxClient.connectWithTimeout(); - MBeanServerConnection mBeanServerConnection = jmxClient.getJmxConnector().getMBeanServerConnection(); + MBeanServerConnection mBeanServerConnection = + jmxClient.getJmxConnector().getMBeanServerConnection(); Set objectNames = mBeanServerConnection.queryNames(null, null); for (ObjectName objectName : objectNames) { - Map attributeInfoMap = getAttributeInfoByObjectName(mBeanServerConnection, objectName); + Map attributeInfoMap = + getAttributeInfoByObjectName(mBeanServerConnection, objectName); metricData.put(objectName.toString(), attributeInfoMap); } jmxMetricData.setCollected(true); } catch (Exception e) { jmxMetricData.setCollected(false); - CollectorException ce = new CollectorException(String.format("%s occurred. URL: %s. Reason: %s", - e.getClass().getCanonicalName(), jmxClient.getJmxServiceURL(), e.getCause()), e); + CollectorException ce = + new CollectorException( + String.format( + "%s occurred. URL: %s. Reason: %s", + e.getClass().getCanonicalName(), jmxClient.getJmxServiceURL(), e.getCause()), + e); jmxMetricData.setMsg(ce.getLocalizedMessage()); log.error("Failed to connect to " + jmxClient.getJmxServiceURL(), ce); } finally { @@ -86,7 +124,8 @@ public List collectJMXData(String jmxurl, JMXQuery jmxQuery) { JMXMetricData jmxMetricData = new JMXMetricData(host, metrics); try { jmxClient.connectWithTimeout(); - MBeanServerConnection mBeanServerConnection = jmxClient.getJmxConnector().getMBeanServerConnection(); + MBeanServerConnection mBeanServerConnection = + jmxClient.getJmxConnector().getMBeanServerConnection(); for (String scope : beanScopes) { ObjectName name = new ObjectName(scope); beans.addAll(mBeanServerConnection.queryNames(name, null)); @@ -97,8 +136,12 @@ public List collectJMXData(String jmxurl, JMXQuery jmxQuery) { jmxMetricData.setCollected(true); } catch (Exception e) { jmxMetricData.setCollected(false); - CollectorException ce = new CollectorException(String.format("%s occurred. URL: %s. Reason: %s", - e.getClass().getCanonicalName(), jmxClient.getJmxServiceURL(), e.getCause()), e); + CollectorException ce = + new CollectorException( + String.format( + "%s occurred. URL: %s. Reason: %s", + e.getClass().getCanonicalName(), jmxClient.getJmxServiceURL(), e.getCause()), + e); jmxMetricData.setMsg(ce.getLocalizedMessage()); log.error("Failed to connect to " + jmxClient.getJmxServiceURL(), ce); } finally { @@ -115,20 +158,27 @@ public List collectJMXData(String jmxurl, JMXQuery jmxQuery) { return jmxMetricDataList; } - private void getMatchingAttributes(LinkedList matchingAttributes, MBeanServerConnection mBeanServerConnection, Set beans, - LinkedList configurationList) { + private void getMatchingAttributes( + LinkedList matchingAttributes, + MBeanServerConnection mBeanServerConnection, + Set beans, + LinkedList configurationList) { for (ObjectName beanName : beans) { MBeanAttributeInfo[] attributeInfos; try { attributeInfos = mBeanServerConnection.getMBeanInfo(beanName).getAttributes(); } catch (Exception e) { - CollectorException ce = new CollectorException(String.format("Get bean's attributes exception. BeanName: %s. Reason: %s", - beanName, e.getCause()), e); + CollectorException ce = + new CollectorException( + String.format( + "Get bean's attributes exception. BeanName: %s. Reason: %s", + beanName, e.getCause()), + e); log.error("Failed to get bean attributes. BeanName is " + beanName, ce); continue; } - for (MBeanAttributeInfo attributeInfo: attributeInfos) { + for (MBeanAttributeInfo attributeInfo : attributeInfos) { JMXAttribute jmxAttribute; String attributeType = attributeInfo.getType(); if (SIMPLE_TYPES.contains(attributeType)) { @@ -141,27 +191,31 @@ private void getMatchingAttributes(LinkedList matchingAttributes, log.debug(beanName + " : " + attributeInfo + " has attributeInfo tabular type"); jmxAttribute = new JMXTabularAttribute(attributeInfo, beanName, mBeanServerConnection); } else { - //try { - log.debug(beanName + " : " + attributeInfo + " has an unsupported type: " + attributeType); - //} catch (NullPointerException e) { + // try { + log.debug( + beanName + " : " + attributeInfo + " has an unsupported type: " + attributeType); + // } catch (NullPointerException e) { // log.error("Caught unexpected NullPointerException"); - //} + // } continue; } - for (JMXConfiguration conf: configurationList) { + for (JMXConfiguration conf : configurationList) { if (jmxAttribute.match(conf)) { jmxAttribute.setMatchingConf(conf); matchingAttributes.add(jmxAttribute); - log.debug(" Matching Attribute: " + jmxAttribute.getAttributeName() + - ", BeanName:" + beanName.getCanonicalName()); + log.debug( + " Matching Attribute: " + + jmxAttribute.getAttributeName() + + ", BeanName:" + + beanName.getCanonicalName()); } } } } } - private Map getAttributeInfoByObjectName(MBeanServerConnection mBeanServerConnection, - ObjectName objectName) { + private Map getAttributeInfoByObjectName( + MBeanServerConnection mBeanServerConnection, ObjectName objectName) { Map attributeInfoMap = new HashMap<>(); try { MBeanInfo mbeanInfo = mBeanServerConnection.getMBeanInfo(objectName); @@ -171,10 +225,16 @@ private Map getAttributeInfoByObjectName(MBeanServerConnection m String attributeName = info.getName(); String attributeValue = ""; try { - attributeValue = mBeanServerConnection.getAttribute(objectName, info.getName()).toString(); + attributeValue = + mBeanServerConnection.getAttribute(objectName, info.getName()).toString(); } catch (Exception e) { attributeValue = "Unavailable"; - log.info("Exception occured when collect ObjectName:" + objectName + ", AttributeName:" + attributeName, e); + log.info( + "Exception occured when collect ObjectName:" + + objectName + + ", AttributeName:" + + attributeName, + e); } attributeInfoMap.put(attributeName, attributeValue); } @@ -185,7 +245,8 @@ private Map getAttributeInfoByObjectName(MBeanServerConnection m return attributeInfoMap; } - public LinkedList> getMetrics(LinkedList matchingAttributes) throws IOException { + public LinkedList> getMetrics(LinkedList matchingAttributes) + throws IOException { LinkedList> metrics = new LinkedList>(); Iterator it = matchingAttributes.iterator(); @@ -194,7 +255,7 @@ public LinkedList> getMetrics(LinkedList m try { LinkedList> jmxAttrMetrics = jmxAttr.getMetrics(); for (HashMap m : jmxAttrMetrics) { - //m.put("check_name", this.checkName); + // m.put("check_name", this.checkName); metrics.add(m); JSONObject metricJson = new JSONObject(m); } @@ -217,19 +278,26 @@ public HashMap listJMXFilterTemplate(String filterKey) { if (!jmxFilterDir.exists() || !jmxFilterDir.isDirectory()) { throw new IOException(); } - for (File yamlFile:jmxFilterDir.listFiles()) { - String fileFullName = yamlFile.getName(); - log.info("Found JMXFilterTemplate filename=" + fileFullName); - if (matchIgnoreCase(filterKey, fileFullName)) { - String[] fileNames = fileFullName.split("\\."); - yamlHash = CommonUtils.yamlParse(yamlFile); - filterTemplateMap.put(fileNames[0], yamlHash); + File[] files = jmxFilterDir.listFiles(); + if (files != null) { + for (File yamlFile : files) { + String fileFullName = yamlFile.getName(); + log.info("Found JMXFilterTemplate filename=" + fileFullName); + if (matchIgnoreCase(filterKey, fileFullName)) { + String[] fileNames = fileFullName.split("\\."); + yamlHash = CommonUtils.yamlParse(yamlFile); + filterTemplateMap.put(fileNames[0], yamlHash); + } } } } catch (IOException e) { - CollectorException ce = new CollectorException(String.format("%s occurred. Reason:%s. Advice:"+ - "Create a directory named JMXFilterTemplate to include filter templates in the project root path:%s.", - e.getClass().getCanonicalName(), e.getLocalizedMessage(), projectRootPath), e); + CollectorException ce = + new CollectorException( + String.format( + "%s occurred. Reason:%s. Advice:Create a directory named JMXFilterTemplate to " + + "include filter templates in the project root path:%s.", + e.getClass().getCanonicalName(), e.getLocalizedMessage(), projectRootPath), + e); log.error("JMXFilterTemplate path does not exist."); filterTemplateMap.put("error", ce.getLocalizedMessage()); } diff --git a/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java b/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java index f334a4a..215ed9c 100644 --- a/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java +++ b/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java @@ -32,7 +32,6 @@ import java.util.Properties; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.function.Function; import java.util.regex.Pattern; @@ -57,7 +56,6 @@ import kafka.javaapi.consumer.SimpleConsumer; import kafka.log.LogConfig; import kafka.server.ConfigType; -import kafka.server.DynamicConfig; import kafka.utils.ZkUtils; import kafka.zk.AdminZkClient; import kafka.zk.KafkaZkClient; @@ -142,10 +140,7 @@ import scala.runtime.AbstractFunction0; import scala.runtime.BoxedUnit; -/** - * Created by gnuhpc on 2017/7/17. - */ - +/** Created by gnuhpc on 2017/7/17. */ @Service @Log4j @Validated @@ -157,63 +152,40 @@ public class KafkaAdminService { private static final long kafkaAdminClientAlterTimeoutMs = 60000; private static final String CONSUMERPATHPREFIX = "/consumers/"; private static final String OFFSETSPATHPREFIX = "/offsets/"; - @Autowired - private ZookeeperUtils zookeeperUtils; + @Autowired private ZookeeperUtils zookeeperUtils; - @Autowired - private KafkaUtils kafkaUtils; + @Autowired private KafkaUtils kafkaUtils; - @Autowired - private KafkaConfig kafkaConfig; + @Autowired private KafkaConfig kafkaConfig; - @Autowired - private OffsetStorage storage; + @Autowired private OffsetStorage storage; - //For AdminUtils use + // For AdminUtils use private ZkUtils zkUtils; - //For zookeeper connection + // For zookeeper connection private CuratorFramework zkClient; - private KafkaZkClient kafkaZkClient; + // private KafkaZkClient kafkaZkClient; - private AdminZkClient adminZkClient; + // private AdminZkClient adminZkClient; // private org.apache.kafka.clients.admin.AdminClient kafkaAdminClient; - //For Json serialized + // For Json serialized private Gson gson; - private scala.Option NONE = scala.Option.apply(null); - - //TODO use autocloseable interface - /* - public KafkaAdminService(final KafkaZkClient kafkaZkClient) { - this.kafkaZkClient = kafkaZkClient; - } - - public KafkaAdminService(final org.apache.kafka.clients.admin.AdminClient kafkaAdminClient) { - this.kafkaAdminClient = kafkaAdminClient; - } - - public KafkaAdminService(final KafkaZkClient kafkaZkClient, final org.apache.kafka.clients.admin.AdminClient kafkaAdminClient) { - this.kafkaZkClient = kafkaZkClient; - this.kafkaAdminClient = kafkaAdminClient; - } */ + private scala.Option none = scala.Option.apply(null); @PostConstruct private void init() { this.zkUtils = zookeeperUtils.getZkUtils(); this.zkClient = zookeeperUtils.getCuratorClient(); - this.kafkaZkClient = zookeeperUtils.getKafkaZkClient(); - this.adminZkClient = new AdminZkClient(kafkaZkClient); -// Properties adminClientProp = new Properties(); -// adminClientProp.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaConfig.getBrokers()); -// //TODO eliminate the init connection -// kafkaAdminClient = KafkaAdminClient.create(adminClientProp); + GsonBuilder builder = new GsonBuilder(); - builder.registerTypeAdapter(DateTime.class, - (JsonDeserializer) (jsonElement, type, jsonDeserializationContext) -> new DateTime( - jsonElement.getAsJsonPrimitive().getAsLong())); + builder.registerTypeAdapter( + DateTime.class, + (JsonDeserializer) (jsonElement, type, jsonDeserializationContext) -> + new DateTime(jsonElement.getAsJsonPrimitive().getAsLong())); this.gson = builder.create(); } @@ -224,17 +196,8 @@ private org.apache.kafka.clients.admin.AdminClient createKafkaAdminClient() { return KafkaAdminClient.create(adminClientProp); } - //TODO use autocloseable interface - /* - @Override - public void close() { - System.out.println("++++++++Kafka Admin Service close ++++++++"); - kafkaAdminClient.close(); - kafkaZkClient.close(); - zkClient.close(); - } - */ public TopicMeta createTopic(TopicDetail topic, String reassignStr) { + if (Topic.hasCollisionChars(topic.getName())) { throw new InvalidTopicException("Invalid topic name, it contains '.' or '_'."); } @@ -245,9 +208,13 @@ public TopicMeta createTopic(TopicDetail topic, String reassignStr) { Topic.validate(topic.getName()); if (Strings.isNullOrEmpty(reassignStr)) { - AdminUtils.createTopic(zkUtils, - topic.getName(), topic.getPartitions(), topic.getFactor(), - topic.getProp(), RackAwareMode.Enforced$.MODULE$); + AdminUtils.createTopic( + zkUtils, + topic.getName(), + topic.getPartitions(), + topic.getFactor(), + topic.getProp(), + RackAwareMode.Enforced$.MODULE$); } else { List argsList = new ArrayList<>(); argsList.add("--topic"); @@ -263,16 +230,21 @@ public TopicMeta createTopic(TopicDetail topic, String reassignStr) { argsList.add("--replica-assignment"); argsList.add(reassignStr); - TopicCommand.createTopic(kafkaZkClient, + KafkaZkClient kafkaZkClient = zookeeperUtils.createKafkaZkClient(); + TopicCommand.createTopic( + kafkaZkClient, new TopicCommand.TopicCommandOptions(argsList.stream().toArray(String[]::new))); + + kafkaZkClient.close(); } try { - //Wait for a second for metadata propergating + // Wait for a second for metadata propergating Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } + return describeTopic(topic.getName()); } @@ -310,26 +282,35 @@ public List listTopicBrief() { Map topicMap; List result; try { - topicMap = describeTopicsResult.all() - .get(kafkaAdminClientGetTimeoutMs, TimeUnit.MILLISECONDS); - result = topicMap.entrySet().parallelStream().map(e -> { - String topic = e.getKey(); - TopicDescription topicDescription = e.getValue(); - List topicPartitionInfoList = topicDescription - .partitions(); - int replicateCount = 0; - int isrCount = 0; - for (org.apache.kafka.common.TopicPartitionInfo topicPartitionInfo : topicPartitionInfoList) { - replicateCount += topicPartitionInfo.replicas().size(); - isrCount += topicPartitionInfo.isr().size(); - } - if (replicateCount == 0) { - return new TopicBrief(topic, topicDescription.partitions().size(), 0); - } else { - return new TopicBrief(topic, topicDescription.partitions().size(), - ((double) isrCount / replicateCount)); - } - }).collect(toList()); + topicMap = + describeTopicsResult.all().get(kafkaAdminClientGetTimeoutMs, TimeUnit.MILLISECONDS); + result = + topicMap + .entrySet() + .parallelStream() + .map( + e -> { + String topic = e.getKey(); + TopicDescription topicDescription = e.getValue(); + List topicPartitionInfoList = + topicDescription.partitions(); + int replicateCount = 0; + int isrCount = 0; + for (org.apache.kafka.common.TopicPartitionInfo topicPartitionInfo : + topicPartitionInfoList) { + replicateCount += topicPartitionInfo.replicas().size(); + isrCount += topicPartitionInfo.isr().size(); + } + if (replicateCount == 0) { + return new TopicBrief(topic, topicDescription.partitions().size(), 0); + } else { + return new TopicBrief( + topic, + topicDescription.partitions().size(), + ((double) isrCount / replicateCount)); + } + }) + .collect(toList()); } catch (Exception exception) { log.warn("Describe all topics exception:" + exception); throw new ApiException("Describe all topics exception:" + exception); @@ -340,18 +321,23 @@ public List listTopicBrief() { } public boolean existTopic(String topicName) { - return kafkaZkClient.topicExists(topicName); + KafkaZkClient kafkaZkClient = zookeeperUtils.createKafkaZkClient(); + boolean exists = kafkaZkClient.topicExists(topicName); + + kafkaZkClient.close(); + + return exists; } public Map describeCluster() { Map clusterDetail = new HashMap<>(); org.apache.kafka.clients.admin.AdminClient kafkaAdminClient = createKafkaAdminClient(); - DescribeClusterOptions describeClusterOptions = new DescribeClusterOptions() - .timeoutMs((int) kafkaAdminClientGetTimeoutMs); + DescribeClusterOptions describeClusterOptions = + new DescribeClusterOptions().timeoutMs((int) kafkaAdminClientGetTimeoutMs); - DescribeClusterResult describeClusterResult = kafkaAdminClient - .describeCluster(describeClusterOptions); + DescribeClusterResult describeClusterResult = + kafkaAdminClient.describeCluster(describeClusterOptions); KafkaFuture clusterIdFuture = describeClusterResult.clusterId(); KafkaFuture controllerFuture = describeClusterResult.controller(); @@ -385,51 +371,65 @@ public Map describeCluster() { } public List listBrokers() { - List brokerList = CollectionConvertor - .seqConvertJavaList(kafkaZkClient.getAllBrokersInCluster()); - - return brokerList.parallelStream().collect(Collectors.toMap(Broker::id, Broker::rack)) - .entrySet().parallelStream() - .map(entry -> { - String brokerInfoStr = null; - try { - //TODO replace zkClient with kafkaZKClient - brokerInfoStr = new String( - zkClient.getData().forPath(ZkUtils.BrokerIdsPath() + "/" + entry.getKey()) - ); - } catch (Exception e) { - e.printStackTrace(); - } - BrokerInfo brokerInfo = gson.fromJson(brokerInfoStr, BrokerInfo.class); - if (entry.getValue().isEmpty()) { - brokerInfo.setRack(""); - } else { - brokerInfo.setRack(entry.getValue().get()); - } - brokerInfo.setId(entry.getKey()); - return brokerInfo; - }).collect(toList()); + KafkaZkClient kafkaZkClient = zookeeperUtils.createKafkaZkClient(); + List brokerList = + CollectionConvertor.seqConvertJavaList(kafkaZkClient.getAllBrokersInCluster()); + + kafkaZkClient.close(); + return brokerList + .parallelStream() + .collect(Collectors.toMap(Broker::id, Broker::rack)) + .entrySet() + .parallelStream() + .map( + entry -> { + String brokerInfoStr = null; + try { + // TODO replace zkClient with kafkaZKClient + brokerInfoStr = + new String( + zkClient.getData().forPath(ZkUtils.BrokerIdsPath() + "/" + entry.getKey())); + } catch (Exception e) { + e.printStackTrace(); + } + BrokerInfo brokerInfo = gson.fromJson(brokerInfoStr, BrokerInfo.class); + if (entry.getValue().isEmpty()) { + brokerInfo.setRack(""); + } else { + brokerInfo.setRack(entry.getValue().get()); + } + brokerInfo.setId(entry.getKey()); + return brokerInfo; + }) + .collect(toList()); } public int getControllerId() { + KafkaZkClient kafkaZkClient = zookeeperUtils.createKafkaZkClient(); + int controllerId = -1; - if (!kafkaZkClient.getControllerId().equals(NONE)) { + if (!kafkaZkClient.getControllerId().equals(none)) { controllerId = (int) kafkaZkClient.getControllerId().get(); } + kafkaZkClient.close(); + return controllerId; } public Map> listLogDirsByBroker(List brokerList) { Map> logDirList = new HashMap<>(); - Map> logDirInfosByBroker = describeLogDirsByBrokerAndTopic( - brokerList, null); - logDirInfosByBroker.entrySet().forEach(e -> { - List dirList = e.getValue().keySet().stream().collect(Collectors.toList()); - Collections.sort(dirList); - logDirList.put(e.getKey(), dirList); - }); + Map> logDirInfosByBroker = + describeLogDirsByBrokerAndTopic(brokerList, null); + logDirInfosByBroker + .entrySet() + .forEach( + e -> { + List dirList = e.getValue().keySet().stream().collect(Collectors.toList()); + Collections.sort(dirList); + logDirList.put(e.getKey(), dirList); + }); return logDirList; } @@ -438,8 +438,8 @@ public Map> describeLogDirsByBrokerAndTopic( List brokerList, List topicList) { org.apache.kafka.clients.admin.AdminClient kafkaAdminClient = createKafkaAdminClient(); - List brokerIdsInCluster = listBrokers().stream().map(brokerInfo -> brokerInfo.getId()) - .collect(Collectors.toList()); + List brokerIdsInCluster = + listBrokers().stream().map(brokerInfo -> brokerInfo.getId()).collect(Collectors.toList()); if (brokerList == null || brokerList.isEmpty()) { brokerList = brokerIdsInCluster; @@ -459,26 +459,34 @@ public Map> describeLogDirsByBrokerAndTopic( } } - DescribeLogDirsOptions describeLogDirsOptions = new DescribeLogDirsOptions() - .timeoutMs((int) kafkaAdminClientGetTimeoutMs); - DescribeLogDirsResult describeLogDirsResult = kafkaAdminClient - .describeLogDirs(brokerList, describeLogDirsOptions); + DescribeLogDirsOptions describeLogDirsOptions = + new DescribeLogDirsOptions().timeoutMs((int) kafkaAdminClientGetTimeoutMs); + DescribeLogDirsResult describeLogDirsResult = + kafkaAdminClient.describeLogDirs(brokerList, describeLogDirsOptions); Map> logDirInfosByBroker = new HashMap<>(); try { - logDirInfosByBroker = describeLogDirsResult.all() - .get(kafkaAdminClientGetTimeoutMs, TimeUnit.MILLISECONDS); + logDirInfosByBroker = + describeLogDirsResult.all().get(kafkaAdminClientGetTimeoutMs, TimeUnit.MILLISECONDS); } catch (Exception exception) { log.warn("Describe log dirs exception:" + exception); throw new ApiException("Describe log dirs exception:" + exception); } finally { if (topicList != null && !topicList.isEmpty()) { - logDirInfosByBroker.entrySet().forEach(e -> { - e.getValue().entrySet().forEach(m -> { - m.getValue().replicaInfos.entrySet().removeIf(t -> - !topicList.contains(t.getKey().topic())); - }); - }); + logDirInfosByBroker + .entrySet() + .forEach( + e -> { + e.getValue() + .entrySet() + .forEach( + m -> { + m.getValue() + .replicaInfos + .entrySet() + .removeIf(t -> !topicList.contains(t.getKey().topic())); + }); + }); } } @@ -492,14 +500,16 @@ public Map describeReplicaLogDirs( org.apache.kafka.clients.admin.AdminClient kafkaAdminClient = createKafkaAdminClient(); Map replicaLogDirInfoMap; - DescribeReplicaLogDirsOptions describeReplicaLogDirsOptions = new DescribeReplicaLogDirsOptions() - .timeoutMs((int) kafkaAdminClientGetTimeoutMs); - DescribeReplicaLogDirsResult describeReplicaLogDirsResult = kafkaAdminClient - .describeReplicaLogDirs(replicas, describeReplicaLogDirsOptions); + DescribeReplicaLogDirsOptions describeReplicaLogDirsOptions = + new DescribeReplicaLogDirsOptions().timeoutMs((int) kafkaAdminClientGetTimeoutMs); + DescribeReplicaLogDirsResult describeReplicaLogDirsResult = + kafkaAdminClient.describeReplicaLogDirs(replicas, describeReplicaLogDirsOptions); try { - replicaLogDirInfoMap = describeReplicaLogDirsResult.all() - .get(kafkaAdminClientGetTimeoutMs, TimeUnit.MILLISECONDS); + replicaLogDirInfoMap = + describeReplicaLogDirsResult + .all() + .get(kafkaAdminClientGetTimeoutMs, TimeUnit.MILLISECONDS); } catch (Exception exception) { log.warn("Describe replica log dirs exception:" + exception); throw new ApiException("Describe replica log dirs exception:" + exception); @@ -520,6 +530,8 @@ public Collection getBrokerConf(int brokerId) { } public Properties getConfigInZk(ConfigResource.Type type, String name) { + KafkaZkClient kafkaZkClient = zookeeperUtils.createKafkaZkClient(); + AdminZkClient adminZkClient = new AdminZkClient(kafkaZkClient); Properties properties = new Properties(); if (type.equals(Type.BROKER)) { @@ -528,11 +540,13 @@ public Properties getConfigInZk(ConfigResource.Type type, String name) { properties = adminZkClient.fetchEntityConfig(ConfigType.Topic(), name); } + kafkaZkClient.close(); + return properties; } - private Collection mergeConfigs(Collection configs, - Properties dynamicProps) { + private Collection mergeConfigs( + Collection configs, Properties dynamicProps) { Collection mergedConfigs = new ArrayList<>(); CustomConfigEntry newConfigEntry; @@ -542,16 +556,17 @@ private Collection mergeConfigs(Collection confi if (dynamicProps.containsKey(key)) { value = dynamicProps.getProperty(key); } - newConfigEntry = new CustomConfigEntry(key, value, entry.isSensitive(), - entry.isReadOnly(), entry.source()); + newConfigEntry = + new CustomConfigEntry( + key, value, entry.isSensitive(), entry.isReadOnly(), entry.source()); mergedConfigs.add(newConfigEntry); } return mergedConfigs; } - private void validateConfigs(Type type, String name, Properties props, - List keysToBeChecked) { + private void validateConfigs( + Type type, String name, Properties props, List keysToBeChecked) { for (Object configKey : keysToBeChecked) { if (!props.containsKey(configKey)) { throw new ApiException(type.name() + ":" + name + " has no such property:" + configKey); @@ -562,7 +577,10 @@ private void validateConfigs(Type type, String name, Properties props, public Properties updateBrokerDynConf(int brokerId, Properties propsToBeUpdated) { Properties props = getConfigInZk(Type.BROKER, String.valueOf(brokerId)); - for (String key:propsToBeUpdated.stringPropertyNames()) { + KafkaZkClient kafkaZkClient = zookeeperUtils.createKafkaZkClient(); + AdminZkClient adminZkClient = new AdminZkClient(kafkaZkClient); + + for (String key : propsToBeUpdated.stringPropertyNames()) { if (props.containsKey(key)) { props.setProperty(key, propsToBeUpdated.getProperty(key)); } else { @@ -573,12 +591,18 @@ public Properties updateBrokerDynConf(int brokerId, Properties propsToBeUpdated) adminZkClient.changeBrokerConfig( JavaConverters.asScalaBufferConverter(Collections.singletonList((Object) brokerId)) .asScala() - .toSeq(), props); + .toSeq(), + props); + + kafkaZkClient.close(); return getConfigInZk(Type.BROKER, String.valueOf(brokerId)); } public void removeConfigInZk(Type type, String name, List configKeysToBeRemoved) { + KafkaZkClient kafkaZkClient = zookeeperUtils.createKafkaZkClient(); + AdminZkClient adminZkClient = new AdminZkClient(kafkaZkClient); + Properties props = getConfigInZk(type, name); validateConfigs(Type.BROKER, String.valueOf(name), props, configKeysToBeRemoved); @@ -590,41 +614,47 @@ public void removeConfigInZk(Type type, String name, List configKeysToBe adminZkClient.changeBrokerConfig( JavaConverters.asScalaBufferConverter(Collections.singletonList((Object) brokerId)) .asScala() - .toSeq(), props); + .toSeq(), + props); } else if (type.equals(Type.TOPIC)) { adminZkClient.changeTopicConfig(name, props); } + + kafkaZkClient.close(); } public TopicMeta describeTopic(@TopicExistConstraint String topicName) { org.apache.kafka.clients.admin.AdminClient kafkaAdminClient = createKafkaAdminClient(); - DescribeTopicsResult describeTopicsResult = kafkaAdminClient - .describeTopics(Collections.singletonList(topicName)); + DescribeTopicsResult describeTopicsResult = + kafkaAdminClient.describeTopics(Collections.singletonList(topicName)); TopicMeta topicMeta = new TopicMeta(topicName); try { - Map topicMap = describeTopicsResult.all() - .get(kafkaAdminClientGetTimeoutMs, TimeUnit.MILLISECONDS); + Map topicMap = + describeTopicsResult.all().get(kafkaAdminClientGetTimeoutMs, TimeUnit.MILLISECONDS); if (topicMap.containsKey(topicName)) { TopicDescription topicDescription = topicMap.get(topicName); List tmList = topicDescription.partitions(); topicMeta.setInternal(topicDescription.isInternal()); topicMeta.setPartitionCount(topicDescription.partitions().size()); topicMeta.setReplicationFactor(tmList.get(0).replicas().size()); - topicMeta.setTopicPartitionInfos(tmList.parallelStream().map( - tm -> { - CustomTopicPartitionInfo customTopicPartitionInfo = new CustomTopicPartitionInfo(); - customTopicPartitionInfo.setTopicPartitionInfo(tm); - customTopicPartitionInfo.setIn_sync(); - customTopicPartitionInfo - .setStartOffset(getBeginningOffset(topicName, tm.partition())); - customTopicPartitionInfo - .setEndOffset(getEndOffset(tm.leader(), topicName, tm.partition())); - customTopicPartitionInfo.setMessageAvailable(); - return customTopicPartitionInfo; - - }).collect(toList()) - ); + topicMeta.setTopicPartitionInfos( + tmList + .parallelStream() + .map( + tm -> { + CustomTopicPartitionInfo customTopicPartitionInfo = + new CustomTopicPartitionInfo(); + customTopicPartitionInfo.setTopicPartitionInfo(tm); + customTopicPartitionInfo.setIn_sync(); + customTopicPartitionInfo.setStartOffset( + getBeginningOffset(topicName, tm.partition())); + customTopicPartitionInfo.setEndOffset( + getEndOffset(tm.leader(), topicName, tm.partition())); + customTopicPartitionInfo.setMessageAvailable(); + return customTopicPartitionInfo; + }) + .collect(toList())); Collections.sort(topicMeta.getTopicPartitionInfos()); } } catch (Exception exception) { @@ -647,16 +677,23 @@ public Map deleteTopicList(List topicList) { } catch (Exception exception) { log.warn("Delete topic exception:" + exception); } finally { - deleteTopicsResult.values().forEach((topic, result) -> { - GeneralResponse generalResponse; - if (result.isDone() && !result.isCompletedExceptionally()) { - generalResponse = GeneralResponse.builder().state(GeneralResponseState.success).build(); - } else { - generalResponse = GeneralResponse.builder().state(GeneralResponseState.failure) - .msg(result.toString()).build(); - } - deleteResults.put(topic, generalResponse); - }); + deleteTopicsResult + .values() + .forEach( + (topic, result) -> { + GeneralResponse generalResponse; + if (result.isDone() && !result.isCompletedExceptionally()) { + generalResponse = + GeneralResponse.builder().state(GeneralResponseState.success).build(); + } else { + generalResponse = + GeneralResponse.builder() + .state(GeneralResponseState.failure) + .msg(result.toString()) + .build(); + } + deleteResults.put(topic, generalResponse); + }); } kafkaAdminClient.close(); @@ -668,8 +705,8 @@ public Collection describeConfig(ConfigResource.Type type, String n org.apache.kafka.clients.admin.AdminClient kafkaAdminClient = createKafkaAdminClient(); ConfigResource configResource = new ConfigResource(type, name); - DescribeConfigsResult ret = kafkaAdminClient - .describeConfigs(Collections.singleton(configResource)); + DescribeConfigsResult ret = + kafkaAdminClient.describeConfigs(Collections.singleton(configResource)); try { configs = ret.all().get(kafkaAdminClientGetTimeoutMs, TimeUnit.MILLISECONDS); } catch (Exception exception) { @@ -681,12 +718,13 @@ public Collection describeConfig(ConfigResource.Type type, String n return configs.get(configResource).entries(); } - public boolean alterConfig(ConfigResource.Type type, String name, - Collection configEntries) { + public boolean alterConfig( + ConfigResource.Type type, String name, Collection configEntries) { org.apache.kafka.clients.admin.AdminClient kafkaAdminClient = createKafkaAdminClient(); Config config = new Config(configEntries); - AlterConfigsResult alterConfigsResult = kafkaAdminClient - .alterConfigs(Collections.singletonMap(new ConfigResource(type, name), config)); + AlterConfigsResult alterConfigsResult = + kafkaAdminClient.alterConfigs( + Collections.singletonMap(new ConfigResource(type, name), config)); try { alterConfigsResult.all().get(kafkaAdminClientAlterTimeoutMs, TimeUnit.MILLISECONDS); @@ -700,11 +738,14 @@ public boolean alterConfig(ConfigResource.Type type, String name, return true; } - public Collection updateTopicConf(@TopicExistConstraint String topic, - Properties props) { - Collection configEntries = props.entrySet().stream().map( - e -> new ConfigEntry(e.getKey().toString(), e.getValue().toString())) - .collect(Collectors.toList()); + public Collection updateTopicConf( + @TopicExistConstraint String topic, Properties props) { + Collection configEntries = + props + .entrySet() + .stream() + .map(e -> new ConfigEntry(e.getKey().toString(), e.getValue().toString())) + .collect(Collectors.toList()); alterConfig(ConfigResource.Type.TOPIC, topic, configEntries); return getTopicConf(topic); @@ -731,10 +772,10 @@ public Properties getTopicConfByKey(@TopicExistConstraint String topic, String k return null; } - public Collection updateTopicConfByKey(@TopicExistConstraint String topic, - String key, String value) { - alterConfig(ConfigResource.Type.TOPIC, topic, - Collections.singletonList(new ConfigEntry(key, value))); + public Collection updateTopicConfByKey( + @TopicExistConstraint String topic, String key, String value) { + alterConfig( + ConfigResource.Type.TOPIC, topic, Collections.singletonList(new ConfigEntry(key, value))); return getTopicConf(topic); } @@ -743,16 +784,16 @@ public Map> listAllConsumerGroups(ConsumerType type) { Map> result = new HashMap<>(); if (type == null || type == ConsumerType.OLD) { - Set oldCGList = listAllOldConsumerGroups(); - if (oldCGList.size() != 0) { - result.put("old", oldCGList); + Set oldConsumerGroupList = listAllOldConsumerGroups(); + if (oldConsumerGroupList.size() != 0) { + result.put("old", oldConsumerGroupList); } } if (type == null || type == ConsumerType.NEW) { - Set newCGList = listAllNewConsumerGroups(); - if (newCGList.size() != 0) { - result.put("new", newCGList); + Set newConsumerGroupList = listAllNewConsumerGroups(); + if (newConsumerGroupList.size() != 0) { + result.put("new", newConsumerGroupList); } } @@ -762,9 +803,11 @@ public Map> listAllConsumerGroups(ConsumerType type) { private Set listAllNewConsumerGroups() { AdminClient adminClient = kafkaUtils.createAdminClient(); log.info("Calling the listAllConsumerGroupsFlattened"); - Set activeGroups = CollectionConvertor - .seqConvertJavaList(adminClient.listAllConsumerGroupsFlattened()).stream() - .map(GroupOverview::groupId).collect(toSet()); + Set activeGroups = + CollectionConvertor.seqConvertJavaList(adminClient.listAllConsumerGroupsFlattened()) + .stream() + .map(GroupOverview::groupId) + .collect(toSet()); log.info("Finish getting new consumers"); adminClient.close(); @@ -773,33 +816,39 @@ private Set listAllNewConsumerGroups() { private Set listAllOldConsumerGroups() { log.info("Finish getting old consumers"); - return CollectionConvertor - .seqConvertJavaList(kafkaZkClient.getChildren(ZkUtils.ConsumersPath())).stream() - .collect(toSet()); + KafkaZkClient kafkaZkClient = zookeeperUtils.createKafkaZkClient(); + + Set oldConsumerGroups = + CollectionConvertor.seqConvertJavaList(kafkaZkClient.getChildren(ZkUtils.ConsumersPath())) + .stream() + .collect(toSet()); + + kafkaZkClient.close(); + + return oldConsumerGroups; } public Map> listConsumerGroupsByTopic( - @TopicExistConstraint String topic, - ConsumerType type) { + @TopicExistConstraint String topic, ConsumerType type) { Map> result = new HashMap<>(); if (type == null || type == ConsumerType.OLD) { - Set oldCGList = null; + Set oldConsumerGroupList = new HashSet<>(); try { - oldCGList = listOldConsumerGroupsByTopic(topic); + oldConsumerGroupList = listOldConsumerGroupsByTopic(topic); } catch (Exception e) { e.printStackTrace(); } - if (oldCGList.size() != 0) { - result.put("old", oldCGList); + if (oldConsumerGroupList.size() != 0) { + result.put("old", oldConsumerGroupList); } } if (type == null || type == ConsumerType.NEW) { - Set newCGList = listNewConsumerGroupsByTopic(topic); + Set newConsumerGroupList = listNewConsumerGroupsByTopic(topic); - if (newCGList.size() != 0) { - result.put("new", newCGList); + if (newConsumerGroupList.size() != 0) { + result.put("new", newConsumerGroupList); } } @@ -821,26 +870,31 @@ private Set listNewConsumerGroupsByTopic(@TopicExistConstraint String to } private Set listOldConsumerGroupsByTopic(@TopicExistConstraint String topic) { - List consumersFromZk = CollectionConvertor - .seqConvertJavaList(kafkaZkClient.getChildren(ZkUtils.ConsumersPath())); - Set cList = new HashSet<>(); + KafkaZkClient kafkaZkClient = zookeeperUtils.createKafkaZkClient(); + + List consumersFromZk = + CollectionConvertor.seqConvertJavaList(kafkaZkClient.getChildren(ZkUtils.ConsumersPath())); + Set consumerList = new HashSet<>(); for (String consumer : consumersFromZk) { String path = ZkUtils.ConsumersPath() + "/" + consumer + "/offsets"; List topics = CollectionConvertor.seqConvertJavaList(kafkaZkClient.getChildren(path)); if (topics != null && topics.contains(topic)) { - cList.add(consumer); + consumerList.add(consumer); } } - return cList; + kafkaZkClient.close(); + + return consumerList; } - public Set listTopicsByCG(String consumerGroup, ConsumerType type) { + public Set listTopicsByConsumerGroup(String consumerGroup, ConsumerType type) { + KafkaZkClient kafkaZkClient = zookeeperUtils.createKafkaZkClient(); Set topicList = new HashSet<>(); if (type == null) { - throw new ApiException("Unknown Type " + type); + throw new ApiException("Bad Request since type is null."); } if (type == ConsumerType.OLD) { @@ -848,8 +902,10 @@ public Set listTopicsByCG(String consumerGroup, ConsumerType type) { throw new RuntimeException(consumerGroup + " non-exist"); } String path = ZkUtils.ConsumersPath() + "/" + consumerGroup + "/offsets"; - topicList = CollectionConvertor.seqConvertJavaList(kafkaZkClient.getChildren(path)).stream() - .collect(toSet()); + topicList = + CollectionConvertor.seqConvertJavaList(kafkaZkClient.getChildren(path)) + .stream() + .collect(toSet()); } else if (type == ConsumerType.NEW) { if (!isNewConsumerGroup(consumerGroup)) { throw new RuntimeException(consumerGroup + " non-exist!"); @@ -859,17 +915,20 @@ public Set listTopicsByCG(String consumerGroup, ConsumerType type) { throw new ApiException("Unknown Type " + type); } - return topicList; + kafkaZkClient.close(); + return topicList; } private List getTopicListByConsumerGroup(String consumerGroup) { AdminClient adminClient = kafkaUtils.createAdminClient(); - Map groupOffsets = CollectionConvertor - .mapConvertJavaMap(adminClient.listGroupOffsets(consumerGroup)); + Map groupOffsets = + CollectionConvertor.mapConvertJavaMap(adminClient.listGroupOffsets(consumerGroup)); adminClient.close(); - return groupOffsets.entrySet().stream() + return groupOffsets + .entrySet() + .stream() .map(topicPartitionObjectEntry -> topicPartitionObjectEntry.getKey().topic()) .collect(toList()); } @@ -879,20 +938,28 @@ public ConsumerGroupMeta getConsumerGroupMeta(String consumerGroup) { AdminClient adminClient = kafkaUtils.createAdminClient(); ConsumerGroupSummary consumerGroupSummary = adminClient.describeConsumerGroup(consumerGroup, 0); - List consumerSummaryList = CollectionConvertor - .optionListConvertJavaList(consumerGroupSummary.consumers().get()); - - ConsumerGroupMeta consumerGroupMeta = ConsumerGroupMeta.builder().groupId(consumerGroup) - .state(ConsumerGroupState.parse(consumerGroupSummary.state())) - .assignmentStrategy(consumerGroupSummary.assignmentStrategy()) - .coordinator(consumerGroupSummary.coordinator()).build(); - - consumerSummaryList.forEach(consumerSummary -> { - List topicPartitions = CollectionConvertor - .listConvertJavaList(consumerSummary.assignment()); - members.add(new MemberDescription(consumerSummary.consumerId(), - consumerSummary.clientId(), consumerSummary.host(), topicPartitions)); - }); + List consumerSummaryList = + CollectionConvertor.optionListConvertJavaList(consumerGroupSummary.consumers().get()); + + ConsumerGroupMeta consumerGroupMeta = + ConsumerGroupMeta.builder() + .groupId(consumerGroup) + .state(ConsumerGroupState.parse(consumerGroupSummary.state())) + .assignmentStrategy(consumerGroupSummary.assignmentStrategy()) + .coordinator(consumerGroupSummary.coordinator()) + .build(); + + consumerSummaryList.forEach( + consumerSummary -> { + List topicPartitions = + CollectionConvertor.listConvertJavaList(consumerSummary.assignment()); + members.add( + new MemberDescription( + consumerSummary.consumerId(), + consumerSummary.clientId(), + consumerSummary.host(), + topicPartitions)); + }); consumerGroupMeta.setMembers(members); adminClient.close(); @@ -905,90 +972,100 @@ public boolean isOldConsumerGroup(String consumerGroup) { } public boolean isNewConsumerGroup(String consumerGroup) { - //Active Consumergroup or Dead ConsumerGroup is OK + // Active Consumergroup or Dead ConsumerGroup is OK return (listAllNewConsumerGroups().contains(consumerGroup)); } - public Map> describeConsumerGroup(String consumerGroup, - ConsumerType type) { + public Map> describeConsumerGroup( + String consumerGroup, ConsumerType type) { Map> result = new HashMap<>(); - Set topicList = listTopicsByCG(consumerGroup, type); + Set topicList = listTopicsByConsumerGroup(consumerGroup, type); if (topicList == null) { - //Return empty result + // Return empty result return result; } if (type == ConsumerType.NEW) { if (!isNewConsumerGroup(consumerGroup)) { throw new RuntimeException(consumerGroup + " non-exist!"); } - List partitionAssignmentStateList = describeNewConsumerGroup( - consumerGroup, false, null); + List partitionAssignmentStateList = + describeNewConsumerGroup(consumerGroup, false, null); result = transPasListToMap(consumerGroup, partitionAssignmentStateList, ConsumerType.NEW); } else if (type == ConsumerType.OLD) { if (!isOldConsumerGroup(consumerGroup)) { throw new RuntimeException(consumerGroup + " non-exist"); } - List partitionAssignmentStateList = describeOldConsumerGroup( - consumerGroup, false, null); + List partitionAssignmentStateList = + describeOldConsumerGroup(consumerGroup, false, null); result = transPasListToMap(consumerGroup, partitionAssignmentStateList, ConsumerType.OLD); } return result; } - private Map> transPasListToMap(String consumerGroup, - List pasList, ConsumerType type) { + private Map> transPasListToMap( + String consumerGroup, List pasList, ConsumerType type) { Map> result = new HashMap<>(); ConsumerGroupSummary consumerGroupSummary; if (type.equals(ConsumerType.NEW)) { AdminClient adminClient = kafkaUtils.createAdminClient(); - consumerGroupSummary = adminClient - .describeConsumerGroup(consumerGroup, 0); + consumerGroupSummary = adminClient.describeConsumerGroup(consumerGroup, 0); adminClient.close(); } else { - //Old consumer group has no state, coordinator, assignmentStrategy info + // Old consumer group has no state, coordinator, assignmentStrategy info consumerGroupSummary = null; } - pasList.forEach(partitionAssignmentState -> { - String topic = partitionAssignmentState.getTopic(); - List consumerGroupDescs; - if (result.containsKey(topic)) { - consumerGroupDescs = result.get(topic); - } else { - consumerGroupDescs = new ArrayList<>(); - } - consumerGroupDescs.add( - convertParitionAssignmentStateToGroupDesc(consumerGroup, consumerGroupSummary, - partitionAssignmentState, type)); - result.put(topic, consumerGroupDescs); - }); + pasList.forEach( + partitionAssignmentState -> { + String topic = partitionAssignmentState.getTopic(); + List consumerGroupDescs; + if (result.containsKey(topic)) { + consumerGroupDescs = result.get(topic); + } else { + consumerGroupDescs = new ArrayList<>(); + } + consumerGroupDescs.add( + convertParitionAssignmentStateToGroupDesc( + consumerGroup, consumerGroupSummary, partitionAssignmentState, type)); + result.put(topic, consumerGroupDescs); + }); return result; } - private ConsumerGroupDesc convertParitionAssignmentStateToGroupDesc(String consumerGroup, - ConsumerGroupSummary consumerGroupSummary, PartitionAssignmentState pas, ConsumerType type) { - ConsumerGroupDesc.ConsumerGroupDescBuilder consumerGroupDescBuilder = ConsumerGroupDesc - .builder().groupName(consumerGroup) - .topic(pas.getTopic()) - .partitionId(pas.getPartition()).currentOffset(pas.getOffset()) - .logEndOffset(pas.getLogEndOffset()).lag(pas.getLag()).consumerId(pas.getConsumerId()) - .consumerId(pas.getConsumerId()).host(pas.getHost()).type(type); + private ConsumerGroupDesc convertParitionAssignmentStateToGroupDesc( + String consumerGroup, + ConsumerGroupSummary consumerGroupSummary, + PartitionAssignmentState pas, + ConsumerType type) { + ConsumerGroupDesc.ConsumerGroupDescBuilder consumerGroupDescBuilder = + ConsumerGroupDesc.builder() + .groupName(consumerGroup) + .topic(pas.getTopic()) + .partitionId(pas.getPartition()) + .currentOffset(pas.getOffset()) + .logEndOffset(pas.getLogEndOffset()) + .lag(pas.getLag()) + .consumerId(pas.getConsumerId()) + .consumerId(pas.getConsumerId()) + .host(pas.getHost()) + .type(type); if (consumerGroupSummary != null) { - consumerGroupDescBuilder = consumerGroupDescBuilder - .state(ConsumerGroupState.parse(consumerGroupSummary.state())) - .assignmentStrategy(consumerGroupSummary.assignmentStrategy()). - coordinator(consumerGroupSummary.coordinator()); + consumerGroupDescBuilder = + consumerGroupDescBuilder + .state(ConsumerGroupState.parse(consumerGroupSummary.state())) + .assignmentStrategy(consumerGroupSummary.assignmentStrategy()) + .coordinator(consumerGroupSummary.coordinator()); } return consumerGroupDescBuilder.build(); } - public List describeNewConsumerGroup(String consumerGroup, - boolean filtered, String topic) { + public List describeNewConsumerGroup( + String consumerGroup, boolean filtered, String topic) { List partitionAssignmentStateList = new ArrayList<>(); if (filtered && !existTopic(topic)) { @@ -997,16 +1074,21 @@ public List describeNewConsumerGroup(String consumerGr AdminClient adminClient = kafkaUtils.createAdminClient(); ConsumerGroupSummary consumerGroupSummary = adminClient.describeConsumerGroup(consumerGroup, 0); - List consumerSummaryList = CollectionConvertor - .listConvertJavaList(consumerGroupSummary.consumers().get()); + List consumerSummaryList = + CollectionConvertor.listConvertJavaList(consumerGroupSummary.consumers().get()); if (consumerSummaryList != null) { - Map offsets = CollectionConvertor - .mapConvertJavaMap(adminClient.listGroupOffsets(consumerGroup)); + Map offsets = + CollectionConvertor.mapConvertJavaMap(adminClient.listGroupOffsets(consumerGroup)); Map offsetsFiltered; if (filtered && existTopic(topic)) { - offsetsFiltered = offsets.entrySet().stream().filter( - topicPartitionObjectEntry -> topicPartitionObjectEntry.getKey().topic().equals(topic)) - .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + offsetsFiltered = + offsets + .entrySet() + .stream() + .filter( + topicPartitionObjectEntry -> + topicPartitionObjectEntry.getKey().topic().equals(topic)) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); } else { offsetsFiltered = offsets; } @@ -1014,39 +1096,63 @@ public List describeNewConsumerGroup(String consumerGr return partitionAssignmentStateList; } else { ArrayList assignedTopicPartitions = new ArrayList<>(); - List rowsWithConsumer = consumerSummaryList.stream() - .flatMap(consumerSummary -> { - List topicPartitions = CollectionConvertor - .listConvertJavaList(consumerSummary.assignment()); - List topicPartitionsFiltered = topicPartitions; - if (filtered) { - topicPartitionsFiltered = topicPartitions.stream() - .filter(topicPartition -> topicPartition.topic().equals(topic)) - .collect(toList()); - } - assignedTopicPartitions.addAll(topicPartitionsFiltered); - Map partitionOffsets = topicPartitionsFiltered.stream() - .collect( - toMap(Function.identity(), - topicPartition -> offsetsFiltered.get(topicPartition))); - return collectConsumerAssignment(consumerGroup, consumerGroupSummary.coordinator(), - topicPartitionsFiltered, partitionOffsets, consumerSummary.consumerId(), - consumerSummary.host(), consumerSummary.clientId()).stream(); - }).collect(toList()); + List rowsWithConsumer = + consumerSummaryList + .stream() + .flatMap( + consumerSummary -> { + List topicPartitions = + CollectionConvertor.listConvertJavaList(consumerSummary.assignment()); + List topicPartitionsFiltered = topicPartitions; + if (filtered) { + topicPartitionsFiltered = + topicPartitions + .stream() + .filter(topicPartition -> topicPartition.topic().equals(topic)) + .collect(toList()); + } + assignedTopicPartitions.addAll(topicPartitionsFiltered); + Map partitionOffsets = + topicPartitionsFiltered + .stream() + .collect( + toMap( + Function.identity(), + topicPartition -> offsetsFiltered.get(topicPartition))); + return collectConsumerAssignment( + consumerGroup, + consumerGroupSummary.coordinator(), + topicPartitionsFiltered, + partitionOffsets, + consumerSummary.consumerId(), + consumerSummary.host(), + consumerSummary.clientId()) + .stream(); + }) + .collect(toList()); List topicPartitionList = new ArrayList<>(); Map partitionOffsets = new HashMap<>(); - offsetsFiltered.entrySet().forEach(topicPartitionObjectEntry -> { - if (!assignedTopicPartitions.contains(topicPartitionObjectEntry.getKey())) { - topicPartitionList.add(topicPartitionObjectEntry.getKey()); - partitionOffsets - .put(topicPartitionObjectEntry.getKey(), topicPartitionObjectEntry.getValue()); - } - }); - List rowsWithoutConsumer = collectConsumerAssignment( - consumerGroup, consumerGroupSummary.coordinator(), - topicPartitionList, partitionOffsets, "-", "-", "-"); + offsetsFiltered + .entrySet() + .forEach( + topicPartitionObjectEntry -> { + if (!assignedTopicPartitions.contains(topicPartitionObjectEntry.getKey())) { + topicPartitionList.add(topicPartitionObjectEntry.getKey()); + partitionOffsets.put( + topicPartitionObjectEntry.getKey(), topicPartitionObjectEntry.getValue()); + } + }); + List rowsWithoutConsumer = + collectConsumerAssignment( + consumerGroup, + consumerGroupSummary.coordinator(), + topicPartitionList, + partitionOffsets, + "-", + "-", + "-"); partitionAssignmentStateList.addAll(rowsWithConsumer); partitionAssignmentStateList.addAll(rowsWithoutConsumer); @@ -1057,34 +1163,55 @@ public List describeNewConsumerGroup(String consumerGr return partitionAssignmentStateList; } - private List collectConsumerAssignment(String group, Node coordinator, - List topicPartitions, Map partitionOffsets, - String consumerId, String host, String clientId) { + private List collectConsumerAssignment( + String group, + Node coordinator, + List topicPartitions, + Map partitionOffsets, + String consumerId, + String host, + String clientId) { if (topicPartitions.size() == 0) { return new ArrayList(); } else { List list = new ArrayList<>(); - topicPartitions.forEach(topicPartition -> { - long logEndOffset = getEndOffset(topicPartition.topic(), topicPartition.partition()); - long offset = (Long) partitionOffsets.get(topicPartition); - long lag = logEndOffset - offset; - list.add(new PartitionAssignmentState(group, coordinator, topicPartition.topic(), - topicPartition.partition(), offset, lag, consumerId, host, clientId, logEndOffset)); - }); + topicPartitions.forEach( + topicPartition -> { + long logEndOffset = getEndOffset(topicPartition.topic(), topicPartition.partition()); + long offset = (Long) partitionOffsets.get(topicPartition); + long lag = logEndOffset - offset; + list.add( + new PartitionAssignmentState( + group, + coordinator, + topicPartition.topic(), + topicPartition.partition(), + offset, + lag, + consumerId, + host, + clientId, + logEndOffset)); + }); return list; } } - public List describeOldConsumerGroup(String consumerGroup, - boolean filtered, String topic) { + public List describeOldConsumerGroup( + String consumerGroup, boolean filtered, String topic) { List partitionAssignmentStateList = new ArrayList<>(); if (filtered && !existTopic(topic)) { return partitionAssignmentStateList; } - String[] agrs = {"--describe", "--zookeeper", zookeeperUtils.getZookeeperConfig().getUris(), - "--group", consumerGroup}; + String[] agrs = { + "--describe", + "--zookeeper", + zookeeperUtils.getZookeeperConfig().getUris(), + "--group", + consumerGroup + }; ConsumerGroupCommand.ConsumerGroupCommandOptions options = new ConsumerGroupCommand.ConsumerGroupCommandOptions(agrs); ConsumerGroupCommand.ZkConsumerGroupService zkConsumerGroupService = @@ -1094,21 +1221,25 @@ public List describeOldConsumerGroup(String consumerGr mapper.registerModule(new DefaultScalaModule()); mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); try { - String source = mapper - .writeValueAsString(zkConsumerGroupService.collectGroupOffsets()._2().get()); + String source = + mapper.writeValueAsString(zkConsumerGroupService.collectGroupOffsets()._2().get()); System.out.println("source:" + source); - partitionAssignmentStateList = mapper.readValue(source, - getCollectionType(mapper, List.class, PartitionAssignmentState.class)); + partitionAssignmentStateList = + mapper.readValue( + source, getCollectionType(mapper, List.class, PartitionAssignmentState.class)); List partitionAssignmentStateListFiltered; if (filtered && existTopic(topic)) { - partitionAssignmentStateListFiltered = partitionAssignmentStateList.stream() - .filter(partitionAssignmentState -> partitionAssignmentState.getTopic().equals(topic)) - .collect(Collectors.toList()); + partitionAssignmentStateListFiltered = + partitionAssignmentStateList + .stream() + .filter( + partitionAssignmentState -> partitionAssignmentState.getTopic().equals(topic)) + .collect(Collectors.toList()); } else { partitionAssignmentStateListFiltered = partitionAssignmentStateList; } - partitionAssignmentStateListFiltered - .sort(Comparator.comparing(PartitionAssignmentState::getTopic) + partitionAssignmentStateListFiltered.sort( + Comparator.comparing(PartitionAssignmentState::getTopic) .thenComparing(PartitionAssignmentState::getPartition)); } catch (JsonProcessingException jsonProcessingException) { log.error("Describe old consumer group exception.", jsonProcessingException); @@ -1119,45 +1250,55 @@ public List describeOldConsumerGroup(String consumerGr return partitionAssignmentStateList; } - private JavaType getCollectionType(ObjectMapper mapper, - Class collectionClass, - Class... elementClasses) { + private JavaType getCollectionType( + ObjectMapper mapper, Class collectionClass, Class... elementClasses) { return mapper.getTypeFactory().constructParametricType(collectionClass, elementClasses); } - public List describeNewCGByTopic(String consumerGroup, + public List describeNewConsumerGroupByTopic(String consumerGroup, String topic) { if (!isNewConsumerGroup(consumerGroup)) { throw new RuntimeException(consumerGroup + " non-exist!"); } - List partitionAssignmentStateList = describeNewConsumerGroup( - consumerGroup, true, topic); + List partitionAssignmentStateList = + describeNewConsumerGroup(consumerGroup, true, topic); AdminClient adminClient = kafkaUtils.createAdminClient(); - ConsumerGroupSummary consumerGroupSummary = adminClient - .describeConsumerGroup(consumerGroup, 0); + ConsumerGroupSummary consumerGroupSummary = adminClient.describeConsumerGroup(consumerGroup, 0); adminClient.close(); - return partitionAssignmentStateList.stream().map( - partitionAssignmentState -> convertParitionAssignmentStateToGroupDesc(consumerGroup, - consumerGroupSummary, partitionAssignmentState, ConsumerType.NEW)) + return partitionAssignmentStateList + .stream() + .map( + partitionAssignmentState -> + convertParitionAssignmentStateToGroupDesc( + consumerGroup, + consumerGroupSummary, + partitionAssignmentState, + ConsumerType.NEW)) .collect(Collectors.toList()); } - public List describeOldCGByTopic(String consumerGroup, - @TopicExistConstraint String topic) throws InterruptedException, ExecutionException { + public List describeOldConsumerGroupByTopic( + String consumerGroup, @TopicExistConstraint String topic) { if (!isOldConsumerGroup(consumerGroup)) { throw new RuntimeException(consumerGroup + " non-exist"); } - List partitionAssignmentStateList = describeOldConsumerGroup( - consumerGroup, true, topic); + List partitionAssignmentStateList = + describeOldConsumerGroup(consumerGroup, true, topic); ConsumerGroupSummary consumerGroupSummary = null; - return partitionAssignmentStateList.stream().map( - partitionAssignmentState -> convertParitionAssignmentStateToGroupDesc(consumerGroup, - consumerGroupSummary, partitionAssignmentState, ConsumerType.NEW)) + return partitionAssignmentStateList + .stream() + .map( + partitionAssignmentState -> + convertParitionAssignmentStateToGroupDesc( + consumerGroup, + consumerGroupSummary, + partitionAssignmentState, + ConsumerType.NEW)) .collect(Collectors.toList()); } @@ -1166,119 +1307,145 @@ public Map addPartitions(List addPartitio org.apache.kafka.clients.admin.AdminClient kafkaAdminClient = createKafkaAdminClient(); Map newPartitionsMap = new HashMap<>(); - addPartitions.forEach(addPartition -> { - String topic = addPartition.getTopic(); - if (!existTopic(topic)) { - addPartitionsResult.put(topic, GeneralResponse.builder().state(GeneralResponseState.failure) - .msg("Topic:" + topic + " non-exist.").build()); - } else { - TopicMeta topicMeta = describeTopic(topic); - int currentPartionCount = topicMeta.getPartitionCount(); - int numPartitionsAdded = addPartition.getNumPartitionsAdded(); - int totalCount = currentPartionCount + numPartitionsAdded; - String assignmentStr = addPartition.getReplicaAssignment(); - List> newAssignments = new ArrayList<>(); - if (assignmentStr != null && !assignmentStr.equals("")) { - String[] ids = assignmentStr.split(","); - for (int i = 0; i < ids.length; i++) { - String[] replicas = ids[i].split(":"); - List replicasInt = new ArrayList<>(); - for (int j = 0; j < replicas.length; j++) { - replicasInt.add(Integer.parseInt(replicas[j])); + addPartitions.forEach( + addPartition -> { + String topic = addPartition.getTopic(); + if (!existTopic(topic)) { + addPartitionsResult.put( + topic, + GeneralResponse.builder() + .state(GeneralResponseState.failure) + .msg("Topic:" + topic + " non-exist.") + .build()); + } else { + TopicMeta topicMeta = describeTopic(topic); + int currentPartionCount = topicMeta.getPartitionCount(); + int numPartitionsAdded = addPartition.getNumPartitionsAdded(); + int totalCount = currentPartionCount + numPartitionsAdded; + String assignmentStr = addPartition.getReplicaAssignment(); + List> newAssignments = new ArrayList<>(); + if (assignmentStr != null && !assignmentStr.equals("")) { + String[] ids = assignmentStr.split(","); + for (int i = 0; i < ids.length; i++) { + String[] replicas = ids[i].split(":"); + List replicasInt = new ArrayList<>(); + for (int j = 0; j < replicas.length; j++) { + replicasInt.add(Integer.parseInt(replicas[j])); + } + newAssignments.add(replicasInt); + } + } + System.out.println("++++++++newAssignments:" + newAssignments); + NewPartitions newPartitions; + if (newAssignments.isEmpty()) { + // The assignment of new replicas to brokers will be decided by the broker. + newPartitions = NewPartitions.increaseTo(totalCount); + } else { + newPartitions = NewPartitions.increaseTo(totalCount, newAssignments); } - newAssignments.add(replicasInt); + newPartitionsMap.put(topic, newPartitions); } - } - System.out.println("++++++++newAssignments:" + newAssignments); - NewPartitions newPartitions; - if (newAssignments.isEmpty()) { - //The assignment of new replicas to brokers will be decided by the broker. - newPartitions = NewPartitions.increaseTo(totalCount); - } else { - newPartitions = NewPartitions.increaseTo(totalCount, newAssignments); - } - newPartitionsMap.put(topic, newPartitions); - } - }); - CreatePartitionsResult createPartitionsResult = kafkaAdminClient - .createPartitions(newPartitionsMap); + }); + CreatePartitionsResult createPartitionsResult = + kafkaAdminClient.createPartitions(newPartitionsMap); try { createPartitionsResult.all().get(kafkaAdminClientAlterTimeoutMs, TimeUnit.MILLISECONDS); } catch (Exception exception) { log.warn("Add partitions exception: " + exception); } finally { Map> result = createPartitionsResult.values(); - result.forEach((topic, createResult) -> { - GeneralResponse generalResponse; - if (!createResult.isCompletedExceptionally() && createResult.isDone()) { - TopicMeta topicMeta = describeTopic(topic); - generalResponse = GeneralResponse.builder().state(GeneralResponseState.success) - .data(topicMeta).build(); - } else { - generalResponse = GeneralResponse.builder().state(GeneralResponseState.failure) - .msg(createResult.toString()).build(); - } - addPartitionsResult.put(topic, generalResponse); - }); + result.forEach( + (topic, createResult) -> { + GeneralResponse generalResponse; + if (!createResult.isCompletedExceptionally() && createResult.isDone()) { + TopicMeta topicMeta = describeTopic(topic); + generalResponse = + GeneralResponse.builder() + .state(GeneralResponseState.success) + .data(topicMeta) + .build(); + } else { + generalResponse = + GeneralResponse.builder() + .state(GeneralResponseState.failure) + .msg(createResult.toString()) + .build(); + } + addPartitionsResult.put(topic, generalResponse); + }); kafkaAdminClient.close(); return addPartitionsResult; } } - //Return + // Return public List generateReassignPartition(ReassignWrapper reassignWrapper) { - Seq brokerSeq = JavaConverters.asScalaBufferConverter(reassignWrapper.getBrokers()).asScala() - .toSeq(); - // - Tuple2 resultTuple2 = ReassignPartitionsCommand - .generateAssignment(kafkaZkClient, brokerSeq, reassignWrapper.generateReassignJsonString(), - false); + KafkaZkClient kafkaZkClient = zookeeperUtils.createKafkaZkClient(); + + Seq brokerSeq = + JavaConverters.asScalaBufferConverter(reassignWrapper.getBrokers()).asScala().toSeq(); + // + Tuple2 resultTuple2 = + ReassignPartitionsCommand.generateAssignment( + kafkaZkClient, brokerSeq, reassignWrapper.generateReassignJsonString(), false); List result = new ArrayList<>(); HashMap emptyMap = new HashMap<>(); - result.add(ReassignPartitionsCommand - .formatAsReassignmentJson( + result.add( + ReassignPartitionsCommand.formatAsReassignmentJson( (scala.collection.Map>) resultTuple2._1(), JavaConverters.mapAsScalaMapConverter(emptyMap).asScala())); - result.add(ReassignPartitionsCommand - .formatAsReassignmentJson( + result.add( + ReassignPartitionsCommand.formatAsReassignmentJson( (scala.collection.Map>) resultTuple2._2(), JavaConverters.mapAsScalaMapConverter(emptyMap).asScala())); + kafkaZkClient.close(); + return result; } - public Map executeReassignPartition(String reassignStr, - Long interBrokerThrottle, Long replicaAlterLogDirsThrottle, Long timeoutMs) { - //Set default value - interBrokerThrottle = (interBrokerThrottle == null) ? -1 : interBrokerThrottle; + public Map executeReassignPartition( + String reassignStr, + Long interBrokerThrottle, + Long replicaAlterLogDirsThrottle, + Long timeoutMs) { + // Set default value + interBrokerThrottle = (interBrokerThrottle == null) ? Long.valueOf(-1) : interBrokerThrottle; replicaAlterLogDirsThrottle = - (replicaAlterLogDirsThrottle == null) ? -1 : replicaAlterLogDirsThrottle; - timeoutMs = (timeoutMs == null) ? 10000 : timeoutMs; + (replicaAlterLogDirsThrottle == null) ? Long.valueOf(-1) : replicaAlterLogDirsThrottle; + timeoutMs = (timeoutMs == null) ? Long.valueOf(10000) : timeoutMs; org.apache.kafka.clients.admin.AdminClient kafkaAdminClient = createKafkaAdminClient(); - - TwoTuple>, - scala.collection.Map> reassignPlan = genReassignPlan( - reassignStr); - scala.collection.mutable.HashMap> partitionsToBeReassignedMap = reassignPlan - .getFirst(); - scala.collection.Map replicatAssignment = reassignPlan - .getSecond(); - - ReassignPartitionsCommand reassignPartitionsCommand = new ReassignPartitionsCommand( - kafkaZkClient, scala.Option.apply(kafkaAdminClient), - partitionsToBeReassignedMap, - replicatAssignment, adminZkClient); - - Function0 postUpdateAction = new AbstractFunction0() { - @Override - public BoxedUnit apply() { - return null; - } - }; - Throttle throttle = new Throttle(interBrokerThrottle, replicaAlterLogDirsThrottle, - postUpdateAction); + KafkaZkClient kafkaZkClient = zookeeperUtils.createKafkaZkClient(); + AdminZkClient adminZkClient = new AdminZkClient(kafkaZkClient); + + TwoTuple< + scala.collection.mutable.HashMap>, + scala.collection.Map> + reassignPlan = genReassignPlan(reassignStr); + scala.collection.mutable.HashMap> partitionsToBeReassignedMap = + reassignPlan.getFirst(); + scala.collection.Map replicatAssignment = + reassignPlan.getSecond(); + + ReassignPartitionsCommand reassignPartitionsCommand = + new ReassignPartitionsCommand( + kafkaZkClient, + scala.Option.apply(kafkaAdminClient), + partitionsToBeReassignedMap, + replicatAssignment, + adminZkClient); + + Function0 postUpdateAction = + new AbstractFunction0() { + @Override + public BoxedUnit apply() { + return null; + } + }; + Throttle throttle = + new Throttle(interBrokerThrottle, replicaAlterLogDirsThrottle, postUpdateAction); if (kafkaZkClient.reassignPartitionsInProgress()) { reassignPartitionsCommand.maybeLimit(throttle); @@ -1291,7 +1458,10 @@ public BoxedUnit apply() { throw new ApiException("Failed to reassign partitions " + reassignPlan.getFirst()); } } + + kafkaZkClient.close(); kafkaAdminClient.close(); + return checkReassignStatus(partitionsToBeReassignedMap, replicatAssignment); } @@ -1299,10 +1469,11 @@ private Map checkReassignStatus( scala.collection.Map> partitionsToBeReassigned, scala.collection.Map replicaAssignement) { Map reassignStatus = new HashMap<>(); - Map reassignedPartitionsStatus = checkIfPartitionReassignmentSucceeded( - partitionsToBeReassigned); - Map replicasReassignmentStatus = checkIfReplicaReassignmentSucceeded( - CollectionConvertor.mapConvertJavaMap(replicaAssignement)); + Map reassignedPartitionsStatus = + checkIfPartitionReassignmentSucceeded(partitionsToBeReassigned); + Map replicasReassignmentStatus = + checkIfReplicaReassignmentSucceeded( + CollectionConvertor.mapConvertJavaMap(replicaAssignement)); reassignStatus.put("partitionsReassignStatus", reassignedPartitionsStatus); reassignStatus.put("replicasReassignStatus", replicasReassignmentStatus); @@ -1313,76 +1484,95 @@ private Map checkReassignStatus( reassignStatus.put("removeThrottle", false); } + reassignStatus.put( + "msg", "If removeThrottle is false, please use check api to remove throttle."); + return reassignStatus; } - private boolean removeThrottle(Map reassignedPartitionsStatus, Map replicasReassignmentStatus) { - for(Map.Entry entry: reassignedPartitionsStatus.entrySet()) { - //Partitions reassignemnt not all done + private boolean removeThrottle( + Map reassignedPartitionsStatus, + Map replicasReassignmentStatus) { + for (Map.Entry entry : reassignedPartitionsStatus.entrySet()) { + // Partitions reassignemnt not all done if (!entry.getValue().equals(ReassignmentStatus.ReassignmentCompleted.code())) { return false; } } - for(Map.Entry entry: replicasReassignmentStatus.entrySet()) { - //Replica reassignement not all done + for (Map.Entry entry : replicasReassignmentStatus.entrySet()) { + // Replica reassignement not all done if (!entry.getValue().equals(ReassignmentStatus.ReassignmentCompleted.code())) { return false; } } - List brokerList = CollectionConvertor - .seqConvertJavaList(kafkaZkClient.getAllBrokersInCluster()); - for (Broker broker:brokerList) { + KafkaZkClient kafkaZkClient = zookeeperUtils.createKafkaZkClient(); + + List brokerList = + CollectionConvertor.seqConvertJavaList(kafkaZkClient.getAllBrokersInCluster()); + for (Broker broker : brokerList) { int brokerId = broker.id(); List keysToBeRemoved = new ArrayList<>(); -// keysToBeRemoved.add(DynamicConfig.Broker.LeaderReplicationThrottledRateProp()); -// keysToBeRemoved.add(DynamicConfig.Broker.FollowerReplicationThrottledRateProp()); -// keysToBeRemoved.add(DynamicConfig.Broker.ReplicaAlterLogDirsIoMaxBytesPerSecondProp()); + // keysToBeRemoved.add(DynamicConfig.Broker.LeaderReplicationThrottledRateProp()); + // keysToBeRemoved.add(DynamicConfig.Broker.FollowerReplicationThrottledRateProp()); + // keysToBeRemoved.add(DynamicConfig.Broker.ReplicaAlterLogDirsIoMaxBytesPerSecondProp()); keysToBeRemoved.add("leader.replication.throttled.rate"); keysToBeRemoved.add("follower.replication.throttled.rate"); keysToBeRemoved.add("replica.alter.log.dirs.io.max.bytes.per.second"); try { removeConfigInZk(Type.BROKER, String.valueOf(brokerId), keysToBeRemoved); } catch (ApiException apiException) { - log.info("Remove property on broker:" + brokerId + " failed since " + apiException.getMessage()); + log.info( + "Remove property on broker:" + brokerId + " failed since " + apiException.getMessage()); } } - Set topics = reassignedPartitionsStatus.keySet().stream().map(tp->tp.topic()).collect(toSet()); - Set topicInReplicas = replicasReassignmentStatus.keySet().stream().map(tpr->tpr.topic()).collect(toSet()); + Set topics = + reassignedPartitionsStatus.keySet().stream().map(tp -> tp.topic()).collect(toSet()); + Set topicInReplicas = + replicasReassignmentStatus.keySet().stream().map(tpr -> tpr.topic()).collect(toSet()); topics.addAll(topicInReplicas); - for(String topic:topics) { + for (String topic : topics) { List keysToBeRemoved = new ArrayList<>(); keysToBeRemoved.add(LogConfig.LeaderReplicationThrottledReplicasProp()); keysToBeRemoved.add(LogConfig.FollowerReplicationThrottledReplicasProp()); try { removeConfigInZk(Type.TOPIC, topic, keysToBeRemoved); } catch (ApiException apiException) { - log.info("Remove property on topic:" + topic + " failed since " + apiException.getMessage()); + log.info( + "Remove property on topic:" + topic + " failed since " + apiException.getMessage()); } } + kafkaZkClient.close(); + return true; } private Map checkIfPartitionReassignmentSucceeded( scala.collection.Map> partitionsToBeReassigned) { Map reassignedPartitionsStatus = new HashMap<>(); + KafkaZkClient kafkaZkClient = zookeeperUtils.createKafkaZkClient(); - scala.collection.immutable.Map> partitionsBeingReassigned = kafkaZkClient - .getPartitionReassignment(); - scala.collection.Iterator topicPartitionIterator = partitionsToBeReassigned - .keysIterator(); + scala.collection.immutable.Map> partitionsBeingReassigned = + kafkaZkClient.getPartitionReassignment(); + scala.collection.Iterator topicPartitionIterator = + partitionsToBeReassigned.keysIterator(); while (topicPartitionIterator.hasNext()) { TopicPartition topicPartition = topicPartitionIterator.next(); - reassignedPartitionsStatus - .put(topicPartition, ReassignPartitionsCommand - .checkIfPartitionReassignmentSucceeded(kafkaZkClient, topicPartition, - partitionsToBeReassigned, partitionsBeingReassigned).status()); + reassignedPartitionsStatus.put( + topicPartition, + ReassignPartitionsCommand.checkIfPartitionReassignmentSucceeded( + kafkaZkClient, + topicPartition, + partitionsToBeReassigned, + partitionsBeingReassigned) + .status()); } + kafkaZkClient.close(); return reassignedPartitionsStatus; } @@ -1403,17 +1593,17 @@ private Map checkIfReplicaReassignmentSucceeded( String newLogDir = newLogDirEntry.getValue(); ReplicaLogDirInfo replicaLogDirInfo = replicaLogDirInfos.get(tpr); if (replicaLogDirInfo.getCurrentReplicaLogDir() == null) { - //tpr log dir not found + // tpr log dir not found replicasReassignmentStatus.put(tpr, ReassignmentStatus.ReassignmentFailed.code()); - } else if (replicaLogDirInfo.getFutureReplicaLogDir() != null && replicaLogDirInfo - .getFutureReplicaLogDir().equals(newLogDir)) { + } else if (replicaLogDirInfo.getFutureReplicaLogDir() != null + && replicaLogDirInfo.getFutureReplicaLogDir().equals(newLogDir)) { replicasReassignmentStatus.put(tpr, ReassignmentStatus.ReassignmentInProgress.code()); - } else if (replicaLogDirInfo.getFutureReplicaLogDir() != null && !replicaLogDirInfo - .getFutureReplicaLogDir().equals(newLogDir)) { - //tpr is being moved to another logdir instead of newLogDir + } else if (replicaLogDirInfo.getFutureReplicaLogDir() != null + && !replicaLogDirInfo.getFutureReplicaLogDir().equals(newLogDir)) { + // tpr is being moved to another logdir instead of newLogDir replicasReassignmentStatus.put(tpr, ReassignmentStatus.ReassignmentFailed.code()); - } else if (replicaLogDirInfo.getCurrentReplicaLogDir() != null && replicaLogDirInfo - .getCurrentReplicaLogDir().equals(newLogDir)) { + } else if (replicaLogDirInfo.getCurrentReplicaLogDir() != null + && replicaLogDirInfo.getCurrentReplicaLogDir().equals(newLogDir)) { replicasReassignmentStatus.put(tpr, ReassignmentStatus.ReassignmentCompleted.code()); } else { replicasReassignmentStatus.put(tpr, ReassignmentStatus.ReassignmentFailed.code()); @@ -1425,32 +1615,38 @@ private Map checkIfReplicaReassignmentSucceeded( } public Map checkReassignStatusByStr(String reassignStr) { - TwoTuple>, - scala.collection.Map> reassignPlan = genReassignPlan( - reassignStr); - scala.collection.mutable.HashMap> partitionsToBeReassignedMap = reassignPlan - .getFirst(); - scala.collection.Map replicatAssignment = reassignPlan - .getSecond(); + TwoTuple< + scala.collection.mutable.HashMap>, + scala.collection.Map> + reassignPlan = genReassignPlan(reassignStr); + scala.collection.mutable.HashMap> partitionsToBeReassignedMap = + reassignPlan.getFirst(); + scala.collection.Map replicatAssignment = + reassignPlan.getSecond(); return checkReassignStatus(partitionsToBeReassignedMap, replicatAssignment); } - private TwoTuple>, - scala.collection.Map> genReassignPlan( - String reassignStr) { + private TwoTuple< + scala.collection.mutable.HashMap>, + scala.collection.Map> + genReassignPlan(String reassignStr) { Tuple2 resultTuple2; + + KafkaZkClient kafkaZkClient = zookeeperUtils.createKafkaZkClient(); + try { - //Parse and validate reassignment json string, return (partitionsToBeReassigned, replicaAssignment) - resultTuple2 = ReassignPartitionsCommand - .parseAndValidate(kafkaZkClient, reassignStr); + // Parse and validate reassignment json string, return (partitionsToBeReassigned, + // replicaAssignment) + resultTuple2 = ReassignPartitionsCommand.parseAndValidate(kafkaZkClient, reassignStr); } catch (Exception exception) { throw new ApiException("Bad Request. " + exception.getMessage()); } - //Change list buffer to map - ListBuffer partitionsToBeReassignedList = (scala.collection.mutable.ListBuffer) resultTuple2 - ._1(); - scala.collection.mutable.HashMap> partitionsToBeReassignedMap = new scala.collection.mutable.HashMap<>(); + // Change list buffer to map + ListBuffer partitionsToBeReassignedList = + (scala.collection.mutable.ListBuffer) resultTuple2._1(); + scala.collection.mutable.HashMap> partitionsToBeReassignedMap = + new scala.collection.mutable.HashMap<>(); for (int i = 0; i < partitionsToBeReassignedList.size(); ++i) { Tuple2 tup = (Tuple2) partitionsToBeReassignedList.apply(i); partitionsToBeReassignedMap.put((TopicPartition) tup._1(), (Seq) tup._2()); @@ -1459,45 +1655,19 @@ scala.collection.Map> genReassignPlan( scala.collection.Map replicatAssignment = (scala.collection.Map) resultTuple2._2(); + kafkaZkClient.close(); + return new TwoTuple<>(partitionsToBeReassignedMap, replicatAssignment); } - /* - public Map checkReassignStatus(String reassignStr) { - Map> partitionsToBeReassigned = JavaConverters.mapAsJavaMapConverter( - zkUtils.parsePartitionReassignmentData(reassignStr)).asJava(); - - Map> partitionsBeingReassigned = JavaConverters.mapAsJavaMapConverter( - zkUtils.getPartitionsBeingReassigned()).asJava().entrySet().stream().collect( - Collectors.toMap( - Map.Entry::getKey, - data -> data.getValue().newReplicas() - )); - - - java.util.Map reassignedPartitionsStatus = - partitionsToBeReassigned.entrySet().stream().collect(Collectors.toMap( - Map.Entry::getKey, - pbr -> ReassignPartitionsCommand.checkIfPartitionReassignmentSucceeded( - zkUtils, - pbr.getKey(), - pbr.getValue(), - JavaConverters.mapAsScalaMapConverter(partitionsToBeReassigned).asScala(), - JavaConverters.mapAsScalaMapConverter(partitionsBeingReassigned).asScala() - ) - )); - - - return reassignedPartitionsStatus.entrySet().stream().collect(Collectors.toMap( - Map.Entry::getKey, - r -> r.getValue().status() - )); - } - */ - public String getMessage(@TopicExistConstraint String topic, int partition, long offset, - String decoder, String avroSchema) { - KafkaConsumer consumer = kafkaUtils - .createNewConsumer(String.valueOf(System.currentTimeMillis())); + public String getMessage( + @TopicExistConstraint String topic, + int partition, + long offset, + String decoder, + String avroSchema) { + KafkaConsumer consumer = + kafkaUtils.createNewConsumer(String.valueOf(System.currentTimeMillis())); TopicPartition tp = new TopicPartition(topic, partition); long beginningOffset = getBeginningOffset(topic, partition); long endOffset = getEndOffset(topic, partition); @@ -1508,26 +1678,35 @@ public String getMessage(@TopicExistConstraint String topic, int partition, long log.error(offset + " error"); consumer.close(); throw new ApiException( - "offsets must be between " + String.valueOf(beginningOffset - + " and " + (endOffset - 1) - ) - ); + "offsets must be between " + String.valueOf(beginningOffset + " and " + (endOffset - 1))); } consumer.assign(Collections.singletonList(tp)); consumer.seek(tp, offset); String last = null; - //ConsumerRecords crs = consumer.poll(channelRetryBackoffMs); + // ConsumerRecords crs = consumer.poll(channelRetryBackoffMs); ConsumerRecords crs = consumer.poll(3000); - log.info("Seek to offset:" + offset + ", topic:" + topic + ", partition:" + partition - + ", crs.count:" + crs.count()); + log.info( + "Seek to offset:" + + offset + + ", topic:" + + topic + + ", partition:" + + partition + + ", crs.count:" + + crs.count()); if (crs.count() != 0) { Iterator> it = crs.iterator(); while (it.hasNext()) { ConsumerRecord initCr = it.next(); - last = "Value: " + initCr.value() + ", Offset: " + String.valueOf(initCr.offset()) - + ", timestamp:" + initCr.timestamp(); + last = + "Value: " + + initCr.value() + + ", Offset: " + + String.valueOf(initCr.offset()) + + ", timestamp:" + + initCr.timestamp(); log.info( "Value: " + initCr.value() + ", initCr.Offset: " + String.valueOf(initCr.offset())); if (last != null && initCr.offset() == offset) { @@ -1540,9 +1719,12 @@ public String getMessage(@TopicExistConstraint String topic, int partition, long return last; } - public Record getRecordByOffset(@TopicExistConstraint String topic, int partition, + public Record getRecordByOffset( + @TopicExistConstraint String topic, + int partition, long offset, - String decoder, String avroSchema) { + String decoder, + String avroSchema) { if (!isTopicPartitionValid(topic, partition)) { throw new ApiException("Bad request. Topic:" + topic + " has no partition:" + partition); } @@ -1550,16 +1732,19 @@ public Record getRecordByOffset(@TopicExistConstraint String topic, int partitio try { consumer = kafkaUtils.createNewConsumer(String.valueOf(System.currentTimeMillis()), decoder); } catch (ClassNotFoundException classNotFoundException) { - throw new ApiException("Bad request. Decoder class:" + decoder - + " not found. ByteArrayDeserializer, ByteBufferDeserializer, BytesDeserializer, " - + "DoubleDeserializer, ExtendedDeserializer.Wrapper, FloatDeserializer, " - + "IntegerDeserializer, LongDeserializer, ShortDeserializer, StringDeserializer " - + "are supported."); + throw new ApiException( + "Bad request. Decoder class:" + + decoder + + " not found. ByteArrayDeserializer, ByteBufferDeserializer, BytesDeserializer, " + + "DoubleDeserializer, ExtendedDeserializer.Wrapper, FloatDeserializer, " + + "IntegerDeserializer, LongDeserializer, ShortDeserializer, StringDeserializer " + + "are supported."); } + TopicPartition tp = new TopicPartition(topic, partition); long beginningOffset = getBeginningOffset(topic, partition); long endOffset = getEndOffset(topic, partition); - System.out.println("++++++beginning offset:" + beginningOffset + ", endoffset:" + endOffset); + if (beginningOffset == endOffset) { throw new ApiException("There is no message in this partition of this topic"); } @@ -1567,22 +1752,24 @@ public Record getRecordByOffset(@TopicExistConstraint String topic, int partitio log.error(offset + " error"); consumer.close(); throw new ApiException( - "offsets must be between " + String.valueOf(beginningOffset - + " and " + (endOffset - 1) - ) - ); + "offsets must be between " + String.valueOf(beginningOffset + " and " + (endOffset - 1))); } consumer.assign(Collections.singletonList(tp)); consumer.seek(tp, offset); Record record = new Record(); - //ConsumerRecords crs = consumer.poll(channelRetryBackoffMs); + // ConsumerRecords crs = consumer.poll(channelRetryBackoffMs); ConsumerRecords crs = consumer.poll(3000); - log.info("Seek to offset:" + offset + ", topic:" + topic + ", partition:" + partition - + ", crs.count:" + crs.count()); - System.out.println("Seek to offset:" + offset + ", topic:" + topic + ", partition:" + partition - + ", crs.count:" + crs.count()); + log.info( + "Seek to offset:" + + offset + + ", topic:" + + topic + + ", partition:" + + partition + + ", crs.count:" + + crs.count()); if (crs.count() != 0) { Iterator> it = crs.iterator(); while (it.hasNext()) { @@ -1595,11 +1782,19 @@ public Record getRecordByOffset(@TopicExistConstraint String topic, int partitio break; } log.info( - "Value: " + initCr.value() + ", initCr.Offset: " + String.valueOf(initCr.offset()) - + ", timestamp:" + initCr.timestamp()); + "Value: " + + initCr.value() + + ", initCr.Offset: " + + String.valueOf(initCr.offset()) + + ", timestamp:" + + initCr.timestamp()); System.out.println( - "Value: " + initCr.value() + ", initCr.Offset: " + String.valueOf(initCr.offset()) - + ", timestamp:" + initCr.timestamp()); + "Value: " + + initCr.value() + + ", initCr.Offset: " + + String.valueOf(initCr.offset()) + + ", timestamp:" + + initCr.timestamp()); } } consumer.close(); @@ -1618,9 +1813,12 @@ private boolean isTopicPartitionValid(String topic, int partition) { return false; } - public GeneralResponse resetOffset(@TopicExistConstraint String topic, int partition, + public GeneralResponse resetOffset( + @TopicExistConstraint String topic, + int partition, String consumerGroup, - ConsumerType type, String offset) { + ConsumerType type, + String offset) { KafkaConsumer consumer = null; log.info("To tell the consumergroup " + consumerGroup + " is new"); if (type != null && type == ConsumerType.NEW) { @@ -1650,7 +1848,7 @@ public GeneralResponse resetOffset(@TopicExistConstraint String topic, int parti try { log.info("The consumergroup " + consumerGroup + " is new. Reset offset now"); consumer = kafkaUtils.createNewConsumer(consumerGroup); - //if type is new or the consumergroup itself is new + // if type is new or the consumergroup itself is new TopicPartition tp = new TopicPartition(topic, partition); consumer.assign(Arrays.asList(tp)); consumer.poll(channelSocketTimeoutMs); @@ -1663,19 +1861,23 @@ public GeneralResponse resetOffset(@TopicExistConstraint String topic, int parti offsetToBeReset = endOffset; log.info("Reset to" + consumer.position(tp)); } else if (isDateTime(offset)) { - //Reset offset by time + // Reset offset by time SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Map timestampsToSearch = new HashMap<>(); try { timestampsToSearch.put(tp, sdf.parse(offset).getTime()); - Map results = consumer - .offsetsForTimes(timestampsToSearch); + Map results = + consumer.offsetsForTimes(timestampsToSearch); OffsetAndTimestamp offsetAndTimestamp = results.get(tp); if (offsetAndTimestamp != null) { offsetToBeReset = offsetAndTimestamp.offset(); - log.info("Reset offset by time" + offsetAndTimestamp.offset() + ", timestamp:" - + offsetAndTimestamp.timestamp() + ", timestampToDate:" + sdf - .format(new Date(offsetAndTimestamp.timestamp()))); + log.info( + "Reset offset by time" + + offsetAndTimestamp.offset() + + ", timestamp:" + + offsetAndTimestamp.timestamp() + + ", timestampToDate:" + + sdf.format(new Date(offsetAndTimestamp.timestamp()))); consumer.seek(tp, offsetToBeReset); consumer.commitSync(); } else { @@ -1691,8 +1893,14 @@ public GeneralResponse resetOffset(@TopicExistConstraint String topic, int parti if (Long.parseLong(offset) < beginningOffset || Long.parseLong(offset) > endOffset) { log.error(offset + " error"); throw new ApiException( - "Invalid request offset:" + offset + ". Topic " + topic + "'s beginning offset:" - + beginningOffset + ", endoffset:" + endOffset); + "Invalid request offset:" + + offset + + ". Topic " + + topic + + "'s beginning offset:" + + beginningOffset + + ", endoffset:" + + endOffset); } offsetToBeReset = Long.parseLong(offset); consumer.seek(tp, offsetToBeReset); @@ -1702,39 +1910,49 @@ public GeneralResponse resetOffset(@TopicExistConstraint String topic, int parti storage.getMap().remove(consumerGroup); throw new ApiException(e); } finally { - consumer.close(); + if (consumer != null) { + consumer.close(); + } } } - //if type is old or the consumer group itself is old + // if type is old or the consumer group itself is old if (type != null && type == ConsumerType.OLD && isOldConsumerGroup(consumerGroup)) { log.info("The consumergroup " + consumerGroup + " is old. Reset offset now"); if (offset.equals("earliest")) { - offset = String.valueOf(beginningOffset); offsetToBeReset = beginningOffset; } else if (offset.equals("latest")) { - offset = String.valueOf(endOffset); offsetToBeReset = endOffset; } else { try { if (Long.parseLong(offset) < beginningOffset || Long.parseLong(offset) > endOffset) { log.info("Setting offset to " + offset + " error"); throw new ApiException( - "Invalid request offset:" + offset + ". Topic " + topic + "'s beginning offset:" - + beginningOffset + ", endoffset:" + endOffset); + "Invalid request offset:" + + offset + + ". Topic " + + topic + + "'s beginning offset:" + + beginningOffset + + ", endoffset:" + + endOffset); } log.info("Offset will be reset to " + offset); offsetToBeReset = Long.parseLong(offset); - zkUtils.zkClient().writeData( - "/consumers/" + consumerGroup + "/offsets/" + topic + "/" + partition, - offset); + zkUtils + .zkClient() + .writeData( + "/consumers/" + consumerGroup + "/offsets/" + topic + "/" + partition, offset); } catch (Exception e) { throw new ApiException(e); } } } - return GeneralResponse.builder().state(GeneralResponseState.success) - .msg("Reset the offset successfully!").data(Long.toString(offsetToBeReset)).build(); + return GeneralResponse.builder() + .state(GeneralResponseState.success) + .msg("Reset the offset successfully!") + .data(Long.toString(offsetToBeReset)) + .build(); } private boolean isDateTime(String offset) { @@ -1750,19 +1968,31 @@ public Map> getLastCommitTime( Map> result = new ConcurrentHashMap<>(); if (type != null && type == ConsumerType.OLD) { - //Get Old Consumer commit time + // Get Old Consumer commit time try { Map oldConsumerOffsetMap = new ConcurrentHashMap<>(); if (zkClient.checkExists().forPath(CONSUMERPATHPREFIX + consumerGroup) != null - && zkClient.checkExists() - .forPath(CONSUMERPATHPREFIX + consumerGroup + OFFSETSPATHPREFIX + topic) != null) { - List offsets = zkClient.getChildren() - .forPath(CONSUMERPATHPREFIX + consumerGroup + OFFSETSPATHPREFIX + topic); + && zkClient + .checkExists() + .forPath(CONSUMERPATHPREFIX + consumerGroup + OFFSETSPATHPREFIX + topic) + != null) { + List offsets = + zkClient + .getChildren() + .forPath(CONSUMERPATHPREFIX + consumerGroup + OFFSETSPATHPREFIX + topic); for (String offset : offsets) { Integer id = Integer.valueOf(offset); - long mtime = zkClient.checkExists().forPath( - CONSUMERPATHPREFIX + consumerGroup + OFFSETSPATHPREFIX + topic + "/" + offset) - .getMtime(); + long mtime = + zkClient + .checkExists() + .forPath( + CONSUMERPATHPREFIX + + consumerGroup + + OFFSETSPATHPREFIX + + topic + + "/" + + offset) + .getMtime(); oldConsumerOffsetMap.put(id, mtime); } @@ -1773,12 +2003,16 @@ public Map> getLastCommitTime( } } else { -// Get New consumer commit time, from offset storage instance + // Get New consumer commit time, from offset storage instance if (storage.get(consumerGroup) != null) { - Map storageResult = storage - .get(consumerGroup); - result.put("new", (storageResult.entrySet().parallelStream() + Map storageResult = + storage.get(consumerGroup); + result.put( + "new", + (storageResult + .entrySet() + .parallelStream() .filter(s -> s.getKey().topicPartition().topic().equals(topic)) .collect( Collectors.toMap( @@ -1787,15 +2021,10 @@ public Map> getLastCommitTime( if (s.getValue() != null) { return s.getValue().commitTimestamp(); } else { - return -1l; + return -1L; } - } - ) - ) - ) - ); + })))); } - } return result; @@ -1818,35 +2047,42 @@ public GeneralResponse deleteConsumerGroup(String consumerGroup, ConsumerType ty List groups = new ArrayList<>(); groups.add(consumerGroup); - scala.collection.immutable.List sList = JavaConverters.asScalaBufferConverter(groups) - .asScala().toList(); - scala.collection.immutable.Map stringErrorsMap = adminClient - .deleteConsumerGroups((scala.collection.immutable.List) (sList)); + scala.collection.immutable.List groupsList = + JavaConverters.asScalaBufferConverter(groups).asScala().toList(); + scala.collection.immutable.Map stringErrorsMap = + adminClient.deleteConsumerGroups((scala.collection.immutable.List) (groupsList)); if (!stringErrorsMap.get(consumerGroup).get().equals(Errors.NONE)) { throw new ApiException( - "Consumer group:" + consumerGroup + " could not be deleted. Error Code:" - + stringErrorsMap.get(consumerGroup).get() + ". Error msg:" + stringErrorsMap - .get(consumerGroup).get().exception()); + "Consumer group:" + + consumerGroup + + " could not be deleted. Error Code:" + + stringErrorsMap.get(consumerGroup).get() + + ". Error msg:" + + stringErrorsMap.get(consumerGroup).get().exception()); } adminClient.close(); } - return GeneralResponse.builder().state(GeneralResponseState.success) - .msg("Consumer group:" + consumerGroup + " has been deleted.").build(); + return GeneralResponse.builder() + .state(GeneralResponseState.success) + .msg("Consumer group:" + consumerGroup + " has been deleted.") + .build(); } private List getTopicPartitions(String t) { List tpList = new ArrayList<>(); List l = Arrays.asList(t); - java.util.Map> tpMap = JavaConverters.mapAsJavaMapConverter(zkUtils - .getPartitionsForTopics( - JavaConverters.asScalaIteratorConverter(l.iterator()).asScala().toSeq())).asJava(); + java.util.Map> tpMap = + JavaConverters.mapAsJavaMapConverter( + zkUtils.getPartitionsForTopics( + JavaConverters.asScalaIteratorConverter(l.iterator()).asScala().toSeq())) + .asJava(); if (tpMap != null) { - ArrayList partitionLists = new ArrayList<>( - JavaConverters.seqAsJavaListConverter(tpMap.get(t)).asJava()); - tpList = partitionLists.stream().map(p -> new TopicAndPartition(t, (Integer) p)) - .collect(toList()); + ArrayList partitionLists = + new ArrayList<>(JavaConverters.seqAsJavaListConverter(tpMap.get(t)).asJava()); + tpList = + partitionLists.stream().map(p -> new TopicAndPartition(t, (Integer) p)).collect(toList()); } return tpList; } @@ -1854,26 +2090,25 @@ private List getTopicPartitions(String t) { private long getOffsets(Node leader, String topic, int partitionId, long time) { TopicAndPartition topicAndPartition = new TopicAndPartition(topic, partitionId); - SimpleConsumer consumer = new SimpleConsumer( - leader.host(), - leader.port(), - 10000, - 1024, - "Kafka-zk-simpleconsumer" - ); - - PartitionOffsetRequestInfo partitionOffsetRequestInfo = new PartitionOffsetRequestInfo(time, - 10000); - OffsetRequest offsetRequest = new OffsetRequest( - ImmutableMap.of(topicAndPartition, partitionOffsetRequestInfo), - kafka.api.OffsetRequest.CurrentVersion(), consumer.clientId()); + SimpleConsumer consumer = + new SimpleConsumer(leader.host(), leader.port(), 10000, 1024, "Kafka-zk-simpleconsumer"); + + PartitionOffsetRequestInfo partitionOffsetRequestInfo = + new PartitionOffsetRequestInfo(time, 10000); + OffsetRequest offsetRequest = + new OffsetRequest( + ImmutableMap.of(topicAndPartition, partitionOffsetRequestInfo), + kafka.api.OffsetRequest.CurrentVersion(), + consumer.clientId()); OffsetResponse offsetResponse = consumer.getOffsetsBefore(offsetRequest); if (offsetResponse.hasError()) { short errorCode = offsetResponse.errorCode(topic, partitionId); log.warn(format("Offset response has error: %d", errorCode)); throw new ApiException( - "could not fetch data from Kafka, error code is '" + errorCode + "'Exception Message:" + "could not fetch data from Kafka, error code is '" + + errorCode + + "'Exception Message:" + offsetResponse.toString()); } @@ -1882,17 +2117,13 @@ private long getOffsets(Node leader, String topic, int partitionId, long time) { return offsets[0]; } - private long getOffsets(PartitionInfo partitionInfo, long time) { - return getOffsets(partitionInfo.leader(), partitionInfo.topic(), partitionInfo.partition(), - time); - } public long getBeginningOffset(String topic, int partitionId) { KafkaConsumer consumer = kafkaUtils.createNewConsumer(KafkaUtils.DEFAULTCP); TopicPartition tp = new TopicPartition(topic, partitionId); consumer.assign(Arrays.asList(tp)); - Map beginningOffsets = consumer - .beginningOffsets(Collections.singletonList(tp)); + Map beginningOffsets = + consumer.beginningOffsets(Collections.singletonList(tp)); consumer.close(); if (beginningOffsets != null) { @@ -1916,22 +2147,19 @@ public long getEndOffset(String topic, int partitionId) { return -1; } - private long getBeginningOffset(Node leader, String topic, int partitionId) { - return getOffsets(leader, topic, partitionId, kafka.api.OffsetRequest.EarliestTime()); - } - private long getEndOffset(Node leader, String topic, int partitionId) { return getOffsets(leader, topic, partitionId, kafka.api.OffsetRequest.LatestTime()); } - public Map countPartition(String topic) { KafkaConsumer consumer = kafkaUtils.createNewConsumer(); List piList = consumer.partitionsFor(topic); - Map result = piList.stream().flatMap(pi -> Arrays.stream(pi.replicas())) - .map(node -> node.id()).collect(Collectors.groupingBy( - Function.identity(), Collectors.counting() - )); + Map result = + piList + .stream() + .flatMap(pi -> Arrays.stream(pi.replicas())) + .map(node -> node.id()) + .collect(Collectors.groupingBy(Function.identity(), Collectors.counting())); consumer.close(); @@ -1942,13 +2170,13 @@ private boolean isConsumerGroupActive(String consumerGroup, ConsumerType type) { if (type == ConsumerType.NEW) { ConsumerGroupMeta groupMeta = getConsumerGroupMeta(consumerGroup); ConsumerGroupState groupState = groupMeta.getState(); - if (groupState.equals(ConsumerGroupState.STABLE) || groupState - .equals(ConsumerGroupState.PREPARING_REBALANCE) || groupState - .equals(ConsumerGroupState.COMPLETING_REBALANCE)) { + if (groupState.equals(ConsumerGroupState.STABLE) + || groupState.equals(ConsumerGroupState.PREPARING_REBALANCE) + || groupState.equals(ConsumerGroupState.COMPLETING_REBALANCE)) { return true; } - if (groupState.equals(ConsumerGroupState.EMPTY) || groupState - .equals(ConsumerGroupState.DEAD)) { + if (groupState.equals(ConsumerGroupState.EMPTY) + || groupState.equals(ConsumerGroupState.DEAD)) { return false; } throw new ApiException( @@ -1970,8 +2198,10 @@ public HealthCheckResult healthCheck() { log.info("HealthCheckTopic:" + healthCheckTopic + " existed:" + healthCheckTopicExist); if (!healthCheckTopicExist) { healthCheckResult.setStatus("unknown"); - healthCheckResult.setMsg("HealthCheckTopic: " + healthCheckTopic - + " Non-Exist. Please create it before doing health check."); + healthCheckResult.setMsg( + "HealthCheckTopic: " + + healthCheckTopic + + " Non-Exist. Please create it before doing health check."); return healthCheckResult; } @@ -1983,8 +2213,8 @@ public HealthCheckResult healthCheck() { log.info("Message:" + message + " has been sent to Partition:" + recordMetadata.partition()); } catch (Exception e) { healthCheckResult.setStatus("error"); - healthCheckResult - .setMsg("Health Check: Produce Message Failure. Exception: " + e.getMessage()); + healthCheckResult.setMsg( + "Health Check: Produce Message Failure. Exception: " + e.getMessage()); log.error("Health Check: Produce Message Failure.", e); return healthCheckResult; } finally { @@ -1994,8 +2224,7 @@ public HealthCheckResult healthCheck() { int retries = 30; int noRecordsCount = 0; while (true) { - final ConsumerRecords consumerRecords = - consumer.poll(1000); + final ConsumerRecords consumerRecords = consumer.poll(1000); if (consumerRecords.count() == 0) { noRecordsCount++; if (noRecordsCount > retries) { @@ -2020,20 +2249,9 @@ public HealthCheckResult healthCheck() { if (healthCheckResult.getStatus() == null) { healthCheckResult.setStatus("error"); - healthCheckResult - .setMsg("Health Check: Consume Message Failure. Consumer can't fetch the message."); + healthCheckResult.setMsg( + "Health Check: Consume Message Failure. Consumer can't fetch the message."); } return healthCheckResult; } - - public void printObjectInJson(Object object) { - try { - ObjectMapper objectMapper = new ObjectMapper(); - //objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY); - System.out.println( - "object:" + objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(object)); - } catch (JsonProcessingException jsonProcessingException) { - jsonProcessingException.printStackTrace(); - } - } } diff --git a/src/main/java/org/gnuhpc/bigdata/service/KafkaConsumerService.java b/src/main/java/org/gnuhpc/bigdata/service/KafkaConsumerService.java index 23e95e7..58a9168 100644 --- a/src/main/java/org/gnuhpc/bigdata/service/KafkaConsumerService.java +++ b/src/main/java/org/gnuhpc/bigdata/service/KafkaConsumerService.java @@ -1,5 +1,9 @@ package org.gnuhpc.bigdata.service; +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; import kafka.common.OffsetAndMetadata; import kafka.coordinator.group.GroupMetadataManager; import kafka.coordinator.group.GroupTopicPartition; @@ -14,72 +18,66 @@ import org.springframework.kafka.support.KafkaHeaders; import org.springframework.messaging.handler.annotation.Header; -import java.nio.ByteBuffer; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; - @Log4j public class KafkaConsumerService implements ConsumerSeekAware { - @Autowired - private OffsetStorage offsetStorage; - private boolean[] resetInitArray; + @Autowired private OffsetStorage offsetStorage; - private final ThreadLocal seekCallBack = new ThreadLocal<>(); + private boolean[] resetInitArray; - public KafkaConsumerService(int internalTopicPartitions) { - this.resetInitArray = new boolean[internalTopicPartitions]; - Arrays.fill(resetInitArray, true); - } + private final ThreadLocal seekCallBack = new ThreadLocal<>(); + + public KafkaConsumerService(int internalTopicPartitions) { + this.resetInitArray = new boolean[internalTopicPartitions]; + Arrays.fill(resetInitArray, true); + } - /** - * Listening offset thread method. - */ - @KafkaListener(topics = "${kafka.offset.topic}") - public void onMessage( - ConsumerRecord record, - @Header(KafkaHeaders.RECEIVED_PARTITION_ID) int partition, - @Header(KafkaHeaders.RECEIVED_TOPIC) String topic) { - //set the offset of the partition being processed to the beginning. If already set, ignore it. -// if (resetInitArray[partition]) { -// long beginningOffset = kafkaAdminService.getBeginningOffset(topic, partition); -// this.seekCallBack.get().seek(topic, partition, beginningOffset + 1); -// resetInitArray[partition] = false; -// } + /** Listening offset thread method. */ + @KafkaListener(topics = "${kafka.offset.topic}") + public void onMessage( + ConsumerRecord record, + @Header(KafkaHeaders.RECEIVED_PARTITION_ID) int partition, + @Header(KafkaHeaders.RECEIVED_TOPIC) String topic) { + // set the offset of the partition being processed to the beginning. If already set, ignore it. + // if (resetInitArray[partition]) { + // long beginningOffset = kafkaAdminService.getBeginningOffset(topic, partition); + // this.seekCallBack.get().seek(topic, partition, beginningOffset + 1); + // resetInitArray[partition] = false; + // } - //Parse the commit offset message and store it in offsetStorage - Map offsetMap; - if (record.key() != null) { - Object offsetKey = GroupMetadataManager.readMessageKey(record.key()); + // Parse the commit offset message and store it in offsetStorage + Map offsetMap; + if (record.key() != null) { + Object offsetKey = GroupMetadataManager.readMessageKey(record.key()); - log.debug(offsetKey); - if (offsetKey instanceof OffsetKey) { - GroupTopicPartition groupTopicPartition = ((OffsetKey) offsetKey).key(); - if (offsetStorage.get(groupTopicPartition.group()) != null) { - offsetMap =offsetStorage.get(groupTopicPartition.group()); - } else { - offsetMap = new HashMap<>(); - } - OffsetAndMetadata offsetValue = GroupMetadataManager.readOffsetMessageValue(record.value()); - offsetMap.put(groupTopicPartition,offsetValue); - offsetStorage.put(groupTopicPartition.group(),offsetMap); - } + log.debug(offsetKey); + if (offsetKey instanceof OffsetKey) { + GroupTopicPartition groupTopicPartition = ((OffsetKey) offsetKey).key(); + if (offsetStorage.get(groupTopicPartition.group()) != null) { + offsetMap = offsetStorage.get(groupTopicPartition.group()); + } else { + offsetMap = new HashMap<>(); } + OffsetAndMetadata offsetValue = GroupMetadataManager.readOffsetMessageValue(record.value()); + offsetMap.put(groupTopicPartition, offsetValue); + offsetStorage.put(groupTopicPartition.group(), offsetMap); + } } + } - @Override - public void registerSeekCallback(ConsumerSeekCallback consumerSeekCallback) { - //set the seekcallback for resetting the offset - this.seekCallBack.set(consumerSeekCallback); - } + @Override + public void registerSeekCallback(ConsumerSeekCallback consumerSeekCallback) { + // set the seekcallback for resetting the offset + this.seekCallBack.set(consumerSeekCallback); + } - @Override - public void onPartitionsAssigned(Map map, ConsumerSeekCallback consumerSeekCallback) { - System.out.println(); - } + @Override + public void onPartitionsAssigned( + Map map, ConsumerSeekCallback consumerSeekCallback) { + System.out.println(); + } - @Override - public void onIdleContainer(Map map, ConsumerSeekCallback consumerSeekCallback) { - } + @Override + public void onIdleContainer( + Map map, ConsumerSeekCallback consumerSeekCallback) {} } diff --git a/src/main/java/org/gnuhpc/bigdata/service/KafkaProducerService.java b/src/main/java/org/gnuhpc/bigdata/service/KafkaProducerService.java index 9dbe707..7a07ac4 100644 --- a/src/main/java/org/gnuhpc/bigdata/service/KafkaProducerService.java +++ b/src/main/java/org/gnuhpc/bigdata/service/KafkaProducerService.java @@ -6,11 +6,11 @@ @Service public class KafkaProducerService { - @Autowired - private KafkaTemplate kafkaTemplate; - public void send(String topic, String data) { - kafkaTemplate.send(topic, data); - kafkaTemplate.flush(); - } + @Autowired private KafkaTemplate kafkaTemplate; + + public void send(String topic, String data) { + kafkaTemplate.send(topic, data); + kafkaTemplate.flush(); + } } diff --git a/src/main/java/org/gnuhpc/bigdata/service/UserService.java b/src/main/java/org/gnuhpc/bigdata/service/UserService.java index 84dfe34..5d6c6d8 100644 --- a/src/main/java/org/gnuhpc/bigdata/service/UserService.java +++ b/src/main/java/org/gnuhpc/bigdata/service/UserService.java @@ -1,5 +1,9 @@ package org.gnuhpc.bigdata.service; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; import lombok.Getter; import lombok.Setter; import lombok.extern.log4j.Log4j; @@ -10,23 +14,22 @@ import org.gnuhpc.bigdata.utils.CommonUtils; import org.springframework.stereotype.Service; -import java.io.IOException; -import java.util.*; - @Getter @Setter @Log4j @Service public class UserService { + private HashMap accounts; public List listUser() { List userList = new ArrayList<>(); try { accounts = CommonUtils.yamlParse(WebSecurityConfig.SECURITY_FILE_PATH); - accounts.forEach((username, value)->{ - userList.add((String)username); - }); + accounts.forEach( + (username, value) -> { + userList.add((String) username); + }); } catch (IOException ioException) { log.error("Failed to get user list. Reason : " + ioException.getLocalizedMessage()); } @@ -42,11 +45,18 @@ public GeneralResponse addUser(User user) { return saveUserInfo(user); } else { log.info("Failed to add user. Reason : User " + username + " already exists."); - return GeneralResponse.builder().state(GeneralResponseState.failure).msg("Failed to add user. Reason : User " + username + " already exists.").build(); + return GeneralResponse.builder() + .state(GeneralResponseState.failure) + .msg("Failed to add user. Reason : User " + username + " already exists.") + .build(); } } catch (IOException ioException) { - log.error("Failed to add user " + username + ". Reason : " + ioException.getLocalizedMessage()); - return GeneralResponse.builder().state(GeneralResponseState.failure).msg("Failed to add user " + username + ". Reason : " + ioException.getLocalizedMessage()).build(); + log.error( + "Failed to add user " + username + ". Reason : " + ioException.getLocalizedMessage()); + return GeneralResponse.builder() + .state(GeneralResponseState.failure) + .msg("Failed to add user " + username + ". Reason : " + ioException.getLocalizedMessage()) + .build(); } } @@ -58,11 +68,22 @@ public GeneralResponse modifyUser(User user) { return saveUserInfo(user); } else { log.info("Failed to modify user. Reason : User " + username + " does not exist."); - return GeneralResponse.builder().state(GeneralResponseState.failure).msg("Failed to modify user. Reason : User " + username + " does not exist.").build(); + return GeneralResponse.builder() + .state(GeneralResponseState.failure) + .msg("Failed to modify user. Reason : User " + username + " does not exist.") + .build(); } } catch (IOException ioException) { - log.error("Failed to modify user " + username + ". Reason : " + ioException.getLocalizedMessage()); - return GeneralResponse.builder().state(GeneralResponseState.failure).msg("Failed to modify user " + username + ". Reason : " + ioException.getLocalizedMessage()).build(); + log.error( + "Failed to modify user " + username + ". Reason : " + ioException.getLocalizedMessage()); + return GeneralResponse.builder() + .state(GeneralResponseState.failure) + .msg( + "Failed to modify user " + + username + + ". Reason : " + + ioException.getLocalizedMessage()) + .build(); } } @@ -72,14 +93,28 @@ public GeneralResponse delUser(String username) { if (exist) { accounts.remove(username); CommonUtils.yamlWrite(WebSecurityConfig.SECURITY_FILE_PATH, accounts); - return GeneralResponse.builder().state(GeneralResponseState.success).msg("Delete user " + username + " successfully.").build(); + return GeneralResponse.builder() + .state(GeneralResponseState.success) + .msg("Delete user " + username + " successfully.") + .build(); } else { log.info("Failed to delete user. Reason : User " + username + " does not exist."); - return GeneralResponse.builder().state(GeneralResponseState.failure).msg("Failed to delete user. Reason : User " + username + " does not exist.").build(); + return GeneralResponse.builder() + .state(GeneralResponseState.failure) + .msg("Failed to delete user. Reason : User " + username + " does not exist.") + .build(); } } catch (IOException ioException) { - log.error("Failed to delete user " + username + ". Reason : " + ioException.getLocalizedMessage()); - return GeneralResponse.builder().state(GeneralResponseState.failure).msg("Failed to delete user " + username + ". Reason : " + ioException.getLocalizedMessage()).build(); + log.error( + "Failed to delete user " + username + ". Reason : " + ioException.getLocalizedMessage()); + return GeneralResponse.builder() + .state(GeneralResponseState.failure) + .msg( + "Failed to delete user " + + username + + ". Reason : " + + ioException.getLocalizedMessage()) + .build(); } } @@ -100,6 +135,9 @@ public GeneralResponse saveUserInfo(User user) throws IOException { userInfo.put("role", user.getRole()); accounts.put(username, userInfo); CommonUtils.yamlWrite(WebSecurityConfig.SECURITY_FILE_PATH, accounts); - return GeneralResponse.builder().state(GeneralResponseState.success).msg("Save user " + username + " info successfully.").build(); + return GeneralResponse.builder() + .state(GeneralResponseState.success) + .msg("Save user " + username + " info successfully.") + .build(); } } diff --git a/src/main/java/org/gnuhpc/bigdata/service/ZookeeperService.java b/src/main/java/org/gnuhpc/bigdata/service/ZookeeperService.java index 67e12f3..4521910 100644 --- a/src/main/java/org/gnuhpc/bigdata/service/ZookeeperService.java +++ b/src/main/java/org/gnuhpc/bigdata/service/ZookeeperService.java @@ -1,6 +1,8 @@ package org.gnuhpc.bigdata.service; import com.google.common.net.HostAndPort; +import java.util.Map; +import java.util.stream.Collectors; import lombok.extern.log4j.Log4j; import org.gnuhpc.bigdata.constant.ZkServerCommand; import org.gnuhpc.bigdata.model.ZkServerEnvironment; @@ -9,42 +11,37 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import java.util.Map; -import java.util.stream.Collectors; - @Service @Log4j public class ZookeeperService { - @Autowired - private ZookeeperUtils zookeeperUtils; - - public Map stat() { - return zookeeperUtils.getZookeeperConfig().getHostAndPort().stream() - .collect(Collectors.toMap( - hp -> hp, - hp -> zookeeperUtils.parseStatResult( - zookeeperUtils.executeCommand( - hp.getHostText(), - hp.getPort(), - ZkServerCommand.stat.toString() - ) - ) - )); - } - public Map environment() { - return zookeeperUtils.getZookeeperConfig().getHostAndPort().stream() - .collect(Collectors.toMap( - hp -> hp, - hp -> zookeeperUtils.parseEnvResult( - zookeeperUtils.executeCommand( - hp.getHostText(), - hp.getPort(), - ZkServerCommand.envi.toString() - ) - ) - )); - } + @Autowired private ZookeeperUtils zookeeperUtils; + public Map stat() { + return zookeeperUtils + .getZookeeperConfig() + .getHostAndPort() + .stream() + .collect( + Collectors.toMap( + hp -> hp, + hp -> + zookeeperUtils.parseStatResult( + zookeeperUtils.executeCommand( + hp.getHostText(), hp.getPort(), ZkServerCommand.stat.toString())))); + } + public Map environment() { + return zookeeperUtils + .getZookeeperConfig() + .getHostAndPort() + .stream() + .collect( + Collectors.toMap( + hp -> hp, + hp -> + zookeeperUtils.parseEnvResult( + zookeeperUtils.executeCommand( + hp.getHostText(), hp.getPort(), ZkServerCommand.envi.toString())))); + } } diff --git a/src/main/java/org/gnuhpc/bigdata/task/FetchOffSetFromZKResult.java b/src/main/java/org/gnuhpc/bigdata/task/FetchOffSetFromZkResult.java similarity index 56% rename from src/main/java/org/gnuhpc/bigdata/task/FetchOffSetFromZKResult.java rename to src/main/java/org/gnuhpc/bigdata/task/FetchOffSetFromZkResult.java index c2d79fb..8d03c8d 100644 --- a/src/main/java/org/gnuhpc/bigdata/task/FetchOffSetFromZKResult.java +++ b/src/main/java/org/gnuhpc/bigdata/task/FetchOffSetFromZkResult.java @@ -7,8 +7,9 @@ @Data @NoArgsConstructor @AllArgsConstructor -public class FetchOffSetFromZKResult { - private String topic; - private int parition; - private long offset; +public class FetchOffSetFromZkResult { + + private String topic; + private int parition; + private long offset; } diff --git a/src/main/java/org/gnuhpc/bigdata/task/FetchOffsetFromZKTask.java b/src/main/java/org/gnuhpc/bigdata/task/FetchOffsetFromZKTask.java deleted file mode 100644 index fed9994..0000000 --- a/src/main/java/org/gnuhpc/bigdata/task/FetchOffsetFromZKTask.java +++ /dev/null @@ -1,41 +0,0 @@ -package org.gnuhpc.bigdata.task; - -import lombok.Data; -import org.gnuhpc.bigdata.utils.ZookeeperUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -import java.util.concurrent.Callable; - -@Data -public class FetchOffsetFromZKTask implements Callable { - private String topic; - private String consumerGroup; - private int partition; - - private ZookeeperUtils zookeeperUtils; - - public FetchOffsetFromZKTask(ZookeeperUtils zookeeperUtils, String topic, String consumerGroup, int partition) { - this.zookeeperUtils = zookeeperUtils; - this.topic = topic; - this.consumerGroup = consumerGroup; - this.partition = partition; - } - - @Override - public FetchOffSetFromZKResult call() throws Exception { - long offset = 0; - FetchOffSetFromZKResult result = new FetchOffSetFromZKResult(); - String path = "/consumers/" + consumerGroup + "/offsets/" + topic + "/" + partition; - if (zookeeperUtils.getCuratorClient().checkExists().forPath(path) != null) { - offset = Long.parseLong(zookeeperUtils.getZkClient() - .readData("/consumers/" + consumerGroup + - "/offsets/" + topic + "/" + partition)); - } - - result.setOffset(offset); - result.setTopic(topic); - result.setParition(partition); - return result; - } -} diff --git a/src/main/java/org/gnuhpc/bigdata/task/FetchOffsetFromZkTask.java b/src/main/java/org/gnuhpc/bigdata/task/FetchOffsetFromZkTask.java new file mode 100644 index 0000000..e052d69 --- /dev/null +++ b/src/main/java/org/gnuhpc/bigdata/task/FetchOffsetFromZkTask.java @@ -0,0 +1,42 @@ +package org.gnuhpc.bigdata.task; + +import java.util.concurrent.Callable; +import lombok.Data; +import org.gnuhpc.bigdata.utils.ZookeeperUtils; + +@Data +public class FetchOffsetFromZkTask implements Callable { + + private String topic; + private String consumerGroup; + private int partition; + + private ZookeeperUtils zookeeperUtils; + + public FetchOffsetFromZkTask( + ZookeeperUtils zookeeperUtils, String topic, String consumerGroup, int partition) { + this.zookeeperUtils = zookeeperUtils; + this.topic = topic; + this.consumerGroup = consumerGroup; + this.partition = partition; + } + + @Override + public FetchOffSetFromZkResult call() throws Exception { + long offset = 0; + FetchOffSetFromZkResult result = new FetchOffSetFromZkResult(); + String path = "/consumers/" + consumerGroup + "/offsets/" + topic + "/" + partition; + if (zookeeperUtils.getCuratorClient().checkExists().forPath(path) != null) { + offset = + Long.parseLong( + zookeeperUtils + .getZkClient() + .readData("/consumers/" + consumerGroup + "/offsets/" + topic + "/" + partition)); + } + + result.setOffset(offset); + result.setTopic(topic); + result.setParition(partition); + return result; + } +} diff --git a/src/main/java/org/gnuhpc/bigdata/utils/ByteUtils.java b/src/main/java/org/gnuhpc/bigdata/utils/ByteUtils.java index a273953..496038a 100644 --- a/src/main/java/org/gnuhpc/bigdata/utils/ByteUtils.java +++ b/src/main/java/org/gnuhpc/bigdata/utils/ByteUtils.java @@ -3,16 +3,17 @@ import java.nio.ByteBuffer; public class ByteUtils { - private static ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES); - public static byte[] longToBytes(long x) { - buffer.putLong(0, x); - return buffer.array(); - } + private static ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES); - public static long bytesToLong(byte[] bytes) { - buffer.put(bytes, 0, bytes.length); - buffer.flip();//need flip - return buffer.getLong(); - } + public static byte[] longToBytes(long x) { + buffer.putLong(0, x); + return buffer.array(); + } + + public static long bytesToLong(byte[] bytes) { + buffer.put(bytes, 0, bytes.length); + buffer.flip(); // need flip + return buffer.getLong(); + } } diff --git a/src/main/java/org/gnuhpc/bigdata/utils/CommonUtils.java b/src/main/java/org/gnuhpc/bigdata/utils/CommonUtils.java index e7b9dc8..0ce7aa5 100644 --- a/src/main/java/org/gnuhpc/bigdata/utils/CommonUtils.java +++ b/src/main/java/org/gnuhpc/bigdata/utils/CommonUtils.java @@ -2,19 +2,19 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; +import java.io.File; +import java.io.IOException; +import java.util.HashMap; import lombok.Getter; import lombok.Setter; import lombok.extern.log4j.Log4j; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; -import java.io.File; -import java.io.IOException; -import java.util.HashMap; - @Log4j @Getter @Setter public class CommonUtils { + public static final String PROJECT_ROOT_FOLDER = CommonUtils.getProjectRootPath(); public static String encode(CharSequence rawPassword) { @@ -28,31 +28,31 @@ public static String getProjectRootPath() { } public static HashMap yamlParse(String filePath) throws IOException { - ObjectMapper mapperForYAML = new ObjectMapper(new YAMLFactory()); + ObjectMapper mapperForYaml = new ObjectMapper(new YAMLFactory()); File file = new File(filePath); HashMap yamlHash = new HashMap<>(); - yamlHash = mapperForYAML.readValue(file, yamlHash.getClass()); + yamlHash = mapperForYaml.readValue(file, yamlHash.getClass()); return yamlHash; } public static HashMap yamlParse(File file) throws IOException { - ObjectMapper mapperForYAML = new ObjectMapper(new YAMLFactory()); + ObjectMapper mapperForYaml = new ObjectMapper(new YAMLFactory()); HashMap yamlHash = new HashMap<>(); - yamlHash = mapperForYAML.readValue(file, yamlHash.getClass()); + yamlHash = mapperForYaml.readValue(file, yamlHash.getClass()); return yamlHash; } public static void yamlWrite(String filePath, Object object) throws IOException { File file = new File(filePath); - ObjectMapper mapperForYAML = new ObjectMapper(new YAMLFactory()); - mapperForYAML.writeValue(file, object); + ObjectMapper mapperForYaml = new ObjectMapper(new YAMLFactory()); + mapperForYaml.writeValue(file, object); } public static void yamlWrite(File file, Object object) throws IOException { - ObjectMapper mapperForYAML = new ObjectMapper(new YAMLFactory()); - mapperForYAML.writeValue(file, object); + ObjectMapper mapperForYaml = new ObjectMapper(new YAMLFactory()); + mapperForYaml.writeValue(file, object); } public static void main(String[] args) throws IOException { diff --git a/src/main/java/org/gnuhpc/bigdata/utils/KafkaUtils.java b/src/main/java/org/gnuhpc/bigdata/utils/KafkaUtils.java index 1d66802..a0c545a 100644 --- a/src/main/java/org/gnuhpc/bigdata/utils/KafkaUtils.java +++ b/src/main/java/org/gnuhpc/bigdata/utils/KafkaUtils.java @@ -1,44 +1,33 @@ package org.gnuhpc.bigdata.utils; +import java.util.Collections; +import java.util.List; +import java.util.Properties; import kafka.admin.AdminClient; -import kafka.admin.AdminUtils; -import kafka.coordinator.group.GroupOverview; import lombok.Getter; import lombok.Setter; import lombok.extern.log4j.Log4j; -import org.apache.kafka.clients.admin.*; import org.apache.kafka.clients.consumer.ConsumerConfig; import org.apache.kafka.clients.consumer.KafkaConsumer; import org.apache.kafka.clients.producer.KafkaProducer; import org.apache.kafka.clients.producer.ProducerConfig; import org.apache.kafka.common.Node; import org.apache.kafka.common.PartitionInfo; -import org.apache.kafka.common.errors.ApiException; import org.apache.kafka.common.serialization.StringDeserializer; -import org.gnuhpc.bigdata.CollectionConvertor; import org.gnuhpc.bigdata.config.KafkaConfig; import org.gnuhpc.bigdata.config.ZookeeperConfig; -import org.gnuhpc.bigdata.constant.ConsumerType; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; -import java.util.*; -import java.util.concurrent.ExecutionException; -import org.apache.kafka.clients.admin.KafkaAdminClient; - -/** - * Created by gnuhpc on 2017/7/12. - */ +/** Created by gnuhpc on 2017/7/12. */ @Log4j @Getter @Setter @Configuration public class KafkaUtils { - @Autowired - private KafkaConfig kafkaConfig; - @Autowired - private ZookeeperConfig zookeeperConfig; + @Autowired private KafkaConfig kafkaConfig; + @Autowired private ZookeeperConfig zookeeperConfig; private AdminClient adminClient; @@ -50,9 +39,11 @@ public class KafkaUtils { public void init() { prop = new Properties(); prop.setProperty(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaConfig.getBrokers()); - prop.setProperty(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, + prop.setProperty( + ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringSerializer"); - prop.setProperty(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, + prop.setProperty( + ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringSerializer"); producer = new KafkaProducer(prop); log.info("Kafka initing..."); @@ -60,7 +51,6 @@ public void init() { adminClient = AdminClient.create(prop); } - public void destroy() { log.info("Kafka destorying..."); } @@ -77,9 +67,10 @@ public KafkaConsumer createNewConsumer(String consumerGroup) { properties.put(ConsumerConfig.SESSION_TIMEOUT_MS_CONFIG, "30000"); properties.put(ConsumerConfig.MAX_PARTITION_FETCH_BYTES_CONFIG, "100000000"); properties.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, "5"); - properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, - StringDeserializer.class.getCanonicalName()); - properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, + properties.put( + ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getCanonicalName()); + properties.put( + ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getCanonicalName()); return new KafkaConsumer(properties); @@ -95,14 +86,17 @@ public KafkaConsumer createNewConsumer(String consumerGroup, String decoder) properties.put(ConsumerConfig.MAX_PARTITION_FETCH_BYTES_CONFIG, "100000000"); properties.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, "5"); if (decoder == null || decoder.isEmpty()) { - properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, + properties.put( + ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getCanonicalName()); - properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, + properties.put( + ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getCanonicalName()); } else { - properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, - Class.forName(decoder).getCanonicalName()); - properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, + properties.put( + ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, Class.forName(decoder).getCanonicalName()); + properties.put( + ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, Class.forName(decoder).getCanonicalName()); } return new KafkaConsumer(properties); @@ -112,9 +106,10 @@ public KafkaConsumer createNewConsumerByTopic(String topic) { Properties properties = new Properties(); properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, getKafkaConfig().getBrokers()); properties.put(ConsumerConfig.GROUP_ID_CONFIG, DEFAULTCP); - properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, - StringDeserializer.class.getCanonicalName()); - properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, + properties.put( + ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getCanonicalName()); + properties.put( + ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getCanonicalName()); KafkaConsumer kafkaConsumer = new KafkaConsumer(properties); kafkaConsumer.subscribe(Collections.singletonList(topic)); @@ -125,9 +120,11 @@ public KafkaConsumer createNewConsumerByTopic(String topic) { public KafkaProducer createProducer() { Properties prop = new Properties(); prop.setProperty(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaConfig.getBrokers()); - prop.setProperty(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, + prop.setProperty( + ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringSerializer"); - prop.setProperty(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, + prop.setProperty( + ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringSerializer"); prop.setProperty(ProducerConfig.RETRIES_CONFIG, "3"); prop.setProperty(ProducerConfig.REQUEST_TIMEOUT_MS_CONFIG, "10000"); @@ -140,8 +137,8 @@ public Node getLeader(String topic, int partitionId) { KafkaConsumer consumer = createNewConsumer(DEFAULTCP); List tmList = consumer.partitionsFor(topic); - PartitionInfo partitionInfo = tmList.stream().filter(pi -> pi.partition() == partitionId) - .findFirst().get(); + PartitionInfo partitionInfo = + tmList.stream().filter(pi -> pi.partition() == partitionId).findFirst().get(); consumer.close(); return partitionInfo.leader(); } diff --git a/src/main/java/org/gnuhpc/bigdata/utils/ZookeeperUtils.java b/src/main/java/org/gnuhpc/bigdata/utils/ZookeeperUtils.java index 65c90d8..2e75a45 100644 --- a/src/main/java/org/gnuhpc/bigdata/utils/ZookeeperUtils.java +++ b/src/main/java/org/gnuhpc/bigdata/utils/ZookeeperUtils.java @@ -1,6 +1,16 @@ package org.gnuhpc.bigdata.utils; import com.google.common.base.Charsets; +import java.io.IOException; +import java.net.InetAddress; +import java.net.Socket; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import kafka.utils.ZKStringSerializer$; import kafka.utils.ZkUtils; import kafka.zk.KafkaZkClient; @@ -25,265 +35,302 @@ import org.gnuhpc.bigdata.model.ZkServerClient; import org.gnuhpc.bigdata.model.ZkServerEnvironment; import org.gnuhpc.bigdata.model.ZkServerStat; -import org.gnuhpc.bigdata.validator.ZKNodePathExistConstraint; +import org.gnuhpc.bigdata.validator.ZkNodePathExistConstraint; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.validation.annotation.Validated; -import java.io.IOException; -import java.net.InetAddress; -import java.net.Socket; -import java.util.*; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Created by huangpengcheng on 2016/7/21 0021. - */ - +/** Created by huangpengcheng on 2016/7/21 0021. */ @Log4j @Setter @Getter @Validated public class ZookeeperUtils { - //For Stat Command parse - private static final String ATTRIBUTE_DELIMITER = "="; - private static final String PROP_DELIMITER = ":"; - private final Pattern versionLinePattern = Pattern.compile(".*: (\\d+\\.\\d+\\.\\d+.*),.* built on (.*)"); - private final Pattern ipv4ClientLinePattern = Pattern.compile("/(\\d+\\.\\d+\\.\\d+\\.\\d+):(\\d+)\\[(\\d+)\\]\\((.*)"); - private final Pattern latenciesPattern = Pattern.compile(".*: (-?\\d+)/(-?\\d+)/(-?\\d+)"); - - - @Autowired - private ZookeeperConfig zookeeperConfig; - - /** - * 初始sleep时间(毫秒) - */ - private static final int BASE_SLEEP_TIME = 1000; - /** - * 最大重试次数 - */ - private static final int MAX_RETRIES_COUNT = 5; - /** - * 最大sleep时间 - */ - private static final int MAX_SLEEP_TIME = 60000; - private static final int SESSION_TIMEOUT = 5000; - private static final int CONNECTION_TIMEOUT = 5000; - private CuratorFramework curatorClient; - private ZkClient zkClient; - private ZkUtils zkUtils; - private KafkaZkClient kafkaZkClient; - - public void init() { - - //1.设置重试策略,重试时间计算策略sleepMs = baseSleepTimeMs * Math.max(1, random.nextInt(1 << (retryCount + 1))); - RetryPolicy retryPolicy = new ExponentialBackoffRetry(BASE_SLEEP_TIME, MAX_RETRIES_COUNT, MAX_SLEEP_TIME); - - //2.初始化客户端 - curatorClient = CuratorFrameworkFactory.builder() - .connectString(zookeeperConfig.getUris()) - .sessionTimeoutMs(SESSION_TIMEOUT) - .connectionTimeoutMs(CONNECTION_TIMEOUT) - .retryPolicy(retryPolicy) -// .namespace("kafka-rest") //命名空间隔离 - .build(); - curatorClient.start(); - try { - curatorClient.blockUntilConnected(); - log.info("Zookeeper:" + zookeeperConfig.getUris() + " Connected.Continue..."); - zkClient = new ZkClient(zookeeperConfig.getUris(), SESSION_TIMEOUT, CONNECTION_TIMEOUT, ZKStringSerializer$.MODULE$); - zkUtils = new ZkUtils(zkClient, new ZkConnection(zookeeperConfig.getUris()), false); - log.info("Zkutils" + zkUtils.toString()); - } catch (InterruptedException e) { - e.printStackTrace(); - log.error("Exception:", e); - } - kafkaZkClient = KafkaZkClient.apply(zookeeperConfig.getUris(), false, 10 * SESSION_TIMEOUT, - CONNECTION_TIMEOUT, Integer.MAX_VALUE, Time.SYSTEM, "testMetricGroup", - "testMetricType"); + // For Stat Command parse + private static final String ATTRIBUTE_DELIMITER = "="; + private static final String PROP_DELIMITER = ":"; + private final Pattern versionLinePattern = + Pattern.compile(".*: (\\d+\\.\\d+\\.\\d+.*),.* built on (.*)"); + private final Pattern ipv4ClientLinePattern = + Pattern.compile("/(\\d+\\.\\d+\\.\\d+\\.\\d+):(\\d+)\\[(\\d+)\\]\\((.*)"); + private final Pattern latenciesPattern = Pattern.compile(".*: (-?\\d+)/(-?\\d+)/(-?\\d+)"); + + @Autowired private ZookeeperConfig zookeeperConfig; + + /** 初始sleep时间(毫秒). */ + private static final int BASE_SLEEP_TIME = 1000; + /** 最大重试次数. */ + private static final int MAX_RETRIES_COUNT = 5; + /** 最大sleep时间. */ + private static final int MAX_SLEEP_TIME = 60000; + + private static final int SESSION_TIMEOUT = 5000; + private static final int CONNECTION_TIMEOUT = 5000; + private CuratorFramework curatorClient; + private ZkClient zkClient; + private ZkUtils zkUtils; + private KafkaZkClient kafkaZkClient; + + public void init() { + + // 1.设置重试策略,重试时间计算策略sleepMs = baseSleepTimeMs * Math.max(1, random.nextInt(1 << (retryCount + + // 1))); + RetryPolicy retryPolicy = + new ExponentialBackoffRetry(BASE_SLEEP_TIME, MAX_RETRIES_COUNT, MAX_SLEEP_TIME); + + // 2.初始化客户端 + curatorClient = + CuratorFrameworkFactory.builder() + .connectString(zookeeperConfig.getUris()) + .sessionTimeoutMs(SESSION_TIMEOUT) + .connectionTimeoutMs(CONNECTION_TIMEOUT) + .retryPolicy(retryPolicy) + // .namespace("kafka-rest") //命名空间隔离 + .build(); + curatorClient.start(); + try { + curatorClient.blockUntilConnected(); + log.info("Zookeeper:" + zookeeperConfig.getUris() + " Connected.Continue..."); + zkClient = + new ZkClient( + zookeeperConfig.getUris(), + SESSION_TIMEOUT, + CONNECTION_TIMEOUT, + ZKStringSerializer$.MODULE$); + zkUtils = new ZkUtils(zkClient, new ZkConnection(zookeeperConfig.getUris()), false); + log.info("Zkutils" + zkUtils.toString()); + } catch (InterruptedException e) { + e.printStackTrace(); + log.error("Exception:", e); } - - public void destroy() { - curatorClient.close(); - zkClient.close(); - kafkaZkClient.close(); - log.info("zookeeper closed."); + kafkaZkClient = + KafkaZkClient.apply( + zookeeperConfig.getUris(), + false, + 10 * SESSION_TIMEOUT, + CONNECTION_TIMEOUT, + Integer.MAX_VALUE, + Time.SYSTEM, + "testMetricGroup", + "testMetricType"); + } + + public void destroy() { + curatorClient.close(); + zkClient.close(); + kafkaZkClient.close(); + log.info("zookeeper closed."); + } + + @Override + public String toString() { + return zookeeperConfig.getUris() + curatorClient.getState(); + } + + public List executeCommand(String host, Integer port, String command) + throws ServiceNotAvailableException { + Socket socket; + try { + socket = new Socket(InetAddress.getByName(host), port); + } catch (IOException e) { + throw new ServiceNotAvailableException( + "zookeeper", "down", "could not connect to host: " + host + " and port: " + port); } - @Override - public String toString() { - return zookeeperConfig.getUris() + curatorClient.getState(); + try { + IOUtils.write(command + "\n", socket.getOutputStream()); + } catch (IOException e) { + throw new ServiceNotAvailableException( + "zookeeper", + "write command failed", + "could not execute command to host: " + + host + + " and port: " + + port + + ", command: " + + command); } - - public List executeCommand(String host, Integer port, String command) throws ServiceNotAvailableException { - Socket socket; - try { - socket = new Socket(InetAddress.getByName(host), port); - } catch (IOException e) { - throw new ServiceNotAvailableException("zookeeper","down", "could not connect to host: " + host + " and port: " + port); - } - - try { - IOUtils.write(command + "\n", socket.getOutputStream()); - } catch (IOException e) { - throw new ServiceNotAvailableException("zookeeper","write command failed", "could not execute command to host: " + host + " and port: " + port + ", command: " + command); - } - - try { - return IOUtils.readLines(socket.getInputStream()); - } catch (IOException e) { - throw new ServiceNotAvailableException("zookeeper","read command failed", "could not execute command to host: " + host + " and port: " + port + ", command: " + command); - } finally { - try { - socket.close(); - } catch (IOException e) { - throw new IllegalStateException("Error disconnecting from host", e); - } - } - + try { + return IOUtils.readLines(socket.getInputStream()); + } catch (IOException e) { + throw new ServiceNotAvailableException( + "zookeeper", + "read command failed", + "could not execute command to host: " + + host + + " and port: " + + port + + ", command: " + + command); + } finally { + try { + socket.close(); + } catch (IOException e) { + throw new IllegalStateException("Error disconnecting from host", e); + } } - - public ZkServerStat parseStatResult(List lines) { - Iterator iterator = lines.iterator(); - - Matcher versionMatcher = versionLinePattern.matcher(iterator.next()); - versionMatcher.find(); - String version = versionMatcher.group(1); - String buildDate = versionMatcher.group(2); - iterator.next(); // Clients: - List clients = parseClientLines(iterator); - - Matcher latenciesMatcher = latenciesPattern.matcher(iterator.next()); - latenciesMatcher.find(); - Integer minLatency = Integer.parseInt(latenciesMatcher.group(1)); - Integer avgLatency = Integer.parseInt(latenciesMatcher.group(2)); - Integer maxLatency = Integer.parseInt(latenciesMatcher.group(3)); - - Integer received = parseIntFromLine(iterator.next(), PROP_DELIMITER); - Integer sent = parseIntFromLine(iterator.next(), PROP_DELIMITER); - Integer connections = parseIntFromLine(iterator.next(), PROP_DELIMITER); - Integer outstanding = parseIntFromLine(iterator.next(), PROP_DELIMITER); - String zxid = parseStringFromLine(iterator.next(), PROP_DELIMITER); - ZkServerMode mode = ZkServerMode.valueOf(StringUtils.capitalize(parseStringFromLine( - iterator.next(), PROP_DELIMITER))); - Integer nodeCount = parseIntFromLine(iterator.next(), PROP_DELIMITER); - - return new ZkServerStat(version, buildDate, clients, minLatency, avgLatency, maxLatency, - received, sent, connections, outstanding, zxid, mode, nodeCount); - - } - - private List parseClientLines(Iterator iterator) { - return parseClients(iterator); + } + + public ZkServerStat parseStatResult(List lines) { + Iterator iterator = lines.iterator(); + + Matcher versionMatcher = versionLinePattern.matcher(iterator.next()); + versionMatcher.find(); + String version = versionMatcher.group(1); + String buildDate = versionMatcher.group(2); + iterator.next(); // Clients: + List clients = parseClientLines(iterator); + + Matcher latenciesMatcher = latenciesPattern.matcher(iterator.next()); + latenciesMatcher.find(); + Integer minLatency = Integer.parseInt(latenciesMatcher.group(1)); + Integer avgLatency = Integer.parseInt(latenciesMatcher.group(2)); + Integer maxLatency = Integer.parseInt(latenciesMatcher.group(3)); + + Integer received = parseIntFromLine(iterator.next(), PROP_DELIMITER); + Integer sent = parseIntFromLine(iterator.next(), PROP_DELIMITER); + Integer connections = parseIntFromLine(iterator.next(), PROP_DELIMITER); + Integer outstanding = parseIntFromLine(iterator.next(), PROP_DELIMITER); + String zxid = parseStringFromLine(iterator.next(), PROP_DELIMITER); + ZkServerMode mode = + ZkServerMode.valueOf( + StringUtils.capitalize(parseStringFromLine(iterator.next(), PROP_DELIMITER))); + Integer nodeCount = parseIntFromLine(iterator.next(), PROP_DELIMITER); + + return new ZkServerStat( + version, + buildDate, + clients, + minLatency, + avgLatency, + maxLatency, + received, + sent, + connections, + outstanding, + zxid, + mode, + nodeCount); + } + + private List parseClientLines(Iterator iterator) { + return parseClients(iterator); + } + + private List parseClients(final Iterator iterator) { + final List clientLines = new LinkedList<>(); + String clientLine = iterator.next(); + while (iterator.hasNext() && clientLine.trim().length() > 0 && clientLine.startsWith(" /")) { + clientLines.add(clientLine); + clientLine = iterator.next(); } - - private List parseClients(final Iterator iterator) { - final List clientLines = new LinkedList<>(); - String clientLine = iterator.next(); - while (iterator.hasNext() && clientLine.trim().length() > 0 && clientLine.startsWith(" /")) { - clientLines.add(clientLine); - clientLine = iterator.next(); + return createClients(clientLines); + } + + private List createClients(List clientLines) { + List clients = new LinkedList<>(); + for (String clientLine : clientLines) { + if (!StringUtils.isWhitespace(clientLine)) { + Matcher matcher = ipv4ClientLinePattern.matcher(clientLine); + // Skip the ipv6 address + if (matcher.find()) { + clients.add(parseClient(matcher)); } - return createClients(clientLines); + } } - - - private List createClients(List clientLines) { - List clients = new LinkedList<>(); - for (String clientLine : clientLines) { - if (!StringUtils.isWhitespace(clientLine)) { - Matcher matcher = ipv4ClientLinePattern.matcher(clientLine); - //Skip the ipv6 address - if(matcher.find()){ - clients.add(parseClient(matcher)); - } - } - } - return clients; + return clients; + } + + private String parseStringFromLine(String line, String delimiter) { + return StringUtils.split(line, delimiter)[1].trim(); + } + + private Integer parseIntFromLine(String line, String delimiter) { + final String value = StringUtils.split(line, delimiter)[1]; + return Integer.parseInt(value.trim()); + } + + private ZkServerClient parseClient(Matcher matcher) { + String host = matcher.group(1); + Integer port = Integer.parseInt(matcher.group(2)); + Integer ops = Integer.parseInt(matcher.group(3)); + String statSegment = matcher.group(4); + if (statSegment.endsWith(")")) { + statSegment = statSegment.substring(0, statSegment.length() - 1); } - - private String parseStringFromLine(String line, String delimiter) { - return StringUtils.split(line, delimiter)[1].trim(); + String[] stats = StringUtils.split(statSegment, ","); + Integer queued = parseIntFromLine(stats[0], ATTRIBUTE_DELIMITER); + Integer received = parseIntFromLine(stats[1], ATTRIBUTE_DELIMITER); + Integer sent = parseIntFromLine(stats[2], ATTRIBUTE_DELIMITER); + ZkServerClient client = new ZkServerClient(host, port, ops, queued, sent, received); + return client; + } + + public ZkServerEnvironment parseEnvResult(final List result) { + Iterator iterator = result.iterator(); + iterator.next(); + ZkServerEnvironment environment = new ZkServerEnvironment(); + while (iterator.hasNext()) { + String line = iterator.next(); + String[] parts = StringUtils.split(line, "="); + environment.add(parts[0], parts[1]); } - - private Integer parseIntFromLine(String line, String delimiter) { - final String value = StringUtils.split(line, delimiter)[1]; - return Integer.parseInt(value.trim()); + return environment; + } + + public List lsPath(@ZkNodePathExistConstraint String path) { + try { + return curatorClient.getChildren().forPath(path); + } catch (Exception e) { + log.error("ls path fail! path: " + path + ", error: {}" + e); + return null; } - - private ZkServerClient parseClient(Matcher matcher) { - String host = matcher.group(1); - Integer port = Integer.parseInt(matcher.group(2)); - Integer ops = Integer.parseInt(matcher.group(3)); - String statSegment = matcher.group(4); - if (statSegment.endsWith(")")) { - statSegment = statSegment.substring(0, statSegment.length() - 1); + } + + public Map getNodeData(@ZkNodePathExistConstraint String path) { + Map map = new HashMap<>(); + + try { + List childrens = curatorClient.getChildren().forPath(path); + GetDataBuilder dataBuilder = curatorClient.getData(); + if (childrens != null && childrens.size() > 0) { + for (int i = 0; i < childrens.size(); i++) { + String child = childrens.get(i); + String childPath = ZKPaths.makePath(path, child); + byte[] bytes = dataBuilder.forPath(childPath); + map.put(childPath, (bytes != null) ? (new String(bytes, Charsets.UTF_8)) : (null)); } - String[] stats = StringUtils.split(statSegment, ","); - Integer queued = parseIntFromLine(stats[0], ATTRIBUTE_DELIMITER); - Integer received = parseIntFromLine(stats[1], ATTRIBUTE_DELIMITER); - Integer sent = parseIntFromLine(stats[2], ATTRIBUTE_DELIMITER); - ZkServerClient client = new ZkServerClient(host, port, ops, queued, sent, received); - return client; + } else { + byte[] bytes = dataBuilder.forPath(path); + map.put(path, (bytes != null) ? (new String(bytes, Charsets.UTF_8)) : (null)); + } + } catch (Exception e) { + log.error("get node data fail! path: " + path + ", error: {}" + e); } - public ZkServerEnvironment parseEnvResult(final List result) { - Iterator iterator = result.iterator(); - iterator.next(); - ZkServerEnvironment environment = new ZkServerEnvironment(); - while (iterator.hasNext()) { - String line = iterator.next(); - String[] parts = StringUtils.split(line, "="); - environment.add(parts[0], parts[1]); - } - return environment; - } - - public List lsPath(@ZKNodePathExistConstraint String path) { - try { - return curatorClient.getChildren().forPath(path); - } catch (Exception e) { - log.error("ls path fail! path: " + path + ", error: {}" + e); - return null; - } - } - - public Map getNodeData(@ZKNodePathExistConstraint String path) { - Map map = new HashMap<>(); - - try { - List childrens = curatorClient.getChildren().forPath(path); - GetDataBuilder dataBuilder = curatorClient.getData(); - if (childrens != null && childrens.size() > 0) { - for (int i = 0; i < childrens.size(); i++) { - String child = childrens.get(i); - String childPath = ZKPaths.makePath(path, child); - byte[] bytes = dataBuilder.forPath(childPath); - map.put(childPath, (bytes!=null)?(new String(bytes, Charsets.UTF_8)):(null)); - } - } else { - byte[] bytes = dataBuilder.forPath(path); - map.put(path, (bytes!=null)?(new String(bytes, Charsets.UTF_8)):(null)); - } - - } catch (Exception e) { - log.error("get node data fail! path: " + path + ", error: {}" + e); - } + return map; + } - return map; - } - - public Stat getNodePathStat(String path) { - try { - return curatorClient.checkExists().forPath(path); - } catch (Exception e) { - log.error("get node data fail! path: " + path + ", error: {}" + e); - } - return null; + public Stat getNodePathStat(String path) { + try { + return curatorClient.checkExists().forPath(path); + } catch (Exception e) { + log.error("get node data fail! path: " + path + ", error: {}" + e); } + return null; + } + + public KafkaZkClient createKafkaZkClient() { + return KafkaZkClient.apply( + zookeeperConfig.getUris(), + false, + 10 * SESSION_TIMEOUT, + CONNECTION_TIMEOUT, + Integer.MAX_VALUE, + Time.SYSTEM, + "testMetricGroup", + "testMetricType"); + } } - diff --git a/src/main/java/org/gnuhpc/bigdata/validator/ConsumerGroupExistConstraint.java b/src/main/java/org/gnuhpc/bigdata/validator/ConsumerGroupExistConstraint.java index 57c2fbe..e8efd18 100644 --- a/src/main/java/org/gnuhpc/bigdata/validator/ConsumerGroupExistConstraint.java +++ b/src/main/java/org/gnuhpc/bigdata/validator/ConsumerGroupExistConstraint.java @@ -1,17 +1,22 @@ package org.gnuhpc.bigdata.validator; +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import javax.validation.Constraint; import javax.validation.Payload; -import java.lang.annotation.*; @Documented @Constraint(validatedBy = ConsumerGroupExistValidator.class) @Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) public @interface ConsumerGroupExistConstraint { - String message() default "Non-exist Consumer Group!"; - Class[] groups() default {}; + String message() default "Non-exist Consumer Group!"; - Class[] payload() default {}; + Class[] groups() default {}; + + Class[] payload() default {}; } diff --git a/src/main/java/org/gnuhpc/bigdata/validator/ConsumerGroupExistValidator.java b/src/main/java/org/gnuhpc/bigdata/validator/ConsumerGroupExistValidator.java index 8eeb58f..51487db 100644 --- a/src/main/java/org/gnuhpc/bigdata/validator/ConsumerGroupExistValidator.java +++ b/src/main/java/org/gnuhpc/bigdata/validator/ConsumerGroupExistValidator.java @@ -1,20 +1,19 @@ package org.gnuhpc.bigdata.validator; +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; import org.gnuhpc.bigdata.service.KafkaAdminService; import org.springframework.beans.factory.annotation.Autowired; -import javax.validation.ConstraintValidator; -import javax.validation.ConstraintValidatorContext; +public class ConsumerGroupExistValidator + implements ConstraintValidator { -public class ConsumerGroupExistValidator implements ConstraintValidator { - @Autowired - private KafkaAdminService kafkaAdminService; + @Autowired private KafkaAdminService kafkaAdminService; - public void initialize(ConsumerGroupExistConstraint constraint) { - } + public void initialize(ConsumerGroupExistConstraint constraint) {} - public boolean isValid(String consumerGroup, ConstraintValidatorContext context) { - return kafkaAdminService.isNewConsumerGroup(consumerGroup) || - kafkaAdminService.isOldConsumerGroup(consumerGroup); - } + public boolean isValid(String consumerGroup, ConstraintValidatorContext context) { + return kafkaAdminService.isNewConsumerGroup(consumerGroup) + || kafkaAdminService.isOldConsumerGroup(consumerGroup); + } } diff --git a/src/main/java/org/gnuhpc/bigdata/validator/TopicExistConstraint.java b/src/main/java/org/gnuhpc/bigdata/validator/TopicExistConstraint.java index 05cf98a..6f88e1e 100644 --- a/src/main/java/org/gnuhpc/bigdata/validator/TopicExistConstraint.java +++ b/src/main/java/org/gnuhpc/bigdata/validator/TopicExistConstraint.java @@ -1,15 +1,22 @@ package org.gnuhpc.bigdata.validator; +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import javax.validation.Constraint; import javax.validation.Payload; -import java.lang.annotation.*; @Documented @Constraint(validatedBy = TopicExistValidator.class) -@Target( { ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER}) +@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) public @interface TopicExistConstraint { - String message() default "Non-exist Topic!"; - Class[] groups() default {}; - Class[] payload() default {}; + + String message() default "Non-exist Topic!"; + + Class[] groups() default {}; + + Class[] payload() default {}; } diff --git a/src/main/java/org/gnuhpc/bigdata/validator/TopicExistValidator.java b/src/main/java/org/gnuhpc/bigdata/validator/TopicExistValidator.java index cb27ba1..08b6f18 100644 --- a/src/main/java/org/gnuhpc/bigdata/validator/TopicExistValidator.java +++ b/src/main/java/org/gnuhpc/bigdata/validator/TopicExistValidator.java @@ -1,18 +1,17 @@ package org.gnuhpc.bigdata.validator; -import org.gnuhpc.bigdata.service.KafkaAdminService; -import org.springframework.beans.factory.annotation.Autowired; - import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; +import org.gnuhpc.bigdata.service.KafkaAdminService; +import org.springframework.beans.factory.annotation.Autowired; public class TopicExistValidator implements ConstraintValidator { - @Autowired - private KafkaAdminService kafkaAdminService; - public void initialize(TopicExistConstraint constraint) { - } - public boolean isValid(String topic, ConstraintValidatorContext context) { - return kafkaAdminService.existTopic(topic); - } + @Autowired private KafkaAdminService kafkaAdminService; + + public void initialize(TopicExistConstraint constraint) {} + + public boolean isValid(String topic, ConstraintValidatorContext context) { + return kafkaAdminService.existTopic(topic); + } } diff --git a/src/main/java/org/gnuhpc/bigdata/validator/ZKNodePathExistConstraint.java b/src/main/java/org/gnuhpc/bigdata/validator/ZkNodePathExistConstraint.java similarity index 71% rename from src/main/java/org/gnuhpc/bigdata/validator/ZKNodePathExistConstraint.java rename to src/main/java/org/gnuhpc/bigdata/validator/ZkNodePathExistConstraint.java index 910b173..72df727 100644 --- a/src/main/java/org/gnuhpc/bigdata/validator/ZKNodePathExistConstraint.java +++ b/src/main/java/org/gnuhpc/bigdata/validator/ZkNodePathExistConstraint.java @@ -1,16 +1,17 @@ package org.gnuhpc.bigdata.validator; -import javax.validation.Constraint; -import javax.validation.Payload; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import javax.validation.Constraint; +import javax.validation.Payload; -@Constraint(validatedBy = ZKNodePathExistValidator.class) -@Target( { ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER}) +@Constraint(validatedBy = ZkNodePathExistValidator.class) +@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) -public @interface ZKNodePathExistConstraint { +public @interface ZkNodePathExistConstraint { + String message() default "Non-exist ZooKeeper Node path!"; Class[] groups() default {}; diff --git a/src/main/java/org/gnuhpc/bigdata/validator/ZKNodePathExistValidator.java b/src/main/java/org/gnuhpc/bigdata/validator/ZkNodePathExistValidator.java similarity index 53% rename from src/main/java/org/gnuhpc/bigdata/validator/ZKNodePathExistValidator.java rename to src/main/java/org/gnuhpc/bigdata/validator/ZkNodePathExistValidator.java index dcf1d5f..7d8cb09 100644 --- a/src/main/java/org/gnuhpc/bigdata/validator/ZKNodePathExistValidator.java +++ b/src/main/java/org/gnuhpc/bigdata/validator/ZkNodePathExistValidator.java @@ -1,19 +1,18 @@ package org.gnuhpc.bigdata.validator; +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; import org.gnuhpc.bigdata.utils.ZookeeperUtils; import org.springframework.beans.factory.annotation.Autowired; -import javax.validation.ConstraintValidator; -import javax.validation.ConstraintValidatorContext; +public class ZkNodePathExistValidator + implements ConstraintValidator { -public class ZKNodePathExistValidator implements ConstraintValidator { - @Autowired - private ZookeeperUtils zookeeperUtils; + @Autowired private ZookeeperUtils zookeeperUtils; - public void initialize(ZKNodePathExistConstraint constraint) { - } + public void initialize(ZkNodePathExistConstraint constraint) {} public boolean isValid(String path, ConstraintValidatorContext context) { - return (zookeeperUtils.getNodePathStat(path)!=null); + return (zookeeperUtils.getNodePathStat(path) != null); } } diff --git a/src/main/resources/distribution/kafka-zk-springboot-distribution.xml b/src/main/resources/distribution/kafka-zk-springboot-distribution.xml index ab60f37..7519391 100644 --- a/src/main/resources/distribution/kafka-zk-springboot-distribution.xml +++ b/src/main/resources/distribution/kafka-zk-springboot-distribution.xml @@ -1,56 +1,56 @@ - assembly - - tar - zip - - - - src/main/resources/bin - bin - - start.sh - start.bat - - 0755 - - - src/main/resources - config - - *.properties - *.xml - *.yml - env/dev/* - env/qa/* - env/uat/* - env/prod/* - - - - target - lib - - *release*.jar - - - - src/main/resources - logs - 0755 - - **/* - - - - ${project.build.directory}/asciidoc - docs - - md/* - html/* - pdf/* - - 0755 - - + assembly + + tar + zip + + + + src/main/resources/bin + bin + + start.sh + start.bat + + 0755 + + + src/main/resources + config + + *.properties + *.xml + *.yml + env/dev/* + env/qa/* + env/uat/* + env/prod/* + + + + target + lib + + *release*.jar + + + + src/main/resources + logs + 0755 + + **/* + + + + ${project.build.directory}/asciidoc + docs + + md/* + html/* + pdf/* + + 0755 + + \ No newline at end of file diff --git a/src/main/resources/log4j.properties b/src/main/resources/log4j.properties index 7ba7f5b..1b5c06b 100644 --- a/src/main/resources/log4j.properties +++ b/src/main/resources/log4j.properties @@ -1,5 +1,4 @@ log4j.rootLogger=INFO, file, stdout - # Direct log messages to a log file log4j.appender.file=org.apache.log4j.RollingFileAppender log4j.appender.file.File=${basedir}/logs/kafka-zk-rest.log @@ -7,7 +6,6 @@ log4j.appender.file.MaxFileSize=10MB log4j.appender.file.MaxBackupIndex=10 log4j.appender.file.layout=org.apache.log4j.PatternLayout log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n - # Direct log messages to stdout log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.Target=System.out diff --git a/src/test/java/org/gnuhpc/bigdata/KafkaRestSpringbootApplicationTests.java b/src/test/java/org/gnuhpc/bigdata/KafkaRestSpringbootApplicationTests.java index d42f702..cad0865 100644 --- a/src/test/java/org/gnuhpc/bigdata/KafkaRestSpringbootApplicationTests.java +++ b/src/test/java/org/gnuhpc/bigdata/KafkaRestSpringbootApplicationTests.java @@ -1,17 +1,8 @@ package org.gnuhpc.bigdata; -import kafka.admin.AdminClient; -import kafka.common.ErrorMapping; -import kafka.common.OffsetMetadataAndError; -import kafka.common.TopicAndPartition; -import kafka.coordinator.group.GroupOverview; -import kafka.javaapi.OffsetFetchRequest; -import kafka.javaapi.OffsetFetchResponse; -import kafka.network.BlockingChannel; import kafka.utils.ZkUtils; import lombok.extern.log4j.Log4j; import org.apache.curator.framework.CuratorFramework; -import org.gnuhpc.bigdata.model.BrokerInfo; import org.gnuhpc.bigdata.service.KafkaAdminService; import org.gnuhpc.bigdata.utils.KafkaUtils; import org.gnuhpc.bigdata.utils.ZookeeperUtils; @@ -23,42 +14,36 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - @SpringBootTest @RunWith(SpringJUnit4ClassRunner.class) @WebAppConfiguration @Log4j public class KafkaRestSpringbootApplicationTests { - @Autowired - private KafkaAdminService kafkaAdminService; + @Autowired private KafkaAdminService kafkaAdminService; + + @Autowired private ZookeeperUtils zookeeperUtils; - @Autowired - private ZookeeperUtils zookeeperUtils; + @Autowired private KafkaUtils kafkaUtils; - @Autowired - private KafkaUtils kafkaUtils; + private ZkUtils zkUtils; - private ZkUtils zkUtils; + private CuratorFramework zkCuratorClient; - private CuratorFramework zkCuratorClient; - @Before - public void before(){ - this.zkUtils = zookeeperUtils.getZkUtils(); - this.zkCuratorClient = zookeeperUtils.getCuratorClient(); - } + @Before + public void before() { + this.zkUtils = zookeeperUtils.getZkUtils(); + this.zkCuratorClient = zookeeperUtils.getCuratorClient(); + } - @Test - public void contextLoads() { - return; - } + @Test + public void contextLoads() { + return; + } - @Test - public void testListBrokers() throws Exception { - //List brokerInfoList = kafkaAdminService.listBrokers(); - //brokerInfoList.stream().forEach(log::info); - } + @Test + public void testListBrokers() throws Exception { + // List brokerInfoList = kafkaAdminService.listBrokers(); + // brokerInfoList.stream().forEach(log::info); + } } diff --git a/src/test/java/org/gnuhpc/bigdata/Swagger2MarkupTest.java b/src/test/java/org/gnuhpc/bigdata/Swagger2MarkupTest.java index 8f802b2..095fbc9 100644 --- a/src/test/java/org/gnuhpc/bigdata/Swagger2MarkupTest.java +++ b/src/test/java/org/gnuhpc/bigdata/Swagger2MarkupTest.java @@ -18,11 +18,16 @@ */ package org.gnuhpc.bigdata; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import java.io.BufferedWriter; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; import org.gnuhpc.bigdata.config.SwaggerConfig; import org.junit.Test; import org.junit.runner.RunWith; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; @@ -34,14 +39,6 @@ import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; -import java.io.BufferedWriter; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Paths; - -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - @WebAppConfiguration @RunWith(SpringJUnit4ClassRunner.class) @AutoConfigureRestDocs(outputDir = "build/asciidoc/snippets") @@ -49,25 +46,26 @@ @AutoConfigureMockMvc public class Swagger2MarkupTest { - @Autowired - private MockMvc mockMvc; + @Autowired private MockMvc mockMvc; - @Test - public void createSpringfoxSwaggerJson() throws Exception { - //String designFirstSwaggerLocation = Swagger2MarkupTest.class.getResource("/swagger.yaml").getPath(); + @Test + public void createSpringfoxSwaggerJson() throws Exception { + // String designFirstSwaggerLocation = + // Swagger2MarkupTest.class.getResource("/swagger.yaml").getPath(); - String outputDir = System.getProperty("io.springfox.staticdocs.outputDir"); - MvcResult mvcResult = this.mockMvc.perform(get("/v2/api-docs") - .accept(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()) - .andReturn(); + String outputDir = System.getProperty("io.springfox.staticdocs.outputDir"); + MvcResult mvcResult = + this.mockMvc + .perform(get("/v2/api-docs").accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andReturn(); - MockHttpServletResponse response = mvcResult.getResponse(); - String swaggerJson = response.getContentAsString(); - Files.createDirectories(Paths.get(outputDir)); - try (BufferedWriter writer = Files.newBufferedWriter(Paths.get(outputDir, "swagger.json"), StandardCharsets.UTF_8)){ - writer.write(swaggerJson); - } + MockHttpServletResponse response = mvcResult.getResponse(); + String swaggerJson = response.getContentAsString(); + Files.createDirectories(Paths.get(outputDir)); + try (BufferedWriter writer = + Files.newBufferedWriter(Paths.get(outputDir, "swagger.json"), StandardCharsets.UTF_8)) { + writer.write(swaggerJson); } - + } } From d7df8d96d334f17ef5f9ccea2fcaaa2b85c2d850 Mon Sep 17 00:00:00 2001 From: tinawenqiao <315524513@qq.com> Date: Fri, 4 Jan 2019 12:46:40 +0800 Subject: [PATCH 06/24] Remove dependency on zk when starting application. --- .../gnuhpc/bigdata/constant/ZkServerMode.java | 5 +- .../bigdata/controller/KafkaController.java | 9 +- .../controller/ZookeeperController.java | 11 +- .../ServiceNotAvailableException.java | 5 +- .../gnuhpc/bigdata/model/ZkServerStat.java | 7 +- .../bigdata/service/KafkaAdminService.java | 60 +++--- .../bigdata/service/ZookeeperService.java | 74 +++++-- .../bigdata/task/FetchOffsetFromZkTask.java | 42 ---- .../org/gnuhpc/bigdata/utils/KafkaUtils.java | 13 -- .../gnuhpc/bigdata/utils/ZookeeperUtils.java | 196 +++++++++--------- .../KafkaRestSpringbootApplicationTests.java | 2 +- 11 files changed, 207 insertions(+), 217 deletions(-) delete mode 100644 src/main/java/org/gnuhpc/bigdata/task/FetchOffsetFromZkTask.java diff --git a/src/main/java/org/gnuhpc/bigdata/constant/ZkServerMode.java b/src/main/java/org/gnuhpc/bigdata/constant/ZkServerMode.java index 6fc7bc4..0309580 100644 --- a/src/main/java/org/gnuhpc/bigdata/constant/ZkServerMode.java +++ b/src/main/java/org/gnuhpc/bigdata/constant/ZkServerMode.java @@ -3,5 +3,8 @@ public enum ZkServerMode { Leader, Follower, - Observer + Observer, + Standalone, + Down, + Unknow } diff --git a/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java b/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java index 2c38bfc..35df05f 100644 --- a/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java +++ b/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java @@ -30,7 +30,6 @@ import org.gnuhpc.bigdata.model.TopicDetail; import org.gnuhpc.bigdata.model.TopicMeta; import org.gnuhpc.bigdata.service.KafkaAdminService; -import org.gnuhpc.bigdata.service.KafkaProducerService; import org.gnuhpc.bigdata.validator.ConsumerGroupExistConstraint; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; @@ -53,7 +52,7 @@ public class KafkaController { @Autowired private KafkaAdminService kafkaAdminService; - @Autowired private KafkaProducerService kafkaProducerService; +// @Autowired private KafkaProducerService kafkaProducerService; @GetMapping(value = "/cluster") @ApiOperation(value = "Describe cluster, nodes, controller info.") @@ -89,7 +88,7 @@ public Map> describeLogDirs( } @GetMapping(value = "/brokers/replicalogdirs") - @ApiOperation(value = "Describe replicat log dirs.") + @ApiOperation(value = "Describe replica log dirs.") public Map describeReplicaLogDirs( @RequestParam List replicas) { return kafkaAdminService.describeReplicaLogDirs(replicas); @@ -153,7 +152,7 @@ public boolean existTopic(@PathVariable String topic) { @ResponseStatus(HttpStatus.CREATED) @ApiOperation(value = "Write a message to the topic, for testing purpose") public GeneralResponse writeMessage(@PathVariable String topic, @RequestBody String message) { - kafkaProducerService.send(topic, message); +// kafkaProducerService.send(topic, message); return GeneralResponse.builder() .state(GeneralResponseState.success) .msg(message + " has been sent") @@ -357,4 +356,6 @@ public GeneralResponse deleteOldConsumerGroup( public HealthCheckResult healthCheck() { return kafkaAdminService.healthCheck(); } + + //TODO add kafkaAdminClient.deleterecords api } diff --git a/src/main/java/org/gnuhpc/bigdata/controller/ZookeeperController.java b/src/main/java/org/gnuhpc/bigdata/controller/ZookeeperController.java index 592c077..23fc268 100644 --- a/src/main/java/org/gnuhpc/bigdata/controller/ZookeeperController.java +++ b/src/main/java/org/gnuhpc/bigdata/controller/ZookeeperController.java @@ -29,15 +29,6 @@ public class ZookeeperController { @ApiOperation(value = "List a zookeeper path") public List ls(@RequestParam String path) { return zookeeperUtils.lsPath(path); - /* - try { - return zookeeperUtils.getCuratorClient().getChildren().forPath(path); - } catch (Exception e) { - e.printStackTrace(); - } - - return null; - */ } @GetMapping("/get/path") @@ -49,7 +40,7 @@ public Map get(@RequestParam String path) { @GetMapping("/connstate") @ApiOperation(value = "Get the connection state of zookeeper") public String zkConnState() { - return zookeeperUtils.getCuratorClient().getState().toString(); + return zookeeperUtils.getState(); } @GetMapping("/stat") diff --git a/src/main/java/org/gnuhpc/bigdata/exception/ServiceNotAvailableException.java b/src/main/java/org/gnuhpc/bigdata/exception/ServiceNotAvailableException.java index b992a16..1403027 100644 --- a/src/main/java/org/gnuhpc/bigdata/exception/ServiceNotAvailableException.java +++ b/src/main/java/org/gnuhpc/bigdata/exception/ServiceNotAvailableException.java @@ -2,15 +2,16 @@ import lombok.Data; import lombok.EqualsAndHashCode; +import org.gnuhpc.bigdata.constant.ZkServerMode; @Data @EqualsAndHashCode public class ServiceNotAvailableException extends RuntimeException { private String serviceType; - private String serviceState; + private ZkServerMode serviceState; - public ServiceNotAvailableException(String serviceType, String serviceState, String message) { + public ServiceNotAvailableException(String serviceType, ZkServerMode serviceState, String message) { super(message); this.serviceType = serviceType; this.serviceState = serviceState; diff --git a/src/main/java/org/gnuhpc/bigdata/model/ZkServerStat.java b/src/main/java/org/gnuhpc/bigdata/model/ZkServerStat.java index 48d1f77..ee583f9 100644 --- a/src/main/java/org/gnuhpc/bigdata/model/ZkServerStat.java +++ b/src/main/java/org/gnuhpc/bigdata/model/ZkServerStat.java @@ -1,12 +1,10 @@ package org.gnuhpc.bigdata.model; import java.util.List; -import lombok.AllArgsConstructor; -import lombok.Data; +import lombok.Builder; import org.gnuhpc.bigdata.constant.ZkServerMode; -@Data -@AllArgsConstructor +@Builder public class ZkServerStat { private final String version; @@ -22,4 +20,5 @@ public class ZkServerStat { private final String zxId; private final ZkServerMode mode; private final Integer nodes; + private final String msg; } diff --git a/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java b/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java index 215ed9c..957b0bf 100644 --- a/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java +++ b/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java @@ -163,15 +163,8 @@ public class KafkaAdminService { // For AdminUtils use private ZkUtils zkUtils; - // For zookeeper connection - private CuratorFramework zkClient; - - // private KafkaZkClient kafkaZkClient; - - // private AdminZkClient adminZkClient; - - // private org.apache.kafka.clients.admin.AdminClient kafkaAdminClient; // For Json serialized + // TODO replace gson with jackson private Gson gson; private scala.Option none = scala.Option.apply(null); @@ -179,12 +172,12 @@ public class KafkaAdminService { @PostConstruct private void init() { this.zkUtils = zookeeperUtils.getZkUtils(); - this.zkClient = zookeeperUtils.getCuratorClient(); GsonBuilder builder = new GsonBuilder(); builder.registerTypeAdapter( DateTime.class, - (JsonDeserializer) (jsonElement, type, jsonDeserializationContext) -> + (JsonDeserializer) + (jsonElement, type, jsonDeserializationContext) -> new DateTime(jsonElement.getAsJsonPrimitive().getAsLong())); this.gson = builder.create(); @@ -193,6 +186,7 @@ private void init() { private org.apache.kafka.clients.admin.AdminClient createKafkaAdminClient() { Properties adminClientProp = new Properties(); adminClientProp.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaConfig.getBrokers()); + return KafkaAdminClient.create(adminClientProp); } @@ -371,12 +365,18 @@ public Map describeCluster() { } public List listBrokers() { + CuratorFramework zkClient = zookeeperUtils.createZkClient(); + if (!zookeeperUtils.isConnected(zkClient)) { + throw new ApiException("Zookeeper is not connected."); + } + KafkaZkClient kafkaZkClient = zookeeperUtils.createKafkaZkClient(); List brokerList = CollectionConvertor.seqConvertJavaList(kafkaZkClient.getAllBrokersInCluster()); - kafkaZkClient.close(); - return brokerList + zkClient.start(); + + List brokerInfoList = brokerList .parallelStream() .collect(Collectors.toMap(Broker::id, Broker::rack)) .entrySet() @@ -402,6 +402,11 @@ public List listBrokers() { return brokerInfo; }) .collect(toList()); + + zkClient.close(); + kafkaZkClient.close(); + + return brokerInfoList; } public int getControllerId() { @@ -1255,8 +1260,8 @@ private JavaType getCollectionType( return mapper.getTypeFactory().constructParametricType(collectionClass, elementClasses); } - public List describeNewConsumerGroupByTopic(String consumerGroup, - String topic) { + public List describeNewConsumerGroupByTopic( + String consumerGroup, String topic) { if (!isNewConsumerGroup(consumerGroup)) { throw new RuntimeException(consumerGroup + " non-exist!"); } @@ -1820,20 +1825,22 @@ public GeneralResponse resetOffset( ConsumerType type, String offset) { KafkaConsumer consumer = null; - log.info("To tell the consumergroup " + consumerGroup + " is new"); if (type != null && type == ConsumerType.NEW) { if (!isNewConsumerGroup(consumerGroup)) { throw new ApiException("Consumer group " + consumerGroup + " is non-exist!"); } } - log.info("To tell the consumergroup " + consumerGroup + " is old"); if (type != null && type == ConsumerType.OLD) { if (!isOldConsumerGroup(consumerGroup)) { throw new ApiException("Consumer group " + consumerGroup + " is non-exist!"); } } + if (!isTopicPartitionValid(topic, partition)) { + throw new ApiException("Topic:" + topic + " has no partition:" + partition); + } + long offsetToBeReset = -1; long beginningOffset = getBeginningOffset(topic, partition); long endOffset = getEndOffset(topic, partition); @@ -1855,11 +1862,11 @@ public GeneralResponse resetOffset( if (offset.equals("earliest")) { consumer.seekToBeginning(Arrays.asList(tp)); offsetToBeReset = beginningOffset; - log.info("Reset to" + consumer.position(tp)); + log.info("Reset to " + consumer.position(tp)); } else if (offset.equals("latest")) { consumer.seekToEnd(Arrays.asList(tp)); offsetToBeReset = endOffset; - log.info("Reset to" + consumer.position(tp)); + log.info("Reset to " + consumer.position(tp)); } else if (isDateTime(offset)) { // Reset offset by time SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); @@ -1879,7 +1886,6 @@ public GeneralResponse resetOffset( + ", timestampToDate:" + sdf.format(new Date(offsetAndTimestamp.timestamp()))); consumer.seek(tp, offsetToBeReset); - consumer.commitSync(); } else { throw new ApiException( "No offset whose timestamp is greater than or equal to the given timestamp:" @@ -1904,8 +1910,8 @@ public GeneralResponse resetOffset( } offsetToBeReset = Long.parseLong(offset); consumer.seek(tp, offsetToBeReset); - consumer.commitSync(); } + consumer.commitSync();; } catch (IllegalStateException e) { storage.getMap().remove(consumerGroup); throw new ApiException(e); @@ -1968,6 +1974,13 @@ public Map> getLastCommitTime( Map> result = new ConcurrentHashMap<>(); if (type != null && type == ConsumerType.OLD) { + CuratorFramework zkClient = zookeeperUtils.createZkClient(); + + if (!zookeeperUtils.isConnected(zkClient)) { + throw new ApiException("Zookeeper is not connected."); + } + + zkClient.start(); // Get Old Consumer commit time try { Map oldConsumerOffsetMap = new ConcurrentHashMap<>(); @@ -1999,12 +2012,12 @@ public Map> getLastCommitTime( result.put("old", oldConsumerOffsetMap); } } catch (Exception e) { - e.printStackTrace(); + log.warn("Get last commit time for consumergroup:" + consumerGroup + " failed. " + e.getLocalizedMessage()); } - + zkClient.close(); } else { // Get New consumer commit time, from offset storage instance - + // TODO find a solution a replace the storage if (storage.get(consumerGroup) != null) { Map storageResult = storage.get(consumerGroup); @@ -2117,7 +2130,6 @@ private long getOffsets(Node leader, String topic, int partitionId, long time) { return offsets[0]; } - public long getBeginningOffset(String topic, int partitionId) { KafkaConsumer consumer = kafkaUtils.createNewConsumer(KafkaUtils.DEFAULTCP); TopicPartition tp = new TopicPartition(topic, partitionId); diff --git a/src/main/java/org/gnuhpc/bigdata/service/ZookeeperService.java b/src/main/java/org/gnuhpc/bigdata/service/ZookeeperService.java index 4521910..658d63b 100644 --- a/src/main/java/org/gnuhpc/bigdata/service/ZookeeperService.java +++ b/src/main/java/org/gnuhpc/bigdata/service/ZookeeperService.java @@ -1,10 +1,12 @@ package org.gnuhpc.bigdata.service; import com.google.common.net.HostAndPort; +import java.util.HashMap; +import java.util.List; import java.util.Map; -import java.util.stream.Collectors; import lombok.extern.log4j.Log4j; import org.gnuhpc.bigdata.constant.ZkServerCommand; +import org.gnuhpc.bigdata.exception.ServiceNotAvailableException; import org.gnuhpc.bigdata.model.ZkServerEnvironment; import org.gnuhpc.bigdata.model.ZkServerStat; import org.gnuhpc.bigdata.utils.ZookeeperUtils; @@ -18,30 +20,56 @@ public class ZookeeperService { @Autowired private ZookeeperUtils zookeeperUtils; public Map stat() { - return zookeeperUtils - .getZookeeperConfig() - .getHostAndPort() - .stream() - .collect( - Collectors.toMap( - hp -> hp, - hp -> - zookeeperUtils.parseStatResult( - zookeeperUtils.executeCommand( - hp.getHostText(), hp.getPort(), ZkServerCommand.stat.toString())))); + List hostAndPortList = zookeeperUtils.getZookeeperConfig().getHostAndPort(); + Map result = new HashMap<>(); + for (int i = 0; i < hostAndPortList.size(); i++) { + HostAndPort hp = hostAndPortList.get(i); + try { + result.put( + hp, + zookeeperUtils.parseStatResult( + zookeeperUtils.executeCommand( + hp.getHostText(), hp.getPort(), ZkServerCommand.stat.toString()))); + } catch (ServiceNotAvailableException serviceNotAvailbleException) { + log.warn( + "Execute " + + ZkServerCommand.stat.toString() + + " command failed. Exception:" + + serviceNotAvailbleException); + result.put( + hp, + ZkServerStat.builder() + .mode(serviceNotAvailbleException.getServiceState()) + .msg(serviceNotAvailbleException.getMessage()) + .build()); + } + } + return result; } public Map environment() { - return zookeeperUtils - .getZookeeperConfig() - .getHostAndPort() - .stream() - .collect( - Collectors.toMap( - hp -> hp, - hp -> - zookeeperUtils.parseEnvResult( - zookeeperUtils.executeCommand( - hp.getHostText(), hp.getPort(), ZkServerCommand.envi.toString())))); + List hostAndPortList = zookeeperUtils.getZookeeperConfig().getHostAndPort(); + Map result = new HashMap<>(); + for (int i = 0; i < hostAndPortList.size(); i++) { + HostAndPort hp = hostAndPortList.get(i); + try { + result.put( + hp, + zookeeperUtils.parseEnvResult( + zookeeperUtils.executeCommand( + hp.getHostText(), hp.getPort(), ZkServerCommand.envi.toString()))); + } catch (ServiceNotAvailableException serviceNotAvailbleException) { + log.warn( + "Execute " + + ZkServerCommand.envi.toString() + + " command failed. Exception:" + + serviceNotAvailbleException); + ZkServerEnvironment zkServerEnvironment = new ZkServerEnvironment(); + zkServerEnvironment.add("mode", serviceNotAvailbleException.getServiceState().toString()); + zkServerEnvironment.add("msg", serviceNotAvailbleException.getMessage()); + result.put(hp, zkServerEnvironment); + } + } + return result; } } diff --git a/src/main/java/org/gnuhpc/bigdata/task/FetchOffsetFromZkTask.java b/src/main/java/org/gnuhpc/bigdata/task/FetchOffsetFromZkTask.java deleted file mode 100644 index e052d69..0000000 --- a/src/main/java/org/gnuhpc/bigdata/task/FetchOffsetFromZkTask.java +++ /dev/null @@ -1,42 +0,0 @@ -package org.gnuhpc.bigdata.task; - -import java.util.concurrent.Callable; -import lombok.Data; -import org.gnuhpc.bigdata.utils.ZookeeperUtils; - -@Data -public class FetchOffsetFromZkTask implements Callable { - - private String topic; - private String consumerGroup; - private int partition; - - private ZookeeperUtils zookeeperUtils; - - public FetchOffsetFromZkTask( - ZookeeperUtils zookeeperUtils, String topic, String consumerGroup, int partition) { - this.zookeeperUtils = zookeeperUtils; - this.topic = topic; - this.consumerGroup = consumerGroup; - this.partition = partition; - } - - @Override - public FetchOffSetFromZkResult call() throws Exception { - long offset = 0; - FetchOffSetFromZkResult result = new FetchOffSetFromZkResult(); - String path = "/consumers/" + consumerGroup + "/offsets/" + topic + "/" + partition; - if (zookeeperUtils.getCuratorClient().checkExists().forPath(path) != null) { - offset = - Long.parseLong( - zookeeperUtils - .getZkClient() - .readData("/consumers/" + consumerGroup + "/offsets/" + topic + "/" + partition)); - } - - result.setOffset(offset); - result.setTopic(topic); - result.setParition(partition); - return result; - } -} diff --git a/src/main/java/org/gnuhpc/bigdata/utils/KafkaUtils.java b/src/main/java/org/gnuhpc/bigdata/utils/KafkaUtils.java index a0c545a..653f563 100644 --- a/src/main/java/org/gnuhpc/bigdata/utils/KafkaUtils.java +++ b/src/main/java/org/gnuhpc/bigdata/utils/KafkaUtils.java @@ -29,7 +29,6 @@ public class KafkaUtils { @Autowired private KafkaConfig kafkaConfig; @Autowired private ZookeeperConfig zookeeperConfig; - private AdminClient adminClient; private KafkaProducer producer; private Properties prop; @@ -37,18 +36,6 @@ public class KafkaUtils { public static final String DEFAULTCP = "kafka-rest-consumergroup"; public void init() { - prop = new Properties(); - prop.setProperty(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaConfig.getBrokers()); - prop.setProperty( - ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, - "org.apache.kafka.common.serialization.StringSerializer"); - prop.setProperty( - ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, - "org.apache.kafka.common.serialization.StringSerializer"); - producer = new KafkaProducer(prop); - log.info("Kafka initing..."); - - adminClient = AdminClient.create(prop); } public void destroy() { diff --git a/src/main/java/org/gnuhpc/bigdata/utils/ZookeeperUtils.java b/src/main/java/org/gnuhpc/bigdata/utils/ZookeeperUtils.java index 2e75a45..6107181 100644 --- a/src/main/java/org/gnuhpc/bigdata/utils/ZookeeperUtils.java +++ b/src/main/java/org/gnuhpc/bigdata/utils/ZookeeperUtils.java @@ -11,14 +11,11 @@ import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; -import kafka.utils.ZKStringSerializer$; import kafka.utils.ZkUtils; import kafka.zk.KafkaZkClient; import lombok.Getter; import lombok.Setter; import lombok.extern.log4j.Log4j; -import org.I0Itec.zkclient.ZkClient; -import org.I0Itec.zkclient.ZkConnection; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.apache.curator.RetryPolicy; @@ -27,6 +24,7 @@ import org.apache.curator.framework.api.GetDataBuilder; import org.apache.curator.retry.ExponentialBackoffRetry; import org.apache.curator.utils.ZKPaths; +import org.apache.kafka.common.errors.ApiException; import org.apache.kafka.common.utils.Time; import org.apache.zookeeper.data.Stat; import org.gnuhpc.bigdata.config.ZookeeperConfig; @@ -66,67 +64,14 @@ public class ZookeeperUtils { private static final int SESSION_TIMEOUT = 5000; private static final int CONNECTION_TIMEOUT = 5000; - private CuratorFramework curatorClient; - private ZkClient zkClient; private ZkUtils zkUtils; - private KafkaZkClient kafkaZkClient; - public void init() { - - // 1.设置重试策略,重试时间计算策略sleepMs = baseSleepTimeMs * Math.max(1, random.nextInt(1 << (retryCount + - // 1))); - RetryPolicy retryPolicy = - new ExponentialBackoffRetry(BASE_SLEEP_TIME, MAX_RETRIES_COUNT, MAX_SLEEP_TIME); - - // 2.初始化客户端 - curatorClient = - CuratorFrameworkFactory.builder() - .connectString(zookeeperConfig.getUris()) - .sessionTimeoutMs(SESSION_TIMEOUT) - .connectionTimeoutMs(CONNECTION_TIMEOUT) - .retryPolicy(retryPolicy) - // .namespace("kafka-rest") //命名空间隔离 - .build(); - curatorClient.start(); - try { - curatorClient.blockUntilConnected(); - log.info("Zookeeper:" + zookeeperConfig.getUris() + " Connected.Continue..."); - zkClient = - new ZkClient( - zookeeperConfig.getUris(), - SESSION_TIMEOUT, - CONNECTION_TIMEOUT, - ZKStringSerializer$.MODULE$); - zkUtils = new ZkUtils(zkClient, new ZkConnection(zookeeperConfig.getUris()), false); - log.info("Zkutils" + zkUtils.toString()); - } catch (InterruptedException e) { - e.printStackTrace(); - log.error("Exception:", e); - } - kafkaZkClient = - KafkaZkClient.apply( - zookeeperConfig.getUris(), - false, - 10 * SESSION_TIMEOUT, - CONNECTION_TIMEOUT, - Integer.MAX_VALUE, - Time.SYSTEM, - "testMetricGroup", - "testMetricType"); - } + public void init() {} public void destroy() { - curatorClient.close(); - zkClient.close(); - kafkaZkClient.close(); log.info("zookeeper closed."); } - @Override - public String toString() { - return zookeeperConfig.getUris() + curatorClient.getState(); - } - public List executeCommand(String host, Integer port, String command) throws ServiceNotAvailableException { Socket socket; @@ -134,7 +79,9 @@ public List executeCommand(String host, Integer port, String command) socket = new Socket(InetAddress.getByName(host), port); } catch (IOException e) { throw new ServiceNotAvailableException( - "zookeeper", "down", "could not connect to host: " + host + " and port: " + port); + "zookeeper", + ZkServerMode.Down, + "could not connect to host: " + host + " and port: " + port); } try { @@ -142,13 +89,8 @@ public List executeCommand(String host, Integer port, String command) } catch (IOException e) { throw new ServiceNotAvailableException( "zookeeper", - "write command failed", - "could not execute command to host: " - + host - + " and port: " - + port - + ", command: " - + command); + ZkServerMode.Unknow, + "could not write to host: " + host + " and port: " + port + ", command: " + command); } try { @@ -156,13 +98,8 @@ public List executeCommand(String host, Integer port, String command) } catch (IOException e) { throw new ServiceNotAvailableException( "zookeeper", - "read command failed", - "could not execute command to host: " - + host - + " and port: " - + port - + ", command: " - + command); + ZkServerMode.Unknow, + "could not read from host: " + host + " and port: " + port + ", command: " + command); } finally { try { socket.close(); @@ -198,20 +135,21 @@ public ZkServerStat parseStatResult(List lines) { StringUtils.capitalize(parseStringFromLine(iterator.next(), PROP_DELIMITER))); Integer nodeCount = parseIntFromLine(iterator.next(), PROP_DELIMITER); - return new ZkServerStat( - version, - buildDate, - clients, - minLatency, - avgLatency, - maxLatency, - received, - sent, - connections, - outstanding, - zxid, - mode, - nodeCount); + return ZkServerStat.builder() + .version(version) + .buildDate(buildDate) + .clients(clients) + .minLatency(minLatency) + .avgLatency(avgLatency) + .maxLatency(maxLatency) + .received(received) + .sent(sent) + .connections(connections) + .outstanding(outstanding) + .zxId(zxid) + .mode(mode) + .nodes(nodeCount) + .build(); } private List parseClientLines(Iterator iterator) { @@ -280,16 +218,35 @@ public ZkServerEnvironment parseEnvResult(final List result) { } public List lsPath(@ZkNodePathExistConstraint String path) { + List stringList = null; + CuratorFramework curatorClient = createZkClient(); + curatorClient.start(); + + if (!isConnected(curatorClient)) { + curatorClient.close(); + throw new ApiException("Zookeeper is not connected."); + } + try { - return curatorClient.getChildren().forPath(path); + stringList = curatorClient.getChildren().forPath(path); } catch (Exception e) { log.error("ls path fail! path: " + path + ", error: {}" + e); - return null; + } finally { + curatorClient.close(); } + + return stringList; } public Map getNodeData(@ZkNodePathExistConstraint String path) { Map map = new HashMap<>(); + CuratorFramework curatorClient = createZkClient(); + curatorClient.start(); + + if (!isConnected(curatorClient)) { + curatorClient.close(); + throw new ApiException("Zookeeper is not connected."); + } try { List childrens = curatorClient.getChildren().forPath(path); @@ -310,27 +267,80 @@ public Map getNodeData(@ZkNodePathExistConstraint String path) { log.error("get node data fail! path: " + path + ", error: {}" + e); } + curatorClient.close(); + return map; } public Stat getNodePathStat(String path) { + CuratorFramework curatorClient = createZkClient(); + curatorClient.start(); + Stat stat = null; + + if (!isConnected(curatorClient)) { + curatorClient.close(); + throw new ApiException("Zookeeper is not connected."); + } + try { - return curatorClient.checkExists().forPath(path); + stat = curatorClient.checkExists().forPath(path); } catch (Exception e) { log.error("get node data fail! path: " + path + ", error: {}" + e); } - return null; + + curatorClient.close(); + + return stat; + } + + public CuratorFramework createZkClient() { + // 1.设置重试策略,重试时间计算策略sleepMs = baseSleepTimeMs * Math.max(1, random.nextInt(1 << (retryCount + + // 1))); + RetryPolicy retryPolicy = + new ExponentialBackoffRetry(BASE_SLEEP_TIME, MAX_RETRIES_COUNT, MAX_SLEEP_TIME); + + // 2.初始化客户端 + CuratorFramework curatorClient = + CuratorFrameworkFactory.builder() + .connectString(zookeeperConfig.getUris()) + .sessionTimeoutMs(SESSION_TIMEOUT) + .connectionTimeoutMs(CONNECTION_TIMEOUT) + .retryPolicy(retryPolicy) + // .namespace("kafka-rest") //命名空间隔离 + .build(); + + return curatorClient; + } + + public String getState() { + CuratorFramework curatorClient = createZkClient(); + curatorClient.start(); + + if (!isConnected(curatorClient)) { + curatorClient.close(); + throw new ApiException("Zookeeper is not connected."); + } + + String stateStr = curatorClient.getState().toString(); + + curatorClient.close(); + + return stateStr; } public KafkaZkClient createKafkaZkClient() { return KafkaZkClient.apply( zookeeperConfig.getUris(), false, - 10 * SESSION_TIMEOUT, + SESSION_TIMEOUT, CONNECTION_TIMEOUT, Integer.MAX_VALUE, Time.SYSTEM, - "testMetricGroup", - "testMetricType"); + "kafka.zk.rest", + "rest"); + } + + public boolean isConnected(CuratorFramework curatorClient) { + return curatorClient.getZookeeperClient().isConnected(); } } diff --git a/src/test/java/org/gnuhpc/bigdata/KafkaRestSpringbootApplicationTests.java b/src/test/java/org/gnuhpc/bigdata/KafkaRestSpringbootApplicationTests.java index cad0865..dd3685e 100644 --- a/src/test/java/org/gnuhpc/bigdata/KafkaRestSpringbootApplicationTests.java +++ b/src/test/java/org/gnuhpc/bigdata/KafkaRestSpringbootApplicationTests.java @@ -33,7 +33,7 @@ public class KafkaRestSpringbootApplicationTests { @Before public void before() { this.zkUtils = zookeeperUtils.getZkUtils(); - this.zkCuratorClient = zookeeperUtils.getCuratorClient(); +// this.zkCuratorClient = zookeeperUtils.getCuratorClient(); } @Test From a83073308a12d69dfc19ceaa088968fe6aa9371f Mon Sep 17 00:00:00 2001 From: tinawenqiao <315524513@qq.com> Date: Fri, 4 Jan 2019 13:49:22 +0800 Subject: [PATCH 07/24] Fix bug when zk and kafka are stopped. --- .../gnuhpc/bigdata/config/KafkaConfig.java | 9 +++++---- .../bigdata/controller/KafkaController.java | 19 +++++++++---------- .../gnuhpc/bigdata/utils/ZookeeperUtils.java | 9 ++++++++- 3 files changed, 22 insertions(+), 15 deletions(-) diff --git a/src/main/java/org/gnuhpc/bigdata/config/KafkaConfig.java b/src/main/java/org/gnuhpc/bigdata/config/KafkaConfig.java index 09e8018..4eb6cbd 100644 --- a/src/main/java/org/gnuhpc/bigdata/config/KafkaConfig.java +++ b/src/main/java/org/gnuhpc/bigdata/config/KafkaConfig.java @@ -13,6 +13,7 @@ import org.apache.kafka.clients.consumer.ConsumerConfig; import org.apache.kafka.common.serialization.ByteBufferDeserializer; import org.gnuhpc.bigdata.componet.OffsetStorage; +import org.gnuhpc.bigdata.service.KafkaConsumerService; import org.gnuhpc.bigdata.utils.KafkaUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; @@ -58,10 +59,10 @@ public OffsetStorage offsetStorage() { return new OffsetStorage(); } - // @Bean - // public KafkaConsumerService kafkaConsumerService() { - // return new KafkaConsumerService(internalTopicPartitions); - // } + @Bean + public KafkaConsumerService kafkaConsumerService() { + return new KafkaConsumerService(internalTopicPartitions); + } @Bean public KafkaListenerContainerFactory> diff --git a/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java b/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java index 35df05f..9533fa9 100644 --- a/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java +++ b/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java @@ -17,7 +17,6 @@ import org.apache.kafka.common.errors.ApiException; import org.apache.kafka.common.requests.DescribeLogDirsResponse.LogDirInfo; import org.gnuhpc.bigdata.constant.ConsumerType; -import org.gnuhpc.bigdata.constant.GeneralResponseState; import org.gnuhpc.bigdata.model.AddPartition; import org.gnuhpc.bigdata.model.BrokerInfo; import org.gnuhpc.bigdata.model.ConsumerGroupDesc; @@ -148,16 +147,16 @@ public boolean existTopic(@PathVariable String topic) { return kafkaAdminService.existTopic(topic); } - @PostMapping(value = "/topics/{topic}/write", consumes = "text/plain") - @ResponseStatus(HttpStatus.CREATED) - @ApiOperation(value = "Write a message to the topic, for testing purpose") - public GeneralResponse writeMessage(@PathVariable String topic, @RequestBody String message) { +// @PostMapping(value = "/topics/{topic}/write", consumes = "text/plain") +// @ResponseStatus(HttpStatus.CREATED) +// @ApiOperation(value = "Write a message to the topic, for testing purpose") +// public GeneralResponse writeMessage(@PathVariable String topic, @RequestBody String message) { // kafkaProducerService.send(topic, message); - return GeneralResponse.builder() - .state(GeneralResponseState.success) - .msg(message + " has been sent") - .build(); - } +// return GeneralResponse.builder() +// .state(GeneralResponseState.success) +// .msg(message + " has been sent") +// .build(); +// } @GetMapping(value = "/consumer/{topic}/{partition}/{offset}") @ApiOperation( diff --git a/src/main/java/org/gnuhpc/bigdata/utils/ZookeeperUtils.java b/src/main/java/org/gnuhpc/bigdata/utils/ZookeeperUtils.java index 6107181..b1741d6 100644 --- a/src/main/java/org/gnuhpc/bigdata/utils/ZookeeperUtils.java +++ b/src/main/java/org/gnuhpc/bigdata/utils/ZookeeperUtils.java @@ -329,7 +329,9 @@ public String getState() { } public KafkaZkClient createKafkaZkClient() { - return KafkaZkClient.apply( + KafkaZkClient kafkaZkClient = null; + try { + kafkaZkClient = KafkaZkClient.apply( zookeeperConfig.getUris(), false, SESSION_TIMEOUT, @@ -338,6 +340,11 @@ public KafkaZkClient createKafkaZkClient() { Time.SYSTEM, "kafka.zk.rest", "rest"); + } catch (Exception exception) { + throw new ApiException("Failed to create kafkaZkClient." + exception.getLocalizedMessage()); + } + + return kafkaZkClient; } public boolean isConnected(CuratorFramework curatorClient) { From c65ea7d9c61a12d3b63babf06a95354127c86cfc Mon Sep 17 00:00:00 2001 From: tinawenqiao <315524513@qq.com> Date: Fri, 4 Jan 2019 15:20:53 +0800 Subject: [PATCH 08/24] Make zkClient,kafkaZkClient,kafkaAdminClient to singleton. --- .../bigdata/service/KafkaAdminService.java | 70 ++---------- .../gnuhpc/bigdata/utils/ZookeeperUtils.java | 107 ++++++++---------- 2 files changed, 53 insertions(+), 124 deletions(-) diff --git a/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java b/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java index 957b0bf..a555384 100644 --- a/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java +++ b/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java @@ -152,6 +152,7 @@ public class KafkaAdminService { private static final long kafkaAdminClientAlterTimeoutMs = 60000; private static final String CONSUMERPATHPREFIX = "/consumers/"; private static final String OFFSETSPATHPREFIX = "/offsets/"; + @Autowired private ZookeeperUtils zookeeperUtils; @Autowired private KafkaUtils kafkaUtils; @@ -163,6 +164,7 @@ public class KafkaAdminService { // For AdminUtils use private ZkUtils zkUtils; + private org.apache.kafka.clients.admin.AdminClient kafkaAdminClient = null; // For Json serialized // TODO replace gson with jackson private Gson gson; @@ -184,10 +186,13 @@ private void init() { } private org.apache.kafka.clients.admin.AdminClient createKafkaAdminClient() { - Properties adminClientProp = new Properties(); - adminClientProp.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaConfig.getBrokers()); + if (this.kafkaAdminClient == null) { + Properties adminClientProp = new Properties(); + adminClientProp.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaConfig.getBrokers()); + this.kafkaAdminClient = KafkaAdminClient.create(adminClientProp); + } - return KafkaAdminClient.create(adminClientProp); + return this.kafkaAdminClient; } public TopicMeta createTopic(TopicDetail topic, String reassignStr) { @@ -228,8 +233,6 @@ public TopicMeta createTopic(TopicDetail topic, String reassignStr) { TopicCommand.createTopic( kafkaZkClient, new TopicCommand.TopicCommandOptions(argsList.stream().toArray(String[]::new))); - - kafkaZkClient.close(); } try { @@ -265,7 +268,6 @@ public Set getAllTopics() { throw new ApiException("List topic exception : " + exception); } - kafkaAdminClient.close(); return topicNames; } @@ -310,7 +312,6 @@ public List listTopicBrief() { throw new ApiException("Describe all topics exception:" + exception); } - kafkaAdminClient.close(); return result; } @@ -318,8 +319,6 @@ public boolean existTopic(String topicName) { KafkaZkClient kafkaZkClient = zookeeperUtils.createKafkaZkClient(); boolean exists = kafkaZkClient.topicExists(topicName); - kafkaZkClient.close(); - return exists; } @@ -359,23 +358,15 @@ public Map describeCluster() { } } - kafkaAdminClient.close(); - return clusterDetail; } public List listBrokers() { CuratorFramework zkClient = zookeeperUtils.createZkClient(); - if (!zookeeperUtils.isConnected(zkClient)) { - throw new ApiException("Zookeeper is not connected."); - } - KafkaZkClient kafkaZkClient = zookeeperUtils.createKafkaZkClient(); List brokerList = CollectionConvertor.seqConvertJavaList(kafkaZkClient.getAllBrokersInCluster()); - zkClient.start(); - List brokerInfoList = brokerList .parallelStream() .collect(Collectors.toMap(Broker::id, Broker::rack)) @@ -403,9 +394,6 @@ public List listBrokers() { }) .collect(toList()); - zkClient.close(); - kafkaZkClient.close(); - return brokerInfoList; } @@ -417,8 +405,6 @@ public int getControllerId() { controllerId = (int) kafkaZkClient.getControllerId().get(); } - kafkaZkClient.close(); - return controllerId; } @@ -495,8 +481,6 @@ public Map> describeLogDirsByBrokerAndTopic( } } - kafkaAdminClient.close(); - return logDirInfosByBroker; } @@ -520,8 +504,6 @@ public Map describeReplicaLogDirs( throw new ApiException("Describe replica log dirs exception:" + exception); } - kafkaAdminClient.close(); - return replicaLogDirInfoMap; } @@ -545,8 +527,6 @@ public Properties getConfigInZk(ConfigResource.Type type, String name) { properties = adminZkClient.fetchEntityConfig(ConfigType.Topic(), name); } - kafkaZkClient.close(); - return properties; } @@ -581,7 +561,6 @@ private void validateConfigs( public Properties updateBrokerDynConf(int brokerId, Properties propsToBeUpdated) { Properties props = getConfigInZk(Type.BROKER, String.valueOf(brokerId)); - KafkaZkClient kafkaZkClient = zookeeperUtils.createKafkaZkClient(); AdminZkClient adminZkClient = new AdminZkClient(kafkaZkClient); @@ -599,8 +578,6 @@ public Properties updateBrokerDynConf(int brokerId, Properties propsToBeUpdated) .toSeq(), props); - kafkaZkClient.close(); - return getConfigInZk(Type.BROKER, String.valueOf(brokerId)); } @@ -625,7 +602,6 @@ public void removeConfigInZk(Type type, String name, List configKeysToBe adminZkClient.changeTopicConfig(name, props); } - kafkaZkClient.close(); } public TopicMeta describeTopic(@TopicExistConstraint String topicName) { @@ -667,7 +643,6 @@ public TopicMeta describeTopic(@TopicExistConstraint String topicName) { throw new ApiException("Describe topic exception." + exception); } - kafkaAdminClient.close(); return topicMeta; } @@ -701,7 +676,6 @@ public Map deleteTopicList(List topicList) { }); } - kafkaAdminClient.close(); return deleteResults; } @@ -719,7 +693,6 @@ public Collection describeConfig(ConfigResource.Type type, String n throw new ApiException("Describe config exception:" + exception.getLocalizedMessage()); } - kafkaAdminClient.close(); return configs.get(configResource).entries(); } @@ -738,8 +711,6 @@ public boolean alterConfig( return false; } - kafkaAdminClient.close(); - return true; } @@ -828,8 +799,6 @@ private Set listAllOldConsumerGroups() { .stream() .collect(toSet()); - kafkaZkClient.close(); - return oldConsumerGroups; } @@ -889,8 +858,6 @@ private Set listOldConsumerGroupsByTopic(@TopicExistConstraint String to } } - kafkaZkClient.close(); - return consumerList; } @@ -920,8 +887,6 @@ public Set listTopicsByConsumerGroup(String consumerGroup, ConsumerType throw new ApiException("Unknown Type " + type); } - kafkaZkClient.close(); - return topicList; } @@ -1379,7 +1344,6 @@ public Map addPartitions(List addPartitio addPartitionsResult.put(topic, generalResponse); }); - kafkaAdminClient.close(); return addPartitionsResult; } } @@ -1405,8 +1369,6 @@ public List generateReassignPartition(ReassignWrapper reassignWrapper) { (scala.collection.Map>) resultTuple2._2(), JavaConverters.mapAsScalaMapConverter(emptyMap).asScala())); - kafkaZkClient.close(); - return result; } @@ -1464,9 +1426,6 @@ public BoxedUnit apply() { } } - kafkaZkClient.close(); - kafkaAdminClient.close(); - return checkReassignStatus(partitionsToBeReassignedMap, replicatAssignment); } @@ -1551,8 +1510,6 @@ private boolean removeThrottle( } } - kafkaZkClient.close(); - return true; } @@ -1577,7 +1534,6 @@ private Map checkIfPartitionReassignmentSucceeded( .status()); } - kafkaZkClient.close(); return reassignedPartitionsStatus; } @@ -1615,7 +1571,6 @@ private Map checkIfReplicaReassignmentSucceeded( } } - kafkaAdminClient.close(); return replicasReassignmentStatus; } @@ -1660,8 +1615,6 @@ public Map checkReassignStatusByStr(String reassignStr) { scala.collection.Map replicatAssignment = (scala.collection.Map) resultTuple2._2(); - kafkaZkClient.close(); - return new TwoTuple<>(partitionsToBeReassignedMap, replicatAssignment); } @@ -1975,12 +1928,6 @@ public Map> getLastCommitTime( if (type != null && type == ConsumerType.OLD) { CuratorFramework zkClient = zookeeperUtils.createZkClient(); - - if (!zookeeperUtils.isConnected(zkClient)) { - throw new ApiException("Zookeeper is not connected."); - } - - zkClient.start(); // Get Old Consumer commit time try { Map oldConsumerOffsetMap = new ConcurrentHashMap<>(); @@ -2014,7 +1961,6 @@ public Map> getLastCommitTime( } catch (Exception e) { log.warn("Get last commit time for consumergroup:" + consumerGroup + " failed. " + e.getLocalizedMessage()); } - zkClient.close(); } else { // Get New consumer commit time, from offset storage instance // TODO find a solution a replace the storage diff --git a/src/main/java/org/gnuhpc/bigdata/utils/ZookeeperUtils.java b/src/main/java/org/gnuhpc/bigdata/utils/ZookeeperUtils.java index b1741d6..66f12cc 100644 --- a/src/main/java/org/gnuhpc/bigdata/utils/ZookeeperUtils.java +++ b/src/main/java/org/gnuhpc/bigdata/utils/ZookeeperUtils.java @@ -9,6 +9,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; import kafka.utils.ZkUtils; @@ -65,6 +66,8 @@ public class ZookeeperUtils { private static final int SESSION_TIMEOUT = 5000; private static final int CONNECTION_TIMEOUT = 5000; private ZkUtils zkUtils; + private CuratorFramework curatorClient = null; + private KafkaZkClient kafkaZkClient = null; public void init() {} @@ -220,19 +223,11 @@ public ZkServerEnvironment parseEnvResult(final List result) { public List lsPath(@ZkNodePathExistConstraint String path) { List stringList = null; CuratorFramework curatorClient = createZkClient(); - curatorClient.start(); - - if (!isConnected(curatorClient)) { - curatorClient.close(); - throw new ApiException("Zookeeper is not connected."); - } try { stringList = curatorClient.getChildren().forPath(path); } catch (Exception e) { log.error("ls path fail! path: " + path + ", error: {}" + e); - } finally { - curatorClient.close(); } return stringList; @@ -241,12 +236,6 @@ public List lsPath(@ZkNodePathExistConstraint String path) { public Map getNodeData(@ZkNodePathExistConstraint String path) { Map map = new HashMap<>(); CuratorFramework curatorClient = createZkClient(); - curatorClient.start(); - - if (!isConnected(curatorClient)) { - curatorClient.close(); - throw new ApiException("Zookeeper is not connected."); - } try { List childrens = curatorClient.getChildren().forPath(path); @@ -267,83 +256,77 @@ public Map getNodeData(@ZkNodePathExistConstraint String path) { log.error("get node data fail! path: " + path + ", error: {}" + e); } - curatorClient.close(); - return map; } public Stat getNodePathStat(String path) { CuratorFramework curatorClient = createZkClient(); - curatorClient.start(); Stat stat = null; - if (!isConnected(curatorClient)) { - curatorClient.close(); - throw new ApiException("Zookeeper is not connected."); - } - try { stat = curatorClient.checkExists().forPath(path); } catch (Exception e) { log.error("get node data fail! path: " + path + ", error: {}" + e); } - curatorClient.close(); - return stat; } public CuratorFramework createZkClient() { - // 1.设置重试策略,重试时间计算策略sleepMs = baseSleepTimeMs * Math.max(1, random.nextInt(1 << (retryCount + - // 1))); - RetryPolicy retryPolicy = - new ExponentialBackoffRetry(BASE_SLEEP_TIME, MAX_RETRIES_COUNT, MAX_SLEEP_TIME); - - // 2.初始化客户端 - CuratorFramework curatorClient = - CuratorFrameworkFactory.builder() - .connectString(zookeeperConfig.getUris()) - .sessionTimeoutMs(SESSION_TIMEOUT) - .connectionTimeoutMs(CONNECTION_TIMEOUT) - .retryPolicy(retryPolicy) - // .namespace("kafka-rest") //命名空间隔离 - .build(); - - return curatorClient; + if (this.curatorClient == null) { + // 1.设置重试策略,重试时间计算策略sleepMs = baseSleepTimeMs * Math.max(1, random.nextInt(1 << (retryCount + + // 1))); + RetryPolicy retryPolicy = + new ExponentialBackoffRetry(BASE_SLEEP_TIME, MAX_RETRIES_COUNT, MAX_SLEEP_TIME); + + // 2.初始化客户端 + curatorClient = + CuratorFrameworkFactory.builder() + .connectString(zookeeperConfig.getUris()) + .sessionTimeoutMs(SESSION_TIMEOUT) + .connectionTimeoutMs(CONNECTION_TIMEOUT) + .retryPolicy(retryPolicy) + // .namespace("kafka-rest") //命名空间隔离 + .build(); + curatorClient.start(); + try { + curatorClient.blockUntilConnected(CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS); + } catch (InterruptedException interruptedException) { + log.warn("Curator Client connect is interrupted." + interruptedException); + } + } + if (!isConnected(curatorClient)) { + throw new ApiException("Zookeeper is not connected."); + } + + return this.curatorClient; } public String getState() { CuratorFramework curatorClient = createZkClient(); - curatorClient.start(); - - if (!isConnected(curatorClient)) { - curatorClient.close(); - throw new ApiException("Zookeeper is not connected."); - } String stateStr = curatorClient.getState().toString(); - curatorClient.close(); - return stateStr; } public KafkaZkClient createKafkaZkClient() { - KafkaZkClient kafkaZkClient = null; - try { - kafkaZkClient = KafkaZkClient.apply( - zookeeperConfig.getUris(), - false, - SESSION_TIMEOUT, - CONNECTION_TIMEOUT, - Integer.MAX_VALUE, - Time.SYSTEM, - "kafka.zk.rest", - "rest"); - } catch (Exception exception) { - throw new ApiException("Failed to create kafkaZkClient." + exception.getLocalizedMessage()); + if (kafkaZkClient == null) { + try { + kafkaZkClient = + KafkaZkClient.apply( + zookeeperConfig.getUris(), + false, + SESSION_TIMEOUT, + CONNECTION_TIMEOUT, + Integer.MAX_VALUE, + Time.SYSTEM, + "kafka.zk.rest", + "rest"); + } catch (Exception exception) { + throw new ApiException("Failed to create kafkaZkClient." + exception.getLocalizedMessage()); + } } - return kafkaZkClient; } From 6feea5cd957a47a7a2be355b9ab1336efeac7045 Mon Sep 17 00:00:00 2001 From: tinawenqiao <315524513@qq.com> Date: Wed, 9 Jan 2019 16:18:35 +0800 Subject: [PATCH 09/24] Use mocktio to test KafkaAdminService. --- pom.xml | 6 + .../bigdata/controller/KafkaController.java | 9 +- .../gnuhpc/bigdata/model/GeneralResponse.java | 2 + .../org/gnuhpc/bigdata/model/TopicDetail.java | 13 +- .../bigdata/service/KafkaAdminService.java | 194 +++-- .../org/gnuhpc/bigdata/utils/KafkaUtils.java | 2 +- .../gnuhpc/bigdata/utils/ZookeeperUtils.java | 12 + .../service/KafkaAdminServiceTest.java | 816 ++++++++++++++++++ .../bigdata/utils/KafkaStarterUtils.java | 97 +++ .../org/gnuhpc/bigdata/utils/ZkStarter.java | 158 ++++ 10 files changed, 1227 insertions(+), 82 deletions(-) create mode 100644 src/test/java/org/gnuhpc/bigdata/service/KafkaAdminServiceTest.java create mode 100644 src/test/java/org/gnuhpc/bigdata/utils/KafkaStarterUtils.java create mode 100644 src/test/java/org/gnuhpc/bigdata/utils/ZkStarter.java diff --git a/pom.xml b/pom.xml index 7f2b042..c02b905 100644 --- a/pom.xml +++ b/pom.xml @@ -260,6 +260,12 @@ curator-framework 2.11.0 + + org.testng + testng + RELEASE + test + diff --git a/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java b/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java index 9533fa9..1cbf08a 100644 --- a/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java +++ b/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java @@ -5,6 +5,7 @@ import io.swagger.annotations.ApiResponse; import io.swagger.annotations.ApiResponses; import java.util.Collection; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; @@ -134,11 +135,11 @@ public List listTopicBrief() { @PostMapping(value = "/topics/create", consumes = "application/json") @ResponseStatus(HttpStatus.CREATED) - @ApiOperation(value = "Create a topic") + @ApiOperation(value = "Create topics") @ApiParam(value = "if reassignStr set, partitions and repli-factor will be ignored.") - public TopicMeta createTopic( - @RequestBody TopicDetail topic, @RequestParam(required = false) String reassignStr) { - return kafkaAdminService.createTopic(topic, reassignStr); + public HashMap createTopic( + @RequestBody List topicList) { + return kafkaAdminService.createTopic(topicList); } @ApiOperation(value = "Tell if a topic exists") diff --git a/src/main/java/org/gnuhpc/bigdata/model/GeneralResponse.java b/src/main/java/org/gnuhpc/bigdata/model/GeneralResponse.java index b04119d..72c0d8c 100644 --- a/src/main/java/org/gnuhpc/bigdata/model/GeneralResponse.java +++ b/src/main/java/org/gnuhpc/bigdata/model/GeneralResponse.java @@ -3,6 +3,7 @@ import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; +import lombok.ToString; import lombok.extern.log4j.Log4j; import org.gnuhpc.bigdata.constant.GeneralResponseState; @@ -10,6 +11,7 @@ @Log4j @AllArgsConstructor @Builder +@ToString public class GeneralResponse { private GeneralResponseState state; diff --git a/src/main/java/org/gnuhpc/bigdata/model/TopicDetail.java b/src/main/java/org/gnuhpc/bigdata/model/TopicDetail.java index 935f5f1..3d28325 100644 --- a/src/main/java/org/gnuhpc/bigdata/model/TopicDetail.java +++ b/src/main/java/org/gnuhpc/bigdata/model/TopicDetail.java @@ -16,7 +16,10 @@ package org.gnuhpc.bigdata.model; +import java.util.List; +import java.util.Map; import java.util.Properties; +import lombok.Builder; import lombok.Getter; import lombok.Setter; import lombok.ToString; @@ -24,19 +27,15 @@ @Setter @Getter @ToString +@Builder public class TopicDetail { public static final int DEFAULT_PARTITION_NUMBER = 2; - public static final int DEFAULT_REPLICATION_FACTOR = 1; + public static final int DEFAULT_REPLICATION_FACTOR = 2; private int partitions; private int factor; private String name; private Properties prop; - - public TopicDetail() { - this.partitions = DEFAULT_PARTITION_NUMBER; - this.factor = DEFAULT_REPLICATION_FACTOR; - this.prop = new Properties(); - } + private Map> replicasAssignments; } diff --git a/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java b/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java index a555384..1c63872 100644 --- a/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java +++ b/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java @@ -10,7 +10,6 @@ import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.module.scala.DefaultScalaModule; -import com.google.common.base.Strings; import com.google.common.collect.ImmutableMap; import com.google.gson.Gson; import com.google.gson.GsonBuilder; @@ -42,10 +41,8 @@ import kafka.admin.AdminClient.ConsumerSummary; import kafka.admin.AdminUtils; import kafka.admin.ConsumerGroupCommand; -import kafka.admin.RackAwareMode; import kafka.admin.ReassignPartitionsCommand; import kafka.admin.ReassignPartitionsCommand.Throttle; -import kafka.admin.TopicCommand; import kafka.api.PartitionOffsetRequestInfo; import kafka.cluster.Broker; import kafka.common.TopicAndPartition; @@ -66,6 +63,8 @@ import org.apache.kafka.clients.admin.Config; import org.apache.kafka.clients.admin.ConfigEntry; import org.apache.kafka.clients.admin.CreatePartitionsResult; +import org.apache.kafka.clients.admin.CreateTopicsOptions; +import org.apache.kafka.clients.admin.CreateTopicsResult; import org.apache.kafka.clients.admin.DeleteTopicsResult; import org.apache.kafka.clients.admin.DescribeClusterOptions; import org.apache.kafka.clients.admin.DescribeClusterResult; @@ -80,7 +79,9 @@ import org.apache.kafka.clients.admin.ListTopicsOptions; import org.apache.kafka.clients.admin.ListTopicsResult; import org.apache.kafka.clients.admin.NewPartitions; +import org.apache.kafka.clients.admin.NewTopic; import org.apache.kafka.clients.admin.TopicDescription; +import org.apache.kafka.clients.consumer.ConsumerConfig; import org.apache.kafka.clients.consumer.ConsumerRecord; import org.apache.kafka.clients.consumer.ConsumerRecords; import org.apache.kafka.clients.consumer.KafkaConsumer; @@ -101,6 +102,7 @@ import org.apache.kafka.common.internals.Topic; import org.apache.kafka.common.protocol.Errors; import org.apache.kafka.common.requests.DescribeLogDirsResponse.LogDirInfo; +import org.apache.kafka.common.serialization.StringDeserializer; import org.gnuhpc.bigdata.CollectionConvertor; import org.gnuhpc.bigdata.componet.OffsetStorage; import org.gnuhpc.bigdata.config.KafkaConfig; @@ -173,8 +175,6 @@ public class KafkaAdminService { @PostConstruct private void init() { - this.zkUtils = zookeeperUtils.getZkUtils(); - GsonBuilder builder = new GsonBuilder(); builder.registerTypeAdapter( DateTime.class, @@ -195,54 +195,80 @@ private org.apache.kafka.clients.admin.AdminClient createKafkaAdminClient() { return this.kafkaAdminClient; } - public TopicMeta createTopic(TopicDetail topic, String reassignStr) { + public HashMap createTopic(List topicList) { + List newTopicList = new ArrayList<>(); + HashMap createResults = new HashMap<>(); - if (Topic.hasCollisionChars(topic.getName())) { - throw new InvalidTopicException("Invalid topic name, it contains '.' or '_'."); - } + for (TopicDetail topic : topicList) { + NewTopic newTopic; + Map> replicasAssignments = topic.getReplicasAssignments(); - if (Strings.isNullOrEmpty(reassignStr) && topic.getPartitions() <= 0) { - throw new InvalidTopicException("Number of partitions must be larger than 0"); - } - Topic.validate(topic.getName()); + try { + Topic.validate(topic.getName()); - if (Strings.isNullOrEmpty(reassignStr)) { - AdminUtils.createTopic( - zkUtils, - topic.getName(), - topic.getPartitions(), - topic.getFactor(), - topic.getProp(), - RackAwareMode.Enforced$.MODULE$); - } else { - List argsList = new ArrayList<>(); - argsList.add("--topic"); - argsList.add(topic.getName()); + if (Topic.hasCollisionChars(topic.getName())) { + throw new InvalidTopicException("Invalid topic name, it contains '.' or '_'."); + } + } catch (Exception exception) { + GeneralResponse generalResponse = + GeneralResponse.builder() + .state(GeneralResponseState.failure) + .msg(exception.getMessage()) + .build(); + createResults.put(topic.getName(), generalResponse); + continue; + } - if (topic.getProp().stringPropertyNames().size() != 0) { - argsList.add("--config"); + if (replicasAssignments != null && !replicasAssignments.isEmpty()) { + newTopic = new NewTopic(topic.getName(), replicasAssignments); + } else { + newTopic = new NewTopic(topic.getName(), topic.getPartitions(), (short) topic.getFactor()); + } - for (String key : topic.getProp().stringPropertyNames()) { - argsList.add(key + "=" + topic.getProp().get(key)); - } + if (topic.getProp() != null) { + newTopic.configs((Map) topic.getProp()); } - argsList.add("--replica-assignment"); - argsList.add(reassignStr); - KafkaZkClient kafkaZkClient = zookeeperUtils.createKafkaZkClient(); - TopicCommand.createTopic( - kafkaZkClient, - new TopicCommand.TopicCommandOptions(argsList.stream().toArray(String[]::new))); + newTopicList.add(newTopic); } + org.apache.kafka.clients.admin.AdminClient kafkaAdminClient = createKafkaAdminClient(); + + CreateTopicsOptions createTopicsOptions = new CreateTopicsOptions(); + createTopicsOptions.timeoutMs((int) kafkaAdminClientAlterTimeoutMs); + CreateTopicsResult createTopicsResult = + kafkaAdminClient.createTopics(newTopicList, createTopicsOptions); + try { - // Wait for a second for metadata propergating - Thread.sleep(3000); - } catch (InterruptedException e) { - e.printStackTrace(); + createTopicsResult.all().get(kafkaAdminClientAlterTimeoutMs, TimeUnit.MILLISECONDS); + } catch (Exception exception) { + log.warn("Create topic exception:" + exception); + } finally { + createTopicsResult + .values() + .forEach( + (topicName, result) -> { + GeneralResponse generalResponse; + if (result.isDone() && !result.isCompletedExceptionally()) { + TopicMeta topicMeta = describeTopic(topicName); + generalResponse = + GeneralResponse.builder() + .state(GeneralResponseState.success) + .data(topicMeta) + .build(); + } else { + generalResponse = + GeneralResponse.builder() + .state(GeneralResponseState.failure) + .msg(result.toString()) + .build(); + } + createResults.put(topicName, generalResponse); + }); } - return describeTopic(topic.getName()); + System.out.println("//////create results:" + createResults.toString()); + return createResults; } public List listTopics() { @@ -367,32 +393,35 @@ public List listBrokers() { List brokerList = CollectionConvertor.seqConvertJavaList(kafkaZkClient.getAllBrokersInCluster()); - List brokerInfoList = brokerList - .parallelStream() - .collect(Collectors.toMap(Broker::id, Broker::rack)) - .entrySet() - .parallelStream() - .map( - entry -> { - String brokerInfoStr = null; - try { - // TODO replace zkClient with kafkaZKClient - brokerInfoStr = - new String( - zkClient.getData().forPath(ZkUtils.BrokerIdsPath() + "/" + entry.getKey())); - } catch (Exception e) { - e.printStackTrace(); - } - BrokerInfo brokerInfo = gson.fromJson(brokerInfoStr, BrokerInfo.class); - if (entry.getValue().isEmpty()) { - brokerInfo.setRack(""); - } else { - brokerInfo.setRack(entry.getValue().get()); - } - brokerInfo.setId(entry.getKey()); - return brokerInfo; - }) - .collect(toList()); + List brokerInfoList = + brokerList + .parallelStream() + .collect(Collectors.toMap(Broker::id, Broker::rack)) + .entrySet() + .parallelStream() + .map( + entry -> { + String brokerInfoStr = null; + try { + // TODO replace zkClient with kafkaZKClient + brokerInfoStr = + new String( + zkClient + .getData() + .forPath(ZkUtils.BrokerIdsPath() + "/" + entry.getKey())); + } catch (Exception e) { + e.printStackTrace(); + } + BrokerInfo brokerInfo = gson.fromJson(brokerInfoStr, BrokerInfo.class); + if (entry.getValue().isEmpty()) { + brokerInfo.setRack(""); + } else { + brokerInfo.setRack(entry.getValue().get()); + } + brokerInfo.setId(entry.getKey()); + return brokerInfo; + }) + .collect(toList()); return brokerInfoList; } @@ -601,7 +630,6 @@ public void removeConfigInZk(Type type, String name, List configKeysToBe } else if (type.equals(Type.TOPIC)) { adminZkClient.changeTopicConfig(name, props); } - } public TopicMeta describeTopic(@TopicExistConstraint String topicName) { @@ -1864,7 +1892,8 @@ public GeneralResponse resetOffset( offsetToBeReset = Long.parseLong(offset); consumer.seek(tp, offsetToBeReset); } - consumer.commitSync();; + consumer.commitSync(); + ; } catch (IllegalStateException e) { storage.getMap().remove(consumerGroup); throw new ApiException(e); @@ -1897,6 +1926,7 @@ public GeneralResponse resetOffset( + endOffset); } log.info("Offset will be reset to " + offset); + zkUtils = zookeeperUtils.getZkUtils(); offsetToBeReset = Long.parseLong(offset); zkUtils .zkClient() @@ -1959,7 +1989,11 @@ public Map> getLastCommitTime( result.put("old", oldConsumerOffsetMap); } } catch (Exception e) { - log.warn("Get last commit time for consumergroup:" + consumerGroup + " failed. " + e.getLocalizedMessage()); + log.warn( + "Get last commit time for consumergroup:" + + consumerGroup + + " failed. " + + e.getLocalizedMessage()); } } else { // Get New consumer commit time, from offset storage instance @@ -1997,6 +2031,7 @@ public GeneralResponse deleteConsumerGroup(String consumerGroup, ConsumerType ty throw new RuntimeException("New consumer group:" + consumerGroup + " non-exist"); } if (type == ConsumerType.OLD) { + zkUtils = zookeeperUtils.getZkUtils(); if (!AdminUtils.deleteConsumerGroupInZK(zkUtils, consumerGroup)) { throw new ApiException( "The consumer " + consumerGroup + " is still active.Please stop it first"); @@ -2032,6 +2067,7 @@ public GeneralResponse deleteConsumerGroup(String consumerGroup, ConsumerType ty private List getTopicPartitions(String t) { List tpList = new ArrayList<>(); List l = Arrays.asList(t); + zkUtils = zookeeperUtils.getZkUtils(); java.util.Map> tpMap = JavaConverters.mapAsJavaMapConverter( zkUtils.getPartitionsForTopics( @@ -2091,6 +2127,23 @@ public long getBeginningOffset(String topic, int partitionId) { return -1; } + private KafkaConsumer createNewConsumer(String consumerGroup) { + Properties properties = new Properties(); + properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaConfig.getBrokers()); + properties.put(ConsumerConfig.GROUP_ID_CONFIG, consumerGroup); + properties.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "false"); + properties.put(ConsumerConfig.SESSION_TIMEOUT_MS_CONFIG, "30000"); + properties.put(ConsumerConfig.MAX_PARTITION_FETCH_BYTES_CONFIG, "100000000"); + properties.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, "5"); + properties.put( + ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getCanonicalName()); + properties.put( + ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, + StringDeserializer.class.getCanonicalName()); + + return new KafkaConsumer(properties); + } + public long getEndOffset(String topic, int partitionId) { KafkaConsumer consumer = kafkaUtils.createNewConsumer(KafkaUtils.DEFAULTCP); TopicPartition tp = new TopicPartition(topic, partitionId); @@ -2140,6 +2193,7 @@ private boolean isConsumerGroupActive(String consumerGroup, ConsumerType type) { throw new ApiException( "Consumer group:" + consumerGroup + " state:" + groupState + " unkown."); } else if (type == ConsumerType.OLD) { + zkUtils = zookeeperUtils.getZkUtils(); return zkUtils.getConsumersInGroup(consumerGroup).nonEmpty(); } else { throw new ApiException("Unknown type " + type); diff --git a/src/main/java/org/gnuhpc/bigdata/utils/KafkaUtils.java b/src/main/java/org/gnuhpc/bigdata/utils/KafkaUtils.java index 653f563..1d6c6c0 100644 --- a/src/main/java/org/gnuhpc/bigdata/utils/KafkaUtils.java +++ b/src/main/java/org/gnuhpc/bigdata/utils/KafkaUtils.java @@ -48,7 +48,7 @@ public KafkaConsumer createNewConsumer() { public KafkaConsumer createNewConsumer(String consumerGroup) { Properties properties = new Properties(); - properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, getKafkaConfig().getBrokers()); + properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaConfig.getBrokers()); properties.put(ConsumerConfig.GROUP_ID_CONFIG, consumerGroup); properties.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "false"); properties.put(ConsumerConfig.SESSION_TIMEOUT_MS_CONFIG, "30000"); diff --git a/src/main/java/org/gnuhpc/bigdata/utils/ZookeeperUtils.java b/src/main/java/org/gnuhpc/bigdata/utils/ZookeeperUtils.java index 66f12cc..1896ec4 100644 --- a/src/main/java/org/gnuhpc/bigdata/utils/ZookeeperUtils.java +++ b/src/main/java/org/gnuhpc/bigdata/utils/ZookeeperUtils.java @@ -12,11 +12,14 @@ import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; +import kafka.utils.ZKStringSerializer$; import kafka.utils.ZkUtils; import kafka.zk.KafkaZkClient; import lombok.Getter; import lombok.Setter; import lombok.extern.log4j.Log4j; +import org.I0Itec.zkclient.ZkClient; +import org.I0Itec.zkclient.ZkConnection; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.apache.curator.RetryPolicy; @@ -330,6 +333,15 @@ public KafkaZkClient createKafkaZkClient() { return kafkaZkClient; } + public ZkUtils getZkUtils() { + if (zkUtils == null) { + ZkClient zkClient = new ZkClient(zookeeperConfig.getUris(), SESSION_TIMEOUT, CONNECTION_TIMEOUT, ZKStringSerializer$.MODULE$); + zkUtils = new ZkUtils(zkClient, new ZkConnection(zookeeperConfig.getUris()), false); + } + + return zkUtils; + } + public boolean isConnected(CuratorFramework curatorClient) { return curatorClient.getZookeeperClient().isConnected(); } diff --git a/src/test/java/org/gnuhpc/bigdata/service/KafkaAdminServiceTest.java b/src/test/java/org/gnuhpc/bigdata/service/KafkaAdminServiceTest.java new file mode 100644 index 0000000..81d7800 --- /dev/null +++ b/src/test/java/org/gnuhpc/bigdata/service/KafkaAdminServiceTest.java @@ -0,0 +1,816 @@ +package org.gnuhpc.bigdata.service; + +import static junit.framework.TestCase.assertTrue; +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.initMocks; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import kafka.server.KafkaServerStartable; +import org.gnuhpc.bigdata.componet.OffsetStorage; +import org.gnuhpc.bigdata.config.KafkaConfig; +import org.gnuhpc.bigdata.config.ZookeeperConfig; +import org.gnuhpc.bigdata.constant.GeneralResponseState; +import org.gnuhpc.bigdata.model.GeneralResponse; +import org.gnuhpc.bigdata.model.TopicBrief; +import org.gnuhpc.bigdata.model.TopicDetail; +import org.gnuhpc.bigdata.model.TopicMeta; +import org.gnuhpc.bigdata.utils.KafkaStarterUtils; +import org.gnuhpc.bigdata.utils.KafkaUtils; +import org.gnuhpc.bigdata.utils.ZkStarter; +import org.gnuhpc.bigdata.utils.ZookeeperUtils; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runners.MethodSorters; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; + +@FixMethodOrder(MethodSorters.JVM) +public class KafkaAdminServiceTest { + + @Mock private KafkaConfig mockKafkaConfig; + @Mock private ZookeeperConfig mockZookeeperConfig; + @Spy private ZookeeperUtils mockZookeeperUtils = new ZookeeperUtils(); + @Spy private KafkaUtils mockKafkaUtils = new KafkaUtils(); + @Mock private OffsetStorage mockStorage; + + @InjectMocks private KafkaAdminService kafkaAdminServiceUnderTest; + + private static KafkaServerStartable kafkaStarter; + + private final String firstTopicName = "firsttopic"; + private final String secondTopicName = "secondtopic"; + private final String nonExistTopicName = "nontopic"; + + @BeforeClass + public static void start() { + ZkStarter.startLocalZkServer(); + kafkaStarter = + KafkaStarterUtils.startServer( + KafkaStarterUtils.DEFAULT_KAFKA_PORT, + KafkaStarterUtils.DEFAULT_BROKER_ID, + KafkaStarterUtils.DEFAULT_ZK_STR, + KafkaStarterUtils.getDefaultKafkaConfiguration()); + } + + @Before + public void setUp() { + initMocks(this); + + String MOCK_KAFKA_BOOTSTRAP_SERVERS = KafkaStarterUtils.DEFAULT_KAFKA_BROKER; + String MOCK_ZK = ZkStarter.DEFAULT_ZK_STR; + + when(mockKafkaConfig.getBrokers()).thenReturn(MOCK_KAFKA_BOOTSTRAP_SERVERS); + when(mockZookeeperConfig.getUris()).thenReturn(MOCK_ZK); + + // when(mockZookeeperUtils.createKafkaZkClient()) + // .thenReturn( + // KafkaZkClient.apply( + // MOCK_ZK, + // false, + // 5000, + // 5000, + // Integer.MAX_VALUE, + // Time.SYSTEM, + // "kafka.zk.rest", + // "rest")); + // when(mockZookeeperUtils.getZkUtils()) + // .thenReturn( + // new ZkUtils( + // new ZkClient(MOCK_ZK, 5000, 5000, ZKStringSerializer$.MODULE$), + // new ZkConnection(MOCK_ZK), + // false)); + + mockKafkaUtils.setKafkaConfig(mockKafkaConfig); + mockZookeeperUtils.setZookeeperConfig(mockZookeeperConfig); + } + + @AfterClass + public static void stop() { + kafkaStarter.shutdown(); + ZkStarter.stopLocalZkServer(); + } + + @Test + public void testListTopics() { + // Setup + final List expectedResult = Arrays.asList(); + + // Run the test + final List result = kafkaAdminServiceUnderTest.listTopics(); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testCreateTopic() { + final List allTopicsBeforeCreate = kafkaAdminServiceUnderTest.listTopics(); + assertTrue(allTopicsBeforeCreate.isEmpty()); + + List topicList = new ArrayList<>(); + // Create first topic by topic name, partition count, replication factor + final int partitionCount = 3; + final int replicationFactor = 1; + final TopicDetail topic = + TopicDetail.builder() + .name(firstTopicName) + .partitions(partitionCount) + .factor(replicationFactor) + .build(); + + // Create second topic by replica assignment + HashMap> replicaAssignments = new HashMap<>(); + replicaAssignments.put(0, Arrays.asList(KafkaStarterUtils.DEFAULT_BROKER_ID)); + TopicDetail topic2 = + TopicDetail.builder().name(secondTopicName).replicasAssignments(replicaAssignments).build(); + + topicList.add(topic); + topicList.add(topic2); + + // Run the test + final Map result = kafkaAdminServiceUnderTest.createTopic(topicList); + + // Verify the first topic result + TopicMeta firstTopicMeta = (TopicMeta) result.get(firstTopicName).getData(); + assertEquals(GeneralResponseState.success, result.get(firstTopicName).getState()); + assertEquals(firstTopicName, firstTopicMeta.getTopicName()); + assertEquals(partitionCount, firstTopicMeta.getPartitionCount()); + assertEquals(replicationFactor, firstTopicMeta.getReplicationFactor()); + + // Verify the second topic result + TopicMeta secondTopicMeta = (TopicMeta) result.get(secondTopicName).getData(); + assertEquals(GeneralResponseState.success, result.get(secondTopicName).getState()); + assertEquals(secondTopicName, secondTopicMeta.getTopicName()); + assertEquals(1, secondTopicMeta.getPartitionCount()); + assertEquals(replicationFactor, secondTopicMeta.getReplicationFactor()); + assertEquals( + KafkaStarterUtils.DEFAULT_BROKER_ID, + secondTopicMeta + .getTopicPartitionInfos() + .get(0) + .getTopicPartitionInfo() + .replicas() + .get(0) + .id()); + } + + @Test + public void testGetAllTopics() { + // Setup + final Set expectedResult = new HashSet<>(); + + // Run the test + final Set result = kafkaAdminServiceUnderTest.getAllTopics(); + + final Set allTopicsAfterCreate = kafkaAdminServiceUnderTest.getAllTopics(); + + // Verify the results + assertEquals(2, allTopicsAfterCreate.size()); + assertTrue(allTopicsAfterCreate.contains(firstTopicName)); + assertTrue(allTopicsAfterCreate.contains(secondTopicName)); + } + + @Test + public void testListTopicBrief() { + // Setup + final List expectedResult = Arrays.asList(); + + // Run the test + final List result = kafkaAdminServiceUnderTest.listTopicBrief(); + + // Verify the results +// assertEquals(expectedResult, result); + } + + @Test + public void testExistTopic() { + // Setup + final boolean expectedResult = false; + + // Run the test +// final boolean firstTopicExist = kafkaAdminServiceUnderTest.existTopic(firstTopicName); +// final boolean nonExistTopicExist = kafkaAdminServiceUnderTest.existTopic(nonExistTopicName); +// +// // Verify the results +// assertTrue(firstTopicExist); +// assertFalse(nonExistTopicExist); + } + + @Test + public void testDescribeCluster() { + // Setup + final Map expectedResult = new HashMap<>(); + + // Run the test + final Map result = kafkaAdminServiceUnderTest.describeCluster(); + + System.out.println("result:" + result); + // Verify the results + assertEquals(1, result.size()); + } +/* + @Test + public void testListBrokers() { + // Setup + final List expectedResult = Arrays.asList(); + + // Run the test + final List result = kafkaAdminServiceUnderTest.listBrokers(); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testGetControllerId() { + // Setup + final int expectedResult = 0; + + // Run the test + final int result = kafkaAdminServiceUnderTest.getControllerId(); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testListLogDirsByBroker() { + // Setup + final List brokerList = Arrays.asList(); + final Map> expectedResult = new HashMap<>(); + + // Run the test + final Map> result = + kafkaAdminServiceUnderTest.listLogDirsByBroker(brokerList); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testDescribeLogDirsByBrokerAndTopic() { + // Setup + final List brokerList = Arrays.asList(); + final List topicList = Arrays.asList(); + final Map> expectedResult = new HashMap<>(); + + // Run the test + final Map> result = + kafkaAdminServiceUnderTest.describeLogDirsByBrokerAndTopic(brokerList, topicList); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testDescribeReplicaLogDirs() { + // Setup + final List replicas = Arrays.asList(); + final Map expectedResult = new HashMap<>(); + + // Run the test + final Map result = + kafkaAdminServiceUnderTest.describeReplicaLogDirs(replicas); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testGetBrokerConf() { + // Setup + final int brokerId = 0; + final Collection expectedResult = Arrays.asList(); + + // Run the test + final Collection result = kafkaAdminServiceUnderTest.getBrokerConf(brokerId); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testGetConfigInZk() { + // Setup + final Type type = null; + final String name = "name"; + final Properties expectedResult = new Properties(); + + // Run the test + final Properties result = kafkaAdminServiceUnderTest.getConfigInZk(type, name); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testUpdateBrokerDynConf() { + // Setup + final int brokerId = 0; + final Properties propsToBeUpdated = new Properties(); + final Properties expectedResult = new Properties(); + + // Run the test + final Properties result = + kafkaAdminServiceUnderTest.updateBrokerDynConf(brokerId, propsToBeUpdated); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testRemoveConfigInZk() { + // Setup + final Type type = null; + final String name = "name"; + final List configKeysToBeRemoved = Arrays.asList(); + + // Run the test + kafkaAdminServiceUnderTest.removeConfigInZk(type, name, configKeysToBeRemoved); + + // Verify the results + } + + @Test + public void testDescribeTopic() { + // Setup + final String topicName = "topicName"; + final TopicMeta expectedResult = null; + + // Run the test + final TopicMeta result = kafkaAdminServiceUnderTest.describeTopic(topicName); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testDeleteTopicList() { + // Setup + final List topicList = Arrays.asList(); + final Map expectedResult = new HashMap<>(); + + // Run the test + final Map result = + kafkaAdminServiceUnderTest.deleteTopicList(topicList); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testDescribeConfig() { + // Setup + final Type type = null; + final String name = "name"; + final Collection expectedResult = Arrays.asList(); + + // Run the test + final Collection result = kafkaAdminServiceUnderTest.describeConfig(type, name); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testAlterConfig() { + // Setup + final Type type = null; + final String name = "name"; + final Collection configEntries = Arrays.asList(); + final boolean expectedResult = false; + + // Run the test + final boolean result = kafkaAdminServiceUnderTest.alterConfig(type, name, configEntries); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testUpdateTopicConf() { + // Setup + final String topic = "topic"; + final Properties props = new Properties(); + final Collection expectedResult = Arrays.asList(); + + // Run the test + final Collection result = + kafkaAdminServiceUnderTest.updateTopicConf(topic, props); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testGetTopicConf() { + // Setup + final String topic = "topic"; + final Collection expectedResult = Arrays.asList(); + + // Run the test + final Collection result = kafkaAdminServiceUnderTest.getTopicConf(topic); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testGetTopicConfByKey() { + // Setup + final String topic = "topic"; + final String key = "key"; + final Properties expectedResult = new Properties(); + + // Run the test + final Properties result = kafkaAdminServiceUnderTest.getTopicConfByKey(topic, key); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testUpdateTopicConfByKey() { + // Setup + final String topic = "topic"; + final String key = "key"; + final String value = "value"; + final Collection expectedResult = Arrays.asList(); + + // Run the test + final Collection result = + kafkaAdminServiceUnderTest.updateTopicConfByKey(topic, key, value); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testListAllConsumerGroups() { + // Setup + final ConsumerType type = null; + final Map> expectedResult = new HashMap<>(); + + // Run the test + final Map> result = kafkaAdminServiceUnderTest.listAllConsumerGroups(type); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testListConsumerGroupsByTopic() { + // Setup + final String topic = "topic"; + final ConsumerType type = null; + final Map> expectedResult = new HashMap<>(); + + // Run the test + final Map> result = + kafkaAdminServiceUnderTest.listConsumerGroupsByTopic(topic, type); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testListTopicsByConsumerGroup() { + // Setup + final String consumerGroup = "consumerGroup"; + final ConsumerType type = null; + final Set expectedResult = new HashSet<>(); + + // Run the test + final Set result = + kafkaAdminServiceUnderTest.listTopicsByConsumerGroup(consumerGroup, type); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testGetConsumerGroupMeta() { + // Setup + final String consumerGroup = "consumerGroup"; + final ConsumerGroupMeta expectedResult = null; + + // Run the test + final ConsumerGroupMeta result = kafkaAdminServiceUnderTest.getConsumerGroupMeta(consumerGroup); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testIsOldConsumerGroup() { + // Setup + final String consumerGroup = "consumerGroup"; + final boolean expectedResult = false; + + // Run the test + final boolean result = kafkaAdminServiceUnderTest.isOldConsumerGroup(consumerGroup); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testIsNewConsumerGroup() { + // Setup + final String consumerGroup = "consumerGroup"; + final boolean expectedResult = false; + + // Run the test + final boolean result = kafkaAdminServiceUnderTest.isNewConsumerGroup(consumerGroup); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testDescribeConsumerGroup() { + // Setup + final String consumerGroup = "consumerGroup"; + final ConsumerType type = null; + final Map> expectedResult = new HashMap<>(); + + // Run the test + final Map> result = + kafkaAdminServiceUnderTest.describeConsumerGroup(consumerGroup, type); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testDescribeNewConsumerGroup() { + // Setup + final String consumerGroup = "consumerGroup"; + final boolean filtered = false; + final String topic = "topic"; + final List expectedResult = Arrays.asList(); + + // Run the test + final List result = + kafkaAdminServiceUnderTest.describeNewConsumerGroup(consumerGroup, filtered, topic); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testDescribeOldConsumerGroup() { + // Setup + final String consumerGroup = "consumerGroup"; + final boolean filtered = false; + final String topic = "topic"; + final List expectedResult = Arrays.asList(); + + // Run the test + final List result = + kafkaAdminServiceUnderTest.describeOldConsumerGroup(consumerGroup, filtered, topic); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testDescribeNewConsumerGroupByTopic() { + // Setup + final String consumerGroup = "consumerGroup"; + final String topic = "topic"; + final List expectedResult = Arrays.asList(); + + // Run the test + final List result = + kafkaAdminServiceUnderTest.describeNewConsumerGroupByTopic(consumerGroup, topic); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testDescribeOldConsumerGroupByTopic() { + // Setup + final String consumerGroup = "consumerGroup"; + final String topic = "topic"; + final List expectedResult = Arrays.asList(); + + // Run the test + final List result = + kafkaAdminServiceUnderTest.describeOldConsumerGroupByTopic(consumerGroup, topic); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testAddPartitions() { + // Setup + final List addPartitions = Arrays.asList(); + final Map expectedResult = new HashMap<>(); + + // Run the test + final Map result = + kafkaAdminServiceUnderTest.addPartitions(addPartitions); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testGenerateReassignPartition() { + // Setup + final ReassignWrapper reassignWrapper = null; + final List expectedResult = Arrays.asList(); + + // Run the test + final List result = + kafkaAdminServiceUnderTest.generateReassignPartition(reassignWrapper); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testExecuteReassignPartition() { + // Setup + final String reassignStr = "reassignStr"; + final Long interBrokerThrottle = 0L; + final Long replicaAlterLogDirsThrottle = 0L; + final Long timeoutMs = 0L; + final Map expectedResult = new HashMap<>(); + + // Run the test + final Map result = + kafkaAdminServiceUnderTest.executeReassignPartition( + reassignStr, interBrokerThrottle, replicaAlterLogDirsThrottle, timeoutMs); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testCheckReassignStatusByStr() { + // Setup + final String reassignStr = "reassignStr"; + final Map expectedResult = new HashMap<>(); + + // Run the test + final Map result = + kafkaAdminServiceUnderTest.checkReassignStatusByStr(reassignStr); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testGetMessage() { + // Setup + final String topic = "topic"; + final int partition = 0; + final long offset = 0L; + final String decoder = "decoder"; + final String avroSchema = "avroSchema"; + final String expectedResult = "result"; + + // Run the test + final String result = + kafkaAdminServiceUnderTest.getMessage(topic, partition, offset, decoder, avroSchema); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testGetRecordByOffset() { + // Setup + final String topic = "topic"; + final int partition = 0; + final long offset = 0L; + final String decoder = "decoder"; + final String avroSchema = "avroSchema"; + final Record expectedResult = null; + + // Run the test + final Record result = + kafkaAdminServiceUnderTest.getRecordByOffset(topic, partition, offset, decoder, avroSchema); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testResetOffset() { + // Setup + final String topic = "topic"; + final int partition = 0; + final String consumerGroup = "consumerGroup"; + final ConsumerType type = null; + final String offset = "offset"; + final GeneralResponse expectedResult = null; + + // Run the test + final GeneralResponse result = + kafkaAdminServiceUnderTest.resetOffset(topic, partition, consumerGroup, type, offset); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testGetLastCommitTime() { + // Setup + final String consumerGroup = "consumerGroup"; + final String topic = "topic"; + final ConsumerType type = null; + final Map> expectedResult = new HashMap<>(); + + // Run the test + final Map> result = + kafkaAdminServiceUnderTest.getLastCommitTime(consumerGroup, topic, type); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testDeleteConsumerGroup() { + // Setup + final String consumerGroup = "consumerGroup"; + final ConsumerType type = null; + final GeneralResponse expectedResult = null; + + // Run the test + final GeneralResponse result = + kafkaAdminServiceUnderTest.deleteConsumerGroup(consumerGroup, type); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testGetBeginningOffset() { + // Setup + final String topic = "topic"; + final int partitionId = 0; + final long expectedResult = 0L; + + // Run the test + final long result = kafkaAdminServiceUnderTest.getBeginningOffset(topic, partitionId); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testGetEndOffset() { + // Setup + final String topic = "topic"; + final int partitionId = 0; + final long expectedResult = 0L; + + // Run the test + final long result = kafkaAdminServiceUnderTest.getEndOffset(topic, partitionId); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testCountPartition() { + // Setup + final String topic = "topic"; + final Map expectedResult = new HashMap<>(); + + // Run the test + final Map result = kafkaAdminServiceUnderTest.countPartition(topic); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testHealthCheck() { + // Setup + final HealthCheckResult expectedResult = null; + + // Run the test + final HealthCheckResult result = kafkaAdminServiceUnderTest.healthCheck(); + + // Verify the results + assertEquals(expectedResult, result); + } + */ +} diff --git a/src/test/java/org/gnuhpc/bigdata/utils/KafkaStarterUtils.java b/src/test/java/org/gnuhpc/bigdata/utils/KafkaStarterUtils.java new file mode 100644 index 0000000..bdf4573 --- /dev/null +++ b/src/test/java/org/gnuhpc/bigdata/utils/KafkaStarterUtils.java @@ -0,0 +1,97 @@ +package org.gnuhpc.bigdata.utils; + +import java.io.File; +import java.util.Properties; +import kafka.admin.TopicCommand; +import kafka.server.KafkaConfig; +import kafka.server.KafkaServerStartable; +import kafka.zk.KafkaZkClient; +import org.I0Itec.zkclient.ZkClient; +import org.apache.kafka.common.errors.TopicExistsException; +import org.apache.kafka.common.utils.Time; + +/** + * Utilities to start Kafka during unit tests. + */ +public class KafkaStarterUtils { + + public static final int DEFAULT_KAFKA_PORT = 19099; + public static final int DEFAULT_BROKER_ID = 0; + public static final String DEFAULT_ZK_STR = ZkStarter.DEFAULT_ZK_STR + "/kafka"; + public static final String DEFAULT_KAFKA_BROKER = "localhost:" + DEFAULT_KAFKA_PORT; + + public static Properties getDefaultKafkaConfiguration() { + return new Properties(); + } + + public static KafkaServerStartable startServer(final int port, final int brokerId, + final String zkStr, final Properties configuration) { + // Create the ZK nodes for Kafka, if needed + int indexOfFirstSlash = zkStr.indexOf('/'); + if (indexOfFirstSlash != -1) { + String bareZkUrl = zkStr.substring(0, indexOfFirstSlash); + String zkNodePath = zkStr.substring(indexOfFirstSlash); + ZkClient client = new ZkClient(bareZkUrl); + client.createPersistent(zkNodePath, true); + client.close(); + } + + File logDir = new File("/tmp/kafka-" + Double.toHexString(Math.random())); + logDir.mkdirs(); + logDir.deleteOnExit(); + + configureKafkaPort(configuration, port); + configureZkConnectionString(configuration, zkStr); + configureBrokerId(configuration, brokerId); + configureKafkaLogDirectory(configuration, logDir); + KafkaConfig config = new KafkaConfig(configuration); + + KafkaServerStartable serverStartable = new KafkaServerStartable(config); + serverStartable.startup(); + + return serverStartable; + } + + public static void configureSegmentSizeBytes(Properties properties, int segmentSize) { + properties.put("log.segment.bytes", Integer.toString(segmentSize)); + } + + public static void configureLogRetentionSizeBytes(Properties properties, int logRetentionSizeBytes) { + properties.put("log.retention.bytes", Integer.toString(logRetentionSizeBytes)); + } + + public static void configureKafkaLogDirectory(Properties configuration, File logDir) { + configuration.put("log.dirs", logDir.getAbsolutePath()); + } + + public static void configureBrokerId(Properties configuration, int brokerId) { + configuration.put("broker.id", Integer.toString(brokerId)); + } + + public static void configureZkConnectionString(Properties configuration, String zkStr) { + configuration.put("zookeeper.connect", zkStr); + } + + public static void configureKafkaPort(Properties configuration, int port) { + configuration.put("port", Integer.toString(port)); + } + + public static void stopServer(KafkaServerStartable serverStartable) { + serverStartable.shutdown(); + } + + public static void createTopic(String kafkaTopic, String zkStr) { + // TopicCommand.main() will call System.exit() finally, which will break maven-surefire-plugin + try { + String[] args = new String[]{"--create", "--zookeeper", zkStr, "--replication-factor", "1", + "--partitions", "1", "--topic", kafkaTopic}; + KafkaZkClient zkClient = KafkaZkClient.apply(zkStr, false, 30000, 30000, Integer.MAX_VALUE, Time.SYSTEM,"kafka.server", + "SessionExpireListener"); + TopicCommand.TopicCommandOptions opts = new TopicCommand.TopicCommandOptions(args); + TopicCommand.createTopic(zkClient, opts); + } catch (TopicExistsException e) { + // Catch TopicExistsException otherwise it will break maven-surefire-plugin + System.out.println("Topic already existed"); + } + } +} diff --git a/src/test/java/org/gnuhpc/bigdata/utils/ZkStarter.java b/src/test/java/org/gnuhpc/bigdata/utils/ZkStarter.java new file mode 100644 index 0000000..4fc9299 --- /dev/null +++ b/src/test/java/org/gnuhpc/bigdata/utils/ZkStarter.java @@ -0,0 +1,158 @@ +package org.gnuhpc.bigdata.utils; + +import java.io.File; +import java.io.IOException; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.Random; +import java.util.concurrent.TimeUnit; +import lombok.extern.log4j.Log4j; +import org.I0Itec.zkclient.ZkClient; +import org.apache.zookeeper.server.ZooKeeperServerMain; +import org.apache.zookeeper.server.quorum.QuorumPeerConfig; + +@Log4j +public class ZkStarter { + +// private static final Logger LOGGER = LoggerFactory.getLogger(ZkStarter.class); + public static final int DEFAULT_ZK_TEST_PORT = getAvailablePort(); + public static final String DEFAULT_ZK_STR = "localhost:" + DEFAULT_ZK_TEST_PORT; + + private static PublicZooKeeperServerMain _zookeeperServerMain = null; + private static String _zkDataDir = null; + + private static int getAvailablePort() { + int port = 0; + + while (true) { + try { + port = new Random().nextInt(10000) + 10000; + (new Socket("127.0.0.1", port)).close(); + new ServerSocket(port).close(); + // Successful connection means the port is taken. + } catch (Exception e) { + // Could not connect. + break; + } + } + + return port; + } + + /** + * Silly class to make protected methods public. + */ + static class PublicZooKeeperServerMain extends ZooKeeperServerMain { + + @Override + public void initializeAndRun(String[] args) + throws QuorumPeerConfig.ConfigException, IOException { + super.initializeAndRun(args); + } + + @Override + public void shutdown() { + super.shutdown(); + } + } + + /** + * Starts an empty local Zk instance on the default port + */ + public static void startLocalZkServer() { + // DEFAULT_ZK_TEST_PORT = new Random().nextInt(10000) + 10000; + // DEFAULT_ZK_STR = "localhost:" + DEFAULT_ZK_TEST_PORT; + try { + startLocalZkServer(DEFAULT_ZK_TEST_PORT); + } catch (Exception e) { + log.error("Failed to start ZK: " + e); + } + } + + /** + * Starts a local Zk instance with a generated empty data directory + * + * @param port The port to listen on + */ + public static void startLocalZkServer(final int port) { + startLocalZkServer(port, org.apache.commons.io.FileUtils.getTempDirectoryPath() + File.separator + + "test-" + System.currentTimeMillis()); + } + + /** + * Starts a local Zk instance + * + * @param port The port to listen on + * @param dataDirPath The path for the Zk data directory + */ + public synchronized static void startLocalZkServer(final int port, final String dataDirPath) { + if (_zookeeperServerMain != null) { + throw new RuntimeException("Zookeeper server is already started!"); + } + + // Start the local ZK server + try { + _zookeeperServerMain = new PublicZooKeeperServerMain(); + log.info("Zookeeper data path - " + dataDirPath); + _zkDataDir = dataDirPath; + final String[] args = new String[]{ + Integer.toString(port), dataDirPath + }; + new Thread() { + @Override + public void run() { + try { + _zookeeperServerMain.initializeAndRun(args); + } catch (QuorumPeerConfig.ConfigException e) { + log.warn("Caught exception while starting ZK", e); + } catch (IOException e) { + log.warn("Caught exception while starting ZK", e); + } + } + }.start(); + } catch (Exception e) { + log.warn("Caught exception while starting ZK", e); + throw new RuntimeException(e); + } + + // Wait until the ZK server is started + ZkClient client = new ZkClient("localhost:" + port, 10000); + client.waitUntilConnected(10L, TimeUnit.SECONDS); + client.close(); + } + + /** + * Stops a local Zk instance, deleting its data directory + */ + public static void stopLocalZkServer() { + try { + stopLocalZkServer(true); + } catch (Exception e) { + log.error("Failed to stop ZK: " + e); + } + } + + /** + * Stops a local Zk instance. + * + * @param deleteDataDir Whether or not to delete the data directory + */ + public synchronized static void stopLocalZkServer(final boolean deleteDataDir) { + if (_zookeeperServerMain != null) { + try { + // Shut down ZK + _zookeeperServerMain.shutdown(); + _zookeeperServerMain = null; + + // Delete the data dir + if (deleteDataDir) { + org.apache.commons.io.FileUtils.deleteDirectory(new File(_zkDataDir)); + } + } catch (Exception e) { + log.warn("Caught exception while stopping ZK server", e); + throw new RuntimeException(e); + } + } + } +} + From 0dfaf3d076342c7bf324e38833aebcf8ebbf8346 Mon Sep 17 00:00:00 2001 From: tinawenqiao <315524513@qq.com> Date: Thu, 17 Jan 2019 14:01:47 +0800 Subject: [PATCH 10/24] Add KafkaAdminServiceTest. --- pom.xml | 7 +- .../bigdata/controller/KafkaController.java | 15 +- .../org/gnuhpc/bigdata/model/BrokerInfo.java | 50 +- .../org/gnuhpc/bigdata/model/ClusterInfo.java | 14 + .../gnuhpc/bigdata/model/ReassignWrapper.java | 17 +- .../bigdata/service/KafkaAdminService.java | 109 +- .../org/gnuhpc/bigdata/utils/KafkaUtils.java | 18 + .../bigdata/utils/TimestampDeserializer.java | 30 + .../KafkaRestSpringbootApplicationTests.java | 1 + .../service/KafkaAdminServiceTest.java | 1473 +++++++++++------ 10 files changed, 1113 insertions(+), 621 deletions(-) create mode 100644 src/main/java/org/gnuhpc/bigdata/model/ClusterInfo.java create mode 100644 src/main/java/org/gnuhpc/bigdata/utils/TimestampDeserializer.java diff --git a/pom.xml b/pom.xml index c02b905..0ec16eb 100644 --- a/pom.xml +++ b/pom.xml @@ -38,8 +38,8 @@ ${swagger.output.dir}/swagger.json - 2.9.1 - 2.9.5 + 2.9.8 + 2.9.8 1.19 @@ -200,12 +200,13 @@ joda-time 2.9.9 + com.fasterxml.jackson.datatype jackson-datatype-joda diff --git a/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java b/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java index 1cbf08a..49bc9a9 100644 --- a/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java +++ b/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java @@ -13,6 +13,7 @@ import joptsimple.internal.Strings; import lombok.extern.log4j.Log4j; import org.apache.kafka.clients.admin.DescribeReplicaLogDirsResult.ReplicaLogDirInfo; +import org.apache.kafka.common.Node; import org.apache.kafka.common.TopicPartitionReplica; import org.apache.kafka.common.config.ConfigResource.Type; import org.apache.kafka.common.errors.ApiException; @@ -20,6 +21,7 @@ import org.gnuhpc.bigdata.constant.ConsumerType; import org.gnuhpc.bigdata.model.AddPartition; import org.gnuhpc.bigdata.model.BrokerInfo; +import org.gnuhpc.bigdata.model.ClusterInfo; import org.gnuhpc.bigdata.model.ConsumerGroupDesc; import org.gnuhpc.bigdata.model.ConsumerGroupMeta; import org.gnuhpc.bigdata.model.CustomConfigEntry; @@ -56,7 +58,7 @@ public class KafkaController { @GetMapping(value = "/cluster") @ApiOperation(value = "Describe cluster, nodes, controller info.") - public Map describeCluster() { + public ClusterInfo describeCluster() { return kafkaAdminService.describeCluster(); } @@ -67,9 +69,9 @@ public List listBrokers() { } @GetMapping(value = "/controller") - @ApiOperation(value = "Get controller id in this cluster") - public int getControllerId() { - return kafkaAdminService.getControllerId(); + @ApiOperation(value = "Get controller in this cluster") + public Node getControllerId() { + return kafkaAdminService.getController(); } @GetMapping(value = "/brokers/logdirs") @@ -181,6 +183,11 @@ public TopicMeta describeTopic(@PathVariable String topic) { @DeleteMapping(value = "/topics") @ApiOperation(value = "Delete a topic list (you should enable topic deletion") public Map deleteTopicList(@RequestParam List topicList) { + // TODO add a function to delete topics completely, + // rmr /brokers/topics/< topic_name > + // rmr /config/topics/< topic_name > + // rmr /admin/delete_topics/< topic_name > + // rm log dirs on all brokers return kafkaAdminService.deleteTopicList(topicList); } diff --git a/src/main/java/org/gnuhpc/bigdata/model/BrokerInfo.java b/src/main/java/org/gnuhpc/bigdata/model/BrokerInfo.java index 5373350..2599f0d 100644 --- a/src/main/java/org/gnuhpc/bigdata/model/BrokerInfo.java +++ b/src/main/java/org/gnuhpc/bigdata/model/BrokerInfo.java @@ -1,7 +1,8 @@ package org.gnuhpc.bigdata.model; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.google.gson.annotations.SerializedName; import java.util.List; import java.util.Map; import lombok.AllArgsConstructor; @@ -9,6 +10,7 @@ import lombok.Setter; import lombok.ToString; import org.gnuhpc.bigdata.serializer.JsonJodaDateTimeSerializer; +import org.gnuhpc.bigdata.utils.TimestampDeserializer; import org.joda.time.DateTime; @Getter @@ -16,24 +18,50 @@ @AllArgsConstructor @ToString public class BrokerInfo { + private Map listener_security_protocol_map; - @SerializedName("listener_security_protocol_map") - private Map securityProtocol; + private List endpoints; - @SerializedName("endpoints") - private List endPoints; - - @SerializedName("jmx_port") - private int jmxPort; + private int jmxport; private String host; - @JsonSerialize(using = JsonJodaDateTimeSerializer.class) - @SerializedName("timestamp") - private DateTime startTime; + public DateTime starttime; private int port; private int version; private String rack = ""; private int id = -1; + + @JsonProperty("jmxPort") + public int getJmxport() { + return jmxport; + } + + @JsonProperty("jmx_port") + public void setJmxPort(int jmxport) { + this.jmxport = jmxport; + } + + @JsonProperty("securityProtocol") + public Map getListener_security_protocol_map() { + return listener_security_protocol_map; + } + + @JsonProperty("listener_security_protocol_map") + public void setSecurityProtocol(Map listener_security_protocol_map) { + this.listener_security_protocol_map = listener_security_protocol_map; + } + + @JsonProperty("startTime") + @JsonSerialize(using = JsonJodaDateTimeSerializer.class) + public DateTime getStarttime() { + return starttime; + } + + @JsonProperty("timestamp") + @JsonDeserialize(using = TimestampDeserializer.class) + public void setStartTime(DateTime timestamp) { + this.starttime = timestamp; + } } diff --git a/src/main/java/org/gnuhpc/bigdata/model/ClusterInfo.java b/src/main/java/org/gnuhpc/bigdata/model/ClusterInfo.java new file mode 100644 index 0000000..313838b --- /dev/null +++ b/src/main/java/org/gnuhpc/bigdata/model/ClusterInfo.java @@ -0,0 +1,14 @@ +package org.gnuhpc.bigdata.model; + +import java.util.Collection; +import lombok.Getter; +import lombok.Setter; +import org.apache.kafka.common.Node; + +@Getter +@Setter +public class ClusterInfo { + private Collection nodes; + private Node controller; + private String clusterId; +} diff --git a/src/main/java/org/gnuhpc/bigdata/model/ReassignWrapper.java b/src/main/java/org/gnuhpc/bigdata/model/ReassignWrapper.java index 84042f0..9296815 100644 --- a/src/main/java/org/gnuhpc/bigdata/model/ReassignWrapper.java +++ b/src/main/java/org/gnuhpc/bigdata/model/ReassignWrapper.java @@ -1,6 +1,7 @@ package org.gnuhpc.bigdata.model; -import com.google.gson.Gson; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -8,11 +9,14 @@ import lombok.Getter; import lombok.Setter; import lombok.ToString; +import lombok.extern.log4j.Log4j; +import org.apache.kafka.common.errors.ApiException; /** Created by gnuhpc on 2017/7/25. */ @Getter @Setter @ToString +@Log4j public class ReassignWrapper { private List topics; @@ -27,7 +31,14 @@ public String generateReassignJsonString() { topicList.add(topicMap); } reassignJsonWrapper.setTopics(topicList); - Gson gson = new Gson(); - return gson.toJson(reassignJsonWrapper); + try { + ObjectMapper objectMapper = new ObjectMapper(); + return objectMapper.writeValueAsString(reassignJsonWrapper); + } catch (JsonProcessingException exeception) { + log.error("Serialize ReassignWrapper object to string error." + exeception); + throw new ApiException("Serialize ReassignWrapper object to string error." + exeception); + } + // Gson gson = new Gson(); + // return gson.toJson(reassignJsonWrapper); } } diff --git a/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java b/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java index 1c63872..a4b222f 100644 --- a/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java +++ b/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java @@ -11,9 +11,6 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.module.scala.DefaultScalaModule; import com.google.common.collect.ImmutableMap; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonDeserializer; import java.io.IOException; import java.text.ParseException; import java.text.SimpleDateFormat; @@ -112,6 +109,7 @@ import org.gnuhpc.bigdata.constant.ReassignmentStatus; import org.gnuhpc.bigdata.model.AddPartition; import org.gnuhpc.bigdata.model.BrokerInfo; +import org.gnuhpc.bigdata.model.ClusterInfo; import org.gnuhpc.bigdata.model.ConsumerGroupDesc; import org.gnuhpc.bigdata.model.ConsumerGroupMeta; import org.gnuhpc.bigdata.model.CustomConfigEntry; @@ -130,7 +128,6 @@ import org.gnuhpc.bigdata.utils.ZookeeperUtils; import org.gnuhpc.bigdata.validator.ConsumerGroupExistConstraint; import org.gnuhpc.bigdata.validator.TopicExistConstraint; -import org.joda.time.DateTime; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; @@ -167,23 +164,11 @@ public class KafkaAdminService { private ZkUtils zkUtils; private org.apache.kafka.clients.admin.AdminClient kafkaAdminClient = null; - // For Json serialized - // TODO replace gson with jackson - private Gson gson; private scala.Option none = scala.Option.apply(null); @PostConstruct - private void init() { - GsonBuilder builder = new GsonBuilder(); - builder.registerTypeAdapter( - DateTime.class, - (JsonDeserializer) - (jsonElement, type, jsonDeserializationContext) -> - new DateTime(jsonElement.getAsJsonPrimitive().getAsLong())); - - this.gson = builder.create(); - } + private void init() {} private org.apache.kafka.clients.admin.AdminClient createKafkaAdminClient() { if (this.kafkaAdminClient == null) { @@ -240,7 +225,7 @@ public HashMap createTopic(List topicList) kafkaAdminClient.createTopics(newTopicList, createTopicsOptions); try { - createTopicsResult.all().get(kafkaAdminClientAlterTimeoutMs, TimeUnit.MILLISECONDS); + createTopicsResult.all().get(kafkaAdminClientGetTimeoutMs, TimeUnit.MILLISECONDS); } catch (Exception exception) { log.warn("Create topic exception:" + exception); } finally { @@ -348,8 +333,9 @@ public boolean existTopic(String topicName) { return exists; } - public Map describeCluster() { - Map clusterDetail = new HashMap<>(); + public ClusterInfo describeCluster() { + // Map clusterDetail = new HashMap<>(); + ClusterInfo clusterInfo = new ClusterInfo(); org.apache.kafka.clients.admin.AdminClient kafkaAdminClient = createKafkaAdminClient(); DescribeClusterOptions describeClusterOptions = @@ -374,17 +360,20 @@ public Map describeCluster() { throw new ApiException("Describe cluster exception:" + exception); } finally { if (clusterIdFuture.isDone() && !clusterIdFuture.isCompletedExceptionally()) { - clusterDetail.put("clusterId", clusterId); + // clusterDetail.put("clusterId", clusterId); + clusterInfo.setClusterId(clusterId); } if (controllerFuture.isDone() && !controllerFuture.isCompletedExceptionally()) { - clusterDetail.put("controllerId", controller); + // clusterDetail.put("controllerId", controller); + clusterInfo.setController(controller); } if (nodesFuture.isDone() && !nodesFuture.isCompletedExceptionally()) { - clusterDetail.put("nodes", nodes); + // clusterDetail.put("nodes", nodes); + clusterInfo.setNodes(nodes); } } - return clusterDetail; + return clusterInfo; } public List listBrokers() { @@ -412,7 +401,13 @@ public List listBrokers() { } catch (Exception e) { e.printStackTrace(); } - BrokerInfo brokerInfo = gson.fromJson(brokerInfoStr, BrokerInfo.class); + BrokerInfo brokerInfo; + try { + ObjectMapper objectMapper = new ObjectMapper(); + brokerInfo = objectMapper.readValue(brokerInfoStr, BrokerInfo.class); + } catch (Exception exception) { + throw new ApiException("List broker exception." + exception); + } if (entry.getValue().isEmpty()) { brokerInfo.setRack(""); } else { @@ -426,15 +421,8 @@ public List listBrokers() { return brokerInfoList; } - public int getControllerId() { - KafkaZkClient kafkaZkClient = zookeeperUtils.createKafkaZkClient(); - - int controllerId = -1; - if (!kafkaZkClient.getControllerId().equals(none)) { - controllerId = (int) kafkaZkClient.getControllerId().get(); - } - - return controllerId; + public Node getController() { + return describeCluster().getController(); } public Map> listLogDirsByBroker(List brokerList) { @@ -470,14 +458,16 @@ public Map> describeLogDirsByBrokerAndTopic( } } } - - if (topicList != null && !topicList.isEmpty()) { - for (String topic : topicList) { - if (!existTopic(topic)) { - throw new ApiException("Bad Request. Topic:" + topic + " non-exist."); - } - } - } + // Delete reason: we want to delete the topic data that no longer found on zk, we can still use + // this + // function to get the topic log dir + // if (topicList != null && !topicList.isEmpty()) { + // for (String topic : topicList) { + // if (!existTopic(topic)) { + // throw new ApiException("Bad Request. Topic:" + topic + " non-exist."); + // } + // } + // } DescribeLogDirsOptions describeLogDirsOptions = new DescribeLogDirsOptions().timeoutMs((int) kafkaAdminClientGetTimeoutMs); @@ -678,8 +668,31 @@ public Map deleteTopicList(List topicList) { org.apache.kafka.clients.admin.AdminClient kafkaAdminClient = createKafkaAdminClient(); HashMap deleteResults = new HashMap<>(); + List topicListToBeDeleted = new ArrayList<>(topicList); + log.warn("Delete topic " + topicList); - DeleteTopicsResult deleteTopicsResult = kafkaAdminClient.deleteTopics(topicList); + for (int i = 0; i < topicList.size(); i++) { + String topic = topicList.get(i); + try { + if (Topic.isInternal(topic)) { + throw new ApiException( + "Topic " + topic + " is a kafka internal topic and is not allowed to be deleted."); + } + if (!existTopic(topic)) { + throw new ApiException("Topic " + topic + " non-exists."); + } + } catch (Exception exception) { + topicListToBeDeleted.remove(topic); + GeneralResponse generalResponse = + GeneralResponse.builder() + .state(GeneralResponseState.failure) + .msg(exception.getMessage()) + .build(); + deleteResults.put(topic, generalResponse); + } + } + + DeleteTopicsResult deleteTopicsResult = kafkaAdminClient.deleteTopics(topicListToBeDeleted); try { deleteTopicsResult.all().get(kafkaAdminClientGetTimeoutMs, TimeUnit.MILLISECONDS); } catch (Exception exception) { @@ -922,7 +935,6 @@ private List getTopicListByConsumerGroup(String consumerGroup) { AdminClient adminClient = kafkaUtils.createAdminClient(); Map groupOffsets = CollectionConvertor.mapConvertJavaMap(adminClient.listGroupOffsets(consumerGroup)); - adminClient.close(); return groupOffsets .entrySet() @@ -960,8 +972,8 @@ public ConsumerGroupMeta getConsumerGroupMeta(String consumerGroup) { }); consumerGroupMeta.setMembers(members); - adminClient.close(); + adminClient.close(); return consumerGroupMeta; } @@ -1078,6 +1090,8 @@ public List describeNewConsumerGroup( Map offsets = CollectionConvertor.mapConvertJavaMap(adminClient.listGroupOffsets(consumerGroup)); Map offsetsFiltered; + + adminClient.close(); if (filtered && existTopic(topic)) { offsetsFiltered = offsets @@ -1157,7 +1171,6 @@ public List describeNewConsumerGroup( } } - adminClient.close(); return partitionAssignmentStateList; } @@ -1221,7 +1234,6 @@ public List describeOldConsumerGroup( try { String source = mapper.writeValueAsString(zkConsumerGroupService.collectGroupOffsets()._2().get()); - System.out.println("source:" + source); partitionAssignmentStateList = mapper.readValue( source, getCollectionType(mapper, List.class, PartitionAssignmentState.class)); @@ -1567,7 +1579,6 @@ private Map checkIfPartitionReassignmentSucceeded( private Map checkIfReplicaReassignmentSucceeded( Map replicaAssignement) { - org.apache.kafka.clients.admin.AdminClient kafkaAdminClient = createKafkaAdminClient(); Map replicasReassignmentStatus = new HashMap<>(); Map replicaLogDirInfos = new HashMap<>(); @@ -2046,6 +2057,7 @@ public GeneralResponse deleteConsumerGroup(String consumerGroup, ConsumerType ty scala.collection.immutable.Map stringErrorsMap = adminClient.deleteConsumerGroups((scala.collection.immutable.List) (groupsList)); + adminClient.close(); if (!stringErrorsMap.get(consumerGroup).get().equals(Errors.NONE)) { throw new ApiException( "Consumer group:" @@ -2055,7 +2067,6 @@ public GeneralResponse deleteConsumerGroup(String consumerGroup, ConsumerType ty + ". Error msg:" + stringErrorsMap.get(consumerGroup).get().exception()); } - adminClient.close(); } return GeneralResponse.builder() diff --git a/src/main/java/org/gnuhpc/bigdata/utils/KafkaUtils.java b/src/main/java/org/gnuhpc/bigdata/utils/KafkaUtils.java index 1d6c6c0..982269c 100644 --- a/src/main/java/org/gnuhpc/bigdata/utils/KafkaUtils.java +++ b/src/main/java/org/gnuhpc/bigdata/utils/KafkaUtils.java @@ -63,6 +63,24 @@ public KafkaConsumer createNewConsumer(String consumerGroup) { return new KafkaConsumer(properties); } + public KafkaConsumer createNewConsumerByClientId(String consumerGroup, String clientId) { + Properties properties = new Properties(); + properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaConfig.getBrokers()); + properties.put(ConsumerConfig.GROUP_ID_CONFIG, consumerGroup); + properties.put(ConsumerConfig.CLIENT_ID_CONFIG, clientId); + properties.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "false"); + properties.put(ConsumerConfig.SESSION_TIMEOUT_MS_CONFIG, "30000"); + properties.put(ConsumerConfig.MAX_PARTITION_FETCH_BYTES_CONFIG, "100000000"); + properties.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, "5"); + properties.put( + ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getCanonicalName()); + properties.put( + ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, + StringDeserializer.class.getCanonicalName()); + + return new KafkaConsumer(properties); + } + public KafkaConsumer createNewConsumer(String consumerGroup, String decoder) throws ClassNotFoundException { Properties properties = new Properties(); diff --git a/src/main/java/org/gnuhpc/bigdata/utils/TimestampDeserializer.java b/src/main/java/org/gnuhpc/bigdata/utils/TimestampDeserializer.java new file mode 100644 index 0000000..c7728fb --- /dev/null +++ b/src/main/java/org/gnuhpc/bigdata/utils/TimestampDeserializer.java @@ -0,0 +1,30 @@ +package org.gnuhpc.bigdata.utils; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import java.io.IOException; +import lombok.Getter; +import lombok.Setter; +import org.joda.time.DateTime; + +@Getter +@Setter +public class TimestampDeserializer extends JsonDeserializer { + public TimestampDeserializer() { + + } + + @Override + public DateTime deserialize( + JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException { + String timestamp = jp.getText().trim(); + + try { + return new DateTime(Long.valueOf(timestamp)); + } catch (NumberFormatException e) { + return null; + } + } +} diff --git a/src/test/java/org/gnuhpc/bigdata/KafkaRestSpringbootApplicationTests.java b/src/test/java/org/gnuhpc/bigdata/KafkaRestSpringbootApplicationTests.java index dd3685e..7769684 100644 --- a/src/test/java/org/gnuhpc/bigdata/KafkaRestSpringbootApplicationTests.java +++ b/src/test/java/org/gnuhpc/bigdata/KafkaRestSpringbootApplicationTests.java @@ -45,5 +45,6 @@ public void contextLoads() { public void testListBrokers() throws Exception { // List brokerInfoList = kafkaAdminService.listBrokers(); // brokerInfoList.stream().forEach(log::info); + System.out.println(kafkaAdminService.listTopics()); } } diff --git a/src/test/java/org/gnuhpc/bigdata/service/KafkaAdminServiceTest.java b/src/test/java/org/gnuhpc/bigdata/service/KafkaAdminServiceTest.java index 81d7800..ea74694 100644 --- a/src/test/java/org/gnuhpc/bigdata/service/KafkaAdminServiceTest.java +++ b/src/test/java/org/gnuhpc/bigdata/service/KafkaAdminServiceTest.java @@ -1,31 +1,41 @@ package org.gnuhpc.bigdata.service; -import static junit.framework.TestCase.assertTrue; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.mockito.Mockito.when; import static org.mockito.MockitoAnnotations.initMocks; +import static org.testng.Assert.assertTrue; +import io.swagger.models.auth.In; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; -import kafka.server.KafkaServerStartable; +import org.apache.kafka.clients.consumer.KafkaConsumer; +import org.apache.kafka.common.Node; import org.gnuhpc.bigdata.componet.OffsetStorage; import org.gnuhpc.bigdata.config.KafkaConfig; import org.gnuhpc.bigdata.config.ZookeeperConfig; +import org.gnuhpc.bigdata.constant.ConsumerType; import org.gnuhpc.bigdata.constant.GeneralResponseState; +import org.gnuhpc.bigdata.model.BrokerInfo; +import org.gnuhpc.bigdata.model.ClusterInfo; +import org.gnuhpc.bigdata.model.ConsumerGroupMeta; +import org.gnuhpc.bigdata.model.CustomConfigEntry; import org.gnuhpc.bigdata.model.GeneralResponse; +import org.gnuhpc.bigdata.model.MemberDescription; +import org.gnuhpc.bigdata.model.PartitionAssignmentState; import org.gnuhpc.bigdata.model.TopicBrief; import org.gnuhpc.bigdata.model.TopicDetail; import org.gnuhpc.bigdata.model.TopicMeta; -import org.gnuhpc.bigdata.utils.KafkaStarterUtils; import org.gnuhpc.bigdata.utils.KafkaUtils; -import org.gnuhpc.bigdata.utils.ZkStarter; import org.gnuhpc.bigdata.utils.ZookeeperUtils; -import org.junit.AfterClass; +import org.junit.After; import org.junit.Before; import org.junit.BeforeClass; import org.junit.FixMethodOrder; @@ -37,42 +47,47 @@ @FixMethodOrder(MethodSorters.JVM) public class KafkaAdminServiceTest { - - @Mock private KafkaConfig mockKafkaConfig; - @Mock private ZookeeperConfig mockZookeeperConfig; + @Mock private static KafkaConfig mockKafkaConfig; + @Mock private static ZookeeperConfig mockZookeeperConfig; @Spy private ZookeeperUtils mockZookeeperUtils = new ZookeeperUtils(); @Spy private KafkaUtils mockKafkaUtils = new KafkaUtils(); @Mock private OffsetStorage mockStorage; @InjectMocks private KafkaAdminService kafkaAdminServiceUnderTest; - private static KafkaServerStartable kafkaStarter; - - private final String firstTopicName = "firsttopic"; - private final String secondTopicName = "secondtopic"; - private final String nonExistTopicName = "nontopic"; + private static final String TEST_KAFKA_BOOTSTRAP_SERVERS = + "localhost:19092,localhost:19093,localhost:19095"; + private static final List TEST_KAFKA_BOOTSTRAP_SERVERS_ID = Arrays.asList(111, 113, 115); + private static final int KAFKA_NODES_COUNT = TEST_KAFKA_BOOTSTRAP_SERVERS_ID.size(); + private static final String TEST_ZK = "localhost:2183"; + private static final int TEST_CONTROLLER_ID = 113; + private static final List TEST_KAFKA_LOG_DIRS = + Arrays.asList( + "/home/xiangli/bigdata/kafka_2.11-1.1.1/kafka-logs,/home/xiangli/bigdata/kafka_2.11-1.1.1/kafka111_2-logs,/home/xiangli/bigdata/kafka_2.11-1.1.1/kafka111_3-logs", + "/home/xiangli/bigdata/kafka_2.11-1.1.1/kafka113-logs,/home/xiangli/bigdata/kafka_2.11-1.1.1/kafka113_2-logs,/home/xiangli/bigdata/kafka_2.11-1.1.1/kafka113_3-logs", + "/home/xiangli/bigdata/kafka_2.11-1.1.1/kafka115-logs,/home/xiangli/bigdata/kafka_2.11-1.1.1/kafka115_2-logs,/home/xiangli/bigdata/kafka_2.11-1.1.1/kafka115_3-logs"); + + private static final String FIRST_TOPIC_TO_TEST = "first"; + private static final String SECOND_TOPIC_TO_TEST = "second"; + private static final String NON_EXIST_TOPIC_TO_TEST = "nontopic"; + private static final String FIRST_CONSUMER_GROUP_TO_TEST = "testConsumerGroup1"; + private static final String SECOND_CONSUMER_GROUP_TO_TEST = "testConsumerGroup2"; + private static final String FIRST_CONSUMER_CLIENT_TO_TEST = "testConsumerClient1"; + private static final String SECOND_CONSUMER_CLIENT_TO_TEST = "testConsumerClient2"; + + public static final String GROUP_METADATA_TOPIC_NAME = "__consumer_offsets"; + public static final int GROUP_METADATA_TOPIC_PARTITION_COUNT = 50; + + private Set allTopicsInClusterBeforeTest; @BeforeClass - public static void start() { - ZkStarter.startLocalZkServer(); - kafkaStarter = - KafkaStarterUtils.startServer( - KafkaStarterUtils.DEFAULT_KAFKA_PORT, - KafkaStarterUtils.DEFAULT_BROKER_ID, - KafkaStarterUtils.DEFAULT_ZK_STR, - KafkaStarterUtils.getDefaultKafkaConfiguration()); - } + public static void start() {} @Before - public void setUp() { + public void setUp() throws InterruptedException { initMocks(this); - - String MOCK_KAFKA_BOOTSTRAP_SERVERS = KafkaStarterUtils.DEFAULT_KAFKA_BROKER; - String MOCK_ZK = ZkStarter.DEFAULT_ZK_STR; - - when(mockKafkaConfig.getBrokers()).thenReturn(MOCK_KAFKA_BOOTSTRAP_SERVERS); - when(mockZookeeperConfig.getUris()).thenReturn(MOCK_ZK); - + when(mockKafkaConfig.getBrokers()).thenReturn(TEST_KAFKA_BOOTSTRAP_SERVERS); + when(mockZookeeperConfig.getUris()).thenReturn(TEST_ZK); // when(mockZookeeperUtils.createKafkaZkClient()) // .thenReturn( // KafkaZkClient.apply( @@ -93,724 +108,1080 @@ public void setUp() { mockKafkaUtils.setKafkaConfig(mockKafkaConfig); mockZookeeperUtils.setZookeeperConfig(mockZookeeperConfig); - } - @AfterClass - public static void stop() { - kafkaStarter.shutdown(); - ZkStarter.stopLocalZkServer(); + clean(); + allTopicsInClusterBeforeTest = kafkaAdminServiceUnderTest.getAllTopics(); } - @Test - public void testListTopics() { - // Setup - final List expectedResult = Arrays.asList(); - - // Run the test - final List result = kafkaAdminServiceUnderTest.listTopics(); - - // Verify the results - assertEquals(expectedResult, result); + private void clean() throws InterruptedException { + // Delete test topics + kafkaAdminServiceUnderTest.deleteTopicList( + Arrays.asList(FIRST_TOPIC_TO_TEST, SECOND_TOPIC_TO_TEST)); + + // Delete test consumers + if (kafkaAdminServiceUnderTest.isNewConsumerGroup(FIRST_CONSUMER_GROUP_TO_TEST)) { + kafkaAdminServiceUnderTest.deleteConsumerGroup( + FIRST_CONSUMER_GROUP_TO_TEST, ConsumerType.NEW); + } + if (kafkaAdminServiceUnderTest.isNewConsumerGroup(SECOND_CONSUMER_GROUP_TO_TEST)) { + kafkaAdminServiceUnderTest.deleteConsumerGroup( + SECOND_CONSUMER_GROUP_TO_TEST, ConsumerType.NEW); + } + Thread.sleep(1000); } - @Test - public void testCreateTopic() { - final List allTopicsBeforeCreate = kafkaAdminServiceUnderTest.listTopics(); - assertTrue(allTopicsBeforeCreate.isEmpty()); + public static HashMap getBrokerIdAndPort() { + HashMap brokerIdAndPortMap = new HashMap<>(); - List topicList = new ArrayList<>(); - // Create first topic by topic name, partition count, replication factor - final int partitionCount = 3; - final int replicationFactor = 1; - final TopicDetail topic = - TopicDetail.builder() - .name(firstTopicName) - .partitions(partitionCount) - .factor(replicationFactor) - .build(); + String[] brokerInfos = TEST_KAFKA_BOOTSTRAP_SERVERS.split(","); + for (int i = 0; i < TEST_KAFKA_BOOTSTRAP_SERVERS_ID.size(); i++) { + int brokerId = TEST_KAFKA_BOOTSTRAP_SERVERS_ID.get(i); + String[] hostAndPort = brokerInfos[i].split(":"); + int port = Integer.parseInt(hostAndPort[1]); + brokerIdAndPortMap.put(brokerId, port); + } - // Create second topic by replica assignment - HashMap> replicaAssignments = new HashMap<>(); - replicaAssignments.put(0, Arrays.asList(KafkaStarterUtils.DEFAULT_BROKER_ID)); - TopicDetail topic2 = - TopicDetail.builder().name(secondTopicName).replicasAssignments(replicaAssignments).build(); - - topicList.add(topic); - topicList.add(topic2); - - // Run the test - final Map result = kafkaAdminServiceUnderTest.createTopic(topicList); - - // Verify the first topic result - TopicMeta firstTopicMeta = (TopicMeta) result.get(firstTopicName).getData(); - assertEquals(GeneralResponseState.success, result.get(firstTopicName).getState()); - assertEquals(firstTopicName, firstTopicMeta.getTopicName()); - assertEquals(partitionCount, firstTopicMeta.getPartitionCount()); - assertEquals(replicationFactor, firstTopicMeta.getReplicationFactor()); - - // Verify the second topic result - TopicMeta secondTopicMeta = (TopicMeta) result.get(secondTopicName).getData(); - assertEquals(GeneralResponseState.success, result.get(secondTopicName).getState()); - assertEquals(secondTopicName, secondTopicMeta.getTopicName()); - assertEquals(1, secondTopicMeta.getPartitionCount()); - assertEquals(replicationFactor, secondTopicMeta.getReplicationFactor()); - assertEquals( - KafkaStarterUtils.DEFAULT_BROKER_ID, - secondTopicMeta - .getTopicPartitionInfos() - .get(0) - .getTopicPartitionInfo() - .replicas() - .get(0) - .id()); + return brokerIdAndPortMap; } - @Test - public void testGetAllTopics() { - // Setup - final Set expectedResult = new HashSet<>(); - - // Run the test - final Set result = kafkaAdminServiceUnderTest.getAllTopics(); - - final Set allTopicsAfterCreate = kafkaAdminServiceUnderTest.getAllTopics(); - - // Verify the results - assertEquals(2, allTopicsAfterCreate.size()); - assertTrue(allTopicsAfterCreate.contains(firstTopicName)); - assertTrue(allTopicsAfterCreate.contains(secondTopicName)); + @After + public void clearDirtyData() throws InterruptedException { + // clean(); } @Test - public void testListTopicBrief() { - // Setup - final List expectedResult = Arrays.asList(); - + public void testDescribeCluster() { // Run the test - final List result = kafkaAdminServiceUnderTest.listTopicBrief(); + final ClusterInfo clusterInfo = kafkaAdminServiceUnderTest.describeCluster(); // Verify the results -// assertEquals(expectedResult, result); - } - - @Test - public void testExistTopic() { - // Setup - final boolean expectedResult = false; - - // Run the test -// final boolean firstTopicExist = kafkaAdminServiceUnderTest.existTopic(firstTopicName); -// final boolean nonExistTopicExist = kafkaAdminServiceUnderTest.existTopic(nonExistTopicName); -// -// // Verify the results -// assertTrue(firstTopicExist); -// assertFalse(nonExistTopicExist); + assertEquals(KAFKA_NODES_COUNT, clusterInfo.getNodes().size()); } @Test - public void testDescribeCluster() { + public void testGetControllerId() { // Setup - final Map expectedResult = new HashMap<>(); + final int expectedControllerId = TEST_CONTROLLER_ID; // Run the test - final Map result = kafkaAdminServiceUnderTest.describeCluster(); + final Node controller = kafkaAdminServiceUnderTest.getController(); - System.out.println("result:" + result); // Verify the results - assertEquals(1, result.size()); + assertEquals(expectedControllerId, controller.id()); } -/* + @Test public void testListBrokers() { // Setup - final List expectedResult = Arrays.asList(); + final HashMap expectedBrokerIdAndPort = getBrokerIdAndPort(); // Run the test - final List result = kafkaAdminServiceUnderTest.listBrokers(); + final List brokerInfoList = kafkaAdminServiceUnderTest.listBrokers(); // Verify the results - assertEquals(expectedResult, result); + for (BrokerInfo brokerInfo : brokerInfoList) { + int brokerId = brokerInfo.getId(); + int brokerPort = brokerInfo.getPort(); + assertTrue(expectedBrokerIdAndPort.containsKey(brokerId)); + if (expectedBrokerIdAndPort.containsKey(brokerId)) { + int expectedBrokerPort = expectedBrokerIdAndPort.get(brokerId); + assertEquals(expectedBrokerPort, brokerPort); + } + } } @Test - public void testGetControllerId() { - // Setup - final int expectedResult = 0; - - // Run the test - final int result = kafkaAdminServiceUnderTest.getControllerId(); - - // Verify the results - assertEquals(expectedResult, result); + public void testGetBrokerConf() { + final HashMap expectedBrokerIdAndPort = getBrokerIdAndPort(); + + for (int i = 0; i < TEST_KAFKA_BOOTSTRAP_SERVERS_ID.size(); i++) { + // Setup + final int expectedBrokerId = TEST_KAFKA_BOOTSTRAP_SERVERS_ID.get(i); + final int expectedBrokerPort = expectedBrokerIdAndPort.get(expectedBrokerId); + + // Run the test + final Collection brokerConf = + kafkaAdminServiceUnderTest.getBrokerConf(expectedBrokerId); + + for (CustomConfigEntry customConfigEntry : brokerConf) { + // Verify the results + // Just verify broker.id, listeners, log dirs + if (customConfigEntry.getName().equals("broker.id")) { + assertEquals(expectedBrokerId, Integer.parseInt(customConfigEntry.getValue())); + } + if (customConfigEntry.getName().equals("listeners")) { + String listeners = customConfigEntry.getValue(); + int brokerPort = Integer.parseInt(listeners.substring(listeners.lastIndexOf(":") + 1)); + assertEquals(expectedBrokerPort, brokerPort); + } + if (customConfigEntry.getName().equals("log.dirs")) { + String logDirs = customConfigEntry.getValue(); + assertEquals(TEST_KAFKA_LOG_DIRS.get(i), logDirs); + } + } + } } @Test public void testListLogDirsByBroker() { // Setup - final List brokerList = Arrays.asList(); - final Map> expectedResult = new HashMap<>(); + final List brokerList = TEST_KAFKA_BOOTSTRAP_SERVERS_ID; + final Map> expectedBrokerLogDirMap = new HashMap<>(); + + for (int i = 0; i < brokerList.size(); i++) { + int brokerId = brokerList.get(i); + String dirs = TEST_KAFKA_LOG_DIRS.get(i); + expectedBrokerLogDirMap.put(brokerId, Arrays.asList(dirs.split(","))); + } // Run the test - final Map> result = + final Map> logDirs = kafkaAdminServiceUnderTest.listLogDirsByBroker(brokerList); // Verify the results - assertEquals(expectedResult, result); + assertEquals(expectedBrokerLogDirMap, logDirs); } - @Test - public void testDescribeLogDirsByBrokerAndTopic() { - // Setup - final List brokerList = Arrays.asList(); - final List topicList = Arrays.asList(); - final Map> expectedResult = new HashMap<>(); - - // Run the test - final Map> result = - kafkaAdminServiceUnderTest.describeLogDirsByBrokerAndTopic(brokerList, topicList); - - // Verify the results - assertEquals(expectedResult, result); + private TopicDetail generateTopicDetail( + String topicName, int partitionCount, int replicationFactor) { + return TopicDetail.builder() + .name(topicName) + .partitions(partitionCount) + .factor(replicationFactor) + .build(); } - @Test - public void testDescribeReplicaLogDirs() { - // Setup - final List replicas = Arrays.asList(); - final Map expectedResult = new HashMap<>(); - - // Run the test - final Map result = - kafkaAdminServiceUnderTest.describeReplicaLogDirs(replicas); - - // Verify the results - assertEquals(expectedResult, result); + private TopicDetail generateTopicDetailByReplicaAssignment( + String topicName, HashMap> replicaAssignments) { + return TopicDetail.builder().name(topicName).replicasAssignments(replicaAssignments).build(); } @Test - public void testGetBrokerConf() { - // Setup - final int brokerId = 0; - final Collection expectedResult = Arrays.asList(); - - // Run the test - final Collection result = kafkaAdminServiceUnderTest.getBrokerConf(brokerId); + public void testCreateTopic() { + List topicList = new ArrayList<>(); + // Create first topic by topic name, partition count, replication factor + int partitionCount = 3; + int replicationFactor = 1; + final TopicDetail topic = generateTopicDetail(FIRST_TOPIC_TO_TEST, 3, 1); - // Verify the results - assertEquals(expectedResult, result); - } + // Create second topic by replica assignment + HashMap> replicaAssignments = new HashMap<>(); + replicaAssignments.put(0, TEST_KAFKA_BOOTSTRAP_SERVERS_ID.subList(0, 1)); + TopicDetail topic2 = + generateTopicDetailByReplicaAssignment(SECOND_TOPIC_TO_TEST, replicaAssignments); - @Test - public void testGetConfigInZk() { - // Setup - final Type type = null; - final String name = "name"; - final Properties expectedResult = new Properties(); + topicList.add(topic); + topicList.add(topic2); // Run the test - final Properties result = kafkaAdminServiceUnderTest.getConfigInZk(type, name); + final Map result = kafkaAdminServiceUnderTest.createTopic(topicList); - // Verify the results - assertEquals(expectedResult, result); - } + // Verify the first topic result + TopicMeta firstTopicMeta = (TopicMeta) result.get(FIRST_TOPIC_TO_TEST).getData(); + assertEquals(GeneralResponseState.success, result.get(FIRST_TOPIC_TO_TEST).getState()); + assertEquals(FIRST_TOPIC_TO_TEST, firstTopicMeta.getTopicName()); + assertEquals(partitionCount, firstTopicMeta.getPartitionCount()); + assertEquals(replicationFactor, firstTopicMeta.getReplicationFactor()); - @Test - public void testUpdateBrokerDynConf() { - // Setup - final int brokerId = 0; - final Properties propsToBeUpdated = new Properties(); - final Properties expectedResult = new Properties(); + // Verify the second topic result + TopicMeta secondTopicMeta = (TopicMeta) result.get(SECOND_TOPIC_TO_TEST).getData(); + assertEquals(GeneralResponseState.success, result.get(SECOND_TOPIC_TO_TEST).getState()); + assertEquals(SECOND_TOPIC_TO_TEST, secondTopicMeta.getTopicName()); + assertEquals(1, secondTopicMeta.getPartitionCount()); + assertEquals(replicationFactor, secondTopicMeta.getReplicationFactor()); + assertEquals( + TEST_KAFKA_BOOTSTRAP_SERVERS_ID.get(0).intValue(), + secondTopicMeta + .getTopicPartitionInfos() + .get(0) + .getTopicPartitionInfo() + .replicas() + .get(0) + .id()); - // Run the test - final Properties result = - kafkaAdminServiceUnderTest.updateBrokerDynConf(brokerId, propsToBeUpdated); + // Verify topics exist in cluster + assertTrue(kafkaAdminServiceUnderTest.existTopic(FIRST_TOPIC_TO_TEST)); + assertTrue(kafkaAdminServiceUnderTest.existTopic(SECOND_TOPIC_TO_TEST)); - // Verify the results - assertEquals(expectedResult, result); + // Verify all topics in cluster + Set expectedAllTopics = new HashSet<>(); + expectedAllTopics.addAll(allTopicsInClusterBeforeTest); + expectedAllTopics.add(FIRST_TOPIC_TO_TEST); + expectedAllTopics.add(SECOND_TOPIC_TO_TEST); + assertEquals(expectedAllTopics, kafkaAdminServiceUnderTest.getAllTopics()); } @Test - public void testRemoveConfigInZk() { - // Setup - final Type type = null; - final String name = "name"; - final List configKeysToBeRemoved = Arrays.asList(); + public void testCreateTopicAlreadyExist() { + List topicListToCreate = new ArrayList<>(); - // Run the test - kafkaAdminServiceUnderTest.removeConfigInZk(type, name, configKeysToBeRemoved); + int paritionCount = 3; + int replicationFactor = 1; + TopicDetail firstTopic = + generateTopicDetail(FIRST_TOPIC_TO_TEST, paritionCount, replicationFactor); - // Verify the results - } + // Create first topic + topicListToCreate.add(firstTopic); + final Map createTopicResult = + kafkaAdminServiceUnderTest.createTopic(topicListToCreate); - @Test - public void testDescribeTopic() { - // Setup - final String topicName = "topicName"; - final TopicMeta expectedResult = null; + //Create first topic again + final Map createTopicExistResult = + kafkaAdminServiceUnderTest.createTopic(topicListToCreate); - // Run the test - final TopicMeta result = kafkaAdminServiceUnderTest.describeTopic(topicName); - - // Verify the results - assertEquals(expectedResult, result); + assertEquals(GeneralResponseState.success, createTopicResult.get(FIRST_TOPIC_TO_TEST).getState()); + assertEquals( + GeneralResponseState.failure, createTopicExistResult.get(FIRST_TOPIC_TO_TEST).getState()); + assertTrue( + createTopicExistResult.get(FIRST_TOPIC_TO_TEST).getMsg().contains("TopicExistsException")); } @Test - public void testDeleteTopicList() { - // Setup - final List topicList = Arrays.asList(); - final Map expectedResult = new HashMap<>(); - - // Run the test - final Map result = - kafkaAdminServiceUnderTest.deleteTopicList(topicList); + public void testCreateTopicWithInvalidReplicator() { + List topicListToCreate = new ArrayList<>(); - // Verify the results - assertEquals(expectedResult, result); - } + int paritionCount = 3; + int replicationFactor = TEST_KAFKA_BOOTSTRAP_SERVERS_ID.size() + 1; - @Test - public void testDescribeConfig() { - // Setup - final Type type = null; - final String name = "name"; - final Collection expectedResult = Arrays.asList(); + System.out.println("////factor:" + replicationFactor); + //Create first topic with replicator that is more than broker count + TopicDetail firstTopic = + generateTopicDetail(FIRST_TOPIC_TO_TEST, paritionCount, replicationFactor); - // Run the test - final Collection result = kafkaAdminServiceUnderTest.describeConfig(type, name); + // Create firsttopic + topicListToCreate.add(firstTopic); + final Map createTopicResult = + kafkaAdminServiceUnderTest.createTopic(topicListToCreate); - // Verify the results - assertEquals(expectedResult, result); + assertEquals( + GeneralResponseState.failure, createTopicResult.get(FIRST_TOPIC_TO_TEST).getState()); + assertTrue( + createTopicResult.get(FIRST_TOPIC_TO_TEST).getMsg().contains("InvalidReplicationFactorException")); } @Test - public void testAlterConfig() { - // Setup - final Type type = null; - final String name = "name"; - final Collection configEntries = Arrays.asList(); - final boolean expectedResult = false; - + public void testListTopics() { // Run the test - final boolean result = kafkaAdminServiceUnderTest.alterConfig(type, name, configEntries); + final List allTopicsList = kafkaAdminServiceUnderTest.listTopics(); - // Verify the results - assertEquals(expectedResult, result); - } + assertEquals(allTopicsInClusterBeforeTest.size(), allTopicsList.size()); - @Test - public void testUpdateTopicConf() { - // Setup - final String topic = "topic"; - final Properties props = new Properties(); - final Collection expectedResult = Arrays.asList(); - - // Run the test - final Collection result = - kafkaAdminServiceUnderTest.updateTopicConf(topic, props); - - // Verify the results - assertEquals(expectedResult, result); + Iterator iterator = allTopicsInClusterBeforeTest.iterator(); + while (iterator.hasNext()) { + String topicName = iterator.next(); + assertTrue(allTopicsList.contains(topicName)); + } } @Test - public void testGetTopicConf() { - // Setup - final String topic = "topic"; - final Collection expectedResult = Arrays.asList(); - + public void testExistTopic() { // Run the test - final Collection result = kafkaAdminServiceUnderTest.getTopicConf(topic); - - // Verify the results - assertEquals(expectedResult, result); + final boolean internalDefaultTopicExist = + kafkaAdminServiceUnderTest.existTopic(GROUP_METADATA_TOPIC_NAME); + final boolean nonExistTopicExist = + kafkaAdminServiceUnderTest.existTopic(NON_EXIST_TOPIC_TO_TEST); + + // // Verify the results + assertTrue(internalDefaultTopicExist); + assertFalse(nonExistTopicExist); } @Test - public void testGetTopicConfByKey() { - // Setup - final String topic = "topic"; - final String key = "key"; - final Properties expectedResult = new Properties(); - + public void testListTopicBrief() { // Run the test - final Properties result = kafkaAdminServiceUnderTest.getTopicConfByKey(topic, key); + final List topicBriefList = kafkaAdminServiceUnderTest.listTopicBrief(); // Verify the results - assertEquals(expectedResult, result); + assertEquals(allTopicsInClusterBeforeTest.size(), topicBriefList.size()); + + Iterator iterator = allTopicsInClusterBeforeTest.iterator(); + while (iterator.hasNext()) { + String topicName = iterator.next(); + boolean exists = false; + for (TopicBrief topicBrief : topicBriefList) { + if (topicBrief.getTopic().equals(topicName)) { + exists = true; + break; + } + } + assertTrue(exists); + } } @Test - public void testUpdateTopicConfByKey() { + public void testDescribeTopic() { // Setup - final String topic = "topic"; - final String key = "key"; - final String value = "value"; - final Collection expectedResult = Arrays.asList(); + final String topicName = GROUP_METADATA_TOPIC_NAME; // Run the test - final Collection result = - kafkaAdminServiceUnderTest.updateTopicConfByKey(topic, key, value); + final TopicMeta topicMeta = kafkaAdminServiceUnderTest.describeTopic(topicName); // Verify the results - assertEquals(expectedResult, result); + assertEquals(true, topicMeta.isInternal()); + assertEquals(GROUP_METADATA_TOPIC_PARTITION_COUNT, topicMeta.getPartitionCount()); + assertEquals(GROUP_METADATA_TOPIC_PARTITION_COUNT, topicMeta.getTopicPartitionInfos().size()); } @Test - public void testListAllConsumerGroups() { + public void testDeleteTopicList() { // Setup - final ConsumerType type = null; - final Map> expectedResult = new HashMap<>(); + List topicListToDelete = new ArrayList<>(); + // Create firsttopic and secondtopic + createTwoTopics(); // Run the test - final Map> result = kafkaAdminServiceUnderTest.listAllConsumerGroups(type); + topicListToDelete.add(FIRST_TOPIC_TO_TEST); + topicListToDelete.add(SECOND_TOPIC_TO_TEST); + topicListToDelete.add(NON_EXIST_TOPIC_TO_TEST); + final Map deleteTopicResult = + kafkaAdminServiceUnderTest.deleteTopicList(topicListToDelete); // Verify the results - assertEquals(expectedResult, result); + assertEquals(3, deleteTopicResult.size()); + assertTrue(deleteTopicResult.containsKey(FIRST_TOPIC_TO_TEST)); + assertTrue(deleteTopicResult.containsKey(SECOND_TOPIC_TO_TEST)); + assertTrue(deleteTopicResult.containsKey(NON_EXIST_TOPIC_TO_TEST)); + + GeneralResponse firstTopicDeleteResult = deleteTopicResult.get(FIRST_TOPIC_TO_TEST); + GeneralResponse secondTopicDeleteResult = deleteTopicResult.get(SECOND_TOPIC_TO_TEST); + GeneralResponse nonExistTopicDeleteResult = deleteTopicResult.get(NON_EXIST_TOPIC_TO_TEST); + + assertEquals(GeneralResponseState.success, firstTopicDeleteResult.getState()); + assertEquals(GeneralResponseState.success, secondTopicDeleteResult.getState()); + // Delete topic:nonexisttopic failure since it has not been created. + assertEquals(GeneralResponseState.failure, nonExistTopicDeleteResult.getState()); } @Test - public void testListConsumerGroupsByTopic() { - // Setup - final String topic = "topic"; - final ConsumerType type = null; - final Map> expectedResult = new HashMap<>(); - - // Run the test - final Map> result = - kafkaAdminServiceUnderTest.listConsumerGroupsByTopic(topic, type); + public void testDeleteInternalTopic() { + List topicListToDelete = new ArrayList<>(); - // Verify the results - assertEquals(expectedResult, result); - } + topicListToDelete.add(GROUP_METADATA_TOPIC_NAME); - @Test - public void testListTopicsByConsumerGroup() { - // Setup - final String consumerGroup = "consumerGroup"; - final ConsumerType type = null; - final Set expectedResult = new HashSet<>(); + final Map deleteTopicResult = + kafkaAdminServiceUnderTest.deleteTopicList(topicListToDelete); - // Run the test - final Set result = - kafkaAdminServiceUnderTest.listTopicsByConsumerGroup(consumerGroup, type); + GeneralResponse topicDeleteResult = deleteTopicResult.get(GROUP_METADATA_TOPIC_NAME); - // Verify the results - assertEquals(expectedResult, result); + assertEquals(GeneralResponseState.failure, topicDeleteResult.getState()); } - @Test - public void testGetConsumerGroupMeta() { - // Setup - final String consumerGroup = "consumerGroup"; - final ConsumerGroupMeta expectedResult = null; + class ConsumerRunnable implements Runnable { + private final KafkaConsumer consumer; - // Run the test - final ConsumerGroupMeta result = kafkaAdminServiceUnderTest.getConsumerGroupMeta(consumerGroup); + public ConsumerRunnable(String groupId, String clientId, List topicList) { + this.consumer = mockKafkaUtils.createNewConsumerByClientId(groupId, clientId); + consumer.subscribe(topicList); + } - // Verify the results - assertEquals(expectedResult, result); - } + @Override + public void run() { + consumer.poll(200); + consumer.commitSync(); + } - @Test - public void testIsOldConsumerGroup() { - // Setup - final String consumerGroup = "consumerGroup"; - final boolean expectedResult = false; - - // Run the test - final boolean result = kafkaAdminServiceUnderTest.isOldConsumerGroup(consumerGroup); - - // Verify the results - assertEquals(expectedResult, result); + public void close() { + consumer.close(); + } } - @Test - public void testIsNewConsumerGroup() { - // Setup - final String consumerGroup = "consumerGroup"; - final boolean expectedResult = false; - - // Run the test - final boolean result = kafkaAdminServiceUnderTest.isNewConsumerGroup(consumerGroup); - - // Verify the results - assertEquals(expectedResult, result); + class ConsumerGroup { + private List consumers; + + public ConsumerGroup( + String groupId, int numConsumers, List clientIdList, List topicList) { + consumers = new ArrayList<>(numConsumers); + for (int i = 0; i < numConsumers; i++) { + ConsumerRunnable consumerRunnable = + new ConsumerRunnable(groupId, clientIdList.get(i), topicList); + consumers.add(consumerRunnable); + } + } + + public void execute() { + for (ConsumerRunnable consumerRunnable : consumers) { + new Thread(consumerRunnable).start(); + } + } + + public void close() { + for (ConsumerRunnable consumerRunnable : consumers) { + consumerRunnable.close(); + } + } } - @Test - public void testDescribeConsumerGroup() { - // Setup - final String consumerGroup = "consumerGroup"; - final ConsumerType type = null; - final Map> expectedResult = new HashMap<>(); - - // Run the test - final Map> result = - kafkaAdminServiceUnderTest.describeConsumerGroup(consumerGroup, type); - - // Verify the results - assertEquals(expectedResult, result); + private KafkaConsumer consumer( + String consumerGroup, String clientId, List subscribedTopicList) { + KafkaConsumer kafkaConsumer = + mockKafkaUtils.createNewConsumerByClientId(consumerGroup, clientId); + kafkaConsumer.subscribe(subscribedTopicList); + kafkaConsumer.poll(1000); + kafkaConsumer.commitSync(); + return kafkaConsumer; } - @Test - public void testDescribeNewConsumerGroup() { - // Setup - final String consumerGroup = "consumerGroup"; - final boolean filtered = false; - final String topic = "topic"; - final List expectedResult = Arrays.asList(); - - // Run the test - final List result = - kafkaAdminServiceUnderTest.describeNewConsumerGroup(consumerGroup, filtered, topic); - - // Verify the results - assertEquals(expectedResult, result); + private KafkaConsumer consumer(String consumerGroup, List subscribedTopicList) { + KafkaConsumer kafkaConsumer = mockKafkaUtils.createNewConsumer(consumerGroup); + kafkaConsumer.subscribe(subscribedTopicList); + kafkaConsumer.poll(3000); + kafkaConsumer.commitSync(); + return kafkaConsumer; } @Test - public void testDescribeOldConsumerGroup() { - // Setup - final String consumerGroup = "consumerGroup"; - final boolean filtered = false; - final String topic = "topic"; - final List expectedResult = Arrays.asList(); - - // Run the test - final List result = - kafkaAdminServiceUnderTest.describeOldConsumerGroup(consumerGroup, filtered, topic); + public void testListAllConsumerGroups() { + // Setup, just test the new consumer groups + final ConsumerType type = ConsumerType.NEW; - // Verify the results - assertEquals(expectedResult, result); - } + String consumerGroup = FIRST_CONSUMER_GROUP_TO_TEST; - @Test - public void testDescribeNewConsumerGroupByTopic() { - // Setup - final String consumerGroup = "consumerGroup"; - final String topic = "topic"; - final List expectedResult = Arrays.asList(); + KafkaConsumer kafkaConsumer = consumer(consumerGroup, Arrays.asList(GROUP_METADATA_TOPIC_NAME)); // Run the test - final List result = - kafkaAdminServiceUnderTest.describeNewConsumerGroupByTopic(consumerGroup, topic); + final Map> consumerGroups = + kafkaAdminServiceUnderTest.listAllConsumerGroups(type); - // Verify the results - assertEquals(expectedResult, result); - } - - @Test - public void testDescribeOldConsumerGroupByTopic() { - // Setup - final String consumerGroup = "consumerGroup"; - final String topic = "topic"; - final List expectedResult = Arrays.asList(); - - // Run the test - final List result = - kafkaAdminServiceUnderTest.describeOldConsumerGroupByTopic(consumerGroup, topic); + Set newConsumerGroups = consumerGroups.get("new"); // Verify the results - assertEquals(expectedResult, result); - } - - @Test - public void testAddPartitions() { - // Setup - final List addPartitions = Arrays.asList(); - final Map expectedResult = new HashMap<>(); - - // Run the test - final Map result = - kafkaAdminServiceUnderTest.addPartitions(addPartitions); + assertTrue(newConsumerGroups.contains(consumerGroup)); - // Verify the results - assertEquals(expectedResult, result); + kafkaConsumer.close(); } @Test - public void testGenerateReassignPartition() { + public void testListConsumerGroupsByTopic() { // Setup - final ReassignWrapper reassignWrapper = null; - final List expectedResult = Arrays.asList(); + final String topic = GROUP_METADATA_TOPIC_NAME; + final ConsumerType type = ConsumerType.NEW; + String testGroup1 = FIRST_CONSUMER_GROUP_TO_TEST; + String testGroup2 = SECOND_CONSUMER_GROUP_TO_TEST; + + List subscribedTopicList = Arrays.asList(GROUP_METADATA_TOPIC_NAME); + KafkaConsumer kafkaConsumer1 = consumer(testGroup1, subscribedTopicList); + KafkaConsumer kafkaConsumer2 = consumer(testGroup2, subscribedTopicList); // Run the test - final List result = - kafkaAdminServiceUnderTest.generateReassignPartition(reassignWrapper); + final Map> consumerGroupsByTopic = + kafkaAdminServiceUnderTest.listConsumerGroupsByTopic(topic, type); // Verify the results - assertEquals(expectedResult, result); - } + Set newConsumerGroupsByTopic = consumerGroupsByTopic.get("new"); + assertTrue(newConsumerGroupsByTopic.contains(testGroup1)); + assertTrue(newConsumerGroupsByTopic.contains(testGroup2)); - @Test - public void testExecuteReassignPartition() { - // Setup - final String reassignStr = "reassignStr"; - final Long interBrokerThrottle = 0L; - final Long replicaAlterLogDirsThrottle = 0L; - final Long timeoutMs = 0L; - final Map expectedResult = new HashMap<>(); + kafkaConsumer1.close(); + kafkaConsumer2.close(); + } - // Run the test - final Map result = - kafkaAdminServiceUnderTest.executeReassignPartition( - reassignStr, interBrokerThrottle, replicaAlterLogDirsThrottle, timeoutMs); + private void createOneTopic() { + List topicListToCreate = new ArrayList<>(); + TopicDetail firstTopic = generateTopicDetail(FIRST_TOPIC_TO_TEST, 2, 1); + topicListToCreate.add(firstTopic); - // Verify the results - assertEquals(expectedResult, result); + kafkaAdminServiceUnderTest.createTopic(topicListToCreate); } - @Test - public void testCheckReassignStatusByStr() { - // Setup - final String reassignStr = "reassignStr"; - final Map expectedResult = new HashMap<>(); - - // Run the test - final Map result = - kafkaAdminServiceUnderTest.checkReassignStatusByStr(reassignStr); + private void createTwoTopics() { + List topicListToCreate = new ArrayList<>(); + TopicDetail firstTopic = generateTopicDetail(FIRST_TOPIC_TO_TEST, 2, 1); + TopicDetail secondTopic = generateTopicDetail(SECOND_TOPIC_TO_TEST, 2, 1); + topicListToCreate.add(firstTopic); + topicListToCreate.add(secondTopic); - // Verify the results - assertEquals(expectedResult, result); + kafkaAdminServiceUnderTest.createTopic(topicListToCreate); } @Test - public void testGetMessage() { + public void testListTopicsByConsumerGroup() { // Setup - final String topic = "topic"; - final int partition = 0; - final long offset = 0L; - final String decoder = "decoder"; - final String avroSchema = "avroSchema"; - final String expectedResult = "result"; + final String consumerGroup = FIRST_CONSUMER_GROUP_TO_TEST; + final ConsumerType type = ConsumerType.NEW; - // Run the test - final String result = - kafkaAdminServiceUnderTest.getMessage(topic, partition, offset, decoder, avroSchema); + // Create two topics + createTwoTopics(); - // Verify the results - assertEquals(expectedResult, result); - } - - @Test - public void testGetRecordByOffset() { - // Setup - final String topic = "topic"; - final int partition = 0; - final long offset = 0L; - final String decoder = "decoder"; - final String avroSchema = "avroSchema"; - final Record expectedResult = null; + List subscribedTopicList = Arrays.asList(FIRST_TOPIC_TO_TEST, SECOND_TOPIC_TO_TEST); + KafkaConsumer kafkaConsumer = consumer(consumerGroup, subscribedTopicList); // Run the test - final Record result = - kafkaAdminServiceUnderTest.getRecordByOffset(topic, partition, offset, decoder, avroSchema); + final Set topicsByConsumerGroup = + kafkaAdminServiceUnderTest.listTopicsByConsumerGroup(consumerGroup, type); + kafkaConsumer.close(); // Verify the results - assertEquals(expectedResult, result); + assertEquals(2, topicsByConsumerGroup.size()); + assertTrue(topicsByConsumerGroup.contains(FIRST_TOPIC_TO_TEST)); + assertTrue(topicsByConsumerGroup.contains(SECOND_TOPIC_TO_TEST)); } @Test - public void testResetOffset() { + public void testGetConsumerGroupMeta() throws InterruptedException { // Setup - final String topic = "topic"; - final int partition = 0; - final String consumerGroup = "consumerGroup"; - final ConsumerType type = null; - final String offset = "offset"; - final GeneralResponse expectedResult = null; + final String consumerGroup = FIRST_CONSUMER_GROUP_TO_TEST; + createTwoTopics(); + + final int numConsumers = 2; + ConsumerGroup group = + new ConsumerGroup( + consumerGroup, + numConsumers, + Arrays.asList(FIRST_CONSUMER_CLIENT_TO_TEST, SECOND_CONSUMER_CLIENT_TO_TEST), + Arrays.asList(FIRST_TOPIC_TO_TEST, SECOND_TOPIC_TO_TEST)); + group.execute(); + Thread.sleep(1000); // Run the test - final GeneralResponse result = - kafkaAdminServiceUnderTest.resetOffset(topic, partition, consumerGroup, type, offset); + final ConsumerGroupMeta groupMeta = + kafkaAdminServiceUnderTest.getConsumerGroupMeta(consumerGroup); // Verify the results - assertEquals(expectedResult, result); + assertEquals(consumerGroup, groupMeta.getGroupId()); + List members = groupMeta.getMembers(); + assertEquals(numConsumers, members.size()); + String memberClientId1 = members.get(0).getClientId(); + String memberClientId2 = members.get(1).getClientId(); + assertTrue( + memberClientId1.equals(FIRST_CONSUMER_CLIENT_TO_TEST) + || memberClientId1.equals(SECOND_CONSUMER_CLIENT_TO_TEST)); + assertTrue( + memberClientId2.equals(FIRST_CONSUMER_CLIENT_TO_TEST) + || memberClientId2.equals(SECOND_CONSUMER_CLIENT_TO_TEST)); + + group.close(); } @Test - public void testGetLastCommitTime() { + public void testDescribeNewConsumerGroupWithoutFilter() throws InterruptedException { // Setup - final String consumerGroup = "consumerGroup"; - final String topic = "topic"; - final ConsumerType type = null; - final Map> expectedResult = new HashMap<>(); - - // Run the test - final Map> result = - kafkaAdminServiceUnderTest.getLastCommitTime(consumerGroup, topic, type); + final String consumerGroup = FIRST_CONSUMER_GROUP_TO_TEST; + final boolean filtered = false; - // Verify the results - assertEquals(expectedResult, result); - } + //Create first topic with 2 partitions and 1 replica + createOneTopic(); - @Test - public void testDeleteConsumerGroup() { - // Setup - final String consumerGroup = "consumerGroup"; - final ConsumerType type = null; - final GeneralResponse expectedResult = null; + //Create a group with only one consumer + ConsumerGroup group = + new ConsumerGroup( + consumerGroup, + 1, + Arrays.asList(FIRST_CONSUMER_CLIENT_TO_TEST), + Arrays.asList(FIRST_TOPIC_TO_TEST)); + group.execute(); + Thread.sleep(1000); // Run the test - final GeneralResponse result = - kafkaAdminServiceUnderTest.deleteConsumerGroup(consumerGroup, type); + final List partitionAssignments = + kafkaAdminServiceUnderTest.describeNewConsumerGroup(consumerGroup, filtered, null); // Verify the results - assertEquals(expectedResult, result); + //Assign the consumer with 2 paritions, since there is only one consumer in the group + assertEquals(2, partitionAssignments.size()); + PartitionAssignmentState assignment1 = partitionAssignments.get(0); + PartitionAssignmentState assignment2 = partitionAssignments.get(1); + assertEquals(consumerGroup, assignment1.getGroup()); + assertEquals(FIRST_TOPIC_TO_TEST, assignment1.getTopic()); + assertEquals(FIRST_CONSUMER_CLIENT_TO_TEST, assignment1.getClientId()); + assertTrue(assignment1.getPartition() == 0 || assignment1.getPartition() == 1); + assertEquals(consumerGroup, assignment2.getGroup()); + assertEquals(FIRST_TOPIC_TO_TEST, assignment2.getTopic()); + assertEquals(FIRST_CONSUMER_CLIENT_TO_TEST, assignment2.getClientId()); + assertTrue(assignment2.getPartition() == 0 || assignment2.getPartition() == 1); + + group.close(); } @Test - public void testGetBeginningOffset() { + public void testDescribeNewConsumerGroupWithFilterTopic() throws InterruptedException { // Setup - final String topic = "topic"; - final int partitionId = 0; - final long expectedResult = 0L; + final String consumerGroup = FIRST_CONSUMER_GROUP_TO_TEST; + final boolean filtered = true; + final String topic = SECOND_TOPIC_TO_TEST; - // Run the test - final long result = kafkaAdminServiceUnderTest.getBeginningOffset(topic, partitionId); + //Create first and second topic + createTwoTopics(); - // Verify the results - assertEquals(expectedResult, result); - } + //Create a group with only one consumer + ConsumerGroup group = + new ConsumerGroup( + consumerGroup, + 1, + Arrays.asList(FIRST_CONSUMER_CLIENT_TO_TEST, SECOND_CONSUMER_CLIENT_TO_TEST), + Arrays.asList(FIRST_TOPIC_TO_TEST, SECOND_TOPIC_TO_TEST)); + group.execute(); - @Test - public void testGetEndOffset() { - // Setup - final String topic = "topic"; - final int partitionId = 0; - final long expectedResult = 0L; + Thread.sleep(1000); // Run the test - final long result = kafkaAdminServiceUnderTest.getEndOffset(topic, partitionId); + final List partitionAssignmentsFilteredByTopic = + kafkaAdminServiceUnderTest.describeNewConsumerGroup(consumerGroup, filtered, topic); + System.out.println("/////result: " + partitionAssignmentsFilteredByTopic); // Verify the results - assertEquals(expectedResult, result); + + assertEquals(2, partitionAssignmentsFilteredByTopic.size()); + PartitionAssignmentState assignment1 = partitionAssignmentsFilteredByTopic.get(0); + PartitionAssignmentState assignment2 = partitionAssignmentsFilteredByTopic.get(1); + assertEquals(consumerGroup, assignment1.getGroup()); + assertEquals(topic, assignment1.getTopic()); + assertEquals(FIRST_CONSUMER_CLIENT_TO_TEST, assignment1.getClientId()); + assertTrue(assignment1.getPartition() == 0 || assignment1.getPartition() == 1); + assertEquals(consumerGroup, assignment2.getGroup()); + assertEquals(topic, assignment2.getTopic()); + assertEquals(FIRST_CONSUMER_CLIENT_TO_TEST, assignment2.getClientId()); + assertTrue(assignment2.getPartition() == 0 || assignment2.getPartition() == 1); + + group.close(); } + /* @Test - public void testCountPartition() { + public void testDescribeLogDirsByBrokerAndTopic() { // Setup - final String topic = "topic"; - final Map expectedResult = new HashMap<>(); + final List brokerList = Arrays.asList(); + final List topicList = Arrays.asList(); + final Map> expectedResult = new HashMap<>(); // Run the test - final Map result = kafkaAdminServiceUnderTest.countPartition(topic); + final Map> result = + kafkaAdminServiceUnderTest.describeLogDirsByBrokerAndTopic(brokerList, topicList); // Verify the results assertEquals(expectedResult, result); } - @Test - public void testHealthCheck() { - // Setup - final HealthCheckResult expectedResult = null; - - // Run the test - final HealthCheckResult result = kafkaAdminServiceUnderTest.healthCheck(); - // Verify the results - assertEquals(expectedResult, result); - } - */ + @Test + public void testDescribeReplicaLogDirs() { + // Setup + final List replicas = Arrays.asList(); + final Map expectedResult = new HashMap<>(); + + // Run the test + final Map result = + kafkaAdminServiceUnderTest.describeReplicaLogDirs(replicas); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testGetConfigInZk() { + // Setup + final Type type = null; + final String name = "name"; + final Properties expectedResult = new Properties(); + + // Run the test + final Properties result = kafkaAdminServiceUnderTest.getConfigInZk(type, name); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testUpdateBrokerDynConf() { + // Setup + final int brokerId = 0; + final Properties propsToBeUpdated = new Properties(); + final Properties expectedResult = new Properties(); + + // Run the test + final Properties result = + kafkaAdminServiceUnderTest.updateBrokerDynConf(brokerId, propsToBeUpdated); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testRemoveConfigInZk() { + // Setup + final Type type = null; + final String name = "name"; + final List configKeysToBeRemoved = Arrays.asList(); + + // Run the test + kafkaAdminServiceUnderTest.removeConfigInZk(type, name, configKeysToBeRemoved); + + // Verify the results + } + + @Test + public void testDescribeConfig() { + // Setup + final Type type = null; + final String name = "name"; + final Collection expectedResult = Arrays.asList(); + + // Run the test + final Collection result = kafkaAdminServiceUnderTest.describeConfig(type, name); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testAlterConfig() { + // Setup + final Type type = null; + final String name = "name"; + final Collection configEntries = Arrays.asList(); + final boolean expectedResult = false; + + // Run the test + final boolean result = kafkaAdminServiceUnderTest.alterConfig(type, name, configEntries); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testUpdateTopicConf() { + // Setup + final String topic = "topic"; + final Properties props = new Properties(); + final Collection expectedResult = Arrays.asList(); + + // Run the test + final Collection result = + kafkaAdminServiceUnderTest.updateTopicConf(topic, props); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testGetTopicConf() { + // Setup + final String topic = "topic"; + final Collection expectedResult = Arrays.asList(); + + // Run the test + final Collection result = kafkaAdminServiceUnderTest.getTopicConf(topic); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testGetTopicConfByKey() { + // Setup + final String topic = "topic"; + final String key = "key"; + final Properties expectedResult = new Properties(); + + // Run the test + final Properties result = kafkaAdminServiceUnderTest.getTopicConfByKey(topic, key); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testUpdateTopicConfByKey() { + // Setup + final String topic = "topic"; + final String key = "key"; + final String value = "value"; + final Collection expectedResult = Arrays.asList(); + + // Run the test + final Collection result = + kafkaAdminServiceUnderTest.updateTopicConfByKey(topic, key, value); + + // Verify the results + assertEquals(expectedResult, result); + } + + + + @Test + public void testIsOldConsumerGroup() { + // Setup + final String consumerGroup = "consumerGroup"; + final boolean expectedResult = false; + + // Run the test + final boolean result = kafkaAdminServiceUnderTest.isOldConsumerGroup(consumerGroup); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testIsNewConsumerGroup() { + // Setup + final String consumerGroup = "consumerGroup"; + final boolean expectedResult = false; + + // Run the test + final boolean result = kafkaAdminServiceUnderTest.isNewConsumerGroup(consumerGroup); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testDescribeConsumerGroup() { + // Setup + final String consumerGroup = "consumerGroup"; + final ConsumerType type = null; + final Map> expectedResult = new HashMap<>(); + + // Run the test + final Map> result = + kafkaAdminServiceUnderTest.describeConsumerGroup(consumerGroup, type); + + // Verify the results + assertEquals(expectedResult, result); + } + + + + @Test + public void testDescribeOldConsumerGroup() { + // Setup + final String consumerGroup = "consumerGroup"; + final boolean filtered = false; + final String topic = "topic"; + final List expectedResult = Arrays.asList(); + + // Run the test + final List result = + kafkaAdminServiceUnderTest.describeOldConsumerGroup(consumerGroup, filtered, topic); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testDescribeNewConsumerGroupByTopic() { + // Setup + final String consumerGroup = "consumerGroup"; + final String topic = "topic"; + final List expectedResult = Arrays.asList(); + + // Run the test + final List result = + kafkaAdminServiceUnderTest.describeNewConsumerGroupByTopic(consumerGroup, topic); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testDescribeOldConsumerGroupByTopic() { + // Setup + final String consumerGroup = "consumerGroup"; + final String topic = "topic"; + final List expectedResult = Arrays.asList(); + + // Run the test + final List result = + kafkaAdminServiceUnderTest.describeOldConsumerGroupByTopic(consumerGroup, topic); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testAddPartitions() { + // Setup + final List addPartitions = Arrays.asList(); + final Map expectedResult = new HashMap<>(); + + // Run the test + final Map result = + kafkaAdminServiceUnderTest.addPartitions(addPartitions); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testGenerateReassignPartition() { + // Setup + final ReassignWrapper reassignWrapper = null; + final List expectedResult = Arrays.asList(); + + // Run the test + final List result = + kafkaAdminServiceUnderTest.generateReassignPartition(reassignWrapper); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testExecuteReassignPartition() { + // Setup + final String reassignStr = "reassignStr"; + final Long interBrokerThrottle = 0L; + final Long replicaAlterLogDirsThrottle = 0L; + final Long timeoutMs = 0L; + final Map expectedResult = new HashMap<>(); + + // Run the test + final Map result = + kafkaAdminServiceUnderTest.executeReassignPartition( + reassignStr, interBrokerThrottle, replicaAlterLogDirsThrottle, timeoutMs); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testCheckReassignStatusByStr() { + // Setup + final String reassignStr = "reassignStr"; + final Map expectedResult = new HashMap<>(); + + // Run the test + final Map result = + kafkaAdminServiceUnderTest.checkReassignStatusByStr(reassignStr); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testGetMessage() { + // Setup + final String topic = "topic"; + final int partition = 0; + final long offset = 0L; + final String decoder = "decoder"; + final String avroSchema = "avroSchema"; + final String expectedResult = "result"; + + // Run the test + final String result = + kafkaAdminServiceUnderTest.getMessage(topic, partition, offset, decoder, avroSchema); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testGetRecordByOffset() { + // Setup + final String topic = "topic"; + final int partition = 0; + final long offset = 0L; + final String decoder = "decoder"; + final String avroSchema = "avroSchema"; + final Record expectedResult = null; + + // Run the test + final Record result = + kafkaAdminServiceUnderTest.getRecordByOffset(topic, partition, offset, decoder, avroSchema); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testResetOffset() { + // Setup + final String topic = "topic"; + final int partition = 0; + final String consumerGroup = "consumerGroup"; + final ConsumerType type = null; + final String offset = "offset"; + final GeneralResponse expectedResult = null; + + // Run the test + final GeneralResponse result = + kafkaAdminServiceUnderTest.resetOffset(topic, partition, consumerGroup, type, offset); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testGetLastCommitTime() { + // Setup + final String consumerGroup = "consumerGroup"; + final String topic = "topic"; + final ConsumerType type = null; + final Map> expectedResult = new HashMap<>(); + + // Run the test + final Map> result = + kafkaAdminServiceUnderTest.getLastCommitTime(consumerGroup, topic, type); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testDeleteConsumerGroup() { + // Setup + final String consumerGroup = "consumerGroup"; + final ConsumerType type = null; + final GeneralResponse expectedResult = null; + + // Run the test + final GeneralResponse result = + kafkaAdminServiceUnderTest.deleteConsumerGroup(consumerGroup, type); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testGetBeginningOffset() { + // Setup + final String topic = "topic"; + final int partitionId = 0; + final long expectedResult = 0L; + + // Run the test + final long result = kafkaAdminServiceUnderTest.getBeginningOffset(topic, partitionId); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testGetEndOffset() { + // Setup + final String topic = "topic"; + final int partitionId = 0; + final long expectedResult = 0L; + + // Run the test + final long result = kafkaAdminServiceUnderTest.getEndOffset(topic, partitionId); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testCountPartition() { + // Setup + final String topic = "topic"; + final Map expectedResult = new HashMap<>(); + + // Run the test + final Map result = kafkaAdminServiceUnderTest.countPartition(topic); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testHealthCheck() { + // Setup + final HealthCheckResult expectedResult = null; + + // Run the test + final HealthCheckResult result = kafkaAdminServiceUnderTest.healthCheck(); + + // Verify the results + assertEquals(expectedResult, result); + } + */ } From db11e67f3b784d5fd9320775abd8038fa878bc6d Mon Sep 17 00:00:00 2001 From: tinawenqiao <315524513@qq.com> Date: Sat, 26 Jan 2019 13:57:23 +0800 Subject: [PATCH 11/24] Add tests and fix some bugs. --- pom.xml | 7 +- ...mentStatus.java => ReassignmentState.java} | 10 +- .../bigdata/controller/KafkaController.java | 20 +- .../gnuhpc/bigdata/model/AddPartition.java | 6 +- .../bigdata/model/ConsumerGroupDesc.java | 9 +- .../bigdata/model/MemberDescription.java | 7 +- .../model/PartitionAssignmentState.java | 15 +- .../gnuhpc/bigdata/model/ReassignModel.java | 43 + .../gnuhpc/bigdata/model/ReassignStatus.java | 18 + .../gnuhpc/bigdata/model/ReassignWrapper.java | 2 - .../java/org/gnuhpc/bigdata/model/Record.java | 98 +- .../TopicPartitionReplicaAssignment.java | 27 + .../bigdata/service/KafkaAdminService.java | 558 +++-- .../org/gnuhpc/bigdata/utils/KafkaUtils.java | 51 + .../service/KafkaAdminServiceTest.java | 2151 +++++++++++++---- 15 files changed, 2349 insertions(+), 673 deletions(-) rename src/main/java/org/gnuhpc/bigdata/constant/{ReassignmentStatus.java => ReassignmentState.java} (68%) create mode 100644 src/main/java/org/gnuhpc/bigdata/model/ReassignModel.java create mode 100644 src/main/java/org/gnuhpc/bigdata/model/ReassignStatus.java create mode 100644 src/main/java/org/gnuhpc/bigdata/model/TopicPartitionReplicaAssignment.java diff --git a/pom.xml b/pom.xml index 0ec16eb..04e9bd8 100644 --- a/pom.xml +++ b/pom.xml @@ -262,11 +262,16 @@ 2.11.0 + org.apache.avro + avro + 1.8.2 + + diff --git a/src/main/java/org/gnuhpc/bigdata/constant/ReassignmentStatus.java b/src/main/java/org/gnuhpc/bigdata/constant/ReassignmentState.java similarity index 68% rename from src/main/java/org/gnuhpc/bigdata/constant/ReassignmentStatus.java rename to src/main/java/org/gnuhpc/bigdata/constant/ReassignmentState.java index 3462949..6f84d2a 100644 --- a/src/main/java/org/gnuhpc/bigdata/constant/ReassignmentStatus.java +++ b/src/main/java/org/gnuhpc/bigdata/constant/ReassignmentState.java @@ -1,6 +1,6 @@ package org.gnuhpc.bigdata.constant; -public enum ReassignmentStatus { +public enum ReassignmentState { ReassignmentFailed(-1, "Reassignment Failed"), ReassignmentInProgress(0, "Reassignment In Progress"), ReassignmentCompleted(1, "Reassignment Completed"); @@ -8,15 +8,15 @@ public enum ReassignmentStatus { private int status; private String msg; - private ReassignmentStatus(int status, String msg) { + private ReassignmentState(int status, String msg) { this.status = status; this.msg = msg; } - public static ReassignmentStatus valueOf(int status) { - ReassignmentStatus[] reassignmentStatusList = values(); + public static ReassignmentState valueOf(int status) { + ReassignmentState[] reassignmentStatusList = values(); for (int i = 0; i < reassignmentStatusList.length; i++) { - ReassignmentStatus reassignmentStatus = reassignmentStatusList[i]; + ReassignmentState reassignmentStatus = reassignmentStatusList[i]; if (reassignmentStatus.status == status) { return reassignmentStatus; } diff --git a/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java b/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java index 49bc9a9..336fec4 100644 --- a/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java +++ b/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java @@ -27,6 +27,8 @@ import org.gnuhpc.bigdata.model.CustomConfigEntry; import org.gnuhpc.bigdata.model.GeneralResponse; import org.gnuhpc.bigdata.model.HealthCheckResult; +import org.gnuhpc.bigdata.model.ReassignModel; +import org.gnuhpc.bigdata.model.ReassignStatus; import org.gnuhpc.bigdata.model.ReassignWrapper; import org.gnuhpc.bigdata.model.TopicBrief; import org.gnuhpc.bigdata.model.TopicDetail; @@ -171,7 +173,7 @@ public String getMessage( @PathVariable int partition, @PathVariable long offset, @RequestParam(required = false) String decoder) { - return kafkaAdminService.getRecordByOffset(topic, partition, offset, decoder, "").getValue(); + return kafkaAdminService.getRecordByOffset(topic, partition, offset, decoder, "").getValue().toString(); } @GetMapping(value = "/topics/{topic}") @@ -231,19 +233,19 @@ public Map addPartition(@RequestBody List @PostMapping(value = "/partitions/reassign/generate") @ApiOperation(value = "Generate plan for the partition reassignment") - public List generateReassignPartitions(@RequestBody ReassignWrapper reassignWrapper) { + public List generateReassignPartitions(@RequestBody ReassignWrapper reassignWrapper) { return kafkaAdminService.generateReassignPartition(reassignWrapper); } @PutMapping(value = "/partitions/reassign/execute") @ApiOperation(value = "Execute the partition reassignment") - public Map executeReassignPartitions( - @RequestBody String reassignStr, + public ReassignStatus executeReassignPartitions( + @RequestBody ReassignModel reassign, long interBrokerThrottle, long replicaAlterLogDirsThrottle, long timeoutMs) { return kafkaAdminService.executeReassignPartition( - reassignStr, interBrokerThrottle, replicaAlterLogDirsThrottle, timeoutMs); + reassign, interBrokerThrottle, replicaAlterLogDirsThrottle, timeoutMs); } @PutMapping(value = "/partitions/reassign/check") @@ -254,8 +256,8 @@ public Map executeReassignPartitions( @ApiResponse(code = 0, message = "Reassignment In Progress"), @ApiResponse(code = -1, message = "Reassignment Failed") }) - public Map checkReassignPartitions(@RequestBody String reassignStr) { - return kafkaAdminService.checkReassignStatusByStr(reassignStr); + public ReassignStatus checkReassignPartitions(@RequestBody ReassignModel reassign) { + return kafkaAdminService.checkReassignStatus(reassign); } @GetMapping(value = "/consumergroups") @@ -327,7 +329,7 @@ public Map> describeConsumerGroup( @ApiOperation( value = "Reset consumer group offset, earliest/latest can be used. Support reset by time for " - + "new consumer group, pass a parameter that satisfies yyyy-MM-dd HH:mm:ss " + + "new consumer group, pass a parameter that satisfies yyyy-MM-dd HH:mm:ss.SSS " + "to offset.") public GeneralResponse resetOffset( @PathVariable String topic, @@ -336,7 +338,7 @@ public GeneralResponse resetOffset( @PathVariable @ApiParam( value = - "[earliest/latest/{long}/yyyy-MM-dd HH:mm:ss] can be supported. " + "[earliest/latest/{long}/yyyy-MM-dd HH:mm:ss.SSS] can be supported. " + "The date type is only valid for new consumer group.") String offset, @PathVariable ConsumerType type) { diff --git a/src/main/java/org/gnuhpc/bigdata/model/AddPartition.java b/src/main/java/org/gnuhpc/bigdata/model/AddPartition.java index a46960b..36b0af5 100644 --- a/src/main/java/org/gnuhpc/bigdata/model/AddPartition.java +++ b/src/main/java/org/gnuhpc/bigdata/model/AddPartition.java @@ -1,5 +1,7 @@ package org.gnuhpc.bigdata.model; +import java.util.List; +import lombok.Builder; import lombok.Getter; import lombok.Setter; import lombok.ToString; @@ -10,9 +12,11 @@ @Setter @Log4j @ToString +@Builder public class AddPartition { String topic; int numPartitionsAdded; - String replicaAssignment; +// String replicaAssignment; + List> replicaAssignment; } diff --git a/src/main/java/org/gnuhpc/bigdata/model/ConsumerGroupDesc.java b/src/main/java/org/gnuhpc/bigdata/model/ConsumerGroupDesc.java index 0b4158f..19c5bf2 100644 --- a/src/main/java/org/gnuhpc/bigdata/model/ConsumerGroupDesc.java +++ b/src/main/java/org/gnuhpc/bigdata/model/ConsumerGroupDesc.java @@ -33,19 +33,14 @@ public class ConsumerGroupDesc implements Comparable { private long lag; private String consumerId; + private String clientId; private String host = "-"; private ConsumerType type; @Override public int compareTo(ConsumerGroupDesc o) { if (this.topic.equals(o.topic)) { - if (this.partitionId == o.partitionId) { - return 0; - } else if (this.partitionId < o.partitionId) { - return -1; - } else { - return 1; - } + return (this.partitionId - o.partitionId); } else { return this.topic.compareTo(o.topic); } diff --git a/src/main/java/org/gnuhpc/bigdata/model/MemberDescription.java b/src/main/java/org/gnuhpc/bigdata/model/MemberDescription.java index eff70ad..4408f2c 100644 --- a/src/main/java/org/gnuhpc/bigdata/model/MemberDescription.java +++ b/src/main/java/org/gnuhpc/bigdata/model/MemberDescription.java @@ -9,10 +9,15 @@ @AllArgsConstructor @Getter @Setter -public class MemberDescription { +public class MemberDescription implements Comparable { private String memberId; private String clientId; private String host; private List assignment; + + @Override + public int compareTo(MemberDescription that) { + return this.getClientId().compareTo(that.clientId); + } } diff --git a/src/main/java/org/gnuhpc/bigdata/model/PartitionAssignmentState.java b/src/main/java/org/gnuhpc/bigdata/model/PartitionAssignmentState.java index 2fd5a2d..5bb90cc 100644 --- a/src/main/java/org/gnuhpc/bigdata/model/PartitionAssignmentState.java +++ b/src/main/java/org/gnuhpc/bigdata/model/PartitionAssignmentState.java @@ -8,7 +8,7 @@ @Data @Builder @AllArgsConstructor -public class PartitionAssignmentState { +public class PartitionAssignmentState implements Comparable{ private String group; private Node coordinator; @@ -20,4 +20,17 @@ public class PartitionAssignmentState { private String host; private String clientId; private long logEndOffset; + + @Override + public int compareTo(PartitionAssignmentState that) { + if (this.getGroup().equals(that.getGroup())) { + if (this.getTopic().equals(that.getTopic())) { + return (this.partition - that.getPartition()); + } else { + return this.getTopic().compareTo(that.getTopic()); + } + } else { + return this.getGroup().compareTo(that.getGroup()); + } + } } diff --git a/src/main/java/org/gnuhpc/bigdata/model/ReassignModel.java b/src/main/java/org/gnuhpc/bigdata/model/ReassignModel.java new file mode 100644 index 0000000..8de3669 --- /dev/null +++ b/src/main/java/org/gnuhpc/bigdata/model/ReassignModel.java @@ -0,0 +1,43 @@ +package org.gnuhpc.bigdata.model; + +import java.util.List; +import lombok.Builder; +import lombok.Data; +import lombok.Getter; +import lombok.Setter; + +/* +{ + "version": 1, + "partitions": [ + { + "topic": "first", + "partition": 1, + "replicas": [ + 115 + ], + "log_dirs": [ + "any" + ] + }, + { + "topic": "first", + "partition": 0, + "replicas": [ + 113 + ], + "log_dirs": [ + "any" + ] + } + ] +} + */ +@Getter +@Setter +@Data +@Builder +public class ReassignModel { + public final int version = 1; + public List partitions; +} diff --git a/src/main/java/org/gnuhpc/bigdata/model/ReassignStatus.java b/src/main/java/org/gnuhpc/bigdata/model/ReassignStatus.java new file mode 100644 index 0000000..85225f4 --- /dev/null +++ b/src/main/java/org/gnuhpc/bigdata/model/ReassignStatus.java @@ -0,0 +1,18 @@ +package org.gnuhpc.bigdata.model; + +import java.util.Map; +import lombok.Data; +import lombok.Getter; +import lombok.Setter; +import org.apache.kafka.common.TopicPartition; +import org.apache.kafka.common.TopicPartitionReplica; + +@Getter +@Setter +@Data +public class ReassignStatus { + Map partitionsReassignStatus; + Map replicasReassignStatus; + boolean removeThrottle; + String msg; +} diff --git a/src/main/java/org/gnuhpc/bigdata/model/ReassignWrapper.java b/src/main/java/org/gnuhpc/bigdata/model/ReassignWrapper.java index 9296815..230a9e2 100644 --- a/src/main/java/org/gnuhpc/bigdata/model/ReassignWrapper.java +++ b/src/main/java/org/gnuhpc/bigdata/model/ReassignWrapper.java @@ -38,7 +38,5 @@ public String generateReassignJsonString() { log.error("Serialize ReassignWrapper object to string error." + exeception); throw new ApiException("Serialize ReassignWrapper object to string error." + exeception); } - // Gson gson = new Gson(); - // return gson.toJson(reassignJsonWrapper); } } diff --git a/src/main/java/org/gnuhpc/bigdata/model/Record.java b/src/main/java/org/gnuhpc/bigdata/model/Record.java index 352ecfe..3a411e1 100644 --- a/src/main/java/org/gnuhpc/bigdata/model/Record.java +++ b/src/main/java/org/gnuhpc/bigdata/model/Record.java @@ -1,12 +1,102 @@ package org.gnuhpc.bigdata.model; +import java.nio.ByteBuffer; +import lombok.Builder; import lombok.Data; +import lombok.Getter; +import lombok.Setter; +import org.apache.kafka.common.errors.ApiException; +import org.apache.kafka.common.utils.Bytes; @Data +@Getter +@Setter +@Builder public class Record { + public String topic; + public long offset; + public Object key = new Object(); + public Object value = new Object(); + public long timestamp; + public Class type; - long offset; - String key; - String value; - long timestamp; + public String getValue() { + try { + if (String.class.isAssignableFrom(type)) { + return value.toString(); + } + + if (Short.class.isAssignableFrom(type)) { + return value.toString(); + } + + if (Integer.class.isAssignableFrom(type)) { + return value.toString(); + } + + if (Long.class.isAssignableFrom(type)) { + return value.toString(); + } + + if (Float.class.isAssignableFrom(type)) { + return value.toString(); + } + + if (Double.class.isAssignableFrom(type)) { + return value.toString(); + } + + if (Bytes.class.isAssignableFrom(type)) { + Bytes bytes = (Bytes) value; + return bytes.toString(); + } + + if (byte[].class.isAssignableFrom(type)) { + byte[] byteArray = (byte[]) value; + return new String(byteArray); + } + + if (ByteBuffer.class.isAssignableFrom(type)) { + ByteBuffer byteBuffer = (ByteBuffer) value; + return new String(byteBuffer.array()); + } + } catch (Exception exception) { + throw new ApiException("Record Cast exception." + exception); + } + + throw new ApiException( + "Unknown class. Supported types are: " + + "String, Short, Integer, Long, Float, Double, ByteArray, ByteBuffer, Bytes"); + } + + @Override + public String toString() { + if (value != null) { + return "topic:" + + topic + + ", offset:" + + offset + + ", key:" + + key + + ", value:" + + getValue() + + ", timestamp:" + + timestamp + + ", type:" + + type; + } else { + return "topic:" + + topic + + ", offset:" + + offset + + ", key:" + + key + + ", value:" + + value + + ", timestamp:" + + timestamp + + ", type:" + + type; + } + } } diff --git a/src/main/java/org/gnuhpc/bigdata/model/TopicPartitionReplicaAssignment.java b/src/main/java/org/gnuhpc/bigdata/model/TopicPartitionReplicaAssignment.java new file mode 100644 index 0000000..4d45890 --- /dev/null +++ b/src/main/java/org/gnuhpc/bigdata/model/TopicPartitionReplicaAssignment.java @@ -0,0 +1,27 @@ +package org.gnuhpc.bigdata.model; + +import java.util.List; +import lombok.Builder; +import lombok.Data; +import lombok.Getter; +import lombok.Setter; + +@Builder +@Getter +@Setter +@Data +public class TopicPartitionReplicaAssignment implements Comparable{ + String topic; + int partition; + List replicas; + List log_dirs; + + @Override + public int compareTo(TopicPartitionReplicaAssignment that) { + if (this.topic.equals(that.topic)) { + return (this.partition - that.partition); + } else { + return this.topic.compareTo(that.topic); + } + } +} diff --git a/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java b/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java index a4b222f..4b77ecc 100644 --- a/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java +++ b/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java @@ -12,6 +12,7 @@ import com.fasterxml.jackson.module.scala.DefaultScalaModule; import com.google.common.collect.ImmutableMap; import java.io.IOException; +import java.nio.ByteBuffer; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -54,6 +55,11 @@ import kafka.zk.AdminZkClient; import kafka.zk.KafkaZkClient; import lombok.extern.log4j.Log4j; +import org.apache.avro.Schema; +import org.apache.avro.generic.GenericDatumReader; +import org.apache.avro.generic.GenericRecord; +import org.apache.avro.io.DatumReader; +import org.apache.avro.io.DecoderFactory; import org.apache.curator.framework.CuratorFramework; import org.apache.kafka.clients.admin.AdminClientConfig; import org.apache.kafka.clients.admin.AlterConfigsResult; @@ -88,7 +94,6 @@ import org.apache.kafka.clients.producer.RecordMetadata; import org.apache.kafka.common.KafkaFuture; import org.apache.kafka.common.Node; -import org.apache.kafka.common.PartitionInfo; import org.apache.kafka.common.TopicPartition; import org.apache.kafka.common.TopicPartitionInfo; import org.apache.kafka.common.TopicPartitionReplica; @@ -99,6 +104,7 @@ import org.apache.kafka.common.internals.Topic; import org.apache.kafka.common.protocol.Errors; import org.apache.kafka.common.requests.DescribeLogDirsResponse.LogDirInfo; +import org.apache.kafka.common.serialization.Serdes; import org.apache.kafka.common.serialization.StringDeserializer; import org.gnuhpc.bigdata.CollectionConvertor; import org.gnuhpc.bigdata.componet.OffsetStorage; @@ -106,7 +112,7 @@ import org.gnuhpc.bigdata.constant.ConsumerGroupState; import org.gnuhpc.bigdata.constant.ConsumerType; import org.gnuhpc.bigdata.constant.GeneralResponseState; -import org.gnuhpc.bigdata.constant.ReassignmentStatus; +import org.gnuhpc.bigdata.constant.ReassignmentState; import org.gnuhpc.bigdata.model.AddPartition; import org.gnuhpc.bigdata.model.BrokerInfo; import org.gnuhpc.bigdata.model.ClusterInfo; @@ -118,6 +124,8 @@ import org.gnuhpc.bigdata.model.HealthCheckResult; import org.gnuhpc.bigdata.model.MemberDescription; import org.gnuhpc.bigdata.model.PartitionAssignmentState; +import org.gnuhpc.bigdata.model.ReassignModel; +import org.gnuhpc.bigdata.model.ReassignStatus; import org.gnuhpc.bigdata.model.ReassignWrapper; import org.gnuhpc.bigdata.model.Record; import org.gnuhpc.bigdata.model.TopicBrief; @@ -151,6 +159,12 @@ public class KafkaAdminService { private static final long kafkaAdminClientAlterTimeoutMs = 60000; private static final String CONSUMERPATHPREFIX = "/consumers/"; private static final String OFFSETSPATHPREFIX = "/offsets/"; + public static final String LeaderReplicationThrottledRateProp = + "leader.replication.throttled.rate"; + public static final String FollowerReplicationThrottledRateProp = + "follower.replication.throttled.rate"; + public static final String ReplicaAlterLogDirsIoMaxBytesPerSecondProp = + "replica.alter.log.dirs.io.max.bytes.per.second"; @Autowired private ZookeeperUtils zookeeperUtils; @@ -820,6 +834,7 @@ public Map> listAllConsumerGroups(ConsumerType type) { private Set listAllNewConsumerGroups() { AdminClient adminClient = kafkaUtils.createAdminClient(); log.info("Calling the listAllConsumerGroupsFlattened"); + // Send LIST_GROUPS Request to kafka Set activeGroups = CollectionConvertor.seqConvertJavaList(adminClient.listAllConsumerGroupsFlattened()) .stream() @@ -912,7 +927,7 @@ public Set listTopicsByConsumerGroup(String consumerGroup, ConsumerType if (type == ConsumerType.OLD) { if (!isOldConsumerGroup(consumerGroup)) { - throw new RuntimeException(consumerGroup + " non-exist"); + throw new ApiException("Consumer group:" + consumerGroup + " non-exist"); } String path = ZkUtils.ConsumersPath() + "/" + consumerGroup + "/offsets"; topicList = @@ -921,7 +936,7 @@ public Set listTopicsByConsumerGroup(String consumerGroup, ConsumerType .collect(toSet()); } else if (type == ConsumerType.NEW) { if (!isNewConsumerGroup(consumerGroup)) { - throw new RuntimeException(consumerGroup + " non-exist!"); + throw new ApiException("Consumer group:" + consumerGroup + " non-exist!"); } topicList.addAll(getTopicListByConsumerGroup(consumerGroup)); } else { @@ -971,6 +986,7 @@ public ConsumerGroupMeta getConsumerGroupMeta(String consumerGroup) { topicPartitions)); }); + Collections.sort(members); consumerGroupMeta.setMembers(members); adminClient.close(); @@ -996,24 +1012,25 @@ public Map> describeConsumerGroup( } if (type == ConsumerType.NEW) { if (!isNewConsumerGroup(consumerGroup)) { - throw new RuntimeException(consumerGroup + " non-exist!"); + throw new ApiException("Consumer group:" + consumerGroup + " non-exist!"); } List partitionAssignmentStateList = describeNewConsumerGroup(consumerGroup, false, null); - result = transPasListToMap(consumerGroup, partitionAssignmentStateList, ConsumerType.NEW); + result = convertPasListToMap(consumerGroup, partitionAssignmentStateList, ConsumerType.NEW); } else if (type == ConsumerType.OLD) { if (!isOldConsumerGroup(consumerGroup)) { - throw new RuntimeException(consumerGroup + " non-exist"); + throw new ApiException("Consumer group:" + consumerGroup + " non-exist"); } List partitionAssignmentStateList = describeOldConsumerGroup(consumerGroup, false, null); - result = transPasListToMap(consumerGroup, partitionAssignmentStateList, ConsumerType.OLD); + result = convertPasListToMap(consumerGroup, partitionAssignmentStateList, ConsumerType.OLD); } return result; } - private Map> transPasListToMap( + // Convert partition assignment to map, key is topic + private Map> convertPasListToMap( String consumerGroup, List pasList, ConsumerType type) { Map> result = new HashMap<>(); ConsumerGroupSummary consumerGroupSummary; @@ -1059,7 +1076,7 @@ private ConsumerGroupDesc convertParitionAssignmentStateToGroupDesc( .logEndOffset(pas.getLogEndOffset()) .lag(pas.getLag()) .consumerId(pas.getConsumerId()) - .consumerId(pas.getConsumerId()) + .clientId(pas.getClientId()) .host(pas.getHost()) .type(type); @@ -1171,6 +1188,7 @@ public List describeNewConsumerGroup( } } + Collections.sort(partitionAssignmentStateList); return partitionAssignmentStateList; } @@ -1204,6 +1222,8 @@ private List collectConsumerAssignment( clientId, logEndOffset)); }); + + Collections.sort(list); return list; } } @@ -1268,7 +1288,7 @@ private JavaType getCollectionType( public List describeNewConsumerGroupByTopic( String consumerGroup, String topic) { if (!isNewConsumerGroup(consumerGroup)) { - throw new RuntimeException(consumerGroup + " non-exist!"); + throw new ApiException("ConsumerGroup:" + consumerGroup + " non-exist!"); } List partitionAssignmentStateList = @@ -1293,7 +1313,7 @@ public List describeNewConsumerGroupByTopic( public List describeOldConsumerGroupByTopic( String consumerGroup, @TopicExistConstraint String topic) { if (!isOldConsumerGroup(consumerGroup)) { - throw new RuntimeException(consumerGroup + " non-exist"); + throw new ApiException("ConsumerGroup:" + consumerGroup + " non-exist"); } List partitionAssignmentStateList = @@ -1332,22 +1352,9 @@ public Map addPartitions(List addPartitio int currentPartionCount = topicMeta.getPartitionCount(); int numPartitionsAdded = addPartition.getNumPartitionsAdded(); int totalCount = currentPartionCount + numPartitionsAdded; - String assignmentStr = addPartition.getReplicaAssignment(); - List> newAssignments = new ArrayList<>(); - if (assignmentStr != null && !assignmentStr.equals("")) { - String[] ids = assignmentStr.split(","); - for (int i = 0; i < ids.length; i++) { - String[] replicas = ids[i].split(":"); - List replicasInt = new ArrayList<>(); - for (int j = 0; j < replicas.length; j++) { - replicasInt.add(Integer.parseInt(replicas[j])); - } - newAssignments.add(replicasInt); - } - } - System.out.println("++++++++newAssignments:" + newAssignments); + List> newAssignments = addPartition.getReplicaAssignment(); NewPartitions newPartitions; - if (newAssignments.isEmpty()) { + if (newAssignments == null || newAssignments.isEmpty()) { // The assignment of new replicas to brokers will be decided by the broker. newPartitions = NewPartitions.increaseTo(totalCount); } else { @@ -1389,31 +1396,47 @@ public Map addPartitions(List addPartitio } // Return - public List generateReassignPartition(ReassignWrapper reassignWrapper) { + public List generateReassignPartition(ReassignWrapper reassignWrapper) { KafkaZkClient kafkaZkClient = zookeeperUtils.createKafkaZkClient(); + List result = new ArrayList<>(); Seq brokerSeq = JavaConverters.asScalaBufferConverter(reassignWrapper.getBrokers()).asScala().toSeq(); // - Tuple2 resultTuple2 = - ReassignPartitionsCommand.generateAssignment( - kafkaZkClient, brokerSeq, reassignWrapper.generateReassignJsonString(), false); - List result = new ArrayList<>(); + Tuple2 resultTuple2; + try { + resultTuple2 = + ReassignPartitionsCommand.generateAssignment( + kafkaZkClient, brokerSeq, reassignWrapper.generateReassignJsonString(), false); + } catch (Exception exception) { + throw new ApiException("Generate reassign plan exception." + exception); + } HashMap emptyMap = new HashMap<>(); - result.add( - ReassignPartitionsCommand.formatAsReassignmentJson( - (scala.collection.Map>) resultTuple2._1(), - JavaConverters.mapAsScalaMapConverter(emptyMap).asScala())); - result.add( - ReassignPartitionsCommand.formatAsReassignmentJson( - (scala.collection.Map>) resultTuple2._2(), - JavaConverters.mapAsScalaMapConverter(emptyMap).asScala())); + ObjectMapper objectMapper = new ObjectMapper(); + try { + result.add( + objectMapper.readValue( + ReassignPartitionsCommand.formatAsReassignmentJson( + (scala.collection.Map>) resultTuple2._2(), + JavaConverters.mapAsScalaMapConverter(emptyMap).asScala()), + ReassignModel.class)); + result.add( + objectMapper.readValue( + ReassignPartitionsCommand.formatAsReassignmentJson( + (scala.collection.Map>) resultTuple2._1(), + JavaConverters.mapAsScalaMapConverter(emptyMap).asScala()), + ReassignModel.class)); + Collections.sort(result.get(0).getPartitions()); + Collections.sort(result.get(1).getPartitions()); + } catch (Exception exception) { + throw new ApiException("Generate reassign plan exception." + exception); + } return result; } - public Map executeReassignPartition( - String reassignStr, + public ReassignStatus executeReassignPartition( + ReassignModel reassignModel, Long interBrokerThrottle, Long replicaAlterLogDirsThrottle, Long timeoutMs) { @@ -1430,7 +1453,14 @@ public Map executeReassignPartition( TwoTuple< scala.collection.mutable.HashMap>, scala.collection.Map> - reassignPlan = genReassignPlan(reassignStr); + reassignPlan; + String reassignStr = ""; + try { + reassignStr = new ObjectMapper().writeValueAsString(reassignModel); + } catch (Exception exception) { + throw new ApiException("Json processing exception." + exception); + } + reassignPlan = genReassignPlan(reassignStr); scala.collection.mutable.HashMap> partitionsToBeReassignedMap = reassignPlan.getFirst(); scala.collection.Map replicatAssignment = @@ -1455,41 +1485,47 @@ public BoxedUnit apply() { new Throttle(interBrokerThrottle, replicaAlterLogDirsThrottle, postUpdateAction); if (kafkaZkClient.reassignPartitionsInProgress()) { + // check whether zk node /admin/reassign_partitions exists reassignPartitionsCommand.maybeLimit(throttle); throw new ApiException( "Failed to reassign partitions because there is an existing assignment running."); } else { - if (reassignPartitionsCommand.reassignPartitions(throttle, timeoutMs)) { - System.out.println("Successfully started reassignment of partitions."); - } else { - throw new ApiException("Failed to reassign partitions " + reassignPlan.getFirst()); + try { + reassignPartitionsCommand.reassignPartitions(throttle, timeoutMs); + log.info("Successfully started reassignment of partitions."); + } catch (Exception exception) { + throw new ApiException( + "Failed to reassign partitions:" + + reassignPlan.getFirst() + + ". Exception:" + + exception.getLocalizedMessage()); } } return checkReassignStatus(partitionsToBeReassignedMap, replicatAssignment); } - private Map checkReassignStatus( + private ReassignStatus checkReassignStatus( scala.collection.Map> partitionsToBeReassigned, scala.collection.Map replicaAssignement) { - Map reassignStatus = new HashMap<>(); + ReassignStatus reassignStatus = new ReassignStatus(); Map reassignedPartitionsStatus = checkIfPartitionReassignmentSucceeded(partitionsToBeReassigned); Map replicasReassignmentStatus = checkIfReplicaReassignmentSucceeded( CollectionConvertor.mapConvertJavaMap(replicaAssignement)); - reassignStatus.put("partitionsReassignStatus", reassignedPartitionsStatus); - reassignStatus.put("replicasReassignStatus", replicasReassignmentStatus); + reassignStatus.setPartitionsReassignStatus(reassignedPartitionsStatus); + reassignStatus.setReplicasReassignStatus(replicasReassignmentStatus); if (removeThrottle(reassignedPartitionsStatus, replicasReassignmentStatus)) { - reassignStatus.put("removeThrottle", true); + reassignStatus.setRemoveThrottle(true); } else { - reassignStatus.put("removeThrottle", false); + reassignStatus.setRemoveThrottle(false); } - reassignStatus.put( - "msg", "If removeThrottle is false, please use check api to remove throttle."); + reassignStatus.setMsg( + "If removeThrottle in response is false, please use check api to remove throttle."); return reassignStatus; } @@ -1499,14 +1535,14 @@ private boolean removeThrottle( Map replicasReassignmentStatus) { for (Map.Entry entry : reassignedPartitionsStatus.entrySet()) { // Partitions reassignemnt not all done - if (!entry.getValue().equals(ReassignmentStatus.ReassignmentCompleted.code())) { + if (!entry.getValue().equals(ReassignmentState.ReassignmentCompleted.code())) { return false; } } for (Map.Entry entry : replicasReassignmentStatus.entrySet()) { // Replica reassignement not all done - if (!entry.getValue().equals(ReassignmentStatus.ReassignmentCompleted.code())) { + if (!entry.getValue().equals(ReassignmentState.ReassignmentCompleted.code())) { return false; } } @@ -1518,12 +1554,14 @@ private boolean removeThrottle( for (Broker broker : brokerList) { int brokerId = broker.id(); List keysToBeRemoved = new ArrayList<>(); - // keysToBeRemoved.add(DynamicConfig.Broker.LeaderReplicationThrottledRateProp()); - // keysToBeRemoved.add(DynamicConfig.Broker.FollowerReplicationThrottledRateProp()); + // We can't access the scala object here + // keysToBeRemoved.add(DynamicConfig.Broker.LeaderReplicationThrottledRateProp()); + // keysToBeRemoved.add(DynamicConfig.Broker.FollowerReplicationThrottledRateProp()); + // // keysToBeRemoved.add(DynamicConfig.Broker.ReplicaAlterLogDirsIoMaxBytesPerSecondProp()); - keysToBeRemoved.add("leader.replication.throttled.rate"); - keysToBeRemoved.add("follower.replication.throttled.rate"); - keysToBeRemoved.add("replica.alter.log.dirs.io.max.bytes.per.second"); + keysToBeRemoved.add(LeaderReplicationThrottledRateProp); + keysToBeRemoved.add(FollowerReplicationThrottledRateProp); + keysToBeRemoved.add(ReplicaAlterLogDirsIoMaxBytesPerSecondProp); try { removeConfigInZk(Type.BROKER, String.valueOf(brokerId), keysToBeRemoved); } catch (ApiException apiException) { @@ -1580,7 +1618,6 @@ private Map checkIfPartitionReassignmentSucceeded( private Map checkIfReplicaReassignmentSucceeded( Map replicaAssignement) { Map replicasReassignmentStatus = new HashMap<>(); - Map replicaLogDirInfos = new HashMap<>(); if (!replicaAssignement.isEmpty()) { @@ -1594,30 +1631,39 @@ private Map checkIfReplicaReassignmentSucceeded( ReplicaLogDirInfo replicaLogDirInfo = replicaLogDirInfos.get(tpr); if (replicaLogDirInfo.getCurrentReplicaLogDir() == null) { // tpr log dir not found - replicasReassignmentStatus.put(tpr, ReassignmentStatus.ReassignmentFailed.code()); + replicasReassignmentStatus.put(tpr, ReassignmentState.ReassignmentFailed.code()); } else if (replicaLogDirInfo.getFutureReplicaLogDir() != null && replicaLogDirInfo.getFutureReplicaLogDir().equals(newLogDir)) { - replicasReassignmentStatus.put(tpr, ReassignmentStatus.ReassignmentInProgress.code()); + replicasReassignmentStatus.put(tpr, ReassignmentState.ReassignmentInProgress.code()); } else if (replicaLogDirInfo.getFutureReplicaLogDir() != null && !replicaLogDirInfo.getFutureReplicaLogDir().equals(newLogDir)) { // tpr is being moved to another logdir instead of newLogDir - replicasReassignmentStatus.put(tpr, ReassignmentStatus.ReassignmentFailed.code()); + replicasReassignmentStatus.put(tpr, ReassignmentState.ReassignmentFailed.code()); } else if (replicaLogDirInfo.getCurrentReplicaLogDir() != null && replicaLogDirInfo.getCurrentReplicaLogDir().equals(newLogDir)) { - replicasReassignmentStatus.put(tpr, ReassignmentStatus.ReassignmentCompleted.code()); + replicasReassignmentStatus.put(tpr, ReassignmentState.ReassignmentCompleted.code()); } else { - replicasReassignmentStatus.put(tpr, ReassignmentStatus.ReassignmentFailed.code()); + replicasReassignmentStatus.put(tpr, ReassignmentState.ReassignmentFailed.code()); } } return replicasReassignmentStatus; } - public Map checkReassignStatusByStr(String reassignStr) { + public ReassignStatus checkReassignStatus(ReassignModel reassignModel) { + String reassignJsonStr = ""; + try { + ObjectMapper objectMapper = new ObjectMapper(); + reassignJsonStr = objectMapper.writeValueAsString(reassignModel); + } catch (JsonProcessingException exception) { + throw new ApiException("Json processing exception." + exception); + } TwoTuple< scala.collection.mutable.HashMap>, scala.collection.Map> - reassignPlan = genReassignPlan(reassignStr); + reassignPlan = genReassignPlan(reassignJsonStr); + System.out.println("///////reassignStr:" + reassignJsonStr); + System.out.println("//////reassignPlan:" + reassignPlan); scala.collection.mutable.HashMap> partitionsToBeReassignedMap = reassignPlan.getFirst(); scala.collection.Map replicatAssignment = @@ -1629,7 +1675,7 @@ public Map checkReassignStatusByStr(String reassignStr) { private TwoTuple< scala.collection.mutable.HashMap>, scala.collection.Map> - genReassignPlan(String reassignStr) { + genReassignPlan(String reassignJsonStr) { Tuple2 resultTuple2; KafkaZkClient kafkaZkClient = zookeeperUtils.createKafkaZkClient(); @@ -1637,7 +1683,7 @@ public Map checkReassignStatusByStr(String reassignStr) { try { // Parse and validate reassignment json string, return (partitionsToBeReassigned, // replicaAssignment) - resultTuple2 = ReassignPartitionsCommand.parseAndValidate(kafkaZkClient, reassignStr); + resultTuple2 = ReassignPartitionsCommand.parseAndValidate(kafkaZkClient, reassignJsonStr); } catch (Exception exception) { throw new ApiException("Bad Request. " + exception.getMessage()); } @@ -1725,10 +1771,19 @@ public Record getRecordByOffset( if (!isTopicPartitionValid(topic, partition)) { throw new ApiException("Bad request. Topic:" + topic + " has no partition:" + partition); } - KafkaConsumer consumer; - try { - consumer = kafkaUtils.createNewConsumer(String.valueOf(System.currentTimeMillis()), decoder); - } catch (ClassNotFoundException classNotFoundException) { + + checkOffsetValid(topic, partition, offset); + + if (avroSchema != null && !avroSchema.isEmpty()) { + return getAvroRecordByOffset(topic, partition, offset, avroSchema); + } + + if (decoder == null || decoder.isEmpty()) { + //default decoder is StringDeserializer + decoder = "StringDeserializer"; + } + + if (!kafkaUtils.DESERIALIZER_TYPE_MAP.containsKey(decoder)) { throw new ApiException( "Bad request. Decoder class:" + decoder @@ -1738,63 +1793,71 @@ public Record getRecordByOffset( + "are supported."); } - TopicPartition tp = new TopicPartition(topic, partition); - long beginningOffset = getBeginningOffset(topic, partition); - long endOffset = getEndOffset(topic, partition); + Class type = kafkaUtils.DESERIALIZER_TYPE_MAP.get(decoder); + String dese = Serdes.serdeFrom(type).deserializer().getClass().getCanonicalName(); - if (beginningOffset == endOffset) { - throw new ApiException("There is no message in this partition of this topic"); - } - if (offset < beginningOffset || offset >= endOffset) { - log.error(offset + " error"); - consumer.close(); - throw new ApiException( - "offsets must be between " + String.valueOf(beginningOffset + " and " + (endOffset - 1))); + KafkaConsumer consumer; + try { + consumer = kafkaUtils.createNewConsumer(String.valueOf(System.currentTimeMillis()), dese); + } catch (ClassNotFoundException classNotFoundException) { + throw new ApiException("Class " + dese + "not found exception." + classNotFoundException); } + + TopicPartition tp = new TopicPartition(topic, partition); consumer.assign(Collections.singletonList(tp)); consumer.seek(tp, offset); - Record record = new Record(); + Record record = Record.builder().topic(topic).type(type).build(); // ConsumerRecords crs = consumer.poll(channelRetryBackoffMs); - ConsumerRecords crs = consumer.poll(3000); - log.info( - "Seek to offset:" - + offset - + ", topic:" - + topic - + ", partition:" - + partition - + ", crs.count:" - + crs.count()); - if (crs.count() != 0) { - Iterator> it = crs.iterator(); - while (it.hasNext()) { - ConsumerRecord initCr = it.next(); - if (initCr.offset() == offset) { - record.setOffset(offset); - record.setTimestamp(initCr.timestamp()); - record.setKey(initCr.key()); - record.setValue(initCr.value()); - break; + try { + ConsumerRecords crs = consumer.poll(3000); + log.info( + "Seek to offset:" + + offset + + ", topic:" + + topic + + ", partition:" + + partition + + ", crs.count:" + + crs.count()); + System.out.println("+++++++++before"); + if (crs.count() != 0) { + Iterator> it = crs.iterator(); + while (it.hasNext()) { + ConsumerRecord initCr = it.next(); + if (initCr.offset() == offset) { + record.setOffset(offset); + record.setTimestamp(initCr.timestamp()); + record.setKey(initCr.key()); + record.setValue(initCr.value()); + break; + } + log.info( + "Value: " + + initCr.value() + + ", initCr.Offset: " + + String.valueOf(initCr.offset()) + + ", timestamp:" + + initCr.timestamp()); } - log.info( - "Value: " - + initCr.value() - + ", initCr.Offset: " - + String.valueOf(initCr.offset()) - + ", timestamp:" - + initCr.timestamp()); - System.out.println( - "Value: " - + initCr.value() - + ", initCr.Offset: " - + String.valueOf(initCr.offset()) - + ", timestamp:" - + initCr.timestamp()); } + } catch (Exception exception) { + throw new ApiException( + "Consume " + + topic + + "-" + + partition + + " offset:" + + offset + + " using " + + decoder + + " exception. " + + exception.getLocalizedMessage()); + } finally { + consumer.close(); } - consumer.close(); + return record; } @@ -1810,6 +1873,87 @@ private boolean isTopicPartitionValid(String topic, int partition) { return false; } + public void checkOffsetValid(String topic, int partition, long offset) { + long beginningOffset = getBeginningOffset(topic, partition); + long endOffset = getEndOffset(topic, partition); + + if (beginningOffset == endOffset) { + throw new ApiException("There is no message in this partition of this topic"); + } + if (offset < beginningOffset || offset >= endOffset) { + log.error(offset + " error"); + throw new ApiException( + "offsets must be between " + String.valueOf(beginningOffset + " and " + (endOffset - 1))); + } + } + + public Record getAvroRecordByOffset(String topic, int partition, long offset, String avroSchema) throws Exception { + TopicPartition tp = new TopicPartition(topic, partition); + KafkaConsumer consumer = kafkaUtils.createNewConsumer(String.valueOf(System.currentTimeMillis()), "org.apache.kafka.common.serialization.ByteArrayDeserializer"); + consumer.assign(Collections.singletonList(tp)); + consumer.seek(tp, offset); + + Record record = Record.builder().topic(topic).build(); + + // ConsumerRecords crs = consumer.poll(channelRetryBackoffMs); + try { + ConsumerRecords crs = consumer.poll(3000); + log.info( + "Seek to offset:" + + offset + + ", topic:" + + topic + + ", partition:" + + partition + + ", crs.count:" + + crs.count()); + System.out.println("+++++++++before"); + if (crs.count() != 0) { + Iterator> it = crs.iterator(); + while (it.hasNext()) { + ConsumerRecord initCr = it.next(); + if (initCr.offset() == offset) { + record.setOffset(offset); + record.setTimestamp(initCr.timestamp()); + record.setKey(initCr.key()); + byte[] bytes = (byte[])initCr.value(); + Schema schema = new Schema.Parser().parse(avroSchema); + DatumReader reader = new GenericDatumReader(schema); + ByteBuffer buffer = ByteBuffer.wrap(bytes); + Object object = + reader.read( + null, DecoderFactory.get().binaryDecoder(buffer.array(), 0, bytes.length, null)); + record.setValue(object); + break; + } + log.info( + "Value: " + + initCr.value() + + ", initCr.Offset: " + + String.valueOf(initCr.offset()) + + ", timestamp:" + + initCr.timestamp()); + } + } + } catch (Exception exception) { + throw new ApiException( + "Consume " + + topic + + "-" + + partition + + " offset:" + + offset + + " using " + + "ByteArrayDeserializer" + + " exception. " + + exception.getLocalizedMessage()); + } finally { + consumer.close(); + } + + return record; + } + public GeneralResponse resetOffset( @TopicExistConstraint String topic, int partition, @@ -1819,18 +1963,27 @@ public GeneralResponse resetOffset( KafkaConsumer consumer = null; if (type != null && type == ConsumerType.NEW) { if (!isNewConsumerGroup(consumerGroup)) { - throw new ApiException("Consumer group " + consumerGroup + " is non-exist!"); + return GeneralResponse.builder() + .state(GeneralResponseState.failure) + .msg("New consumer group:" + consumerGroup + " non-exists!") + .build(); } } if (type != null && type == ConsumerType.OLD) { if (!isOldConsumerGroup(consumerGroup)) { - throw new ApiException("Consumer group " + consumerGroup + " is non-exist!"); + return GeneralResponse.builder() + .state(GeneralResponseState.failure) + .msg("Old consumer group:" + consumerGroup + " non-exists!") + .build(); } } if (!isTopicPartitionValid(topic, partition)) { - throw new ApiException("Topic:" + topic + " has no partition:" + partition); + return GeneralResponse.builder() + .state(GeneralResponseState.failure) + .msg("Topic:" + topic + " has no partition:" + partition) + .build(); } long offsetToBeReset = -1; @@ -1839,8 +1992,10 @@ public GeneralResponse resetOffset( log.info("To tell the consumergroup " + consumerGroup + " is active now"); if (isConsumerGroupActive(consumerGroup, type)) { - throw new ApiException( - "Offsets can only be reset if the group " + consumerGroup + " is inactive"); + return GeneralResponse.builder() + .state(GeneralResponseState.failure) + .msg("Offsets can only be reset if the group " + consumerGroup + " is inactive") + .build(); } if (type != null && type == ConsumerType.NEW && isNewConsumerGroup(consumerGroup)) { @@ -1854,14 +2009,30 @@ public GeneralResponse resetOffset( if (offset.equals("earliest")) { consumer.seekToBeginning(Arrays.asList(tp)); offsetToBeReset = beginningOffset; - log.info("Reset to " + consumer.position(tp)); + log.info( + "Reset offset for consumer group:" + + consumerGroup + + " on " + + topic + + "-" + + partition + + " to " + + consumer.position(tp)); } else if (offset.equals("latest")) { consumer.seekToEnd(Arrays.asList(tp)); offsetToBeReset = endOffset; - log.info("Reset to " + consumer.position(tp)); + log.info( + "Reset offset for consumer group:" + + consumerGroup + + " on " + + topic + + "-" + + partition + + " to " + + consumer.position(tp)); } else if (isDateTime(offset)) { // Reset offset by time - SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); Map timestampsToSearch = new HashMap<>(); try { timestampsToSearch.put(tp, sdf.parse(offset).getTime()); @@ -1871,7 +2042,9 @@ public GeneralResponse resetOffset( if (offsetAndTimestamp != null) { offsetToBeReset = offsetAndTimestamp.offset(); log.info( - "Reset offset by time" + "Reset consumer group:" + + consumerGroup + + " offset by time. Reset to offset:" + offsetAndTimestamp.offset() + ", timestamp:" + offsetAndTimestamp.timestamp() @@ -1879,35 +2052,45 @@ public GeneralResponse resetOffset( + sdf.format(new Date(offsetAndTimestamp.timestamp()))); consumer.seek(tp, offsetToBeReset); } else { - throw new ApiException( - "No offset whose timestamp is greater than or equal to the given timestamp:" - + offset); + return GeneralResponse.builder() + .state(GeneralResponseState.failure) + .msg( + "No offset's timestamp is greater than or equal to the given timestamp:" + + offset) + .build(); } } catch (ParseException parseException) { - throw new ApiException( - "Invalid offset format. Date format should be yyyy-MM-dd HH:mm:ss."); + return GeneralResponse.builder() + .state(GeneralResponseState.failure) + .msg("Invalid offset format. Date format should be yyyy-MM-dd HH:mm:ss.SSS .") + .build(); } } else { if (Long.parseLong(offset) < beginningOffset || Long.parseLong(offset) > endOffset) { - log.error(offset + " error"); - throw new ApiException( - "Invalid request offset:" - + offset - + ". Topic " - + topic - + "'s beginning offset:" - + beginningOffset - + ", endoffset:" - + endOffset); + log.warn(offset + " error"); + return GeneralResponse.builder() + .state(GeneralResponseState.failure) + .msg( + "Invalid request offset:" + + offset + + ". Topic " + + topic + + "'s beginning offset:" + + beginningOffset + + ", endoffset:" + + endOffset) + .build(); } offsetToBeReset = Long.parseLong(offset); consumer.seek(tp, offsetToBeReset); } consumer.commitSync(); - ; } catch (IllegalStateException e) { storage.getMap().remove(consumerGroup); - throw new ApiException(e); + return GeneralResponse.builder() + .state(GeneralResponseState.failure) + .msg(e.getLocalizedMessage()) + .build(); } finally { if (consumer != null) { consumer.close(); @@ -1926,15 +2109,18 @@ public GeneralResponse resetOffset( try { if (Long.parseLong(offset) < beginningOffset || Long.parseLong(offset) > endOffset) { log.info("Setting offset to " + offset + " error"); - throw new ApiException( - "Invalid request offset:" - + offset - + ". Topic " - + topic - + "'s beginning offset:" - + beginningOffset - + ", endoffset:" - + endOffset); + return GeneralResponse.builder() + .state(GeneralResponseState.failure) + .msg( + "Invalid request offset:" + + offset + + ". Topic " + + topic + + "'s beginning offset:" + + beginningOffset + + ", endoffset:" + + endOffset) + .build(); } log.info("Offset will be reset to " + offset); zkUtils = zookeeperUtils.getZkUtils(); @@ -1944,7 +2130,10 @@ public GeneralResponse resetOffset( .writeData( "/consumers/" + consumerGroup + "/offsets/" + topic + "/" + partition, offset); } catch (Exception e) { - throw new ApiException(e); + return GeneralResponse.builder() + .state(GeneralResponseState.failure) + .msg(e.getLocalizedMessage()) + .build(); } } } @@ -1956,7 +2145,7 @@ public GeneralResponse resetOffset( } private boolean isDateTime(String offset) { - String patternStr = "\\d\\d\\d\\d-[0-1]\\d-[0-3]\\d\\s+[0-2]\\d:[0-5]\\d:[0-5]\\d"; + String patternStr = "\\d\\d\\d\\d-[0-1]\\d-[0-3]\\d\\s+[0-2]\\d:[0-5]\\d:[0-5]\\d\\.\\d\\d\\d"; Pattern timePattern = Pattern.compile(patternStr); return timePattern.matcher(offset).find(); } @@ -2036,16 +2225,24 @@ public Map> getLastCommitTime( public GeneralResponse deleteConsumerGroup(String consumerGroup, ConsumerType type) { if (type == ConsumerType.OLD && !isOldConsumerGroup(consumerGroup)) { - throw new RuntimeException("Old consumer group:" + consumerGroup + " non-exist"); + return GeneralResponse.builder() + .state(GeneralResponseState.failure) + .msg("Old consumer group:" + consumerGroup + " non-exist") + .build(); } if (type == ConsumerType.NEW && !isNewConsumerGroup(consumerGroup)) { - throw new RuntimeException("New consumer group:" + consumerGroup + " non-exist"); + return GeneralResponse.builder() + .state(GeneralResponseState.failure) + .msg("New consumer group:" + consumerGroup + " non-exist") + .build(); } if (type == ConsumerType.OLD) { zkUtils = zookeeperUtils.getZkUtils(); if (!AdminUtils.deleteConsumerGroupInZK(zkUtils, consumerGroup)) { - throw new ApiException( - "The consumer " + consumerGroup + " is still active.Please stop it first"); + return GeneralResponse.builder() + .state(GeneralResponseState.failure) + .msg("The consumer " + consumerGroup + " is still active.Please stop it first") + .build(); } } else if (type == ConsumerType.NEW) { AdminClient adminClient = kafkaUtils.createAdminClient(); @@ -2059,19 +2256,23 @@ public GeneralResponse deleteConsumerGroup(String consumerGroup, ConsumerType ty adminClient.close(); if (!stringErrorsMap.get(consumerGroup).get().equals(Errors.NONE)) { - throw new ApiException( - "Consumer group:" - + consumerGroup - + " could not be deleted. Error Code:" - + stringErrorsMap.get(consumerGroup).get() - + ". Error msg:" - + stringErrorsMap.get(consumerGroup).get().exception()); + return GeneralResponse.builder() + .state(GeneralResponseState.failure) + .msg( + "Consumer group:" + + consumerGroup + + " could not be deleted. Error Code:" + + stringErrorsMap.get(consumerGroup).get() + + ". Error msg:" + + stringErrorsMap.get(consumerGroup).get().exception()) + .build(); } } return GeneralResponse.builder() .state(GeneralResponseState.success) .msg("Consumer group:" + consumerGroup + " has been deleted.") + .data(consumerGroup) .build(); } @@ -2173,21 +2374,6 @@ private long getEndOffset(Node leader, String topic, int partitionId) { return getOffsets(leader, topic, partitionId, kafka.api.OffsetRequest.LatestTime()); } - public Map countPartition(String topic) { - KafkaConsumer consumer = kafkaUtils.createNewConsumer(); - List piList = consumer.partitionsFor(topic); - Map result = - piList - .stream() - .flatMap(pi -> Arrays.stream(pi.replicas())) - .map(node -> node.id()) - .collect(Collectors.groupingBy(Function.identity(), Collectors.counting())); - - consumer.close(); - - return result; - } - private boolean isConsumerGroupActive(String consumerGroup, ConsumerType type) { if (type == ConsumerType.NEW) { ConsumerGroupMeta groupMeta = getConsumerGroupMeta(consumerGroup); diff --git a/src/main/java/org/gnuhpc/bigdata/utils/KafkaUtils.java b/src/main/java/org/gnuhpc/bigdata/utils/KafkaUtils.java index 982269c..8dabc5f 100644 --- a/src/main/java/org/gnuhpc/bigdata/utils/KafkaUtils.java +++ b/src/main/java/org/gnuhpc/bigdata/utils/KafkaUtils.java @@ -1,7 +1,11 @@ package org.gnuhpc.bigdata.utils; +import java.nio.ByteBuffer; +import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Properties; import kafka.admin.AdminClient; import lombok.Getter; @@ -14,6 +18,8 @@ import org.apache.kafka.common.Node; import org.apache.kafka.common.PartitionInfo; import org.apache.kafka.common.serialization.StringDeserializer; +import org.apache.kafka.common.serialization.StringSerializer; +import org.apache.kafka.common.utils.Bytes; import org.gnuhpc.bigdata.config.KafkaConfig; import org.gnuhpc.bigdata.config.ZookeeperConfig; import org.springframework.beans.factory.annotation.Autowired; @@ -34,6 +40,19 @@ public class KafkaUtils { private Properties prop; public static final String DEFAULTCP = "kafka-rest-consumergroup"; + public static final Map> DESERIALIZER_TYPE_MAP = new HashMap() { + { + put("StringDeserializer", String.class); + put("ShortDeserializer", Short.class); + put("IntegerDeserializer", Integer.class); + put("LongDeserializer", Long.class); + put("FloatDeserializer", Float.class); + put("DoubleDeserializer", Double.class); + put("ByteArrayDeserializer", byte[].class); + put("ByteBufferDeserializer", ByteBuffer.class); + put("BytesDeserializer", Bytes.class); + } + }; public void init() { } @@ -138,6 +157,38 @@ public KafkaProducer createProducer() { return producer; } + public KafkaProducer createProducer(String encoder) throws ClassNotFoundException { + Properties prop = new Properties(); + prop.setProperty(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaConfig.getBrokers()); + prop.setProperty( + ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, + "org.apache.kafka.common.serialization.StringSerializer"); + prop.setProperty( + ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, + "org.apache.kafka.common.serialization.StringSerializer"); + prop.setProperty(ProducerConfig.RETRIES_CONFIG, "3"); + prop.setProperty(ProducerConfig.REQUEST_TIMEOUT_MS_CONFIG, "10000"); + producer = new KafkaProducer(prop); + + if (encoder == null || encoder.isEmpty()) { + prop.put( + ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, + StringSerializer.class.getCanonicalName()); + prop.put( + ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, + StringSerializer.class.getCanonicalName()); + } else { + prop.put( + ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, Class.forName(encoder).getCanonicalName()); + prop.put( + ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, + Class.forName(encoder).getCanonicalName()); + } + producer = new KafkaProducer(prop); + + return producer; + } + public Node getLeader(String topic, int partitionId) { KafkaConsumer consumer = createNewConsumer(DEFAULTCP); List tmList = consumer.partitionsFor(topic); diff --git a/src/test/java/org/gnuhpc/bigdata/service/KafkaAdminServiceTest.java b/src/test/java/org/gnuhpc/bigdata/service/KafkaAdminServiceTest.java index ea74694..0dd3281 100644 --- a/src/test/java/org/gnuhpc/bigdata/service/KafkaAdminServiceTest.java +++ b/src/test/java/org/gnuhpc/bigdata/service/KafkaAdminServiceTest.java @@ -2,37 +2,67 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.when; import static org.mockito.MockitoAnnotations.initMocks; -import static org.testng.Assert.assertTrue; +//import static org.testng.Assert.assertFalse; +//import static org.testng.Assert.assertTrue; -import io.swagger.models.auth.In; +import java.nio.ByteBuffer; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Properties; import java.util.Set; +import lombok.extern.log4j.Log4j; +import org.apache.kafka.clients.admin.DescribeReplicaLogDirsResult.ReplicaLogDirInfo; +import org.apache.kafka.clients.consumer.ConsumerRecord; +import org.apache.kafka.clients.consumer.ConsumerRecords; import org.apache.kafka.clients.consumer.KafkaConsumer; +import org.apache.kafka.clients.producer.KafkaProducer; +import org.apache.kafka.clients.producer.ProducerRecord; +import org.apache.kafka.clients.producer.RecordMetadata; import org.apache.kafka.common.Node; -import org.gnuhpc.bigdata.componet.OffsetStorage; +import org.apache.kafka.common.TopicPartition; +import org.apache.kafka.common.TopicPartitionInfo; +import org.apache.kafka.common.TopicPartitionReplica; +import org.apache.kafka.common.config.ConfigResource.Type; +import org.apache.kafka.common.errors.ApiException; +import org.apache.kafka.common.requests.DescribeLogDirsResponse.LogDirInfo; +import org.apache.kafka.common.serialization.Serde; +import org.apache.kafka.common.serialization.Serdes; +import org.apache.kafka.common.utils.Bytes; import org.gnuhpc.bigdata.config.KafkaConfig; import org.gnuhpc.bigdata.config.ZookeeperConfig; import org.gnuhpc.bigdata.constant.ConsumerType; import org.gnuhpc.bigdata.constant.GeneralResponseState; +import org.gnuhpc.bigdata.constant.ReassignmentState; +import org.gnuhpc.bigdata.model.AddPartition; import org.gnuhpc.bigdata.model.BrokerInfo; import org.gnuhpc.bigdata.model.ClusterInfo; +import org.gnuhpc.bigdata.model.ConsumerGroupDesc; import org.gnuhpc.bigdata.model.ConsumerGroupMeta; import org.gnuhpc.bigdata.model.CustomConfigEntry; +import org.gnuhpc.bigdata.model.CustomTopicPartitionInfo; import org.gnuhpc.bigdata.model.GeneralResponse; +import org.gnuhpc.bigdata.model.HealthCheckResult; import org.gnuhpc.bigdata.model.MemberDescription; import org.gnuhpc.bigdata.model.PartitionAssignmentState; +import org.gnuhpc.bigdata.model.ReassignModel; +import org.gnuhpc.bigdata.model.ReassignStatus; +import org.gnuhpc.bigdata.model.ReassignWrapper; +import org.gnuhpc.bigdata.model.Record; import org.gnuhpc.bigdata.model.TopicBrief; import org.gnuhpc.bigdata.model.TopicDetail; import org.gnuhpc.bigdata.model.TopicMeta; +import org.gnuhpc.bigdata.model.TopicPartitionReplicaAssignment; import org.gnuhpc.bigdata.utils.KafkaUtils; import org.gnuhpc.bigdata.utils.ZookeeperUtils; import org.junit.After; @@ -46,12 +76,12 @@ import org.mockito.Spy; @FixMethodOrder(MethodSorters.JVM) +@Log4j public class KafkaAdminServiceTest { @Mock private static KafkaConfig mockKafkaConfig; @Mock private static ZookeeperConfig mockZookeeperConfig; @Spy private ZookeeperUtils mockZookeeperUtils = new ZookeeperUtils(); @Spy private KafkaUtils mockKafkaUtils = new KafkaUtils(); - @Mock private OffsetStorage mockStorage; @InjectMocks private KafkaAdminService kafkaAdminServiceUnderTest; @@ -60,16 +90,17 @@ public class KafkaAdminServiceTest { private static final List TEST_KAFKA_BOOTSTRAP_SERVERS_ID = Arrays.asList(111, 113, 115); private static final int KAFKA_NODES_COUNT = TEST_KAFKA_BOOTSTRAP_SERVERS_ID.size(); private static final String TEST_ZK = "localhost:2183"; - private static final int TEST_CONTROLLER_ID = 113; + private static final int TEST_CONTROLLER_ID = 115; private static final List TEST_KAFKA_LOG_DIRS = Arrays.asList( "/home/xiangli/bigdata/kafka_2.11-1.1.1/kafka-logs,/home/xiangli/bigdata/kafka_2.11-1.1.1/kafka111_2-logs,/home/xiangli/bigdata/kafka_2.11-1.1.1/kafka111_3-logs", "/home/xiangli/bigdata/kafka_2.11-1.1.1/kafka113-logs,/home/xiangli/bigdata/kafka_2.11-1.1.1/kafka113_2-logs,/home/xiangli/bigdata/kafka_2.11-1.1.1/kafka113_3-logs", "/home/xiangli/bigdata/kafka_2.11-1.1.1/kafka115-logs,/home/xiangli/bigdata/kafka_2.11-1.1.1/kafka115_2-logs,/home/xiangli/bigdata/kafka_2.11-1.1.1/kafka115_3-logs"); - private static final String FIRST_TOPIC_TO_TEST = "first"; + private static final String FIRST_TOPIC_TO_TEST = "first2"; private static final String SECOND_TOPIC_TO_TEST = "second"; private static final String NON_EXIST_TOPIC_TO_TEST = "nontopic"; + private static final int TEST_TOPIC_PARTITION_COUNT = 2; private static final String FIRST_CONSUMER_GROUP_TO_TEST = "testConsumerGroup1"; private static final String SECOND_CONSUMER_GROUP_TO_TEST = "testConsumerGroup2"; private static final String FIRST_CONSUMER_CLIENT_TO_TEST = "testConsumerClient1"; @@ -88,23 +119,6 @@ public void setUp() throws InterruptedException { initMocks(this); when(mockKafkaConfig.getBrokers()).thenReturn(TEST_KAFKA_BOOTSTRAP_SERVERS); when(mockZookeeperConfig.getUris()).thenReturn(TEST_ZK); - // when(mockZookeeperUtils.createKafkaZkClient()) - // .thenReturn( - // KafkaZkClient.apply( - // MOCK_ZK, - // false, - // 5000, - // 5000, - // Integer.MAX_VALUE, - // Time.SYSTEM, - // "kafka.zk.rest", - // "rest")); - // when(mockZookeeperUtils.getZkUtils()) - // .thenReturn( - // new ZkUtils( - // new ZkClient(MOCK_ZK, 5000, 5000, ZKStringSerializer$.MODULE$), - // new ZkConnection(MOCK_ZK), - // false)); mockKafkaUtils.setKafkaConfig(mockKafkaConfig); mockZookeeperUtils.setZookeeperConfig(mockZookeeperConfig); @@ -113,21 +127,18 @@ public void setUp() throws InterruptedException { allTopicsInClusterBeforeTest = kafkaAdminServiceUnderTest.getAllTopics(); } - private void clean() throws InterruptedException { + private void clean() { // Delete test topics kafkaAdminServiceUnderTest.deleteTopicList( Arrays.asList(FIRST_TOPIC_TO_TEST, SECOND_TOPIC_TO_TEST)); // Delete test consumers - if (kafkaAdminServiceUnderTest.isNewConsumerGroup(FIRST_CONSUMER_GROUP_TO_TEST)) { - kafkaAdminServiceUnderTest.deleteConsumerGroup( - FIRST_CONSUMER_GROUP_TO_TEST, ConsumerType.NEW); - } - if (kafkaAdminServiceUnderTest.isNewConsumerGroup(SECOND_CONSUMER_GROUP_TO_TEST)) { - kafkaAdminServiceUnderTest.deleteConsumerGroup( - SECOND_CONSUMER_GROUP_TO_TEST, ConsumerType.NEW); - } - Thread.sleep(1000); + GeneralResponse deleteConsumer1Response = + kafkaAdminServiceUnderTest.deleteConsumerGroup( + FIRST_CONSUMER_GROUP_TO_TEST, ConsumerType.NEW); + GeneralResponse deleteConsumer2Response = + kafkaAdminServiceUnderTest.deleteConsumerGroup( + SECOND_CONSUMER_GROUP_TO_TEST, ConsumerType.NEW); } public static HashMap getBrokerIdAndPort() { @@ -149,6 +160,20 @@ public void clearDirtyData() throws InterruptedException { // clean(); } + private TopicDetail generateTopicDetail( + String topicName, int partitionCount, int replicationFactor) { + return TopicDetail.builder() + .name(topicName) + .partitions(partitionCount) + .factor(replicationFactor) + .build(); + } + + private TopicDetail generateTopicDetailByReplicaAssignment( + String topicName, HashMap> replicaAssignments) { + return TopicDetail.builder().name(topicName).replicasAssignments(replicaAssignments).build(); + } + @Test public void testDescribeCluster() { // Run the test @@ -242,20 +267,6 @@ public void testListLogDirsByBroker() { assertEquals(expectedBrokerLogDirMap, logDirs); } - private TopicDetail generateTopicDetail( - String topicName, int partitionCount, int replicationFactor) { - return TopicDetail.builder() - .name(topicName) - .partitions(partitionCount) - .factor(replicationFactor) - .build(); - } - - private TopicDetail generateTopicDetailByReplicaAssignment( - String topicName, HashMap> replicaAssignments) { - return TopicDetail.builder().name(topicName).replicasAssignments(replicaAssignments).build(); - } - @Test public void testCreateTopic() { List topicList = new ArrayList<>(); @@ -325,11 +336,12 @@ public void testCreateTopicAlreadyExist() { final Map createTopicResult = kafkaAdminServiceUnderTest.createTopic(topicListToCreate); - //Create first topic again + // Create first topic again final Map createTopicExistResult = kafkaAdminServiceUnderTest.createTopic(topicListToCreate); - assertEquals(GeneralResponseState.success, createTopicResult.get(FIRST_TOPIC_TO_TEST).getState()); + assertEquals( + GeneralResponseState.success, createTopicResult.get(FIRST_TOPIC_TO_TEST).getState()); assertEquals( GeneralResponseState.failure, createTopicExistResult.get(FIRST_TOPIC_TO_TEST).getState()); assertTrue( @@ -343,8 +355,7 @@ public void testCreateTopicWithInvalidReplicator() { int paritionCount = 3; int replicationFactor = TEST_KAFKA_BOOTSTRAP_SERVERS_ID.size() + 1; - System.out.println("////factor:" + replicationFactor); - //Create first topic with replicator that is more than broker count + // Create first topic with replicator that is more than broker count TopicDetail firstTopic = generateTopicDetail(FIRST_TOPIC_TO_TEST, paritionCount, replicationFactor); @@ -356,7 +367,10 @@ public void testCreateTopicWithInvalidReplicator() { assertEquals( GeneralResponseState.failure, createTopicResult.get(FIRST_TOPIC_TO_TEST).getState()); assertTrue( - createTopicResult.get(FIRST_TOPIC_TO_TEST).getMsg().contains("InvalidReplicationFactorException")); + createTopicResult + .get(FIRST_TOPIC_TO_TEST) + .getMsg() + .contains("InvalidReplicationFactorException")); } @Test @@ -427,7 +441,7 @@ public void testDeleteTopicList() { // Setup List topicListToDelete = new ArrayList<>(); // Create firsttopic and secondtopic - createTwoTopics(); + createTwoTopics(1); // Run the test topicListToDelete.add(FIRST_TOPIC_TO_TEST); @@ -487,10 +501,12 @@ public void close() { class ConsumerGroup { private List consumers; + private List threadList; public ConsumerGroup( String groupId, int numConsumers, List clientIdList, List topicList) { consumers = new ArrayList<>(numConsumers); + threadList = new ArrayList<>(); for (int i = 0; i < numConsumers; i++) { ConsumerRunnable consumerRunnable = new ConsumerRunnable(groupId, clientIdList.get(i), topicList); @@ -500,67 +516,85 @@ public ConsumerGroup( public void execute() { for (ConsumerRunnable consumerRunnable : consumers) { - new Thread(consumerRunnable).start(); + Thread thread = new Thread(consumerRunnable); + threadList.add(thread); + thread.start(); } } public void close() { - for (ConsumerRunnable consumerRunnable : consumers) { - consumerRunnable.close(); + for (int i = 0; i < consumers.size(); i++) { + try { + ConsumerRunnable consumerRunnable = consumers.get(i); + consumerRunnable.close(); + } catch (Exception closeException) { + threadList.get(i).stop(); + } } } } - private KafkaConsumer consumer( - String consumerGroup, String clientId, List subscribedTopicList) { - KafkaConsumer kafkaConsumer = - mockKafkaUtils.createNewConsumerByClientId(consumerGroup, clientId); - kafkaConsumer.subscribe(subscribedTopicList); - kafkaConsumer.poll(1000); - kafkaConsumer.commitSync(); - return kafkaConsumer; - } - - private KafkaConsumer consumer(String consumerGroup, List subscribedTopicList) { - KafkaConsumer kafkaConsumer = mockKafkaUtils.createNewConsumer(consumerGroup); - kafkaConsumer.subscribe(subscribedTopicList); - kafkaConsumer.poll(3000); - kafkaConsumer.commitSync(); - return kafkaConsumer; - } - @Test - public void testListAllConsumerGroups() { + public void testListAllConsumerGroupsAndIsNewConsumerGroup() throws InterruptedException { // Setup, just test the new consumer groups final ConsumerType type = ConsumerType.NEW; String consumerGroup = FIRST_CONSUMER_GROUP_TO_TEST; - KafkaConsumer kafkaConsumer = consumer(consumerGroup, Arrays.asList(GROUP_METADATA_TOPIC_NAME)); + createOneTopic(); + + final int numConsumers = 1; + ConsumerGroup group = + new ConsumerGroup( + consumerGroup, + numConsumers, + Arrays.asList(FIRST_CONSUMER_CLIENT_TO_TEST), + Arrays.asList(FIRST_TOPIC_TO_TEST)); + + group.execute(); + Thread.sleep(1000); // Run the test final Map> consumerGroups = kafkaAdminServiceUnderTest.listAllConsumerGroups(type); + final boolean isNewConsumerGroup = kafkaAdminServiceUnderTest.isNewConsumerGroup(consumerGroup); Set newConsumerGroups = consumerGroups.get("new"); // Verify the results assertTrue(newConsumerGroups.contains(consumerGroup)); + assertTrue(isNewConsumerGroup); - kafkaConsumer.close(); + group.close(); } @Test - public void testListConsumerGroupsByTopic() { + public void testListConsumerGroupsByTopic() throws InterruptedException { // Setup - final String topic = GROUP_METADATA_TOPIC_NAME; + final String topic = FIRST_TOPIC_TO_TEST; final ConsumerType type = ConsumerType.NEW; String testGroup1 = FIRST_CONSUMER_GROUP_TO_TEST; String testGroup2 = SECOND_CONSUMER_GROUP_TO_TEST; - List subscribedTopicList = Arrays.asList(GROUP_METADATA_TOPIC_NAME); - KafkaConsumer kafkaConsumer1 = consumer(testGroup1, subscribedTopicList); - KafkaConsumer kafkaConsumer2 = consumer(testGroup2, subscribedTopicList); + createOneTopic(); + List subscribedTopicList = Arrays.asList(topic); + + final int numConsumers = 1; + ConsumerGroup group1 = + new ConsumerGroup( + testGroup1, + numConsumers, + Arrays.asList(FIRST_CONSUMER_CLIENT_TO_TEST), + subscribedTopicList); + ConsumerGroup group2 = + new ConsumerGroup( + testGroup2, + numConsumers, + Arrays.asList(SECOND_CONSUMER_CLIENT_TO_TEST), + subscribedTopicList); + group1.execute(); + group2.execute(); + Thread.sleep(1000); // Run the test final Map> consumerGroupsByTopic = @@ -571,22 +605,42 @@ public void testListConsumerGroupsByTopic() { assertTrue(newConsumerGroupsByTopic.contains(testGroup1)); assertTrue(newConsumerGroupsByTopic.contains(testGroup2)); - kafkaConsumer1.close(); - kafkaConsumer2.close(); + group1.close(); + group2.close(); } private void createOneTopic() { List topicListToCreate = new ArrayList<>(); - TopicDetail firstTopic = generateTopicDetail(FIRST_TOPIC_TO_TEST, 2, 1); + TopicDetail firstTopic = + generateTopicDetail(FIRST_TOPIC_TO_TEST, TEST_TOPIC_PARTITION_COUNT, 1); topicListToCreate.add(firstTopic); kafkaAdminServiceUnderTest.createTopic(topicListToCreate); } - private void createTwoTopics() { + private void createOneTopic(String topic, int partitionCount, int repcliaFactor) { + List topicListToCreate = new ArrayList<>(); + TopicDetail topicDetail = generateTopicDetail(topic, partitionCount, repcliaFactor); + topicListToCreate.add(topicDetail); + + kafkaAdminServiceUnderTest.createTopic(topicListToCreate); + } + + private void createOneTopic(String topic, Map> replicaAssignment) { List topicListToCreate = new ArrayList<>(); - TopicDetail firstTopic = generateTopicDetail(FIRST_TOPIC_TO_TEST, 2, 1); - TopicDetail secondTopic = generateTopicDetail(SECOND_TOPIC_TO_TEST, 2, 1); + TopicDetail topicDetail = + TopicDetail.builder().name(topic).replicasAssignments(replicaAssignment).build(); + topicListToCreate.add(topicDetail); + + kafkaAdminServiceUnderTest.createTopic(topicListToCreate); + } + + private void createTwoTopics(int replicationFactor) { + List topicListToCreate = new ArrayList<>(); + TopicDetail firstTopic = + generateTopicDetail(FIRST_TOPIC_TO_TEST, TEST_TOPIC_PARTITION_COUNT, replicationFactor); + TopicDetail secondTopic = + generateTopicDetail(SECOND_TOPIC_TO_TEST, TEST_TOPIC_PARTITION_COUNT, replicationFactor); topicListToCreate.add(firstTopic); topicListToCreate.add(secondTopic); @@ -594,40 +648,50 @@ private void createTwoTopics() { } @Test - public void testListTopicsByConsumerGroup() { + public void testListTopicsByConsumerGroup() throws InterruptedException { // Setup final String consumerGroup = FIRST_CONSUMER_GROUP_TO_TEST; final ConsumerType type = ConsumerType.NEW; // Create two topics - createTwoTopics(); + createTwoTopics(1); List subscribedTopicList = Arrays.asList(FIRST_TOPIC_TO_TEST, SECOND_TOPIC_TO_TEST); - KafkaConsumer kafkaConsumer = consumer(consumerGroup, subscribedTopicList); + final int numConsumers = 1; + ConsumerGroup group = + new ConsumerGroup( + consumerGroup, + numConsumers, + Arrays.asList(FIRST_CONSUMER_CLIENT_TO_TEST), + subscribedTopicList); + + group.execute(); + Thread.sleep(1000); // Run the test final Set topicsByConsumerGroup = kafkaAdminServiceUnderTest.listTopicsByConsumerGroup(consumerGroup, type); - kafkaConsumer.close(); // Verify the results assertEquals(2, topicsByConsumerGroup.size()); assertTrue(topicsByConsumerGroup.contains(FIRST_TOPIC_TO_TEST)); assertTrue(topicsByConsumerGroup.contains(SECOND_TOPIC_TO_TEST)); + + group.close(); } @Test public void testGetConsumerGroupMeta() throws InterruptedException { // Setup - final String consumerGroup = FIRST_CONSUMER_GROUP_TO_TEST; - createTwoTopics(); + final String consumerGroup = SECOND_CONSUMER_GROUP_TO_TEST; + createTwoTopics(1); - final int numConsumers = 2; + final int numConsumers = 1; ConsumerGroup group = new ConsumerGroup( consumerGroup, numConsumers, - Arrays.asList(FIRST_CONSUMER_CLIENT_TO_TEST, SECOND_CONSUMER_CLIENT_TO_TEST), + Arrays.asList(FIRST_CONSUMER_CLIENT_TO_TEST), Arrays.asList(FIRST_TOPIC_TO_TEST, SECOND_TOPIC_TO_TEST)); group.execute(); Thread.sleep(1000); @@ -641,13 +705,7 @@ public void testGetConsumerGroupMeta() throws InterruptedException { List members = groupMeta.getMembers(); assertEquals(numConsumers, members.size()); String memberClientId1 = members.get(0).getClientId(); - String memberClientId2 = members.get(1).getClientId(); - assertTrue( - memberClientId1.equals(FIRST_CONSUMER_CLIENT_TO_TEST) - || memberClientId1.equals(SECOND_CONSUMER_CLIENT_TO_TEST)); - assertTrue( - memberClientId2.equals(FIRST_CONSUMER_CLIENT_TO_TEST) - || memberClientId2.equals(SECOND_CONSUMER_CLIENT_TO_TEST)); + assertTrue(memberClientId1.equals(FIRST_CONSUMER_CLIENT_TO_TEST)); group.close(); } @@ -658,10 +716,10 @@ public void testDescribeNewConsumerGroupWithoutFilter() throws InterruptedExcept final String consumerGroup = FIRST_CONSUMER_GROUP_TO_TEST; final boolean filtered = false; - //Create first topic with 2 partitions and 1 replica + // Create first topic with 2 partitions and 1 replica createOneTopic(); - //Create a group with only one consumer + // Create a group with only one consumer ConsumerGroup group = new ConsumerGroup( consumerGroup, @@ -676,18 +734,18 @@ public void testDescribeNewConsumerGroupWithoutFilter() throws InterruptedExcept kafkaAdminServiceUnderTest.describeNewConsumerGroup(consumerGroup, filtered, null); // Verify the results - //Assign the consumer with 2 paritions, since there is only one consumer in the group + // Assign the consumer with 2 partitions, since there is only one consumer in the group assertEquals(2, partitionAssignments.size()); PartitionAssignmentState assignment1 = partitionAssignments.get(0); PartitionAssignmentState assignment2 = partitionAssignments.get(1); assertEquals(consumerGroup, assignment1.getGroup()); assertEquals(FIRST_TOPIC_TO_TEST, assignment1.getTopic()); assertEquals(FIRST_CONSUMER_CLIENT_TO_TEST, assignment1.getClientId()); - assertTrue(assignment1.getPartition() == 0 || assignment1.getPartition() == 1); + assertTrue(assignment1.getPartition() == 0); assertEquals(consumerGroup, assignment2.getGroup()); assertEquals(FIRST_TOPIC_TO_TEST, assignment2.getTopic()); assertEquals(FIRST_CONSUMER_CLIENT_TO_TEST, assignment2.getClientId()); - assertTrue(assignment2.getPartition() == 0 || assignment2.getPartition() == 1); + assertTrue(assignment2.getPartition() == 1); group.close(); } @@ -699,489 +757,1670 @@ public void testDescribeNewConsumerGroupWithFilterTopic() throws InterruptedExce final boolean filtered = true; final String topic = SECOND_TOPIC_TO_TEST; - //Create first and second topic - createTwoTopics(); + // Create first and second topic + createTwoTopics(1); - //Create a group with only one consumer + // Create a group with only one consumer ConsumerGroup group = new ConsumerGroup( consumerGroup, 1, - Arrays.asList(FIRST_CONSUMER_CLIENT_TO_TEST, SECOND_CONSUMER_CLIENT_TO_TEST), + Arrays.asList(FIRST_CONSUMER_CLIENT_TO_TEST), Arrays.asList(FIRST_TOPIC_TO_TEST, SECOND_TOPIC_TO_TEST)); group.execute(); - Thread.sleep(1000); // Run the test final List partitionAssignmentsFilteredByTopic = kafkaAdminServiceUnderTest.describeNewConsumerGroup(consumerGroup, filtered, topic); - System.out.println("/////result: " + partitionAssignmentsFilteredByTopic); // Verify the results - assertEquals(2, partitionAssignmentsFilteredByTopic.size()); PartitionAssignmentState assignment1 = partitionAssignmentsFilteredByTopic.get(0); PartitionAssignmentState assignment2 = partitionAssignmentsFilteredByTopic.get(1); assertEquals(consumerGroup, assignment1.getGroup()); assertEquals(topic, assignment1.getTopic()); assertEquals(FIRST_CONSUMER_CLIENT_TO_TEST, assignment1.getClientId()); - assertTrue(assignment1.getPartition() == 0 || assignment1.getPartition() == 1); + assertTrue(assignment1.getPartition() == 0); assertEquals(consumerGroup, assignment2.getGroup()); assertEquals(topic, assignment2.getTopic()); assertEquals(FIRST_CONSUMER_CLIENT_TO_TEST, assignment2.getClientId()); - assertTrue(assignment2.getPartition() == 0 || assignment2.getPartition() == 1); + assertTrue(assignment2.getPartition() == 1); group.close(); } - /* @Test - public void testDescribeLogDirsByBrokerAndTopic() { + public void testDescribeConsumerGroup() throws InterruptedException { // Setup - final List brokerList = Arrays.asList(); - final List topicList = Arrays.asList(); - final Map> expectedResult = new HashMap<>(); + final String consumerGroup = SECOND_CONSUMER_GROUP_TO_TEST; + final ConsumerType type = ConsumerType.NEW; + + // Create first topic with 2 partitions and 1 replica + createTwoTopics(1); + + // Create a group with only one consumer + ConsumerGroup group = + new ConsumerGroup( + consumerGroup, + 1, + Arrays.asList(FIRST_CONSUMER_CLIENT_TO_TEST), + Arrays.asList(FIRST_TOPIC_TO_TEST, SECOND_TOPIC_TO_TEST)); + group.execute(); + Thread.sleep(1000); // Run the test - final Map> result = - kafkaAdminServiceUnderTest.describeLogDirsByBrokerAndTopic(brokerList, topicList); + final Map> result = + kafkaAdminServiceUnderTest.describeConsumerGroup(consumerGroup, type); // Verify the results - assertEquals(expectedResult, result); - } + assertEquals(2, result.size()); + assertTrue(result.containsKey(FIRST_TOPIC_TO_TEST)); + assertTrue(result.containsKey(SECOND_TOPIC_TO_TEST)); + List firstTopicConsumersDesc = result.get(FIRST_TOPIC_TO_TEST); + List secondTopicConsumersDesc = result.get(SECOND_TOPIC_TO_TEST); + assertEquals(2, firstTopicConsumersDesc.size()); + assertEquals(consumerGroup, firstTopicConsumersDesc.get(0).getGroupName()); + assertEquals(consumerGroup, firstTopicConsumersDesc.get(1).getGroupName()); + assertEquals(FIRST_CONSUMER_CLIENT_TO_TEST, firstTopicConsumersDesc.get(0).getClientId()); + assertEquals(FIRST_CONSUMER_CLIENT_TO_TEST, firstTopicConsumersDesc.get(1).getClientId()); + assertEquals(0, firstTopicConsumersDesc.get(0).getPartitionId()); + assertEquals(1, firstTopicConsumersDesc.get(1).getPartitionId()); + + assertEquals(2, secondTopicConsumersDesc.size()); + assertEquals(consumerGroup, secondTopicConsumersDesc.get(0).getGroupName()); + assertEquals(consumerGroup, secondTopicConsumersDesc.get(1).getGroupName()); + assertEquals(FIRST_CONSUMER_CLIENT_TO_TEST, secondTopicConsumersDesc.get(0).getClientId()); + assertEquals(FIRST_CONSUMER_CLIENT_TO_TEST, secondTopicConsumersDesc.get(1).getClientId()); + assertEquals(0, secondTopicConsumersDesc.get(0).getPartitionId()); + assertEquals(1, secondTopicConsumersDesc.get(1).getPartitionId()); + group.close(); + } - @Test - public void testDescribeReplicaLogDirs() { - // Setup - final List replicas = Arrays.asList(); - final Map expectedResult = new HashMap<>(); - - // Run the test - final Map result = - kafkaAdminServiceUnderTest.describeReplicaLogDirs(replicas); + private boolean isCollectionEqual(Collection collection1, Collection collection2) { + if (collection1.size() != collection2.size()) return false; - // Verify the results - assertEquals(expectedResult, result); + Iterator iterator = collection2.iterator(); + while (iterator.hasNext()) { + Object object = iterator.next(); + if (!collection1.contains(object)) { + return false; + } } - @Test - public void testGetConfigInZk() { - // Setup - final Type type = null; - final String name = "name"; - final Properties expectedResult = new Properties(); + return true; + } - // Run the test - final Properties result = kafkaAdminServiceUnderTest.getConfigInZk(type, name); + @Test + public void testDeleteConsumerGroup() throws InterruptedException { + // Setup + final String consumerGroup = FIRST_CONSUMER_GROUP_TO_TEST; + final ConsumerType type = ConsumerType.NEW; - // Verify the results - assertEquals(expectedResult, result); - } + Set allConsumerGroupsBefore = + kafkaAdminServiceUnderTest.listAllConsumerGroups(type).get("new"); + // Create first topic with 2 partitions and 1 replica + createOneTopic(); - @Test - public void testUpdateBrokerDynConf() { - // Setup - final int brokerId = 0; - final Properties propsToBeUpdated = new Properties(); - final Properties expectedResult = new Properties(); + // Create a group with only one consumer + ConsumerGroup group = + new ConsumerGroup( + consumerGroup, + 1, + Arrays.asList(FIRST_CONSUMER_CLIENT_TO_TEST), + Arrays.asList(FIRST_TOPIC_TO_TEST)); + group.execute(); + Thread.sleep(1000); + Set allConsumerGroupsAfterAdd = + kafkaAdminServiceUnderTest.listAllConsumerGroups(type).get("new"); + Set expectedAllConsumerGroupsAfterAdd = new HashSet<>(allConsumerGroupsBefore); + expectedAllConsumerGroupsAfterAdd.add(consumerGroup); - // Run the test - final Properties result = - kafkaAdminServiceUnderTest.updateBrokerDynConf(brokerId, propsToBeUpdated); + group.close(); + // Run the test + final GeneralResponse deleteResult = + kafkaAdminServiceUnderTest.deleteConsumerGroup(consumerGroup, type); - // Verify the results - assertEquals(expectedResult, result); - } + // Verify the results + assertEquals(expectedAllConsumerGroupsAfterAdd, allConsumerGroupsAfterAdd); + assertEquals(GeneralResponseState.success, deleteResult.getState()); + assertEquals(consumerGroup, deleteResult.getData()); - @Test - public void testRemoveConfigInZk() { - // Setup - final Type type = null; - final String name = "name"; - final List configKeysToBeRemoved = Arrays.asList(); + Set allConsumerGroupsAfterDel = + kafkaAdminServiceUnderTest.listAllConsumerGroups(type).get("new"); + assertTrue(isCollectionEqual(allConsumerGroupsBefore, allConsumerGroupsAfterDel)); + } - // Run the test - kafkaAdminServiceUnderTest.removeConfigInZk(type, name, configKeysToBeRemoved); + @Test + public void testDeleteConsumerGroupNonEmpty() throws InterruptedException { + // Setup + final String consumerGroup = FIRST_CONSUMER_GROUP_TO_TEST; + final ConsumerType type = ConsumerType.NEW; - // Verify the results - } + // Create first topic with 2 partitions and 1 replica + createOneTopic(); - @Test - public void testDescribeConfig() { - // Setup - final Type type = null; - final String name = "name"; - final Collection expectedResult = Arrays.asList(); + // Create a group with only one consumer + ConsumerGroup group = + new ConsumerGroup( + consumerGroup, + 1, + Arrays.asList(FIRST_CONSUMER_CLIENT_TO_TEST), + Arrays.asList(FIRST_TOPIC_TO_TEST)); + group.execute(); + Thread.sleep(1000); - // Run the test - final Collection result = kafkaAdminServiceUnderTest.describeConfig(type, name); + // Run the test + final GeneralResponse deleteResult = + kafkaAdminServiceUnderTest.deleteConsumerGroup(consumerGroup, type); - // Verify the results - assertEquals(expectedResult, result); - } + // Verify the results + assertEquals(GeneralResponseState.failure, deleteResult.getState()); + assertTrue(deleteResult.getMsg().contains("GroupNotEmptyException")); - @Test - public void testAlterConfig() { - // Setup - final Type type = null; - final String name = "name"; - final Collection configEntries = Arrays.asList(); - final boolean expectedResult = false; + group.close(); + } - // Run the test - final boolean result = kafkaAdminServiceUnderTest.alterConfig(type, name, configEntries); + @Test + public void testAddPartitionsByAddPartitionCount() { + // Setup + final int numPartitionsToAdd = 1; + final List addPartitions = + Arrays.asList( + AddPartition.builder() + .topic(FIRST_TOPIC_TO_TEST) + .numPartitionsAdded(numPartitionsToAdd) + .build(), + AddPartition.builder() + .topic(SECOND_TOPIC_TO_TEST) + .numPartitionsAdded(numPartitionsToAdd) + .build()); + + // Create first and second topic with 2 partitions and 1 replica + createTwoTopics(1); + // Run the test + final Map addPartitionsResult = + kafkaAdminServiceUnderTest.addPartitions(addPartitions); - // Verify the results - assertEquals(expectedResult, result); - } + // Verify the results + GeneralResponse firstTopicResult = addPartitionsResult.get(FIRST_TOPIC_TO_TEST); + assertEquals(GeneralResponseState.success, firstTopicResult.getState()); + TopicMeta firstTopicMeta = (TopicMeta) firstTopicResult.getData(); + assertEquals(FIRST_TOPIC_TO_TEST, firstTopicMeta.getTopicName()); + assertEquals( + TEST_TOPIC_PARTITION_COUNT + numPartitionsToAdd, firstTopicMeta.getPartitionCount()); - @Test - public void testUpdateTopicConf() { - // Setup - final String topic = "topic"; - final Properties props = new Properties(); - final Collection expectedResult = Arrays.asList(); + GeneralResponse secondTopicResult = addPartitionsResult.get(SECOND_TOPIC_TO_TEST); + assertEquals(GeneralResponseState.success, secondTopicResult.getState()); + TopicMeta secondTopicMeta = (TopicMeta) secondTopicResult.getData(); + assertEquals(SECOND_TOPIC_TO_TEST, secondTopicMeta.getTopicName()); + assertEquals( + TEST_TOPIC_PARTITION_COUNT + numPartitionsToAdd, secondTopicMeta.getPartitionCount()); + } - // Run the test - final Collection result = - kafkaAdminServiceUnderTest.updateTopicConf(topic, props); + @Test + public void testAddPartitionsByReplicaAssignment() { + // Setup + final int brokerCount = TEST_KAFKA_BOOTSTRAP_SERVERS_ID.size(); + final int replicaFactor = brokerCount - 1; + List> replicaAssignment = + Arrays.asList( + TEST_KAFKA_BOOTSTRAP_SERVERS_ID.subList(0, brokerCount - 1), + TEST_KAFKA_BOOTSTRAP_SERVERS_ID.subList(1, brokerCount)); + final int numPartitionsToAdd = 2; + AddPartition addPartition = + AddPartition.builder() + .topic(FIRST_TOPIC_TO_TEST) + .numPartitionsAdded(numPartitionsToAdd) + .replicaAssignment(replicaAssignment) + .build(); + + // In my env, replicaAssignment is [[111,113],[113,115]] + final List addPartitions = Arrays.asList(addPartition); + + // Create first topic with 2 partitions and 2 replicas + createOneTopic(FIRST_TOPIC_TO_TEST, TEST_TOPIC_PARTITION_COUNT, replicaFactor); + // Run the test + final Map addPartitionsResult = + kafkaAdminServiceUnderTest.addPartitions(addPartitions); - // Verify the results - assertEquals(expectedResult, result); + // Verify the results + GeneralResponse response = addPartitionsResult.get(FIRST_TOPIC_TO_TEST); + assertEquals(GeneralResponseState.success, response.getState()); + + TopicMeta topicMeta = (TopicMeta) response.getData(); + int partitionCount = topicMeta.getPartitionCount(); + assertEquals(TEST_TOPIC_PARTITION_COUNT + numPartitionsToAdd, partitionCount); + + List topicPartitionInfoList = topicMeta.getTopicPartitionInfos(); + TopicPartitionInfo firstAddedPartition = + topicPartitionInfoList.get(partitionCount - numPartitionsToAdd).getTopicPartitionInfo(); + assertEquals(replicaFactor, firstAddedPartition.replicas().size()); + for (int i = 0; i < firstAddedPartition.replicas().size(); i++) { + assertEquals( + replicaAssignment.get(0).get(i).intValue(), firstAddedPartition.replicas().get(i).id()); } - @Test - public void testGetTopicConf() { - // Setup - final String topic = "topic"; - final Collection expectedResult = Arrays.asList(); - - // Run the test - final Collection result = kafkaAdminServiceUnderTest.getTopicConf(topic); - - // Verify the results - assertEquals(expectedResult, result); + TopicPartitionInfo secondAddedPartition = + topicPartitionInfoList.get(partitionCount - numPartitionsToAdd + 1).getTopicPartitionInfo(); + assertEquals(replicaFactor, secondAddedPartition.replicas().size()); + for (int i = 0; i < secondAddedPartition.replicas().size(); i++) { + assertEquals( + replicaAssignment.get(1).get(i).intValue(), secondAddedPartition.replicas().get(i).id()); } + } - @Test - public void testGetTopicConfByKey() { - // Setup - final String topic = "topic"; - final String key = "key"; - final Properties expectedResult = new Properties(); - - // Run the test - final Properties result = kafkaAdminServiceUnderTest.getTopicConfByKey(topic, key); - - // Verify the results - assertEquals(expectedResult, result); + private int getMaxBrokerId(List brokerIdList) { + int max = -1; + for (int id : brokerIdList) { + if (id > max) { + max = id; + } } - @Test - public void testUpdateTopicConfByKey() { - // Setup - final String topic = "topic"; - final String key = "key"; - final String value = "value"; - final Collection expectedResult = Arrays.asList(); + return max; + } - // Run the test - final Collection result = - kafkaAdminServiceUnderTest.updateTopicConfByKey(topic, key, value); + @Test + public void testAddPartitionsByReplicaAssignmentWithInvalidBrokerId() { + // Setup + final int replicaFactor = 1; + // In my env, kafka broker id list is [111, 113, 115]. Invalid broker id is 116. + final int invalidBrokerId = getMaxBrokerId(TEST_KAFKA_BOOTSTRAP_SERVERS_ID) + 1; + List> replicaAssignment = Arrays.asList(Arrays.asList(invalidBrokerId)); + final int numPartitionsToAdd = 1; + AddPartition addPartition = + AddPartition.builder() + .topic(FIRST_TOPIC_TO_TEST) + .numPartitionsAdded(numPartitionsToAdd) + .replicaAssignment(replicaAssignment) + .build(); + + final List addPartitions = Arrays.asList(addPartition); + + // Create first topic with 2 partitions and 1 replica + createOneTopic(FIRST_TOPIC_TO_TEST, TEST_TOPIC_PARTITION_COUNT, replicaFactor); + // Run the test + final Map addPartitionsResult = + kafkaAdminServiceUnderTest.addPartitions(addPartitions); - // Verify the results - assertEquals(expectedResult, result); - } + // Verify the results + GeneralResponse response = addPartitionsResult.get(FIRST_TOPIC_TO_TEST); + assertEquals(GeneralResponseState.failure, response.getState()); + assertTrue(response.getMsg().contains("InvalidReplicaAssignmentException: Unknown broker")); + } + @Test + public void testAddPartitionsByReplicaAssignmentWithInconsistentReplicationFactor() { + // Setup + final int brokerCount = TEST_KAFKA_BOOTSTRAP_SERVERS_ID.size(); + final int replicaFactor = brokerCount - 1; + // In my env, replicaAssignment is [[111,113],[115]]. And it is invalid. + List> replicaAssignment = + Arrays.asList( + TEST_KAFKA_BOOTSTRAP_SERVERS_ID.subList(0, brokerCount - 1), + TEST_KAFKA_BOOTSTRAP_SERVERS_ID.subList(2, brokerCount)); + final int numPartitionsToAdd = 2; + AddPartition addPartition = + AddPartition.builder() + .topic(FIRST_TOPIC_TO_TEST) + .numPartitionsAdded(numPartitionsToAdd) + .replicaAssignment(replicaAssignment) + .build(); + + final List addPartitions = Arrays.asList(addPartition); + + // Create first topic with 2 partitions and 2 replicas + createOneTopic(FIRST_TOPIC_TO_TEST, TEST_TOPIC_PARTITION_COUNT, replicaFactor); + // Run the test + final Map addPartitionsResult = + kafkaAdminServiceUnderTest.addPartitions(addPartitions); + // Verify the results + GeneralResponse response = addPartitionsResult.get(FIRST_TOPIC_TO_TEST); + assertEquals(GeneralResponseState.failure, response.getState()); + assertTrue( + response + .getMsg() + .contains( + "InvalidReplicaAssignmentException: Inconsistent replication factor between partitions")); + } - @Test - public void testIsOldConsumerGroup() { - // Setup - final String consumerGroup = "consumerGroup"; - final boolean expectedResult = false; + @Test + public void testAddPartitionsByReplicaAssignmentWithInconsistentPartitionCount() { + // Setup + final int brokerCount = TEST_KAFKA_BOOTSTRAP_SERVERS_ID.size(); + final int replicaFactor = 1; + // In my env, replicaAssignment is [[111,113],[113,115]]. But numPartitionsToAdd is 3, so it is + // invalid. + List> replicaAssignment = + Arrays.asList( + TEST_KAFKA_BOOTSTRAP_SERVERS_ID.subList(0, brokerCount - 1), + TEST_KAFKA_BOOTSTRAP_SERVERS_ID.subList(1, brokerCount)); + final int numPartitionsToAdd = 3; + AddPartition addPartition = + AddPartition.builder() + .topic(FIRST_TOPIC_TO_TEST) + .numPartitionsAdded(numPartitionsToAdd) + .replicaAssignment(replicaAssignment) + .build(); + + final List addPartitions = Arrays.asList(addPartition); + + // Create first topic with 2 partitions and 2 replicas + createOneTopic(FIRST_TOPIC_TO_TEST, TEST_TOPIC_PARTITION_COUNT, replicaFactor); + // Run the test + final Map addPartitionsResult = + kafkaAdminServiceUnderTest.addPartitions(addPartitions); - // Run the test - final boolean result = kafkaAdminServiceUnderTest.isOldConsumerGroup(consumerGroup); + // Verify the results + GeneralResponse response = addPartitionsResult.get(FIRST_TOPIC_TO_TEST); + assertEquals(GeneralResponseState.failure, response.getState()); + assertTrue( + response + .getMsg() + .contains( + "InvalidReplicaAssignmentException: Increasing the number of partitions by " + + numPartitionsToAdd + + " but " + + replicaAssignment.size() + + " assignments provided.")); + } - // Verify the results - assertEquals(expectedResult, result); - } + @Test + public void testAddPartitionsOnNonExistTopic() { + // Setup + final int numPartitionsToAdd = 1; + final List addPartitions = + Arrays.asList( + AddPartition.builder() + .topic(NON_EXIST_TOPIC_TO_TEST) + .numPartitionsAdded(numPartitionsToAdd) + .build()); - @Test - public void testIsNewConsumerGroup() { - // Setup - final String consumerGroup = "consumerGroup"; - final boolean expectedResult = false; + // Run the test + final Map addPartitionsResult = + kafkaAdminServiceUnderTest.addPartitions(addPartitions); - // Run the test - final boolean result = kafkaAdminServiceUnderTest.isNewConsumerGroup(consumerGroup); + // Verify the results + GeneralResponse response = addPartitionsResult.get(NON_EXIST_TOPIC_TO_TEST); + assertEquals(GeneralResponseState.failure, response.getState()); + assertEquals("Topic:" + NON_EXIST_TOPIC_TO_TEST + " non-exist.", response.getMsg()); + } - // Verify the results - assertEquals(expectedResult, result); - } + @Test + public void testGenerateReassignPartition() { + // Setup + // Create first topic with 2 partitions and 1 replica + String topic = FIRST_TOPIC_TO_TEST; + createOneTopic(); + TopicMeta topicMeta = kafkaAdminServiceUnderTest.describeTopic(topic); - @Test - public void testDescribeConsumerGroup() { - // Setup - final String consumerGroup = "consumerGroup"; - final ConsumerType type = null; - final Map> expectedResult = new HashMap<>(); + final ReassignWrapper reassignWrapper = new ReassignWrapper(); + reassignWrapper.setTopics(Arrays.asList(topic)); + // Reassign on only one broker + List assignedBrokerList = Arrays.asList(TEST_KAFKA_BOOTSTRAP_SERVERS_ID.get(0)); + reassignWrapper.setBrokers(assignedBrokerList); - // Run the test - final Map> result = - kafkaAdminServiceUnderTest.describeConsumerGroup(consumerGroup, type); + // Run the test + final List reassignResult = + kafkaAdminServiceUnderTest.generateReassignPartition(reassignWrapper); + assertEquals(2, reassignResult.size()); - // Verify the results - assertEquals(expectedResult, result); + // Verify the results + // The first element is current topic assignment + List currentTopicPartitionReplicaAssignments = + reassignResult.get(0).getPartitions(); + List expectedTopicPartitionInfos = topicMeta.getTopicPartitionInfos(); + assertEquals( + expectedTopicPartitionInfos.size(), currentTopicPartitionReplicaAssignments.size()); + for (int i = 0; i < expectedTopicPartitionInfos.size(); i++) { + int expectedBrokerId = + expectedTopicPartitionInfos.get(i).getTopicPartitionInfo().replicas().get(0).id(); + int brokerId = currentTopicPartitionReplicaAssignments.get(i).getReplicas().get(0).intValue(); + assertEquals(expectedBrokerId, brokerId); } + // The second element is proposed topic assignment + List proposedTopicPartitionReplicaAssignments = + reassignResult.get(1).getPartitions(); + for (TopicPartitionReplicaAssignment tpra : proposedTopicPartitionReplicaAssignments) { + List proposedReplicas = tpra.getReplicas(); + assertTrue(isCollectionEqual(assignedBrokerList, proposedReplicas)); + } + } + @Test + public void testGenerateReassignPartitionWithInvalidReplicator() { + // Setup + // Create first topic with 1 partition and 2 replicas + String topic = FIRST_TOPIC_TO_TEST; + createOneTopic(topic, 1, 2); - @Test - public void testDescribeOldConsumerGroup() { - // Setup - final String consumerGroup = "consumerGroup"; - final boolean filtered = false; - final String topic = "topic"; - final List expectedResult = Arrays.asList(); - - // Run the test - final List result = - kafkaAdminServiceUnderTest.describeOldConsumerGroup(consumerGroup, filtered, topic); + final ReassignWrapper reassignWrapper = new ReassignWrapper(); + reassignWrapper.setTopics(Arrays.asList(topic)); + // Reassign on only one broker, but replica factor is 2, so it's invalid + List assignedBrokerList = Arrays.asList(TEST_KAFKA_BOOTSTRAP_SERVERS_ID.get(0)); + reassignWrapper.setBrokers(assignedBrokerList); - // Verify the results - assertEquals(expectedResult, result); + // Run the test + try { + kafkaAdminServiceUnderTest.generateReassignPartition(reassignWrapper); + } catch (ApiException apiException) { + assertTrue( + apiException + .getMessage() + .contains("InvalidReplicationFactorException: Replication factor")); } + } - @Test - public void testDescribeNewConsumerGroupByTopic() { - // Setup - final String consumerGroup = "consumerGroup"; - final String topic = "topic"; - final List expectedResult = Arrays.asList(); - - // Run the test - final List result = - kafkaAdminServiceUnderTest.describeNewConsumerGroupByTopic(consumerGroup, topic); - - // Verify the results - assertEquals(expectedResult, result); + private boolean isReassignComplete(Map partitionsReassignStatus) { + Iterator> iterator = + partitionsReassignStatus.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry entry = iterator.next(); + if (entry.getValue().intValue() != ReassignmentState.ReassignmentCompleted.code()) { + return false; + } } - @Test - public void testDescribeOldConsumerGroupByTopic() { - // Setup - final String consumerGroup = "consumerGroup"; - final String topic = "topic"; - final List expectedResult = Arrays.asList(); + return true; + } - // Run the test - final List result = - kafkaAdminServiceUnderTest.describeOldConsumerGroupByTopic(consumerGroup, topic); + private boolean isReassignComplete( + Map partitionsReassignStatus, + Map replicasReassignStatus) { + Iterator> iterator = + partitionsReassignStatus.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry entry = iterator.next(); + if (entry.getValue().intValue() != ReassignmentState.ReassignmentCompleted.code()) { + return false; + } + } - // Verify the results - assertEquals(expectedResult, result); + Iterator> iterator2 = + replicasReassignStatus.entrySet().iterator(); + while (iterator2.hasNext()) { + Map.Entry entry = iterator2.next(); + if (entry.getValue().intValue() != ReassignmentState.ReassignmentCompleted.code()) { + return false; + } } - @Test - public void testAddPartitions() { - // Setup - final List addPartitions = Arrays.asList(); - final Map expectedResult = new HashMap<>(); + return true; + } - // Run the test - final Map result = - kafkaAdminServiceUnderTest.addPartitions(addPartitions); + @Test + public void testExecuteReassignPartition() throws InterruptedException { + // Setup + final Long interBrokerThrottle = -2000L; + final Long replicaAlterLogDirsThrottle = -3000L; + final Long timeoutMs = 10000L; - // Verify the results - assertEquals(expectedResult, result); - } + // Create first topic with 2 partitions and 1 replica + createOneTopic(); + List assignedBrokerList = Arrays.asList(TEST_KAFKA_BOOTSTRAP_SERVERS_ID.get(0)); + + List topicPartitionReplicaAssignment = new ArrayList<>(); + TopicPartitionReplicaAssignment tpr = + TopicPartitionReplicaAssignment.builder() + .topic(FIRST_TOPIC_TO_TEST) + .partition(0) + .replicas(assignedBrokerList) + .log_dirs(Arrays.asList("any")) + .build(); + TopicPartitionReplicaAssignment tpr2 = + TopicPartitionReplicaAssignment.builder() + .topic(FIRST_TOPIC_TO_TEST) + .partition(1) + .replicas(assignedBrokerList) + .log_dirs(Arrays.asList("any")) + .build(); + topicPartitionReplicaAssignment.add(tpr); + topicPartitionReplicaAssignment.add(tpr2); + ReassignModel reassignModel = + ReassignModel.builder().partitions(topicPartitionReplicaAssignment).build(); - @Test - public void testGenerateReassignPartition() { - // Setup - final ReassignWrapper reassignWrapper = null; - final List expectedResult = Arrays.asList(); + // Run the test + final ReassignStatus reassignResult = + kafkaAdminServiceUnderTest.executeReassignPartition( + reassignModel, interBrokerThrottle, replicaAlterLogDirsThrottle, timeoutMs); - // Run the test - final List result = - kafkaAdminServiceUnderTest.generateReassignPartition(reassignWrapper); + // Verify the results + Map partitionsReassignStatus = + reassignResult.getPartitionsReassignStatus(); + TopicPartition topicPartition0 = new TopicPartition(FIRST_TOPIC_TO_TEST, 0); + int partition0ReassignState = partitionsReassignStatus.get(topicPartition0).intValue(); + assertTrue( + partition0ReassignState == ReassignmentState.ReassignmentInProgress.code() + || partition0ReassignState == ReassignmentState.ReassignmentCompleted.code()); + TopicPartition topicPartition1 = new TopicPartition(FIRST_TOPIC_TO_TEST, 1); + int partition1ReassignState = partitionsReassignStatus.get(topicPartition1).intValue(); + assertTrue( + partition1ReassignState == ReassignmentState.ReassignmentInProgress.code() + || partition1ReassignState == ReassignmentState.ReassignmentCompleted.code()); + if (partition0ReassignState == ReassignmentState.ReassignmentCompleted.code() + && partition1ReassignState == ReassignmentState.ReassignmentCompleted.code()) { + // If all completed, removeThrottle will be true + assertTrue(reassignResult.isRemoveThrottle()); + } else { + assertFalse(reassignResult.isRemoveThrottle()); + } - // Verify the results - assertEquals(expectedResult, result); + // Wait reassign to complete + int count = 30; + while (true) { + Thread.sleep(2000); + ReassignStatus reassignmentStatusFinal = + kafkaAdminServiceUnderTest.checkReassignStatus(reassignModel); + Map partitionsReassignStatusFinal = + reassignmentStatusFinal.getPartitionsReassignStatus(); + if (isReassignComplete(partitionsReassignStatusFinal)) { + for (int i = 0; i < TEST_KAFKA_BOOTSTRAP_SERVERS_ID.size(); i++) { + Properties properties = + kafkaAdminServiceUnderTest.getConfigInZk( + Type.BROKER, String.valueOf(TEST_KAFKA_BOOTSTRAP_SERVERS_ID.get(i))); + assertFalse(properties.containsKey(KafkaAdminService.LeaderReplicationThrottledRateProp)); + assertFalse( + properties.containsKey(KafkaAdminService.FollowerReplicationThrottledRateProp)); + assertFalse( + properties.containsKey(KafkaAdminService.ReplicaAlterLogDirsIoMaxBytesPerSecondProp)); + } + break; + } + count++; + if (count == 30) { + break; + } } - @Test - public void testExecuteReassignPartition() { - // Setup - final String reassignStr = "reassignStr"; - final Long interBrokerThrottle = 0L; - final Long replicaAlterLogDirsThrottle = 0L; - final Long timeoutMs = 0L; - final Map expectedResult = new HashMap<>(); + // Describe topic + List topicPartitionInfos = + kafkaAdminServiceUnderTest.describeTopic(FIRST_TOPIC_TO_TEST).getTopicPartitionInfos(); + for (CustomTopicPartitionInfo ctpi : topicPartitionInfos) { + assertEquals(1, ctpi.getTopicPartitionInfo().replicas().size()); + assertEquals( + TEST_KAFKA_BOOTSTRAP_SERVERS_ID.get(0).intValue(), + ctpi.getTopicPartitionInfo().replicas().get(0).id()); + } + } - // Run the test - final Map result = - kafkaAdminServiceUnderTest.executeReassignPartition( - reassignStr, interBrokerThrottle, replicaAlterLogDirsThrottle, timeoutMs); + private void waitReassignToComplete(ReassignModel reassignModel) throws InterruptedException { + int count = 30; + while (true) { + Thread.sleep(2000); + ReassignStatus reassignmentStatusFinal = + kafkaAdminServiceUnderTest.checkReassignStatus(reassignModel); + Map partitionsReassignStatusFinal = + reassignmentStatusFinal.getPartitionsReassignStatus(); + Map replicasReassignStatusFinal = + reassignmentStatusFinal.getReplicasReassignStatus(); + if (isReassignComplete(partitionsReassignStatusFinal, replicasReassignStatusFinal)) { + break; + } + count++; + if (count == 30) { + break; + } + } + } - // Verify the results - assertEquals(expectedResult, result); + @Test + public void testExecuteReassignReplicasInnerBroker() throws InterruptedException { + // Setup + final Long interBrokerThrottle = -1L; + final Long replicaAlterLogDirsThrottle = -1L; + final Long timeoutMs = 10000L; + + // Create first topic with 1 partition and 1 replica on broker 111 + int brokerId = TEST_KAFKA_BOOTSTRAP_SERVERS_ID.get(0); + List assignedBrokerList = TEST_KAFKA_BOOTSTRAP_SERVERS_ID.subList(0, 1); + + Map> replicaAssignment = new HashMap<>(); + replicaAssignment.put(0, assignedBrokerList); + createOneTopic(FIRST_TOPIC_TO_TEST, replicaAssignment); + + // Get first-0 log dir + TopicPartitionReplica topicPartitionReplica = + new TopicPartitionReplica(FIRST_TOPIC_TO_TEST, 0, brokerId); + List replicas = Arrays.asList(topicPartitionReplica); + String replicaLogDirBeforeReassign = + kafkaAdminServiceUnderTest + .describeReplicaLogDirs(replicas) + .get(topicPartitionReplica) + .getCurrentReplicaLogDir(); + + // Find another log dir on broker 111 to reassign + String replicaLogDirToReassign = "any"; + String[] logDirs = TEST_KAFKA_LOG_DIRS.get(0).split(","); + for (int i = 0; i < logDirs.length; i++) { + if (!logDirs[i].equals(replicaLogDirBeforeReassign)) { + replicaLogDirToReassign = logDirs[i]; + break; + } } - @Test - public void testCheckReassignStatusByStr() { - // Setup - final String reassignStr = "reassignStr"; - final Map expectedResult = new HashMap<>(); + List topicPartitionReplicaAssignment = new ArrayList<>(); + TopicPartitionReplicaAssignment tpr = + TopicPartitionReplicaAssignment.builder() + .topic(FIRST_TOPIC_TO_TEST) + .partition(0) + .replicas(assignedBrokerList) + .log_dirs(Arrays.asList(replicaLogDirToReassign)) + .build(); - // Run the test - final Map result = - kafkaAdminServiceUnderTest.checkReassignStatusByStr(reassignStr); + topicPartitionReplicaAssignment.add(tpr); + ReassignModel reassignModel = + ReassignModel.builder().partitions(topicPartitionReplicaAssignment).build(); + + // Run the test + final ReassignStatus reassignResult = + kafkaAdminServiceUnderTest.executeReassignPartition( + reassignModel, interBrokerThrottle, replicaAlterLogDirsThrottle, timeoutMs); + + // Verify the results + Map partitionsReassignStatus = + reassignResult.getPartitionsReassignStatus(); + TopicPartition topicPartition = new TopicPartition(FIRST_TOPIC_TO_TEST, 0); + int partitionReassignState = partitionsReassignStatus.get(topicPartition).intValue(); + assertTrue( + partitionReassignState == ReassignmentState.ReassignmentInProgress.code() + || partitionReassignState == ReassignmentState.ReassignmentCompleted.code()); - // Verify the results - assertEquals(expectedResult, result); + Map replicasReassignStatus = + reassignResult.getReplicasReassignStatus(); + int replicaReassignState = replicasReassignStatus.get(topicPartitionReplica); + assertTrue( + replicaReassignState == ReassignmentState.ReassignmentInProgress.code() + || replicaReassignState == ReassignmentState.ReassignmentCompleted.code()); + if (partitionReassignState == ReassignmentState.ReassignmentCompleted.code() + && replicaReassignState == ReassignmentState.ReassignmentCompleted.code()) { + // If all completed, removeThrottle will be true + assertTrue(reassignResult.isRemoveThrottle()); + } else { + assertFalse(reassignResult.isRemoveThrottle()); } - @Test - public void testGetMessage() { - // Setup - final String topic = "topic"; - final int partition = 0; - final long offset = 0L; - final String decoder = "decoder"; - final String avroSchema = "avroSchema"; - final String expectedResult = "result"; + // Wait reassign to complete + waitReassignToComplete(reassignModel); - // Run the test - final String result = - kafkaAdminServiceUnderTest.getMessage(topic, partition, offset, decoder, avroSchema); + // Describe topic log dir + String currentLogDir = + kafkaAdminServiceUnderTest + .describeReplicaLogDirs(replicas) + .get(topicPartitionReplica) + .getCurrentReplicaLogDir(); + assertEquals(replicaLogDirToReassign, currentLogDir); + } + + @Test + public void testExecuteReassignReplicasInterBroker() throws InterruptedException { + // Setup + final Long interBrokerThrottle = -1L; + final Long replicaAlterLogDirsThrottle = -1L; + final Long timeoutMs = 10000L; + + // Create first topic with 1 partition and 1 replica on broker 111 + List assignedBrokerListBefore = TEST_KAFKA_BOOTSTRAP_SERVERS_ID.subList(0, 1); + + Map> replicaAssignment = new HashMap<>(); + replicaAssignment.put(0, assignedBrokerListBefore); + createOneTopic(FIRST_TOPIC_TO_TEST, replicaAssignment); + + // Find a log dir on broker 113 to reassign + String replicaLogDirToReassign = TEST_KAFKA_LOG_DIRS.get(1).split(",")[0]; + + int newBrokerId = TEST_KAFKA_BOOTSTRAP_SERVERS_ID.get(1); + List topicPartitionReplicaAssignment = new ArrayList<>(); + TopicPartitionReplicaAssignment tpr = + TopicPartitionReplicaAssignment.builder() + .topic(FIRST_TOPIC_TO_TEST) + .partition(0) + .replicas(Arrays.asList(newBrokerId)) + .log_dirs(Arrays.asList(replicaLogDirToReassign)) + .build(); + + topicPartitionReplicaAssignment.add(tpr); + ReassignModel reassignModel = + ReassignModel.builder().partitions(topicPartitionReplicaAssignment).build(); - // Verify the results - assertEquals(expectedResult, result); + // Run the test + final ReassignStatus reassignResult = + kafkaAdminServiceUnderTest.executeReassignPartition( + reassignModel, interBrokerThrottle, replicaAlterLogDirsThrottle, timeoutMs); + + // Verify the results + Map partitionsReassignStatus = + reassignResult.getPartitionsReassignStatus(); + TopicPartition topicPartition = new TopicPartition(FIRST_TOPIC_TO_TEST, 0); + int partitionReassignState = partitionsReassignStatus.get(topicPartition).intValue(); + assertTrue( + partitionReassignState == ReassignmentState.ReassignmentInProgress.code() + || partitionReassignState == ReassignmentState.ReassignmentCompleted.code()); + + TopicPartitionReplica topicPartitionReplica = + new TopicPartitionReplica(FIRST_TOPIC_TO_TEST, 0, newBrokerId); + Map replicasReassignStatus = + reassignResult.getReplicasReassignStatus(); + int replicaReassignState = replicasReassignStatus.get(topicPartitionReplica); + assertTrue( + replicaReassignState == ReassignmentState.ReassignmentInProgress.code() + || replicaReassignState == ReassignmentState.ReassignmentCompleted.code()); + if (partitionReassignState == ReassignmentState.ReassignmentCompleted.code() + && replicaReassignState == ReassignmentState.ReassignmentCompleted.code()) { + // If all completed, removeThrottle will be true + assertTrue(reassignResult.isRemoveThrottle()); + } else { + assertFalse(reassignResult.isRemoveThrottle()); } - @Test - public void testGetRecordByOffset() { - // Setup - final String topic = "topic"; - final int partition = 0; - final long offset = 0L; - final String decoder = "decoder"; - final String avroSchema = "avroSchema"; - final Record expectedResult = null; + // Wait reassign to complete + waitReassignToComplete(reassignModel); - // Run the test - final Record result = - kafkaAdminServiceUnderTest.getRecordByOffset(topic, partition, offset, decoder, avroSchema); + // Describe topic log dir + String currentLogDir = + kafkaAdminServiceUnderTest + .describeReplicaLogDirs(Arrays.asList(topicPartitionReplica)) + .get(topicPartitionReplica) + .getCurrentReplicaLogDir(); - // Verify the results - assertEquals(expectedResult, result); + assertEquals(replicaLogDirToReassign, currentLogDir); + } + + private List produceRecords(String topic, int recordsCount) { + KafkaProducer kafkaProducer = mockKafkaUtils.createProducer(); + List recordMetadataList = new ArrayList<>(); + + for (int i = 0; i < recordsCount; i++) { + ProducerRecord record = new ProducerRecord(topic, "record" + i); + try { + RecordMetadata metadata = (RecordMetadata) kafkaProducer.send(record).get(); + recordMetadataList.add(metadata); + } catch (Exception exception) { + log.error("Produce record:" + i + " error." + exception); + } } - @Test - public void testResetOffset() { - // Setup - final String topic = "topic"; - final int partition = 0; - final String consumerGroup = "consumerGroup"; - final ConsumerType type = null; - final String offset = "offset"; - final GeneralResponse expectedResult = null; + kafkaProducer.close(); + return recordMetadataList; + } - // Run the test - final GeneralResponse result = - kafkaAdminServiceUnderTest.resetOffset(topic, partition, consumerGroup, type, offset); + private Map, List> produceRecords( + String topic, Map, List> testData) { + Map, List> dataOffsetMap = new HashMap<>(); + KafkaProducer kafkaProducer = null; + + for (Map.Entry, List> test : testData.entrySet()) { + Class type = test.getKey(); + List offsetList = new ArrayList<>(); + try { + kafkaProducer = + mockKafkaUtils.createProducer( + Serdes.serdeFrom(type).serializer().getClass().getCanonicalName()); + for (Object value : test.getValue()) { + ProducerRecord record = new ProducerRecord(topic, value); + try { + RecordMetadata metadata = (RecordMetadata) kafkaProducer.send(record).get(); + Long offset = metadata.offset(); + offsetList.add(offset); + } catch (Exception exception) { + log.error("Send record exception." + exception); + } + } + } catch (ClassNotFoundException classNotFoundException) { + log.error("Encoder class not found. " + classNotFoundException); + } + System.out.println("////type:" + type + ", offsetList:" + offsetList); + dataOffsetMap.put(type, offsetList); + } + + kafkaProducer.close(); + return dataOffsetMap; + } - // Verify the results - assertEquals(expectedResult, result); + private List produceRecords(String topic, int recordsCount, String encoder) { + KafkaProducer kafkaProducer = null; + try { + kafkaProducer = mockKafkaUtils.createProducer(encoder); + } catch (ClassNotFoundException exception) { + System.out.println("///exception:" + exception); + } + List recordMetadataList = new ArrayList<>(); + + for (int i = 0; i < recordsCount; i++) { + String value = "record" + i; + // ProducerRecord record = new ProducerRecord(topic, new Bytes(value.getBytes())); + // ProducerRecord record = new ProducerRecord(topic, value.getBytes()); + // ByteBuffer byteBuffer = + // ByteBuffer.allocate(value.getBytes().length).put(value.getBytes()); + ProducerRecord record = new ProducerRecord(topic, i + 0.01); + try { + RecordMetadata metadata = (RecordMetadata) kafkaProducer.send(record).get(); + recordMetadataList.add(metadata); + System.out.println("metada:" + metadata.offset()); + } catch (Exception exception) { + log.error("Produce record:" + i + " error." + exception); + } } - @Test - public void testGetLastCommitTime() { - // Setup - final String consumerGroup = "consumerGroup"; - final String topic = "topic"; - final ConsumerType type = null; - final Map> expectedResult = new HashMap<>(); + kafkaProducer.close(); + return recordMetadataList; + } - // Run the test - final Map> result = - kafkaAdminServiceUnderTest.getLastCommitTime(consumerGroup, topic, type); + @Test + public void testResetOffsetToBeginning() throws InterruptedException { + // Setup + final String topic = FIRST_TOPIC_TO_TEST; + final int partition = 0; + final String consumerGroup = FIRST_CONSUMER_GROUP_TO_TEST; + final ConsumerType type = ConsumerType.NEW; + final String offset = "earliest"; + final int recordsCount = 5; - // Verify the results - assertEquals(expectedResult, result); + // Create first topic with 1 partition and 1 replica + createOneTopic(topic, 1, 1); + + // Produce data + produceRecords(topic, recordsCount); + + // Create a group with only one consumer + ConsumerGroup group = + new ConsumerGroup( + consumerGroup, + 1, + Arrays.asList(FIRST_CONSUMER_CLIENT_TO_TEST), + Arrays.asList(FIRST_TOPIC_TO_TEST)); + group.execute(); + Thread.sleep(1000); + + // Reset offset can only be done when consumer group is inactive + group.close(); + + // Run the test + final GeneralResponse resetResult = + kafkaAdminServiceUnderTest.resetOffset(topic, partition, consumerGroup, type, offset); + assertEquals(GeneralResponseState.success, resetResult.getState()); + assertEquals(0L, Long.parseLong(resetResult.getData().toString())); + // Verify the results + KafkaConsumer consumer = mockKafkaUtils.createNewConsumer(consumerGroup); + consumer.subscribe(Arrays.asList(topic)); + while (true) { + ConsumerRecords messages = consumer.poll(100); + if (messages.count() == 0) break; + assertEquals(recordsCount, messages.count()); + int i = 0; + for (ConsumerRecord message : messages) { + // Consumer will consume start from offset:0 + assertEquals(i, message.offset()); + i++; + } } + consumer.close(); + } - @Test - public void testDeleteConsumerGroup() { - // Setup - final String consumerGroup = "consumerGroup"; - final ConsumerType type = null; - final GeneralResponse expectedResult = null; + @Test + public void testResetOffsetToLatest() throws InterruptedException { + // Setup + final String topic = FIRST_TOPIC_TO_TEST; + final int partition = 0; + final String consumerGroup = FIRST_CONSUMER_GROUP_TO_TEST; + final ConsumerType type = ConsumerType.NEW; + final String offset = "latest"; + final int recordsCount = 5; - // Run the test - final GeneralResponse result = - kafkaAdminServiceUnderTest.deleteConsumerGroup(consumerGroup, type); + // Create first topic with 1 partition and 1 replica + createOneTopic(topic, 1, 1); + + // Produce data + produceRecords(topic, recordsCount); + + // Create a group with only one consumer + ConsumerGroup group = + new ConsumerGroup( + consumerGroup, + 1, + Arrays.asList(FIRST_CONSUMER_CLIENT_TO_TEST), + Arrays.asList(FIRST_TOPIC_TO_TEST)); + group.execute(); + Thread.sleep(1000); - // Verify the results - assertEquals(expectedResult, result); + // Reset offset can only be done when consumer group is inactive + group.close(); + + // Run the test + final GeneralResponse resetResult = + kafkaAdminServiceUnderTest.resetOffset(topic, partition, consumerGroup, type, offset); + assertEquals(GeneralResponseState.success, resetResult.getState()); + assertEquals(recordsCount, Long.parseLong(resetResult.getData().toString())); + // Verify the results + // Produce another 5 records + produceRecords(topic, recordsCount); + KafkaConsumer consumer = mockKafkaUtils.createNewConsumer(consumerGroup); + consumer.subscribe(Arrays.asList(topic)); + while (true) { + ConsumerRecords messages = consumer.poll(100); + if (messages.count() == 0) break; + assertEquals(recordsCount, messages.count()); + int i = 0; + for (ConsumerRecord message : messages) { + // Consumer will consume start from offset:5 + assertEquals(recordsCount + i, message.offset()); + i++; + } } + consumer.close(); + } - @Test - public void testGetBeginningOffset() { - // Setup - final String topic = "topic"; - final int partitionId = 0; - final long expectedResult = 0L; + @Test + public void testResetOffsetToSpecificOffset() throws InterruptedException { + // Setup + final String topic = FIRST_TOPIC_TO_TEST; + final int partition = 0; + final String consumerGroup = FIRST_CONSUMER_GROUP_TO_TEST; + final ConsumerType type = ConsumerType.NEW; + final String offset = "2"; + final int recordsCount = 5; - // Run the test - final long result = kafkaAdminServiceUnderTest.getBeginningOffset(topic, partitionId); + // Create first topic with 1 partition and 1 replica + createOneTopic(topic, 1, 1); + + // Produce data + produceRecords(topic, recordsCount); - // Verify the results - assertEquals(expectedResult, result); + // Create a group with only one consumer + ConsumerGroup group = + new ConsumerGroup( + consumerGroup, + 1, + Arrays.asList(FIRST_CONSUMER_CLIENT_TO_TEST), + Arrays.asList(FIRST_TOPIC_TO_TEST)); + group.execute(); + Thread.sleep(1000); + + // Reset offset can only be done when consumer group is inactive + group.close(); + + // Run the test + final GeneralResponse resetResult = + kafkaAdminServiceUnderTest.resetOffset(topic, partition, consumerGroup, type, offset); + + assertEquals(GeneralResponseState.success, resetResult.getState()); + assertEquals(Long.parseLong(offset), Long.parseLong(resetResult.getData().toString())); + // Verify the results + KafkaConsumer consumer = mockKafkaUtils.createNewConsumer(consumerGroup); + consumer.subscribe(Arrays.asList(topic)); + while (true) { + ConsumerRecords messages = consumer.poll(100); + if (messages.count() == 0) break; + assertEquals(recordsCount - Long.parseLong(offset), messages.count()); + int i = 0; + for (ConsumerRecord message : messages) { + // Consumer will consume start from offset:3 + assertEquals(Long.parseLong(offset) + i, message.offset()); + i++; + } } + consumer.close(); + } - @Test - public void testGetEndOffset() { - // Setup - final String topic = "topic"; - final int partitionId = 0; - final long expectedResult = 0L; + @Test + public void testResetOffsetToSpecificOffsetButOutOfRange() throws InterruptedException { + // Setup + final String topic = FIRST_TOPIC_TO_TEST; + final int partition = 0; + final String consumerGroup = FIRST_CONSUMER_GROUP_TO_TEST; + final ConsumerType type = ConsumerType.NEW; + final String offset = "10"; + final int recordsCount = 5; - // Run the test - final long result = kafkaAdminServiceUnderTest.getEndOffset(topic, partitionId); + // Create first topic with 1 partition and 1 replica + createOneTopic(topic, 1, 1); + + // Produce data + produceRecords(topic, recordsCount); + + // Create a group with only one consumer + ConsumerGroup group = + new ConsumerGroup( + consumerGroup, + 1, + Arrays.asList(FIRST_CONSUMER_CLIENT_TO_TEST), + Arrays.asList(FIRST_TOPIC_TO_TEST)); + group.execute(); + Thread.sleep(1000); + + // Reset offset can only be done when consumer group is inactive + group.close(); + + // Run the test + final GeneralResponse resetResult = + kafkaAdminServiceUnderTest.resetOffset(topic, partition, consumerGroup, type, offset); + + assertEquals(GeneralResponseState.failure, resetResult.getState()); + assertTrue(resetResult.getMsg().contains("Invalid request offset:" + offset)); + } + + @Test + public void testResetOffsetByTime() throws InterruptedException { + // Setup + final String topic = FIRST_TOPIC_TO_TEST; + final int partition = 0; + final String consumerGroup = FIRST_CONSUMER_GROUP_TO_TEST; + final ConsumerType type = ConsumerType.NEW; + final int recordsCount = 5; + + // Create first topic with 1 partition and 1 replica + createOneTopic(topic, 1, 1); + + // Produce data + List metadataList = produceRecords(topic, recordsCount); + + // Get the record timestamp to reset + int recordIndexToReset = 3; + Long timeToReset = metadataList.get(recordIndexToReset).timestamp(); + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); + final String offset = sdf.format(new Date(timeToReset)); + + // Create a group with only one consumer + ConsumerGroup group = + new ConsumerGroup( + consumerGroup, + 1, + Arrays.asList(FIRST_CONSUMER_CLIENT_TO_TEST), + Arrays.asList(FIRST_TOPIC_TO_TEST)); + group.execute(); + Thread.sleep(1000); - // Verify the results - assertEquals(expectedResult, result); + // Reset offset can only be done when consumer group is inactive + group.close(); + + // Run the test + final GeneralResponse resetResult = + kafkaAdminServiceUnderTest.resetOffset(topic, partition, consumerGroup, type, offset); + + assertEquals(GeneralResponseState.success, resetResult.getState()); + assertEquals(recordIndexToReset, Long.parseLong(resetResult.getData().toString())); + // Verify the results + KafkaConsumer consumer = mockKafkaUtils.createNewConsumer(consumerGroup); + consumer.subscribe(Arrays.asList(topic)); + while (true) { + ConsumerRecords messages = consumer.poll(100); + if (messages.count() == 0) break; + assertEquals(recordsCount - recordIndexToReset, messages.count()); + int i = 0; + for (ConsumerRecord message : messages) { + // Consumer will consume start from offset:0 + assertEquals(i + recordIndexToReset, message.offset()); + i++; + } } + consumer.close(); + } - @Test - public void testCountPartition() { - // Setup - final String topic = "topic"; - final Map expectedResult = new HashMap<>(); + @Test + public void testResetOffsetByTimeButMoreThanEndOffsetTime() throws InterruptedException { + // Setup + final String topic = FIRST_TOPIC_TO_TEST; + final int partition = 0; + final String consumerGroup = FIRST_CONSUMER_GROUP_TO_TEST; + final ConsumerType type = ConsumerType.NEW; + final int recordsCount = 5; - // Run the test - final Map result = kafkaAdminServiceUnderTest.countPartition(topic); + // Create first topic with 1 partition and 1 replica + createOneTopic(topic, 1, 1); + + // Produce data + List metadataList = produceRecords(topic, recordsCount); + + // Get the (last record timestamp + 1000) to reset + Long timeToReset = metadataList.get(recordsCount - 1).timestamp() + 1000; + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); + final String offset = sdf.format(new Date(timeToReset)); + + // Create a group with only one consumer + ConsumerGroup group = + new ConsumerGroup( + consumerGroup, + 1, + Arrays.asList(FIRST_CONSUMER_CLIENT_TO_TEST), + Arrays.asList(FIRST_TOPIC_TO_TEST)); + group.execute(); + Thread.sleep(1000); + + // Reset offset can only be done when consumer group is inactive + group.close(); + + // Run the test + final GeneralResponse resetResult = + kafkaAdminServiceUnderTest.resetOffset(topic, partition, consumerGroup, type, offset); + + assertEquals(GeneralResponseState.failure, resetResult.getState()); + assertTrue( + resetResult + .getMsg() + .contains( + "No offset's timestamp is greater than or equal to the given timestamp:" + offset)); + } + + @Test + public void testResetOffsetOnActiveConsumerGroup() throws InterruptedException { + // Setup + final String topic = FIRST_TOPIC_TO_TEST; + final int partition = 0; + final String consumerGroup = FIRST_CONSUMER_GROUP_TO_TEST; + final ConsumerType type = ConsumerType.NEW; + final String offset = "1"; + final int recordsCount = 5; + + // Create first topic with 1 partition and 1 replica + createOneTopic(topic, 1, 1); + + // Produce data + produceRecords(topic, recordsCount); + + // Create a group with only one consumer + ConsumerGroup group = + new ConsumerGroup( + consumerGroup, + 1, + Arrays.asList(FIRST_CONSUMER_CLIENT_TO_TEST), + Arrays.asList(FIRST_TOPIC_TO_TEST)); + group.execute(); + Thread.sleep(1000); + + // Run the test + final GeneralResponse resetResult = + kafkaAdminServiceUnderTest.resetOffset(topic, partition, consumerGroup, type, offset); + + assertEquals(GeneralResponseState.failure, resetResult.getState()); + assertTrue( + resetResult + .getMsg() + .contains("Offsets can only be reset if the group " + consumerGroup + " is inactive")); + + group.close(); + } + + @Test + public void testResetOffsetOnNonExistConsumerGroup() throws InterruptedException { + // Setup + final String topic = FIRST_TOPIC_TO_TEST; + final int partition = 0; + final String consumerGroup = FIRST_CONSUMER_GROUP_TO_TEST; + final ConsumerType type = ConsumerType.NEW; + final String offset = "1"; + final int recordsCount = 5; + + // Create first topic with 1 partition and 1 replica + createOneTopic(topic, 1, 1); + + // Produce data + produceRecords(topic, recordsCount); + + // Create a group with only one consumer + ConsumerGroup group = + new ConsumerGroup( + consumerGroup, + 1, + Arrays.asList(FIRST_CONSUMER_CLIENT_TO_TEST), + Arrays.asList(FIRST_TOPIC_TO_TEST)); + group.execute(); + Thread.sleep(1000); + + // Reset offset can only be done when consumer group is inactive + group.close(); + + // Run the test + final GeneralResponse resetResult = + kafkaAdminServiceUnderTest.resetOffset( + topic, partition, SECOND_CONSUMER_GROUP_TO_TEST, type, offset); + + assertEquals(GeneralResponseState.failure, resetResult.getState()); + assertTrue(resetResult.getMsg().contains("non-exists")); + } + + @Test + public void testResetOffsetOnInvalidPartition() throws InterruptedException { + // Setup + final String topic = FIRST_TOPIC_TO_TEST; + final int partition = 1; + final String consumerGroup = FIRST_CONSUMER_GROUP_TO_TEST; + final ConsumerType type = ConsumerType.NEW; + final String offset = "1"; + final int recordsCount = 5; + + // Create first topic with 1 partition and 1 replica + createOneTopic(topic, 1, 1); + + // Produce data + produceRecords(topic, recordsCount); + + // Create a group with only one consumer + ConsumerGroup group = + new ConsumerGroup( + consumerGroup, + 1, + Arrays.asList(FIRST_CONSUMER_CLIENT_TO_TEST), + Arrays.asList(FIRST_TOPIC_TO_TEST)); + group.execute(); + Thread.sleep(1000); + + // Reset offset can only be done when consumer group is inactive + group.close(); + + // Run the test + final GeneralResponse resetResult = + kafkaAdminServiceUnderTest.resetOffset(topic, partition, consumerGroup, type, offset); + + assertEquals(GeneralResponseState.failure, resetResult.getState()); + assertTrue(resetResult.getMsg().contains("has no partition")); + } + + @Test + public void testDescribeLogDirsByBrokerAndTopic() { + // Setup + final String topic = FIRST_TOPIC_TO_TEST; + final List topicList = Arrays.asList(topic); + + int brokerId = TEST_KAFKA_BOOTSTRAP_SERVERS_ID.get(0); + final List brokerList = Arrays.asList(brokerId); + Map> replicaAssignment = new HashMap<>(); + replicaAssignment.put(0, brokerList); + + // Create first topic with 1 partition on broker 111 + createOneTopic(topic, replicaAssignment); + + // Run the test + final Map> result = + kafkaAdminServiceUnderTest.describeLogDirsByBrokerAndTopic(brokerList, topicList); - // Verify the results - assertEquals(expectedResult, result); + // Verify the results + assertTrue(result.containsKey(brokerId)); + + Map logDirInfoMap = result.get(brokerId); + String[] logDirsOnBroker = TEST_KAFKA_LOG_DIRS.get(0).split(","); + TopicPartition topicPartition = new TopicPartition(topic, 0); + boolean logDirExist = false; + for (int i = 0; i < logDirsOnBroker.length; i++) { + String logDir = logDirsOnBroker[i]; + assertTrue(logDirInfoMap.containsKey(logDir)); + LogDirInfo logDirInfo = logDirInfoMap.get(logDir); + logDirExist = logDirInfo.replicaInfos.containsKey(topicPartition); + if (logDirExist) { + break; + } } + assertTrue(logDirExist); + } - @Test - public void testHealthCheck() { - // Setup - final HealthCheckResult expectedResult = null; + @Test + public void testDescribeReplicaLogDirs() { + // Setup + final String topic = FIRST_TOPIC_TO_TEST; + int brokerId = TEST_KAFKA_BOOTSTRAP_SERVERS_ID.get(0); + final List brokerList = Arrays.asList(brokerId); + Map> replicaAssignment = new HashMap<>(); + replicaAssignment.put(0, brokerList); - // Run the test - final HealthCheckResult result = kafkaAdminServiceUnderTest.healthCheck(); + // Create first topic with 1 partition on broker 111 + createOneTopic(topic, replicaAssignment); + + TopicPartitionReplica topicPartitionReplica = new TopicPartitionReplica(topic, 0, brokerId); + final List replicas = Arrays.asList(topicPartitionReplica); + // Run the test + final Map result = + kafkaAdminServiceUnderTest.describeReplicaLogDirs(replicas); + + // Verify the results + String currentLogDir = result.get(topicPartitionReplica).getCurrentReplicaLogDir(); + final Map> logDirsByBrokerAndTopic = + kafkaAdminServiceUnderTest.describeLogDirsByBrokerAndTopic( + brokerList, Arrays.asList(topic)); + Map logDirInfoMap = logDirsByBrokerAndTopic.get(brokerId); + assertTrue(logDirInfoMap.containsKey(currentLogDir)); + TopicPartition topicPartition = new TopicPartition(topic, 0); + assertTrue(logDirInfoMap.get(currentLogDir).replicaInfos.containsKey(topicPartition)); + } + + @Test + public void testGetBeginningOffset() { + // Setup + final String topic = FIRST_TOPIC_TO_TEST; + final int partitionId = 0; + final int recordsCount = 5; + + // Create first topic with 1 partition and 1 replica + createOneTopic(topic, 1, 1); + + // Produce data + produceRecords(topic, recordsCount); + + // Run the test + final long result = kafkaAdminServiceUnderTest.getBeginningOffset(topic, partitionId); + + // Verify the results + assertEquals(0, result); + } + + @Test + public void testGetEndOffset() { + // Setup + final String topic = FIRST_TOPIC_TO_TEST; + final int partitionId = 0; + final int recordsCount = 5; + + // Create first topic with 1 partition and 1 replica + createOneTopic(topic, 1, 1); + + // Produce data + produceRecords(topic, recordsCount); - // Verify the results - assertEquals(expectedResult, result); + // Run the test + final long result = kafkaAdminServiceUnderTest.getEndOffset(topic, partitionId); + + // Verify the results + assertEquals(recordsCount, result); + } + + @Test + public void testGetMessage() { + // Setup + final String topic = FIRST_TOPIC_TO_TEST; + final int partition = 0; + final long offset = 1L; + final String decoder = "decoder"; + final String avroSchema = "avroSchema"; + final int recordsCount = 5; + + // Create first topic with 1 partition and 1 replica + createOneTopic(topic, 1, 1); + + // Produce data + List metadataList = produceRecords(topic, recordsCount); + + // Run the test + final String result = + kafkaAdminServiceUnderTest.getMessage(topic, partition, offset, decoder, avroSchema); + + // Verify the results + String expectedResult = + "Value: record" + + offset + + ", Offset: " + + offset + + ", timestamp:" + + metadataList.get(1).timestamp(); + assertEquals(expectedResult, result); + } + + @Test + public void testGetRecordByOffset() { + final Map, List> testData = + new HashMap() { + { + put(String.class, Arrays.asList("my string")); + put(Short.class, Arrays.asList((short) 32767, (short) -32768)); + put(Integer.class, Arrays.asList((int) 423412424, (int) -41243432)); + put(Long.class, Arrays.asList(922337203685477580L, -922337203685477581L)); + put(Float.class, Arrays.asList(5678567.12312f, -5678567.12341f)); + put(Double.class, Arrays.asList(5678567.12312d, -5678567.12341d)); + put(byte[].class, Arrays.asList("my string".getBytes())); + put( + ByteBuffer.class, + Arrays.asList(ByteBuffer.allocate(10).put("my string".getBytes()))); + put(Bytes.class, Arrays.asList(new Bytes("my string".getBytes()))); + } + }; + // Setup + final String topic = FIRST_TOPIC_TO_TEST; + final int partition = 0; + final String avroSchema = "avroSchema"; + + // Create first topic + createOneTopic(topic, 1, 1); + + // Produce records + try { + Map, List> dataOffsetMap = produceRecords(topic, testData); + for (Map.Entry, List> entry : dataOffsetMap.entrySet()) { + Class type = entry.getKey(); + List offsetList = entry.getValue(); + Serde serde = Serdes.serdeFrom(type); + for (int i = 0; i < offsetList.size(); i++) { + Long offset = offsetList.get(i); + String decoder = serde.deserializer().getClass().getSimpleName(); + Record result = + kafkaAdminServiceUnderTest.getRecordByOffset( + topic, partition, offset, decoder, avroSchema); + Object exceptedValue = + serde + .deserializer() + .deserialize( + topic, serde.serializer().serialize(topic, testData.get(type).get(i))); + if (type.equals(ByteBuffer.class)) { + ByteBuffer byteBuffer = (ByteBuffer) exceptedValue; + assertEquals(new String(byteBuffer.array()), result.getValue()); + } else if (type.equals(byte[].class)) { + assertEquals(new String((byte[]) exceptedValue), result.getValue()); + } else { + assertEquals(exceptedValue.toString(), result.getValue()); + } + } + } + } catch (Exception exception) { + log.error("Catch exception." + exception); } - */ + } + + @Test + public void testGetRecordByOffsetWithInvalidDecoder() { + final Map, List> testData = + new HashMap() { + { + put(String.class, Arrays.asList("my string")); + } + }; + // Setup + final String topic = FIRST_TOPIC_TO_TEST; + final int partition = 0; + final String avroSchema = "avroSchema"; + + // Create first topic + createOneTopic(topic, 1, 1); + + String decoder = "DoubleDeserializer"; + // Produce records + try { + Map, List> dataOffsetMap = produceRecords(topic, testData); + for (Map.Entry, List> entry : dataOffsetMap.entrySet()) { + List offsetList = entry.getValue(); + for (int i = 0; i < offsetList.size(); i++) { + Long offset = offsetList.get(i); + // Use DoubleDeserializer to dese string record + Record result = + kafkaAdminServiceUnderTest.getRecordByOffset( + topic, partition, offset, decoder, avroSchema); + System.out.println("result:" + result); + } + } + } catch (ApiException apiException) { + assertTrue( + apiException + .getMessage() + .contains( + "Consume " + + topic + + "-" + + partition + + " offset:" + + 0 + + " using " + decoder + " exception.")); + } + } + + @Test + public void testGetRecordByAvroDeseriliazer() { + + } + @Test + public void testHealthCheck() { + // Setup + final HealthCheckResult expectedResult = null; + + // Run the test + final HealthCheckResult result = kafkaAdminServiceUnderTest.healthCheck(); + + // Verify the results +// assertEquals(expectedResult, result); + } + /* + @Test + public void testGetConfigInZk() { + // Setup + final Type type = null; + final String name = "name"; + final Properties expectedResult = new Properties(); + + // Run the test + final Properties result = kafkaAdminServiceUnderTest.getConfigInZk(type, name); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testUpdateBrokerDynConf() { + // Setup + final int brokerId = 0; + final Properties propsToBeUpdated = new Properties(); + final Properties expectedResult = new Properties(); + + // Run the test + final Properties result = + kafkaAdminServiceUnderTest.updateBrokerDynConf(brokerId, propsToBeUpdated); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testRemoveConfigInZk() { + // Setup + final Type type = null; + final String name = "name"; + final List configKeysToBeRemoved = Arrays.asList(); + + // Run the test + kafkaAdminServiceUnderTest.removeConfigInZk(type, name, configKeysToBeRemoved); + + // Verify the results + } + + @Test + public void testDescribeConfig() { + // Setup + final Type type = null; + final String name = "name"; + final Collection expectedResult = Arrays.asList(); + + // Run the test + final Collection result = kafkaAdminServiceUnderTest.describeConfig(type, name); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testAlterConfig() { + // Setup + final Type type = null; + final String name = "name"; + final Collection configEntries = Arrays.asList(); + final boolean expectedResult = false; + + // Run the test + final boolean result = kafkaAdminServiceUnderTest.alterConfig(type, name, configEntries); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testUpdateTopicConf() { + // Setup + final String topic = "topic"; + final Properties props = new Properties(); + final Collection expectedResult = Arrays.asList(); + + // Run the test + final Collection result = + kafkaAdminServiceUnderTest.updateTopicConf(topic, props); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testGetTopicConf() { + // Setup + final String topic = "topic"; + final Collection expectedResult = Arrays.asList(); + + // Run the test + final Collection result = kafkaAdminServiceUnderTest.getTopicConf(topic); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testGetTopicConfByKey() { + // Setup + final String topic = "topic"; + final String key = "key"; + final Properties expectedResult = new Properties(); + + // Run the test + final Properties result = kafkaAdminServiceUnderTest.getTopicConfByKey(topic, key); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testUpdateTopicConfByKey() { + // Setup + final String topic = "topic"; + final String key = "key"; + final String value = "value"; + final Collection expectedResult = Arrays.asList(); + + // Run the test + final Collection result = + kafkaAdminServiceUnderTest.updateTopicConfByKey(topic, key, value); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + public void testGetLastCommitTime() { + // Setup + final String consumerGroup = "consumerGroup"; + final String topic = "topic"; + final ConsumerType type = null; + final Map> expectedResult = new HashMap<>(); + + // Run the test + final Map> result = + kafkaAdminServiceUnderTest.getLastCommitTime(consumerGroup, topic, type); + + // Verify the results + assertEquals(expectedResult, result); + } + + + */ } From 82c4dbf3645df5334975517f2f880056a20abeca Mon Sep 17 00:00:00 2001 From: tinawenqiao <315524513@qq.com> Date: Tue, 29 Jan 2019 15:52:03 +0800 Subject: [PATCH 12/24] Add avro deserialize. --- pom.xml | 18 ++ .../bigdata/controller/KafkaController.java | 2 +- .../bigdata/model/HealthCheckResult.java | 2 + .../java/org/gnuhpc/bigdata/model/Record.java | 9 +- .../bigdata/service/KafkaAdminService.java | 158 +++++++++----- .../org/gnuhpc/bigdata/utils/KafkaUtils.java | 3 + .../gnuhpc/bigdata/utils/ZookeeperUtils.java | 3 + .../service/KafkaAdminServiceTest.java | 199 ++++++++++++++---- .../org/gnuhpc/bigdata/service/avro/user.avsc | 9 + 9 files changed, 299 insertions(+), 104 deletions(-) create mode 100644 src/test/java/org/gnuhpc/bigdata/service/avro/user.avsc diff --git a/pom.xml b/pom.xml index 04e9bd8..f752e88 100644 --- a/pom.xml +++ b/pom.xml @@ -481,6 +481,24 @@ + + + org.apache.avro + avro-maven-plugin + 1.8.2 + + + generate-sources + + schema + + + ${project.basedir}/src/test/java/org/gnuhpc/bigdata/service/avro/ + ${project.basedir}/src/test/java/ + + + + diff --git a/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java b/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java index 336fec4..5726907 100644 --- a/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java +++ b/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java @@ -173,7 +173,7 @@ public String getMessage( @PathVariable int partition, @PathVariable long offset, @RequestParam(required = false) String decoder) { - return kafkaAdminService.getRecordByOffset(topic, partition, offset, decoder, "").getValue().toString(); + return kafkaAdminService.getRecordByOffset(topic, partition, offset, decoder, "").getValue(); } @GetMapping(value = "/topics/{topic}") diff --git a/src/main/java/org/gnuhpc/bigdata/model/HealthCheckResult.java b/src/main/java/org/gnuhpc/bigdata/model/HealthCheckResult.java index 77a0563..a1960ef 100644 --- a/src/main/java/org/gnuhpc/bigdata/model/HealthCheckResult.java +++ b/src/main/java/org/gnuhpc/bigdata/model/HealthCheckResult.java @@ -2,11 +2,13 @@ import com.fasterxml.jackson.annotation.JsonFormat; import java.time.LocalDateTime; +import lombok.Data; import lombok.Getter; import lombok.Setter; @Getter @Setter +@Data public class HealthCheckResult { @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss") diff --git a/src/main/java/org/gnuhpc/bigdata/model/Record.java b/src/main/java/org/gnuhpc/bigdata/model/Record.java index 3a411e1..eaf6028 100644 --- a/src/main/java/org/gnuhpc/bigdata/model/Record.java +++ b/src/main/java/org/gnuhpc/bigdata/model/Record.java @@ -19,6 +19,7 @@ public class Record { public Object value = new Object(); public long timestamp; public Class type; + String decoder; public String getValue() { try { @@ -52,8 +53,12 @@ public String getValue() { } if (byte[].class.isAssignableFrom(type)) { - byte[] byteArray = (byte[]) value; - return new String(byteArray); + if (decoder.equals("AvorDeserializer")) { + return value.toString(); + } else { + byte[] byteArray = (byte[]) value; + return new String(byteArray); + } } if (ByteBuffer.class.isAssignableFrom(type)) { diff --git a/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java b/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java index 4b77ecc..1231932 100644 --- a/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java +++ b/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java @@ -84,7 +84,6 @@ import org.apache.kafka.clients.admin.NewPartitions; import org.apache.kafka.clients.admin.NewTopic; import org.apache.kafka.clients.admin.TopicDescription; -import org.apache.kafka.clients.consumer.ConsumerConfig; import org.apache.kafka.clients.consumer.ConsumerRecord; import org.apache.kafka.clients.consumer.ConsumerRecords; import org.apache.kafka.clients.consumer.KafkaConsumer; @@ -105,7 +104,6 @@ import org.apache.kafka.common.protocol.Errors; import org.apache.kafka.common.requests.DescribeLogDirsResponse.LogDirInfo; import org.apache.kafka.common.serialization.Serdes; -import org.apache.kafka.common.serialization.StringDeserializer; import org.gnuhpc.bigdata.CollectionConvertor; import org.gnuhpc.bigdata.componet.OffsetStorage; import org.gnuhpc.bigdata.config.KafkaConfig; @@ -179,6 +177,8 @@ public class KafkaAdminService { private org.apache.kafka.clients.admin.AdminClient kafkaAdminClient = null; + private AdminClient oldAdminClient = null; + private scala.Option none = scala.Option.apply(null); @PostConstruct @@ -266,7 +266,6 @@ public HashMap createTopic(List topicList) }); } - System.out.println("//////create results:" + createResults.toString()); return createResults; } @@ -816,14 +815,18 @@ public Map> listAllConsumerGroups(ConsumerType type) { if (type == null || type == ConsumerType.OLD) { Set oldConsumerGroupList = listAllOldConsumerGroups(); - if (oldConsumerGroupList.size() != 0) { + if (oldConsumerGroupList == null || oldConsumerGroupList.size() == 0) { + result.put("old", new HashSet<>()); + } else { result.put("old", oldConsumerGroupList); } } if (type == null || type == ConsumerType.NEW) { Set newConsumerGroupList = listAllNewConsumerGroups(); - if (newConsumerGroupList.size() != 0) { + if (newConsumerGroupList == null || newConsumerGroupList.size() == 0) { + result.put("new", new HashSet<>()); + } else { result.put("new", newConsumerGroupList); } } @@ -1662,8 +1665,6 @@ public ReassignStatus checkReassignStatus(ReassignModel reassignModel) { scala.collection.mutable.HashMap>, scala.collection.Map> reassignPlan = genReassignPlan(reassignJsonStr); - System.out.println("///////reassignStr:" + reassignJsonStr); - System.out.println("//////reassignPlan:" + reassignPlan); scala.collection.mutable.HashMap> partitionsToBeReassignedMap = reassignPlan.getFirst(); scala.collection.Map replicatAssignment = @@ -1774,25 +1775,29 @@ public Record getRecordByOffset( checkOffsetValid(topic, partition, offset); - if (avroSchema != null && !avroSchema.isEmpty()) { - return getAvroRecordByOffset(topic, partition, offset, avroSchema); - } - - if (decoder == null || decoder.isEmpty()) { - //default decoder is StringDeserializer - decoder = "StringDeserializer"; - } - if (!kafkaUtils.DESERIALIZER_TYPE_MAP.containsKey(decoder)) { throw new ApiException( "Bad request. Decoder class:" + decoder + " not found. ByteArrayDeserializer, ByteBufferDeserializer, BytesDeserializer, " + "DoubleDeserializer, ExtendedDeserializer.Wrapper, FloatDeserializer, " - + "IntegerDeserializer, LongDeserializer, ShortDeserializer, StringDeserializer " + + "IntegerDeserializer, LongDeserializer, ShortDeserializer, StringDeserializer, AvroDeserializer " + "are supported."); } + if (decoder != null && decoder.equals("AvroDeserializer")) { + if (avroSchema == null || avroSchema.isEmpty()) { + throw new ApiException("Bad request. Schema is needed when choosing AvroDeserializer."); + } else { + return getAvroRecordByOffset(topic, partition, offset, avroSchema); + } + } + + if (decoder == null || decoder.isEmpty()) { + // default decoder is StringDeserializer + decoder = "StringDeserializer"; + } + Class type = kafkaUtils.DESERIALIZER_TYPE_MAP.get(decoder); String dese = Serdes.serdeFrom(type).deserializer().getClass().getCanonicalName(); @@ -1807,9 +1812,8 @@ public Record getRecordByOffset( consumer.assign(Collections.singletonList(tp)); consumer.seek(tp, offset); - Record record = Record.builder().topic(topic).type(type).build(); + Record record = Record.builder().topic(topic).type(type).decoder(decoder).build(); - // ConsumerRecords crs = consumer.poll(channelRetryBackoffMs); try { ConsumerRecords crs = consumer.poll(3000); log.info( @@ -1821,7 +1825,6 @@ public Record getRecordByOffset( + partition + ", crs.count:" + crs.count()); - System.out.println("+++++++++before"); if (crs.count() != 0) { Iterator> it = crs.iterator(); while (it.hasNext()) { @@ -1830,7 +1833,19 @@ public Record getRecordByOffset( record.setOffset(offset); record.setTimestamp(initCr.timestamp()); record.setKey(initCr.key()); - record.setValue(initCr.value()); + if (decoder.equals("AvroDeserializer")) { + byte[] bytes = (byte[]) initCr.value(); + Schema schema = new Schema.Parser().parse(avroSchema); + DatumReader reader = new GenericDatumReader(schema); + ByteBuffer buffer = ByteBuffer.wrap(bytes); + Object object = + reader.read( + null, + DecoderFactory.get().binaryDecoder(buffer.array(), 0, bytes.length, null)); + record.setValue(object); + } else { + record.setValue(initCr.value()); + } break; } log.info( @@ -1877,6 +1892,15 @@ public void checkOffsetValid(String topic, int partition, long offset) { long beginningOffset = getBeginningOffset(topic, partition); long endOffset = getEndOffset(topic, partition); + log.info( + "Topic:" + + topic + + ", partition:" + + partition + + " begin offset:" + + beginningOffset + + ", end offset:" + + endOffset); if (beginningOffset == endOffset) { throw new ApiException("There is no message in this partition of this topic"); } @@ -1887,17 +1911,25 @@ public void checkOffsetValid(String topic, int partition, long offset) { } } - public Record getAvroRecordByOffset(String topic, int partition, long offset, String avroSchema) throws Exception { + public Record getAvroRecordByOffset(String topic, int partition, long offset, String avroSchema) { TopicPartition tp = new TopicPartition(topic, partition); - KafkaConsumer consumer = kafkaUtils.createNewConsumer(String.valueOf(System.currentTimeMillis()), "org.apache.kafka.common.serialization.ByteArrayDeserializer"); + KafkaConsumer consumer = null; + try { + consumer = + kafkaUtils.createNewConsumer( + String.valueOf(System.currentTimeMillis()), + "org.apache.kafka.common.serialization.ByteArrayDeserializer"); + } catch (Exception exception) { + log.error("Create consumer exception." + exception); + } consumer.assign(Collections.singletonList(tp)); consumer.seek(tp, offset); - Record record = Record.builder().topic(topic).build(); + Record record = + Record.builder().topic(topic).type(byte[].class).decoder("AvorDeserializer").build(); - // ConsumerRecords crs = consumer.poll(channelRetryBackoffMs); try { - ConsumerRecords crs = consumer.poll(3000); + ConsumerRecords crs = consumer.poll(3000); log.info( "Seek to offset:" + offset @@ -1907,23 +1939,15 @@ public Record getAvroRecordByOffset(String topic, int partition, long offset, St + partition + ", crs.count:" + crs.count()); - System.out.println("+++++++++before"); if (crs.count() != 0) { - Iterator> it = crs.iterator(); + Iterator> it = crs.iterator(); while (it.hasNext()) { - ConsumerRecord initCr = it.next(); + ConsumerRecord initCr = it.next(); if (initCr.offset() == offset) { record.setOffset(offset); record.setTimestamp(initCr.timestamp()); record.setKey(initCr.key()); - byte[] bytes = (byte[])initCr.value(); - Schema schema = new Schema.Parser().parse(avroSchema); - DatumReader reader = new GenericDatumReader(schema); - ByteBuffer buffer = ByteBuffer.wrap(bytes); - Object object = - reader.read( - null, DecoderFactory.get().binaryDecoder(buffer.array(), 0, bytes.length, null)); - record.setValue(object); + record.setValue(avroDeserialize(initCr.value(), avroSchema)); break; } log.info( @@ -1954,6 +1978,22 @@ public Record getAvroRecordByOffset(String topic, int partition, long offset, St return record; } + private Object avroDeserialize(byte[] bytes, String avroSchema) { + Schema schema = new Schema.Parser().parse(avroSchema); + DatumReader reader = new GenericDatumReader(schema); + ByteBuffer buffer = ByteBuffer.wrap(bytes); + Object object = null; + try { + object = + reader.read( + null, DecoderFactory.get().binaryDecoder(buffer.array(), 0, bytes.length, null)); + } catch (IOException exception) { + throw new ApiException("Avro Deserialize exception. " + exception); + } + + return object; + } + public GeneralResponse resetOffset( @TopicExistConstraint String topic, int partition, @@ -2256,6 +2296,12 @@ public GeneralResponse deleteConsumerGroup(String consumerGroup, ConsumerType ty adminClient.close(); if (!stringErrorsMap.get(consumerGroup).get().equals(Errors.NONE)) { + log.info("Consumer group:" + + consumerGroup + + " could not be deleted. Error Code:" + + stringErrorsMap.get(consumerGroup).get() + + ". Error msg:" + + stringErrorsMap.get(consumerGroup).get().exception()); return GeneralResponse.builder() .state(GeneralResponseState.failure) .msg( @@ -2269,6 +2315,8 @@ public GeneralResponse deleteConsumerGroup(String consumerGroup, ConsumerType ty } } + log.info("Consumer group:" + consumerGroup + " has been deleted."); + return GeneralResponse.builder() .state(GeneralResponseState.success) .msg("Consumer group:" + consumerGroup + " has been deleted.") @@ -2339,23 +2387,6 @@ public long getBeginningOffset(String topic, int partitionId) { return -1; } - private KafkaConsumer createNewConsumer(String consumerGroup) { - Properties properties = new Properties(); - properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaConfig.getBrokers()); - properties.put(ConsumerConfig.GROUP_ID_CONFIG, consumerGroup); - properties.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "false"); - properties.put(ConsumerConfig.SESSION_TIMEOUT_MS_CONFIG, "30000"); - properties.put(ConsumerConfig.MAX_PARTITION_FETCH_BYTES_CONFIG, "100000000"); - properties.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, "5"); - properties.put( - ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getCanonicalName()); - properties.put( - ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, - StringDeserializer.class.getCanonicalName()); - - return new KafkaConsumer(properties); - } - public long getEndOffset(String topic, int partitionId) { KafkaConsumer consumer = kafkaUtils.createNewConsumer(KafkaUtils.DEFAULTCP); TopicPartition tp = new TopicPartition(topic, partitionId); @@ -2401,7 +2432,8 @@ public HealthCheckResult healthCheck() { String healthCheckTopic = kafkaConfig.getHealthCheckTopic(); HealthCheckResult healthCheckResult = new HealthCheckResult(); KafkaProducer producer = kafkaUtils.createProducer(); - KafkaConsumer consumer = kafkaUtils.createNewConsumerByTopic(healthCheckTopic); + int partitionId; + long offset; boolean healthCheckTopicExist = existTopic(healthCheckTopic); log.info("HealthCheckTopic:" + healthCheckTopic + " existed:" + healthCheckTopicExist); @@ -2414,12 +2446,20 @@ public HealthCheckResult healthCheck() { return healthCheckResult; } - String message = "healthcheck_" + System.currentTimeMillis(); + String message = "health check_" + System.currentTimeMillis(); ProducerRecord record = new ProducerRecord(healthCheckTopic, null, message); log.info("Generate message:" + message); try { RecordMetadata recordMetadata = (RecordMetadata) producer.send(record).get(); - log.info("Message:" + message + " has been sent to Partition:" + recordMetadata.partition()); + partitionId = recordMetadata.partition(); + offset = recordMetadata.offset(); + log.info( + "Message:" + + message + + " has been sent to Partition:" + + partitionId + + ", offset:" + + offset); } catch (Exception e) { healthCheckResult.setStatus("error"); healthCheckResult.setMsg( @@ -2430,6 +2470,10 @@ public HealthCheckResult healthCheck() { producer.close(); } + KafkaConsumer consumer = kafkaUtils.createNewConsumer(); + TopicPartition topicPartition = new TopicPartition(healthCheckTopic, partitionId); + consumer.assign(Arrays.asList(topicPartition)); + consumer.seek(topicPartition, offset); int retries = 30; int noRecordsCount = 0; while (true) { diff --git a/src/main/java/org/gnuhpc/bigdata/utils/KafkaUtils.java b/src/main/java/org/gnuhpc/bigdata/utils/KafkaUtils.java index 8dabc5f..59efcf4 100644 --- a/src/main/java/org/gnuhpc/bigdata/utils/KafkaUtils.java +++ b/src/main/java/org/gnuhpc/bigdata/utils/KafkaUtils.java @@ -1,6 +1,7 @@ package org.gnuhpc.bigdata.utils; import java.nio.ByteBuffer; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; @@ -24,6 +25,7 @@ import org.gnuhpc.bigdata.config.ZookeeperConfig; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; +import scala.collection.JavaConverters; /** Created by gnuhpc on 2017/7/12. */ @Log4j @@ -51,6 +53,7 @@ public class KafkaUtils { put("ByteArrayDeserializer", byte[].class); put("ByteBufferDeserializer", ByteBuffer.class); put("BytesDeserializer", Bytes.class); + put("AvroDeserializer", byte[].class); } }; diff --git a/src/main/java/org/gnuhpc/bigdata/utils/ZookeeperUtils.java b/src/main/java/org/gnuhpc/bigdata/utils/ZookeeperUtils.java index 1896ec4..ce50f00 100644 --- a/src/main/java/org/gnuhpc/bigdata/utils/ZookeeperUtils.java +++ b/src/main/java/org/gnuhpc/bigdata/utils/ZookeeperUtils.java @@ -277,6 +277,7 @@ public Stat getNodePathStat(String path) { public CuratorFramework createZkClient() { if (this.curatorClient == null) { + log.info("///////Curator Client is null"); // 1.设置重试策略,重试时间计算策略sleepMs = baseSleepTimeMs * Math.max(1, random.nextInt(1 << (retryCount + // 1))); RetryPolicy retryPolicy = @@ -315,6 +316,7 @@ public String getState() { public KafkaZkClient createKafkaZkClient() { if (kafkaZkClient == null) { + log.info("KafkaZkClient is null. Prepare to create."); try { kafkaZkClient = KafkaZkClient.apply( @@ -335,6 +337,7 @@ public KafkaZkClient createKafkaZkClient() { public ZkUtils getZkUtils() { if (zkUtils == null) { + log.info("ZkUtils is null. Prepare to create."); ZkClient zkClient = new ZkClient(zookeeperConfig.getUris(), SESSION_TIMEOUT, CONNECTION_TIMEOUT, ZKStringSerializer$.MODULE$); zkUtils = new ZkUtils(zkClient, new ZkConnection(zookeeperConfig.getUris()), false); } diff --git a/src/test/java/org/gnuhpc/bigdata/service/KafkaAdminServiceTest.java b/src/test/java/org/gnuhpc/bigdata/service/KafkaAdminServiceTest.java index 0dd3281..c0a1f69 100644 --- a/src/test/java/org/gnuhpc/bigdata/service/KafkaAdminServiceTest.java +++ b/src/test/java/org/gnuhpc/bigdata/service/KafkaAdminServiceTest.java @@ -5,9 +5,8 @@ import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.when; import static org.mockito.MockitoAnnotations.initMocks; -//import static org.testng.Assert.assertFalse; -//import static org.testng.Assert.assertTrue; +import java.io.ByteArrayOutputStream; import java.nio.ByteBuffer; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -21,7 +20,19 @@ import java.util.Map; import java.util.Properties; import java.util.Set; +import java.util.concurrent.TimeUnit; +import kafka.zk.KafkaZkClient; import lombok.extern.log4j.Log4j; +import org.apache.avro.Schema; +import org.apache.avro.generic.GenericDatumWriter; +import org.apache.avro.generic.GenericRecord; +import org.apache.avro.io.BinaryEncoder; +import org.apache.avro.io.DatumWriter; +import org.apache.avro.io.EncoderFactory; +import org.apache.curator.RetryPolicy; +import org.apache.curator.framework.CuratorFramework; +import org.apache.curator.framework.CuratorFrameworkFactory; +import org.apache.curator.retry.ExponentialBackoffRetry; import org.apache.kafka.clients.admin.DescribeReplicaLogDirsResult.ReplicaLogDirInfo; import org.apache.kafka.clients.consumer.ConsumerRecord; import org.apache.kafka.clients.consumer.ConsumerRecords; @@ -39,6 +50,7 @@ import org.apache.kafka.common.serialization.Serde; import org.apache.kafka.common.serialization.Serdes; import org.apache.kafka.common.utils.Bytes; +import org.apache.kafka.common.utils.Time; import org.gnuhpc.bigdata.config.KafkaConfig; import org.gnuhpc.bigdata.config.ZookeeperConfig; import org.gnuhpc.bigdata.constant.ConsumerType; @@ -63,6 +75,7 @@ import org.gnuhpc.bigdata.model.TopicDetail; import org.gnuhpc.bigdata.model.TopicMeta; import org.gnuhpc.bigdata.model.TopicPartitionReplicaAssignment; +import org.gnuhpc.bigdata.service.avro.User; import org.gnuhpc.bigdata.utils.KafkaUtils; import org.gnuhpc.bigdata.utils.ZookeeperUtils; import org.junit.After; @@ -90,16 +103,17 @@ public class KafkaAdminServiceTest { private static final List TEST_KAFKA_BOOTSTRAP_SERVERS_ID = Arrays.asList(111, 113, 115); private static final int KAFKA_NODES_COUNT = TEST_KAFKA_BOOTSTRAP_SERVERS_ID.size(); private static final String TEST_ZK = "localhost:2183"; - private static final int TEST_CONTROLLER_ID = 115; + private static final int TEST_CONTROLLER_ID = 111; private static final List TEST_KAFKA_LOG_DIRS = Arrays.asList( "/home/xiangli/bigdata/kafka_2.11-1.1.1/kafka-logs,/home/xiangli/bigdata/kafka_2.11-1.1.1/kafka111_2-logs,/home/xiangli/bigdata/kafka_2.11-1.1.1/kafka111_3-logs", "/home/xiangli/bigdata/kafka_2.11-1.1.1/kafka113-logs,/home/xiangli/bigdata/kafka_2.11-1.1.1/kafka113_2-logs,/home/xiangli/bigdata/kafka_2.11-1.1.1/kafka113_3-logs", "/home/xiangli/bigdata/kafka_2.11-1.1.1/kafka115-logs,/home/xiangli/bigdata/kafka_2.11-1.1.1/kafka115_2-logs,/home/xiangli/bigdata/kafka_2.11-1.1.1/kafka115_3-logs"); - private static final String FIRST_TOPIC_TO_TEST = "first2"; + private static final String FIRST_TOPIC_TO_TEST = "first"; private static final String SECOND_TOPIC_TO_TEST = "second"; private static final String NON_EXIST_TOPIC_TO_TEST = "nontopic"; + private static final String HEALTH_CHECK_TOPIC_TO_TEST = "healthtest"; private static final int TEST_TOPIC_PARTITION_COUNT = 2; private static final String FIRST_CONSUMER_GROUP_TO_TEST = "testConsumerGroup1"; private static final String SECOND_CONSUMER_GROUP_TO_TEST = "testConsumerGroup2"; @@ -110,27 +124,64 @@ public class KafkaAdminServiceTest { public static final int GROUP_METADATA_TOPIC_PARTITION_COUNT = 50; private Set allTopicsInClusterBeforeTest; + private static final KafkaZkClient kafkaZkClient = + KafkaZkClient.apply( + TEST_ZK, + false, + 5000, + 5000, + Integer.MAX_VALUE, + Time.SYSTEM, + "kafka.zk.rest", + "rest"); + + private static final RetryPolicy retryPolicy = + new ExponentialBackoffRetry(1000, 5, 60000); + + private static CuratorFramework curatorClient = null; @BeforeClass - public static void start() {} + public static void start() { + RetryPolicy retryPolicy = + new ExponentialBackoffRetry(5000, 5, 60000); + + // 2.初始化客户端 + curatorClient = + CuratorFrameworkFactory.builder() + .connectString(TEST_ZK) + .sessionTimeoutMs(5000) + .connectionTimeoutMs(5000) + .retryPolicy(retryPolicy) + // .namespace("kafka-rest") //命名空间隔离 + .build(); + curatorClient.start(); + try { + curatorClient.blockUntilConnected(5000, TimeUnit.MILLISECONDS); + } catch (InterruptedException interruptedException) { + log.warn("Curator Client connect is interrupted." + interruptedException); + } + } @Before public void setUp() throws InterruptedException { initMocks(this); when(mockKafkaConfig.getBrokers()).thenReturn(TEST_KAFKA_BOOTSTRAP_SERVERS); + when(mockKafkaConfig.getHealthCheckTopic()).thenReturn(HEALTH_CHECK_TOPIC_TO_TEST); when(mockZookeeperConfig.getUris()).thenReturn(TEST_ZK); mockKafkaUtils.setKafkaConfig(mockKafkaConfig); mockZookeeperUtils.setZookeeperConfig(mockZookeeperConfig); + mockZookeeperUtils.setKafkaZkClient(kafkaZkClient); + mockZookeeperUtils.setCuratorClient(curatorClient); clean(); allTopicsInClusterBeforeTest = kafkaAdminServiceUnderTest.getAllTopics(); } - private void clean() { + private void clean() throws InterruptedException { // Delete test topics kafkaAdminServiceUnderTest.deleteTopicList( - Arrays.asList(FIRST_TOPIC_TO_TEST, SECOND_TOPIC_TO_TEST)); + Arrays.asList(FIRST_TOPIC_TO_TEST, SECOND_TOPIC_TO_TEST, HEALTH_CHECK_TOPIC_TO_TEST)); // Delete test consumers GeneralResponse deleteConsumer1Response = @@ -157,7 +208,9 @@ public static HashMap getBrokerIdAndPort() { @After public void clearDirtyData() throws InterruptedException { - // clean(); + clean(); + +// Thread.sleep(10000); } private TopicDetail generateTopicDetail( @@ -174,6 +227,29 @@ private TopicDetail generateTopicDetailByReplicaAssignment( return TopicDetail.builder().name(topicName).replicasAssignments(replicaAssignments).build(); } + @Test + public void testHealthCheckButHealthTopicNonExist() throws InterruptedException { + // Run the test + final HealthCheckResult healthCheckResult = kafkaAdminServiceUnderTest.healthCheck(); + + // Verify the results + assertEquals("unknown", healthCheckResult.getStatus()); + assertTrue(healthCheckResult.msg.contains("Non-Exist")); + } + + @Test + public void testHealthCheck() { + // Set up + // Create health check topic + createOneTopic(HEALTH_CHECK_TOPIC_TO_TEST, 1, 1); + + // Run the test + final HealthCheckResult healthCheckResult = kafkaAdminServiceUnderTest.healthCheck(); + + // Verify the results + assertEquals("ok", healthCheckResult.getStatus()); + } + @Test public void testDescribeCluster() { // Run the test @@ -1572,7 +1648,6 @@ private Map, List> produceRecords( } catch (ClassNotFoundException classNotFoundException) { log.error("Encoder class not found. " + classNotFoundException); } - System.out.println("////type:" + type + ", offsetList:" + offsetList); dataOffsetMap.put(type, offsetList); } @@ -1580,33 +1655,39 @@ private Map, List> produceRecords( return dataOffsetMap; } - private List produceRecords(String topic, int recordsCount, String encoder) { + private List produceAvroRecords(String topic, List testData) { KafkaProducer kafkaProducer = null; - try { - kafkaProducer = mockKafkaUtils.createProducer(encoder); - } catch (ClassNotFoundException exception) { - System.out.println("///exception:" + exception); - } - List recordMetadataList = new ArrayList<>(); - for (int i = 0; i < recordsCount; i++) { - String value = "record" + i; - // ProducerRecord record = new ProducerRecord(topic, new Bytes(value.getBytes())); - // ProducerRecord record = new ProducerRecord(topic, value.getBytes()); - // ByteBuffer byteBuffer = - // ByteBuffer.allocate(value.getBytes().length).put(value.getBytes()); - ProducerRecord record = new ProducerRecord(topic, i + 0.01); - try { - RecordMetadata metadata = (RecordMetadata) kafkaProducer.send(record).get(); - recordMetadataList.add(metadata); - System.out.println("metada:" + metadata.offset()); - } catch (Exception exception) { - log.error("Produce record:" + i + " error." + exception); + Class type = byte[].class; + List offsetList = new ArrayList<>(); + try { + kafkaProducer = + mockKafkaUtils.createProducer( + Serdes.serdeFrom(type).serializer().getClass().getCanonicalName()); + for (byte[] value : testData) { + ProducerRecord record = new ProducerRecord(topic, value); + try { + RecordMetadata metadata = (RecordMetadata) kafkaProducer.send(record).get(); + log.info( + "Avro record has sent to topic:" + + topic + + ", partition:" + + metadata.partition() + + ", offset:" + + metadata.offset()); + Long offset = metadata.offset(); + offsetList.add(offset); + } catch (Exception exception) { + log.error("Send avro record exception." + exception); + } } + } catch (ClassNotFoundException classNotFoundException) { + log.error("Encoder class not found. " + classNotFoundException); } kafkaProducer.close(); - return recordMetadataList; + + return offsetList; } @Test @@ -2242,7 +2323,6 @@ public void testGetRecordByOffsetWithInvalidDecoder() { Record result = kafkaAdminServiceUnderTest.getRecordByOffset( topic, partition, offset, decoder, avroSchema); - System.out.println("result:" + result); } } } catch (ApiException apiException) { @@ -2256,25 +2336,58 @@ public void testGetRecordByOffsetWithInvalidDecoder() { + partition + " offset:" + 0 - + " using " + decoder + " exception.")); + + " using " + + decoder + + " exception.")); } } @Test - public void testGetRecordByAvroDeseriliazer() { + public void testGetRecordByAvroDeseriliazer() throws InterruptedException { + String topic = FIRST_TOPIC_TO_TEST; - } - @Test - public void testHealthCheck() { - // Setup - final HealthCheckResult expectedResult = null; + // create first topic + createOneTopic(); - // Run the test - final HealthCheckResult result = kafkaAdminServiceUnderTest.healthCheck(); + String schemaStr = + "{\"namespace\": \"com.example.avro.model\",\n" + + " \"type\": \"record\",\n" + + " \"name\": \"User\",\n" + + " \"fields\": [\n" + + " {\"name\": \"name\", \"type\": \"string\"},\n" + + " {\"name\": \"favorite_number\", \"type\": [\"int\", \"null\"]},\n" + + " {\"name\": \"favorite_color\", \"type\": [\"string\", \"null\"]}\n" + + " ]\n" + + "}"; + + Schema schema = new Schema.Parser().parse(schemaStr); + + User user = new User("cmbc", 1, "green"); + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + BinaryEncoder binaryEncoder = + EncoderFactory.get().directBinaryEncoder(byteArrayOutputStream, null); + DatumWriter datumWriter = new GenericDatumWriter(schema); + byte[] bytes = null; + List offsetList = new ArrayList<>(); + try { + datumWriter.write(user, binaryEncoder); + binaryEncoder.flush(); + bytes = byteArrayOutputStream.toByteArray(); + offsetList = produceAvroRecords(topic, Arrays.asList(bytes)); + byteArrayOutputStream.close(); + } catch (Exception exception) { + log.error("exception:" + exception); + } - // Verify the results -// assertEquals(expectedResult, result); + String decoder = "AvroDeserializer"; + if (offsetList.size() > 0) { + Record result = + kafkaAdminServiceUnderTest.getRecordByOffset( + topic, 0, offsetList.get(0), decoder, schemaStr); + assertEquals(user.toString(), result.getValue()); + } } + /* @Test public void testGetConfigInZk() { @@ -2420,7 +2533,5 @@ public void testGetLastCommitTime() { // Verify the results assertEquals(expectedResult, result); } - - */ } diff --git a/src/test/java/org/gnuhpc/bigdata/service/avro/user.avsc b/src/test/java/org/gnuhpc/bigdata/service/avro/user.avsc new file mode 100644 index 0000000..ae14e8c --- /dev/null +++ b/src/test/java/org/gnuhpc/bigdata/service/avro/user.avsc @@ -0,0 +1,9 @@ +{"namespace": "org.gnuhpc.bigdata.service.avro", + "type": "record", + "name": "User", + "fields": [ + {"name": "name", "type": "string"}, + {"name": "favorite_number", "type": ["int", "null"]}, + {"name": "favorite_color", "type": ["string", "null"]} + ] +} \ No newline at end of file From 6dfc2d6e38dbb773240208044b1e0978a962168d Mon Sep 17 00:00:00 2001 From: tinawenqiao <315524513@qq.com> Date: Tue, 29 Jan 2019 15:52:43 +0800 Subject: [PATCH 13/24] Add avro test. --- .../org/gnuhpc/bigdata/service/avro/User.java | 378 ++++++++++++++++++ 1 file changed, 378 insertions(+) create mode 100644 src/test/java/org/gnuhpc/bigdata/service/avro/User.java diff --git a/src/test/java/org/gnuhpc/bigdata/service/avro/User.java b/src/test/java/org/gnuhpc/bigdata/service/avro/User.java new file mode 100644 index 0000000..975549e --- /dev/null +++ b/src/test/java/org/gnuhpc/bigdata/service/avro/User.java @@ -0,0 +1,378 @@ +/** + * Autogenerated by Avro + * + * DO NOT EDIT DIRECTLY + */ +package org.gnuhpc.bigdata.service.avro; + +import org.apache.avro.specific.SpecificData; +import org.apache.avro.message.BinaryMessageEncoder; +import org.apache.avro.message.BinaryMessageDecoder; +import org.apache.avro.message.SchemaStore; + +@SuppressWarnings("all") +@org.apache.avro.specific.AvroGenerated +public class User extends org.apache.avro.specific.SpecificRecordBase implements org.apache.avro.specific.SpecificRecord { + private static final long serialVersionUID = -365718983487324253L; + public static final org.apache.avro.Schema SCHEMA$ = new org.apache.avro.Schema.Parser().parse("{\"type\":\"record\",\"name\":\"User\",\"namespace\":\"org.gnuhpc.bigdata.service.avro\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"favorite_number\",\"type\":[\"int\",\"null\"]},{\"name\":\"favorite_color\",\"type\":[\"string\",\"null\"]}]}"); + public static org.apache.avro.Schema getClassSchema() { return SCHEMA$; } + + private static SpecificData MODEL$ = new SpecificData(); + + private static final BinaryMessageEncoder ENCODER = + new BinaryMessageEncoder(MODEL$, SCHEMA$); + + private static final BinaryMessageDecoder DECODER = + new BinaryMessageDecoder(MODEL$, SCHEMA$); + + /** + * Return the BinaryMessageDecoder instance used by this class. + */ + public static BinaryMessageDecoder getDecoder() { + return DECODER; + } + + /** + * Create a new BinaryMessageDecoder instance for this class that uses the specified {@link SchemaStore}. + * @param resolver a {@link SchemaStore} used to find schemas by fingerprint + */ + public static BinaryMessageDecoder createDecoder(SchemaStore resolver) { + return new BinaryMessageDecoder(MODEL$, SCHEMA$, resolver); + } + + /** Serializes this User to a ByteBuffer. */ + public java.nio.ByteBuffer toByteBuffer() throws java.io.IOException { + return ENCODER.encode(this); + } + + /** Deserializes a User from a ByteBuffer. */ + public static User fromByteBuffer( + java.nio.ByteBuffer b) throws java.io.IOException { + return DECODER.decode(b); + } + + @Deprecated public java.lang.CharSequence name; + @Deprecated public java.lang.Integer favorite_number; + @Deprecated public java.lang.CharSequence favorite_color; + + /** + * Default constructor. Note that this does not initialize fields + * to their default values from the schema. If that is desired then + * one should use newBuilder(). + */ + public User() {} + + /** + * All-args constructor. + * @param name The new value for name + * @param favorite_number The new value for favorite_number + * @param favorite_color The new value for favorite_color + */ + public User(java.lang.CharSequence name, java.lang.Integer favorite_number, java.lang.CharSequence favorite_color) { + this.name = name; + this.favorite_number = favorite_number; + this.favorite_color = favorite_color; + } + + public org.apache.avro.Schema getSchema() { return SCHEMA$; } + // Used by DatumWriter. Applications should not call. + public java.lang.Object get(int field$) { + switch (field$) { + case 0: return name; + case 1: return favorite_number; + case 2: return favorite_color; + default: throw new org.apache.avro.AvroRuntimeException("Bad index"); + } + } + + // Used by DatumReader. Applications should not call. + @SuppressWarnings(value="unchecked") + public void put(int field$, java.lang.Object value$) { + switch (field$) { + case 0: name = (java.lang.CharSequence)value$; break; + case 1: favorite_number = (java.lang.Integer)value$; break; + case 2: favorite_color = (java.lang.CharSequence)value$; break; + default: throw new org.apache.avro.AvroRuntimeException("Bad index"); + } + } + + /** + * Gets the value of the 'name' field. + * @return The value of the 'name' field. + */ + public java.lang.CharSequence getName() { + return name; + } + + /** + * Sets the value of the 'name' field. + * @param value the value to set. + */ + public void setName(java.lang.CharSequence value) { + this.name = value; + } + + /** + * Gets the value of the 'favorite_number' field. + * @return The value of the 'favorite_number' field. + */ + public java.lang.Integer getFavoriteNumber() { + return favorite_number; + } + + /** + * Sets the value of the 'favorite_number' field. + * @param value the value to set. + */ + public void setFavoriteNumber(java.lang.Integer value) { + this.favorite_number = value; + } + + /** + * Gets the value of the 'favorite_color' field. + * @return The value of the 'favorite_color' field. + */ + public java.lang.CharSequence getFavoriteColor() { + return favorite_color; + } + + /** + * Sets the value of the 'favorite_color' field. + * @param value the value to set. + */ + public void setFavoriteColor(java.lang.CharSequence value) { + this.favorite_color = value; + } + + /** + * Creates a new User RecordBuilder. + * @return A new User RecordBuilder + */ + public static org.gnuhpc.bigdata.service.avro.User.Builder newBuilder() { + return new org.gnuhpc.bigdata.service.avro.User.Builder(); + } + + /** + * Creates a new User RecordBuilder by copying an existing Builder. + * @param other The existing builder to copy. + * @return A new User RecordBuilder + */ + public static org.gnuhpc.bigdata.service.avro.User.Builder newBuilder(org.gnuhpc.bigdata.service.avro.User.Builder other) { + return new org.gnuhpc.bigdata.service.avro.User.Builder(other); + } + + /** + * Creates a new User RecordBuilder by copying an existing User instance. + * @param other The existing instance to copy. + * @return A new User RecordBuilder + */ + public static org.gnuhpc.bigdata.service.avro.User.Builder newBuilder(org.gnuhpc.bigdata.service.avro.User other) { + return new org.gnuhpc.bigdata.service.avro.User.Builder(other); + } + + /** + * RecordBuilder for User instances. + */ + public static class Builder extends org.apache.avro.specific.SpecificRecordBuilderBase + implements org.apache.avro.data.RecordBuilder { + + private java.lang.CharSequence name; + private java.lang.Integer favorite_number; + private java.lang.CharSequence favorite_color; + + /** Creates a new Builder */ + private Builder() { + super(SCHEMA$); + } + + /** + * Creates a Builder by copying an existing Builder. + * @param other The existing Builder to copy. + */ + private Builder(org.gnuhpc.bigdata.service.avro.User.Builder other) { + super(other); + if (isValidValue(fields()[0], other.name)) { + this.name = data().deepCopy(fields()[0].schema(), other.name); + fieldSetFlags()[0] = true; + } + if (isValidValue(fields()[1], other.favorite_number)) { + this.favorite_number = data().deepCopy(fields()[1].schema(), other.favorite_number); + fieldSetFlags()[1] = true; + } + if (isValidValue(fields()[2], other.favorite_color)) { + this.favorite_color = data().deepCopy(fields()[2].schema(), other.favorite_color); + fieldSetFlags()[2] = true; + } + } + + /** + * Creates a Builder by copying an existing User instance + * @param other The existing instance to copy. + */ + private Builder(org.gnuhpc.bigdata.service.avro.User other) { + super(SCHEMA$); + if (isValidValue(fields()[0], other.name)) { + this.name = data().deepCopy(fields()[0].schema(), other.name); + fieldSetFlags()[0] = true; + } + if (isValidValue(fields()[1], other.favorite_number)) { + this.favorite_number = data().deepCopy(fields()[1].schema(), other.favorite_number); + fieldSetFlags()[1] = true; + } + if (isValidValue(fields()[2], other.favorite_color)) { + this.favorite_color = data().deepCopy(fields()[2].schema(), other.favorite_color); + fieldSetFlags()[2] = true; + } + } + + /** + * Gets the value of the 'name' field. + * @return The value. + */ + public java.lang.CharSequence getName() { + return name; + } + + /** + * Sets the value of the 'name' field. + * @param value The value of 'name'. + * @return This builder. + */ + public org.gnuhpc.bigdata.service.avro.User.Builder setName(java.lang.CharSequence value) { + validate(fields()[0], value); + this.name = value; + fieldSetFlags()[0] = true; + return this; + } + + /** + * Checks whether the 'name' field has been set. + * @return True if the 'name' field has been set, false otherwise. + */ + public boolean hasName() { + return fieldSetFlags()[0]; + } + + + /** + * Clears the value of the 'name' field. + * @return This builder. + */ + public org.gnuhpc.bigdata.service.avro.User.Builder clearName() { + name = null; + fieldSetFlags()[0] = false; + return this; + } + + /** + * Gets the value of the 'favorite_number' field. + * @return The value. + */ + public java.lang.Integer getFavoriteNumber() { + return favorite_number; + } + + /** + * Sets the value of the 'favorite_number' field. + * @param value The value of 'favorite_number'. + * @return This builder. + */ + public org.gnuhpc.bigdata.service.avro.User.Builder setFavoriteNumber(java.lang.Integer value) { + validate(fields()[1], value); + this.favorite_number = value; + fieldSetFlags()[1] = true; + return this; + } + + /** + * Checks whether the 'favorite_number' field has been set. + * @return True if the 'favorite_number' field has been set, false otherwise. + */ + public boolean hasFavoriteNumber() { + return fieldSetFlags()[1]; + } + + + /** + * Clears the value of the 'favorite_number' field. + * @return This builder. + */ + public org.gnuhpc.bigdata.service.avro.User.Builder clearFavoriteNumber() { + favorite_number = null; + fieldSetFlags()[1] = false; + return this; + } + + /** + * Gets the value of the 'favorite_color' field. + * @return The value. + */ + public java.lang.CharSequence getFavoriteColor() { + return favorite_color; + } + + /** + * Sets the value of the 'favorite_color' field. + * @param value The value of 'favorite_color'. + * @return This builder. + */ + public org.gnuhpc.bigdata.service.avro.User.Builder setFavoriteColor(java.lang.CharSequence value) { + validate(fields()[2], value); + this.favorite_color = value; + fieldSetFlags()[2] = true; + return this; + } + + /** + * Checks whether the 'favorite_color' field has been set. + * @return True if the 'favorite_color' field has been set, false otherwise. + */ + public boolean hasFavoriteColor() { + return fieldSetFlags()[2]; + } + + + /** + * Clears the value of the 'favorite_color' field. + * @return This builder. + */ + public org.gnuhpc.bigdata.service.avro.User.Builder clearFavoriteColor() { + favorite_color = null; + fieldSetFlags()[2] = false; + return this; + } + + @Override + @SuppressWarnings("unchecked") + public User build() { + try { + User record = new User(); + record.name = fieldSetFlags()[0] ? this.name : (java.lang.CharSequence) defaultValue(fields()[0]); + record.favorite_number = fieldSetFlags()[1] ? this.favorite_number : (java.lang.Integer) defaultValue(fields()[1]); + record.favorite_color = fieldSetFlags()[2] ? this.favorite_color : (java.lang.CharSequence) defaultValue(fields()[2]); + return record; + } catch (java.lang.Exception e) { + throw new org.apache.avro.AvroRuntimeException(e); + } + } + } + + @SuppressWarnings("unchecked") + private static final org.apache.avro.io.DatumWriter + WRITER$ = (org.apache.avro.io.DatumWriter)MODEL$.createDatumWriter(SCHEMA$); + + @Override public void writeExternal(java.io.ObjectOutput out) + throws java.io.IOException { + WRITER$.write(this, SpecificData.getEncoder(out)); + } + + @SuppressWarnings("unchecked") + private static final org.apache.avro.io.DatumReader + READER$ = (org.apache.avro.io.DatumReader)MODEL$.createDatumReader(SCHEMA$); + + @Override public void readExternal(java.io.ObjectInput in) + throws java.io.IOException { + READER$.read(this, SpecificData.getDecoder(in)); + } + +} From 6c89dcbc4112a0e3f4cbbcf7524f41c203ff3064 Mon Sep 17 00:00:00 2001 From: tinawenqiao <315524513@qq.com> Date: Fri, 1 Feb 2019 16:24:24 +0800 Subject: [PATCH 14/24] Add get replica info on broker by logdir. Change describe consumer group by topic and it can support get all consumer infos by topic. --- .../bigdata/controller/KafkaController.java | 7 +- .../gnuhpc/bigdata/model/AddPartition.java | 1 - .../bigdata/service/KafkaAdminService.java | 74 ++++++++++++------- .../service/KafkaAdminServiceTest.java | 36 ++++++++- 4 files changed, 87 insertions(+), 31 deletions(-) diff --git a/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java b/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java index 5726907..be381aa 100644 --- a/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java +++ b/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java @@ -87,8 +87,9 @@ public Map> listLogDirs( @ApiOperation(value = "Describe log dirs by broker list and topic list") public Map> describeLogDirs( @RequestParam(required = false) List brokerList, + @RequestParam(required = false) List logDirList, @RequestParam(required = false) List topicList) { - return kafkaAdminService.describeLogDirsByBrokerAndTopic(brokerList, topicList); + return kafkaAdminService.describeLogDirsByBrokerAndTopic(brokerList, logDirList, topicList); } @GetMapping(value = "/brokers/replicalogdirs") @@ -292,10 +293,10 @@ public ConsumerGroupMeta getConsumerGroupMeta(@PathVariable String consumerGroup throw new ApiException("New consumer group:" + consumerGroup + " non-exist."); } - @GetMapping(value = "/consumergroups/{consumerGroup}/{type}/topic/{topic}") + @GetMapping(value = "/consumergroups/{type}/topic/{topic}") @ApiOperation(value = "Describe consumer groups by topic, showing lag and offset") public List describeConsumerGroupByTopic( - @ConsumerGroupExistConstraint @PathVariable String consumerGroup, + @RequestParam(required = false) String consumerGroup, @PathVariable ConsumerType type, @PathVariable String topic) { if (!Strings.isNullOrEmpty(topic)) { diff --git a/src/main/java/org/gnuhpc/bigdata/model/AddPartition.java b/src/main/java/org/gnuhpc/bigdata/model/AddPartition.java index 36b0af5..a75d7d0 100644 --- a/src/main/java/org/gnuhpc/bigdata/model/AddPartition.java +++ b/src/main/java/org/gnuhpc/bigdata/model/AddPartition.java @@ -17,6 +17,5 @@ public class AddPartition { String topic; int numPartitionsAdded; -// String replicaAssignment; List> replicaAssignment; } diff --git a/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java b/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java index 1231932..de03bda 100644 --- a/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java +++ b/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java @@ -442,7 +442,7 @@ public Map> listLogDirsByBroker(List brokerList) Map> logDirList = new HashMap<>(); Map> logDirInfosByBroker = - describeLogDirsByBrokerAndTopic(brokerList, null); + describeLogDirsByBrokerAndTopic(brokerList, null,null); logDirInfosByBroker .entrySet() .forEach( @@ -456,7 +456,7 @@ public Map> listLogDirsByBroker(List brokerList) } public Map> describeLogDirsByBrokerAndTopic( - List brokerList, List topicList) { + List brokerList, List logDirList, List topicList) { org.apache.kafka.clients.admin.AdminClient kafkaAdminClient = createKafkaAdminClient(); List brokerIdsInCluster = @@ -495,6 +495,11 @@ public Map> describeLogDirsByBrokerAndTopic( log.warn("Describe log dirs exception:" + exception); throw new ApiException("Describe log dirs exception:" + exception); } finally { + if (logDirList != null && !logDirList.isEmpty()) { + logDirInfosByBroker.entrySet().forEach(e->{ + e.getValue().entrySet().removeIf(m -> !logDirList.contains(m.getKey())); + }); + } if (topicList != null && !topicList.isEmpty()) { logDirInfosByBroker .entrySet() @@ -1288,12 +1293,7 @@ private JavaType getCollectionType( return mapper.getTypeFactory().constructParametricType(collectionClass, elementClasses); } - public List describeNewConsumerGroupByTopic( - String consumerGroup, String topic) { - if (!isNewConsumerGroup(consumerGroup)) { - throw new ApiException("ConsumerGroup:" + consumerGroup + " non-exist!"); - } - + private List getNewConsumerGroupDescByConsumerGroupAndTopic(String consumerGroup, String topic) { List partitionAssignmentStateList = describeNewConsumerGroup(consumerGroup, true, topic); @@ -1313,14 +1313,30 @@ public List describeNewConsumerGroupByTopic( .collect(Collectors.toList()); } - public List describeOldConsumerGroupByTopic( + public List describeNewConsumerGroupByTopic( String consumerGroup, @TopicExistConstraint String topic) { - if (!isOldConsumerGroup(consumerGroup)) { - throw new ApiException("ConsumerGroup:" + consumerGroup + " non-exist"); + if (consumerGroup != null && !isNewConsumerGroup(consumerGroup)) { + throw new ApiException("ConsumerGroup:" + consumerGroup + " non-exist!"); + } + + List consumerGroupDescList = new ArrayList<>(); + if (consumerGroup == null || consumerGroup.length() == 0) { + // To search all consumer groups + Set allNewConsumerGroups = listAllNewConsumerGroups(); + + for (String cg:allNewConsumerGroups) { + consumerGroupDescList.addAll(getNewConsumerGroupDescByConsumerGroupAndTopic(cg, topic)); + } + return consumerGroupDescList; + } else { + return getNewConsumerGroupDescByConsumerGroupAndTopic(consumerGroup, topic); } + } + private List getOldConsumerGroupDescByConsumerGroupAndTopic(String consumerGroup, String topic) { List partitionAssignmentStateList = describeOldConsumerGroup(consumerGroup, true, topic); + ConsumerGroupSummary consumerGroupSummary = null; return partitionAssignmentStateList @@ -1331,10 +1347,30 @@ public List describeOldConsumerGroupByTopic( consumerGroup, consumerGroupSummary, partitionAssignmentState, - ConsumerType.NEW)) + ConsumerType.OLD)) .collect(Collectors.toList()); } + public List describeOldConsumerGroupByTopic( + String consumerGroup, @TopicExistConstraint String topic) { + if (!isOldConsumerGroup(consumerGroup)) { + throw new ApiException("ConsumerGroup:" + consumerGroup + " non-exist"); + } + + List consumerGroupDescList = new ArrayList<>(); + if (consumerGroup == null || consumerGroup.length() == 0) { + // To search all consumer groups + Set allNewConsumerGroups = listAllNewConsumerGroups(); + + for (String cg:allNewConsumerGroups) { + consumerGroupDescList.addAll(getOldConsumerGroupDescByConsumerGroupAndTopic(cg, topic)); + } + return consumerGroupDescList; + } else { + return getOldConsumerGroupDescByConsumerGroupAndTopic(consumerGroup, topic); + } + } + public Map addPartitions(List addPartitions) { Map addPartitionsResult = new HashMap<>(); org.apache.kafka.clients.admin.AdminClient kafkaAdminClient = createKafkaAdminClient(); @@ -1833,19 +1869,7 @@ public Record getRecordByOffset( record.setOffset(offset); record.setTimestamp(initCr.timestamp()); record.setKey(initCr.key()); - if (decoder.equals("AvroDeserializer")) { - byte[] bytes = (byte[]) initCr.value(); - Schema schema = new Schema.Parser().parse(avroSchema); - DatumReader reader = new GenericDatumReader(schema); - ByteBuffer buffer = ByteBuffer.wrap(bytes); - Object object = - reader.read( - null, - DecoderFactory.get().binaryDecoder(buffer.array(), 0, bytes.length, null)); - record.setValue(object); - } else { - record.setValue(initCr.value()); - } + record.setValue(initCr.value()); break; } log.info( diff --git a/src/test/java/org/gnuhpc/bigdata/service/KafkaAdminServiceTest.java b/src/test/java/org/gnuhpc/bigdata/service/KafkaAdminServiceTest.java index c0a1f69..310b438 100644 --- a/src/test/java/org/gnuhpc/bigdata/service/KafkaAdminServiceTest.java +++ b/src/test/java/org/gnuhpc/bigdata/service/KafkaAdminServiceTest.java @@ -2115,7 +2115,7 @@ public void testDescribeLogDirsByBrokerAndTopic() { // Run the test final Map> result = - kafkaAdminServiceUnderTest.describeLogDirsByBrokerAndTopic(brokerList, topicList); + kafkaAdminServiceUnderTest.describeLogDirsByBrokerAndTopic(brokerList, null, topicList); // Verify the results assertTrue(result.containsKey(brokerId)); @@ -2136,6 +2136,38 @@ public void testDescribeLogDirsByBrokerAndTopic() { assertTrue(logDirExist); } + @Test + public void testDescribeLogDirsByBrokerLogDirAndTopic() { + // Setup + final String topic = FIRST_TOPIC_TO_TEST; + final List topicList = Arrays.asList(topic); + + int brokerId = TEST_KAFKA_BOOTSTRAP_SERVERS_ID.get(0); + final List brokerList = Arrays.asList(brokerId); + Map> replicaAssignment = new HashMap<>(); + replicaAssignment.put(0, brokerList); + + // Create first topic with 1 partition on broker 111 + createOneTopic(topic, replicaAssignment); + + TopicPartitionReplica topicPartitionReplica = new TopicPartitionReplica(topic, 0, brokerId); + Map replicaReplicaLogDirInfoMap = kafkaAdminServiceUnderTest.describeReplicaLogDirs(Arrays.asList(topicPartitionReplica)); + String logDir = replicaReplicaLogDirInfoMap.get(topicPartitionReplica).getCurrentReplicaLogDir(); + + // Run the test + final Map> result = + kafkaAdminServiceUnderTest.describeLogDirsByBrokerAndTopic(brokerList, Arrays.asList(logDir), topicList); + + // Verify the results + assertTrue(result.containsKey(brokerId)); + Map logDirInfoMap = result.get(brokerId); + assertEquals(1, logDirInfoMap.size()); + assertTrue(logDirInfoMap.containsKey(logDir)); + LogDirInfo logDirInfo = logDirInfoMap.get(logDir); + TopicPartition topicPartition = new TopicPartition(topic, 0); + assertTrue(logDirInfo.replicaInfos.containsKey(topicPartition)); + } + @Test public void testDescribeReplicaLogDirs() { // Setup @@ -2158,7 +2190,7 @@ public void testDescribeReplicaLogDirs() { String currentLogDir = result.get(topicPartitionReplica).getCurrentReplicaLogDir(); final Map> logDirsByBrokerAndTopic = kafkaAdminServiceUnderTest.describeLogDirsByBrokerAndTopic( - brokerList, Arrays.asList(topic)); + brokerList, null, Arrays.asList(topic)); Map logDirInfoMap = logDirsByBrokerAndTopic.get(brokerId); assertTrue(logDirInfoMap.containsKey(currentLogDir)); TopicPartition topicPartition = new TopicPartition(topic, 0); From 30fb00a7893a380f588bdfdaa5386a2e51bc43a1 Mon Sep 17 00:00:00 2001 From: tinawenqiao <315524513@qq.com> Date: Tue, 12 Feb 2019 11:03:00 +0800 Subject: [PATCH 15/24] Change getMessage api and modify some desc about deserializer. --- .../java/org/gnuhpc/bigdata/controller/KafkaController.java | 5 +++-- .../java/org/gnuhpc/bigdata/service/KafkaAdminService.java | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java b/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java index be381aa..3c46a9d 100644 --- a/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java +++ b/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java @@ -30,6 +30,7 @@ import org.gnuhpc.bigdata.model.ReassignModel; import org.gnuhpc.bigdata.model.ReassignStatus; import org.gnuhpc.bigdata.model.ReassignWrapper; +import org.gnuhpc.bigdata.model.Record; import org.gnuhpc.bigdata.model.TopicBrief; import org.gnuhpc.bigdata.model.TopicDetail; import org.gnuhpc.bigdata.model.TopicMeta; @@ -169,12 +170,12 @@ public boolean existTopic(@PathVariable String topic) { value = "Get the message from the offset of the partition in the topic" + ", decoder is not supported yet") - public String getMessage( + public Record getMessage( @PathVariable String topic, @PathVariable int partition, @PathVariable long offset, @RequestParam(required = false) String decoder) { - return kafkaAdminService.getRecordByOffset(topic, partition, offset, decoder, "").getValue(); + return kafkaAdminService.getRecordByOffset(topic, partition, offset, decoder, ""); } @GetMapping(value = "/topics/{topic}") diff --git a/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java b/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java index de03bda..f324dd6 100644 --- a/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java +++ b/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java @@ -1816,7 +1816,7 @@ public Record getRecordByOffset( "Bad request. Decoder class:" + decoder + " not found. ByteArrayDeserializer, ByteBufferDeserializer, BytesDeserializer, " - + "DoubleDeserializer, ExtendedDeserializer.Wrapper, FloatDeserializer, " + + "DoubleDeserializer, FloatDeserializer, " + "IntegerDeserializer, LongDeserializer, ShortDeserializer, StringDeserializer, AvroDeserializer " + "are supported."); } From faa24603d98901afdee438a53c4b3fe800d71cb4 Mon Sep 17 00:00:00 2001 From: tinawenqiao <315524513@qq.com> Date: Tue, 12 Feb 2019 15:16:25 +0800 Subject: [PATCH 16/24] Modify pom.xml. --- docs/definitions.adoc | 315 +- docs/index.html | 14445 +++++++--------- docs/index.pdf | Bin 1023450 -> 1289556 bytes docs/paths.adoc | 642 +- pom.xml | 8 +- src/docs/swagger/swagger.json | 2471 +-- .../KafkaRestSpringbootApplicationTests.java | 50 - 7 files changed, 7308 insertions(+), 10623 deletions(-) delete mode 100644 src/test/java/org/gnuhpc/bigdata/KafkaRestSpringbootApplicationTests.java diff --git a/docs/definitions.adoc b/docs/definitions.adoc index 1833743..940cb86 100644 --- a/docs/definitions.adoc +++ b/docs/definitions.adoc @@ -11,7 +11,7 @@ |**numPartitionsAdded** + __optional__|integer(int32) |**replicaAssignment** + -__optional__|string +__optional__|< < integer(int32) > array > array |**topic** + __optional__|string |=== @@ -23,7 +23,7 @@ __optional__|string [options="header", cols=".^3,.^4"] |=== |Name|Schema -|**endPoints** + +|**endpoints** + __optional__|< string > array |**host** + __optional__|string @@ -44,14 +44,35 @@ __optional__|integer(int32) |=== +[[_clusterinfo]] +=== ClusterInfo + +[options="header", cols=".^3,.^4"] +|=== +|Name|Schema +|**clusterId** + +__optional__|string +|**controller** + +__optional__|<<_node,Node>> +|**nodes** + +__optional__|< <<_node,Node>> > array +|=== + + [[_consumergroupdesc]] === ConsumerGroupDesc [options="header", cols=".^3,.^4"] |=== |Name|Schema +|**assignmentStrategy** + +__optional__|string +|**clientId** + +__optional__|string |**consumerId** + __optional__|string +|**coordinator** + +__optional__|<<_node,Node>> |**currentOffset** + __optional__|integer(int64) |**groupName** + @@ -65,7 +86,7 @@ __optional__|integer(int64) |**partitionId** + __optional__|integer(int32) |**state** + -__optional__|enum (RUNNING, PENDING) +__optional__|enum (Unknown, PreparingRebalance, CompletingRebalance, Stable, Dead, Empty) |**topic** + __optional__|string |**type** + @@ -73,12 +94,75 @@ __optional__|enum (NEW, OLD) |=== +[[_consumergroupmeta]] +=== ConsumerGroupMeta + +[options="header", cols=".^3,.^4"] +|=== +|Name|Schema +|**assignmentStrategy** + +__optional__|string +|**coordinator** + +__optional__|<<_node,Node>> +|**groupId** + +__optional__|string +|**members** + +__optional__|< <<_memberdescription,MemberDescription>> > array +|**state** + +__optional__|enum (Unknown, PreparingRebalance, CompletingRebalance, Stable, Dead, Empty) +|=== + + +[[_customconfigentry]] +=== CustomConfigEntry + +[options="header", cols=".^3,.^4"] +|=== +|Name|Schema +|**isReadOnly** + +__optional__|boolean +|**isSensitive** + +__optional__|boolean +|**name** + +__optional__|string +|**readOnly** + +__optional__|boolean +|**sensitive** + +__optional__|boolean +|**source** + +__optional__|enum (DYNAMIC_TOPIC_CONFIG, DYNAMIC_BROKER_CONFIG, DYNAMIC_DEFAULT_BROKER_CONFIG, STATIC_BROKER_CONFIG, DEFAULT_CONFIG, UNKNOWN) +|**value** + +__optional__|string +|=== + + +[[_customtopicpartitioninfo]] +=== CustomTopicPartitionInfo + +[options="header", cols=".^3,.^4"] +|=== +|Name|Schema +|**endOffset** + +__optional__|integer(int64) +|**in_sync** + +__optional__|boolean +|**messageAvailable** + +__optional__|integer(int64) +|**startOffset** + +__optional__|integer(int64) +|**topicPartitionInfo** + +__optional__|<<_topicpartitioninfo,TopicPartitionInfo>> +|=== + + [[_generalresponse]] === GeneralResponse [options="header", cols=".^3,.^4"] |=== |Name|Schema +|**data** + +__optional__|object |**msg** + __optional__|string |**state** + @@ -112,6 +196,10 @@ __optional__|**Example** : `"yyyy-MM-dd HH:mm:ss"`|string [options="header", cols=".^3,.^4"] |=== |Name|Schema +|**hasBracketlessColons** + +__optional__|boolean +|**host** + +__optional__|string |**hostText** + __optional__|string |**port** + @@ -204,11 +292,69 @@ __optional__|< <<_jmxconfiguration,JMXConfiguration>> > array |=== +[[_logdirinfo]] +=== LogDirInfo + +[options="header", cols=".^3,.^4"] +|=== +|Name|Schema +|**error** + +__optional__|enum (UNKNOWN_SERVER_ERROR, NONE, OFFSET_OUT_OF_RANGE, CORRUPT_MESSAGE, UNKNOWN_TOPIC_OR_PARTITION, INVALID_FETCH_SIZE, LEADER_NOT_AVAILABLE, NOT_LEADER_FOR_PARTITION, REQUEST_TIMED_OUT, BROKER_NOT_AVAILABLE, REPLICA_NOT_AVAILABLE, MESSAGE_TOO_LARGE, STALE_CONTROLLER_EPOCH, OFFSET_METADATA_TOO_LARGE, NETWORK_EXCEPTION, COORDINATOR_LOAD_IN_PROGRESS, COORDINATOR_NOT_AVAILABLE, NOT_COORDINATOR, INVALID_TOPIC_EXCEPTION, RECORD_LIST_TOO_LARGE, NOT_ENOUGH_REPLICAS, NOT_ENOUGH_REPLICAS_AFTER_APPEND, INVALID_REQUIRED_ACKS, ILLEGAL_GENERATION, INCONSISTENT_GROUP_PROTOCOL, INVALID_GROUP_ID, UNKNOWN_MEMBER_ID, INVALID_SESSION_TIMEOUT, REBALANCE_IN_PROGRESS, INVALID_COMMIT_OFFSET_SIZE, TOPIC_AUTHORIZATION_FAILED, GROUP_AUTHORIZATION_FAILED, CLUSTER_AUTHORIZATION_FAILED, INVALID_TIMESTAMP, UNSUPPORTED_SASL_MECHANISM, ILLEGAL_SASL_STATE, UNSUPPORTED_VERSION, TOPIC_ALREADY_EXISTS, INVALID_PARTITIONS, INVALID_REPLICATION_FACTOR, INVALID_REPLICA_ASSIGNMENT, INVALID_CONFIG, NOT_CONTROLLER, INVALID_REQUEST, UNSUPPORTED_FOR_MESSAGE_FORMAT, POLICY_VIOLATION, OUT_OF_ORDER_SEQUENCE_NUMBER, DUPLICATE_SEQUENCE_NUMBER, INVALID_PRODUCER_EPOCH, INVALID_TXN_STATE, INVALID_PRODUCER_ID_MAPPING, INVALID_TRANSACTION_TIMEOUT, CONCURRENT_TRANSACTIONS, TRANSACTION_COORDINATOR_FENCED, TRANSACTIONAL_ID_AUTHORIZATION_FAILED, SECURITY_DISABLED, OPERATION_NOT_ATTEMPTED, KAFKA_STORAGE_ERROR, LOG_DIR_NOT_FOUND, SASL_AUTHENTICATION_FAILED, UNKNOWN_PRODUCER_ID, REASSIGNMENT_IN_PROGRESS, DELEGATION_TOKEN_AUTH_DISABLED, DELEGATION_TOKEN_NOT_FOUND, DELEGATION_TOKEN_OWNER_MISMATCH, DELEGATION_TOKEN_REQUEST_NOT_ALLOWED, DELEGATION_TOKEN_AUTHORIZATION_FAILED, DELEGATION_TOKEN_EXPIRED, INVALID_PRINCIPAL_TYPE, NON_EMPTY_GROUP, GROUP_ID_NOT_FOUND, FETCH_SESSION_ID_NOT_FOUND, INVALID_FETCH_SESSION_EPOCH) +|**replicaInfos** + +__optional__|< string, <<_replicainfo,ReplicaInfo>> > map +|=== + + [[_bbed2f02db402d6ae09a0dcf86682c45]] === Map«int,long» __Type__ : < string, integer(int64) > map +[[_95991ed347668e76e825df5c80149c08]] +=== Map«string,LogDirInfo» +__Type__ : < string, <<_logdirinfo,LogDirInfo>> > map + + +[[_memberdescription]] +=== MemberDescription + +[options="header", cols=".^3,.^4"] +|=== +|Name|Schema +|**assignment** + +__optional__|< <<_topicpartition,TopicPartition>> > array +|**clientId** + +__optional__|string +|**host** + +__optional__|string +|**memberId** + +__optional__|string +|=== + + +[[_node]] +=== Node + +[options="header", cols=".^3,.^4"] +|=== +|Name|Schema +|**empty** + +__optional__|boolean +|**hash** + +__optional__|integer(int32) +|**host** + +__optional__|string +|**id** + +__optional__|integer(int32) +|**idString** + +__optional__|string +|**port** + +__optional__|integer(int32) +|**rack** + +__optional__|string +|=== + + [[_pattern]] === Pattern @@ -217,6 +363,40 @@ __Type__ : < string, integer(int64) > map |Name|Schema |**cursor** + __optional__|integer(int32) +|**flags** + +__optional__|integer(int32) +|**pattern** + +__optional__|string +|=== + + +[[_reassignmodel]] +=== ReassignModel + +[options="header", cols=".^3,.^4"] +|=== +|Name|Schema +|**partitions** + +__optional__|< <<_topicpartitionreplicaassignment,TopicPartitionReplicaAssignment>> > array +|**version** + +__optional__|integer(int32) +|=== + + +[[_reassignstatus]] +=== ReassignStatus + +[options="header", cols=".^3,.^4"] +|=== +|Name|Schema +|**msg** + +__optional__|string +|**partitionsReassignStatus** + +__optional__|< string, integer(int32) > map +|**removeThrottle** + +__optional__|boolean +|**replicasReassignStatus** + +__optional__|< string, integer(int32) > map |=== @@ -233,9 +413,57 @@ __optional__|< string > array |=== -[[_topicandpartition]] -=== TopicAndPartition -__Type__ : object +[[_record]] +=== Record + +[options="header", cols=".^3,.^4"] +|=== +|Name|Schema +|**decoder** + +__optional__|string +|**key** + +__optional__|object +|**offset** + +__optional__|integer(int64) +|**timestamp** + +__optional__|integer(int64) +|**topic** + +__optional__|string +|**value** + +__optional__|string +|=== + + +[[_replicainfo]] +=== ReplicaInfo + +[options="header", cols=".^3,.^4"] +|=== +|Name|Schema +|**isFuture** + +__optional__|boolean +|**offsetLag** + +__optional__|integer(int64) +|**size** + +__optional__|integer(int64) +|=== + + +[[_replicalogdirinfo]] +=== ReplicaLogDirInfo + +[options="header", cols=".^3,.^4"] +|=== +|Name|Schema +|**currentReplicaLogDir** + +__optional__|string +|**currentReplicaOffsetLag** + +__optional__|integer(int64) +|**futureReplicaLogDir** + +__optional__|string +|**futureReplicaOffsetLag** + +__optional__|integer(int64) +|=== [[_topicbrief]] @@ -267,6 +495,8 @@ __optional__|string __optional__|integer(int32) |**prop** + __optional__|< string, object > map +|**replicasAssignments** + +__optional__|< string, < integer(int32) > array > map |=== @@ -276,16 +506,31 @@ __optional__|< string, object > map [options="header", cols=".^3,.^4"] |=== |Name|Schema +|**internal** + +__optional__|boolean |**partitionCount** + __optional__|integer(int32) |**replicationFactor** + __optional__|integer(int32) -|**topicCustomConfigs** + -__optional__|< string, object > map |**topicName** + __optional__|string |**topicPartitionInfos** + -__optional__|< <<_topicpartitioninfo,TopicPartitionInfo>> > array +__optional__|< <<_customtopicpartitioninfo,CustomTopicPartitionInfo>> > array +|=== + + +[[_topicpartition]] +=== TopicPartition + +[options="header", cols=".^3,.^4"] +|=== +|Name|Schema +|**hash** + +__optional__|integer(int32) +|**partition** + +__optional__|integer(int32) +|**topic** + +__optional__|string |=== @@ -295,22 +540,48 @@ __optional__|< <<_topicpartitioninfo,TopicPartitionInfo>> > array [options="header", cols=".^3,.^4"] |=== |Name|Schema -|**endOffset** + -__optional__|integer(int64) -|**in_sync** + -__optional__|boolean |**isr** + -__optional__|< string > array +__optional__|< <<_node,Node>> > array |**leader** + -__optional__|string -|**messageAvailable** + -__optional__|integer(int64) -|**partitionId** + +__optional__|<<_node,Node>> +|**partition** + __optional__|integer(int32) |**replicas** + +__optional__|< <<_node,Node>> > array +|=== + + +[[_topicpartitionreplica]] +=== TopicPartitionReplica + +[options="header", cols=".^3,.^4"] +|=== +|Name|Schema +|**brokerId** + +__optional__|integer(int32) +|**hash** + +__optional__|integer(int32) +|**partition** + +__optional__|integer(int32) +|**topic** + +__optional__|string +|=== + + +[[_topicpartitionreplicaassignment]] +=== TopicPartitionReplicaAssignment + +[options="header", cols=".^3,.^4"] +|=== +|Name|Schema +|**log_dirs** + __optional__|< string > array -|**startOffset** + -__optional__|integer(int64) +|**partition** + +__optional__|integer(int32) +|**replicas** + +__optional__|< integer(int32) > array +|**topic** + +__optional__|string |=== @@ -380,7 +651,9 @@ __optional__|integer(int32) |**minLatency** + __optional__|integer(int32) |**mode** + -__optional__|enum (Leader, Follower, Observer) +__optional__|enum (Leader, Follower, Observer, Standalone, Down, Unknow) +|**msg** + +__optional__|string |**nodes** + __optional__|integer(int32) |**outstanding** + diff --git a/docs/index.html b/docs/index.html index d5fe9b4..c3cf3ec 100644 --- a/docs/index.html +++ b/docs/index.html @@ -1,7894 +1,6577 @@ - - - - - Kafka REST API SwaggerUI - - + + + + +Kafka REST API SwaggerUI + +
-
-

1. Overview

-
-
-

Kafka REST API SwaggerUI

-
-
-

1.1. - Version information

-
-

Version : 0.1.0

-
-
-
-

1.2. - Contact information

-
-

Contact : gnuhpc
- Contact Email : gnuhpc@gmail.com

-
-
-
-

1.3. URI scheme

-
-

Host : localhost:8080
- BasePath : /

-
-
-
-

1.4. Tags

-
-
    -
  • -

    collector-controller : Rest API for Collecting JMX Metric Data

    -
  • -
  • -

    kafka-controller : Kafka Controller

    -
  • -
  • -

    user-controller : Security User Management Controller.

    -
  • -
  • -

    zookeeper-controller : Zookeeper Controller

    -
  • -
-
-
-
-
-
-

2. Chapter of - manual content 1

-
-
-

This is some dummy text

-
-
-

2.1. Sub chapter

-
-

Dummy text of sub chapter

-
-
-
-
-
-

3. Chapter of - manual content 2

-
-
-

This is some dummy text

-
-
-
-
-

4. Resources

-
-
-

4.1. - Collector-controller

-
-

Rest API for Collecting JMX Metric Data

-
-
-

4.1.1. Fetch - all JMX metric data

-
-
-
GET /jmx/v1
-
-
-
-
Parameters
- - - - - - - - - - - - - - - - - - - - - - - -
TypeNameDescriptionSchema

- Query

jmxurl
- optional

Parameter - jmxurl should be a comma-separated list of {IP:Port} or set to 'default'

string

-
-
-
-
Responses
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

- 200

OK

< JMXMetricDataV1 > array

- 401

- Unauthorized

No - Content

- 403

Forbidden

-

No - Content

- 404

Not Found

-

No - Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.1.2. Fetch - JMX metric data with query filter. You can get the query filter template through the API - /jmx/v2/filters.

-
-
-
POST /jmx/v2
-
-
-
-
Parameters
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TypeNameDescriptionSchema

- Query

jmxurl
- optional

Parameter - jmxurl should be a comma-separated list of {IP:Port} or set to 'default'

string

-

- Body

jmxQuery
- required

jmxQuery

-

JMXQuery

-
-
-
Responses
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

- 200

OK

< JMXMetricData > array

- 201

Created

-

No - Content

- 401

- Unauthorized

No - Content

- 403

Forbidden

-

No - Content

- 404

Not Found

-

No - Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.1.3. - List the query filter templates with the filterKey. If filterKey is set to empty, it - will return all the templates.

-
-
-
GET /jmx/v2/filters
-
-
-
-
Parameters
- - - - - - - - - - - - - - - - - - - - - - - -
TypeNameDescriptionSchema

- Query

filterKey
- required

filterKey

-

string

-
-
-
-
Responses
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

- 200

OK

< string, - object > map

- 401

- Unauthorized

No - Content

- 403

Forbidden

-

No - Content

- 404

Not Found

-

No - Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-
-

4.2. - Kafka-controller

-
-

Kafka Controller

-
-
-

4.2.1. - List brokers in this cluster

-
-
-
GET /kafka/brokers
-
-
-
-
Responses
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

- 200

OK

< BrokerInfo > array

- 401

- Unauthorized

No - Content

- 403

Forbidden

-

No - Content

- 404

Not Found

-

No - Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.2.2. Get - the message from the offset of the partition in the topic, decoder is not supported yet -

-
-
-
GET /kafka/consumer/{topic}/{partition}/{offset}
-
-
-
-
Parameters
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TypeNameDescriptionSchema

- Path

offset
- required

offset

-

- integer(int64)

- Path

partition
- required

partition

-

- integer(int32)

- Path

- topic
- required

topic

-

string

-

- Query

decoder
- optional

decoder

-

string

-
-
-
-
Responses
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

- 200

OK

string

-

- 401

- Unauthorized

No - Content

- 403

Forbidden

-

No - Content

- 404

Not Found

-

No - Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.2.3. - Delete old Consumer Group

-
-
-
DELETE /kafka/consumergroup/{consumergroup}
-
-
-
-
Parameters
- - - - - - - - - - - - - - - - - - - - - - - -
TypeNameDescriptionSchema

- Path

consumergroup
- required

- consumergroup

string

-
-
-
-
Responses
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

- 200

OK

GeneralResponse

- 204

No - Content

No - Content

- 401

- Unauthorized

No - Content

- 403

Forbidden

-

No - Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.2.4. - getLastCommitTimestamp

-
-
-
GET /kafka/consumergroup/{consumergroup}/{type}/topic/{topic}/lastcommittime
-
-
-
-
Parameters
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TypeNameDescriptionSchema

- Path

consumergroup
- required

- consumergroup

string

-

- Path

- topic
- required

topic

-

string

-

- Path

- type
- required

type

enum (NEW, - OLD)

-
-
-
Responses
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

- 200

OK

< string, - < string, integer(int64) > map > map

- 401

- Unauthorized

No - Content

- 403

Forbidden

-

No - Content

- 404

Not Found

-

No - Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.2.5. - Reset consumer group offset, earliest/latest can be used

-
-
-
PUT /kafka/consumergroup/{consumergroup}/{type}/topic/{topic}/{partition}/{offset}
-
-
-
-
Parameters
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TypeNameDescriptionSchema

- Path

consumergroup
- required

- consumergroup

string

-

- Path

offset
- required

offset

-

string

-

- Path

partition
- required

partition

-

- integer(int32)

- Path

- topic
- required

topic

-

string

-

- Path

- type
- required

type

enum (NEW, - OLD)

-
-
-
Responses
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

- 200

OK

GeneralResponse

- 201

Created

-

No - Content

- 401

- Unauthorized

No - Content

- 403

Forbidden

-

No - Content

- 404

Not Found

-

No - Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.2.6. - List all consumer groups from zk and kafka

-
-
-
GET /kafka/consumergroups
-
-
-
-
Parameters
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TypeNameDescriptionSchema

- Query

- topic
- optional

topic

-

string

-

- Query

- type
- optional

type

enum (NEW, - OLD)

-
-
-
Responses
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

- 200

OK

< string, - < string > array > map

- 401

- Unauthorized

No - Content

- 403

Forbidden

-

No - Content

- 404

Not Found

-

No - Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.2.7. - Describe consumer groups, showing lag and offset, may be slow if multi topic are - listened

-
-
-
GET /kafka/consumergroups/{consumerGroup}/{type}
-
-
-
-
Parameters
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TypeNameDescriptionSchema

- Path

consumerGroup
- required

- consumerGroup

string

-

- Path

- type
- required

type

enum (NEW, - OLD)

-
-
-
Responses
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

- 200

OK

< string, - < ConsumerGroupDesc > array > map

-

- 401

- Unauthorized

No - Content

- 403

Forbidden

-

No - Content

- 404

Not Found

-

No - Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.2.8. - Get the topics involved of the specify consumer group

-
-
-
GET /kafka/consumergroups/{consumerGroup}/{type}/topic
-
-
-
-
Parameters
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TypeNameDescriptionSchema

- Path

consumerGroup
- required

- consumerGroup

string

-

- Path

- type
- required

type

enum (NEW, - OLD)

-
-
-
Responses
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

- 200

OK

< string - > array

- 401

- Unauthorized

No - Content

- 403

Forbidden

-

No - Content

- 404

Not Found

-

No - Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.2.9. - Describe consumer groups by topic, showing lag and offset

-
-
-
GET /kafka/consumergroups/{consumerGroup}/{type}/topic/{topic}
-
-
-
-
Parameters
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TypeNameDescriptionSchema

- Path

consumerGroup
- required

- consumerGroup

string

-

- Path

- topic
- required

topic

-

string

-

- Path

- type
- required

type

enum (NEW, - OLD)

-
-
-
Responses
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

- 200

OK

< ConsumerGroupDesc > array

- 401

- Unauthorized

No - Content

- 403

Forbidden

-

No - Content

- 404

Not Found

-

No - Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.2.10. - Check the cluster health.

-
-
-
GET /kafka/health
-
-
-
-
Responses
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

- 200

OK

HealthCheckResult

- 401

- Unauthorized

No - Content

- 403

Forbidden

-

No - Content

- 404

Not Found

-

No - Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.2.11. - Add a partition to the topic

-
-
-
POST /kafka/partitions/add
-
-
-
-
Parameters
- - - - - - - - - - - - - - - - - - - - - - - -
TypeNameDescriptionSchema

- Body

addPartition
- required

- addPartition

AddPartition

-
-
-
Responses
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

- 200

OK

TopicMeta

- 201

Created

-

No - Content

- 401

- Unauthorized

No - Content

- 403

Forbidden

-

No - Content

- 404

Not Found

-

No - Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.2.12. - Check the partition reassignment process

-
-
-
PUT /kafka/partitions/reassign/check
-
-
-
-
Parameters
- - - - - - - - - - - - - - - - - - - - - - - -
TypeNameDescriptionSchema

- Body

reassignStr
- required

- reassignStr

string

-
-
-
-
Responses
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

- -1

Reassignment - Failed

No - Content

- 0

Reassignment - In Progress

No - Content

- 1

Reassignment - Completed

No - Content

- 200

OK

< string, - integer(int32) > map

- 201

Created

-

No - Content

- 401

- Unauthorized

No - Content

- 403

Forbidden

-

No - Content

- 404

Not Found

-

No - Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.2.13. - Execute the partition reassignment

-
-
-
PUT /kafka/partitions/reassign/execute
-
-
-
-
Parameters
- - - - - - - - - - - - - - - - - - - - - - - -
TypeNameDescriptionSchema

- Body

reassignStr
- required

- reassignStr

string

-
-
-
-
Responses
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

- 200

OK

< string, - integer(int32) > map

- 201

Created

-

No - Content

- 401

- Unauthorized

No - Content

- 403

Forbidden

-

No - Content

- 404

Not Found

-

No - Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.2.14. - Generate plan for the partition reassignment

-
-
-
POST /kafka/partitions/reassign/generate
-
-
-
-
Parameters
- - - - - - - - - - - - - - - - - - - - - - - -
TypeNameDescriptionSchema

- Body

reassignWrapper
- required

- reassignWrapper

ReassignWrapper

-
-
-
Responses
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

- 200

OK

< string - > array

- 201

Created

-

No - Content

- 401

- Unauthorized

No - Content

- 403

Forbidden

-

No - Content

- 404

Not Found

-

No - Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.2.15. - List topics

-
-
-
GET /kafka/topics
-
-
-
-
Responses
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

- 200

OK

< string - > array

- 401

- Unauthorized

No - Content

- 403

Forbidden

-

No - Content

- 404

Not Found

-

No - Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.2.16. - Create a topic

-
-
-
POST /kafka/topics/create
-
-
-
-
Parameters
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TypeNameDescriptionSchema

- Query

reassignStr
- optional

- reassignStr

string

-

- Body

- topic
- required

topic

-

TopicDetail

-
-
-
Responses
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

- 201

Created

-

TopicMeta

- 401

- Unauthorized

No - Content

- 403

Forbidden

-

No - Content

- 404

Not Found

-

No - Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.2.17. - Describe a topic by fetching the metadata and config

-
-
-
GET /kafka/topics/{topic}
-
-
-
-
Parameters
- - - - - - - - - - - - - - - - - - - - - - - -
TypeNameDescriptionSchema

- Path

- topic
- required

topic

-

string

-
-
-
-
Responses
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

- 200

OK

TopicMeta

- 401

- Unauthorized

No - Content

- 403

Forbidden

-

No - Content

- 404

Not Found

-

No - Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.2.18. - Delete a topic (you should enable topic deletion

-
-
-
DELETE /kafka/topics/{topic}
-
-
-
-
Parameters
- - - - - - - - - - - - - - - - - - - - - - - -
TypeNameDescriptionSchema

- Path

- topic
- required

topic

-

string

-
-
-
-
Responses
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

- 200

OK

GeneralResponse

- 204

No - Content

No - Content

- 401

- Unauthorized

No - Content

- 403

Forbidden

-

No - Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.2.19. - Create topic configs

-
-
-
POST /kafka/topics/{topic}/conf
-
-
-
-
Parameters
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TypeNameDescriptionSchema

- Path

- topic
- required

topic

-

string

-

- Body

- prop
- required

prop

< string, - object > map

-
-
-
Responses
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

- 200

OK

< string, - object > map

- 201

Created

-

No - Content

- 401

- Unauthorized

No - Content

- 403

Forbidden

-

No - Content

- 404

Not Found

-

No - Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.2.20. - Get topic configs

-
-
-
GET /kafka/topics/{topic}/conf
-
-
-
-
Parameters
- - - - - - - - - - - - - - - - - - - - - - - -
TypeNameDescriptionSchema

- Path

- topic
- required

topic

-

string

-
-
-
-
Responses
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

- 200

OK

< string, - object > map

- 401

- Unauthorized

No - Content

- 403

Forbidden

-

No - Content

- 404

Not Found

-

No - Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.2.21. - Update topic configs

-
-
-
PUT /kafka/topics/{topic}/conf
-
-
-
-
Parameters
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TypeNameDescriptionSchema

- Path

- topic
- required

topic

-

string

-

- Body

- prop
- required

prop

< string, - object > map

-
-
-
Responses
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

- 200

OK

< string, - object > map

- 201

Created

-

No - Content

- 401

- Unauthorized

No - Content

- 403

Forbidden

-

No - Content

- 404

Not Found

-

No - Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.2.22. - Delete topic configs

-
-
-
DELETE /kafka/topics/{topic}/conf
-
-
-
-
Parameters
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TypeNameDescriptionSchema

- Path

- topic
- required

topic

-

string

-

- Body

delProps
- required

delProps

-

< string - > array

-
-
-
Responses
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

- 200

OK

< string, - object > map

- 204

No - Content

No - Content

- 401

- Unauthorized

No - Content

- 403

Forbidden

-

No - Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.2.23. - Get topic config by key

-
-
-
GET /kafka/topics/{topic}/conf/{key}
-
-
-
-
Parameters
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TypeNameDescriptionSchema

- Path

- key
- required

key

string

-

- Path

- topic
- required

topic

-

string

-
-
-
-
Responses
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

- 200

OK

< string, - object > map

- 401

- Unauthorized

No - Content

- 403

Forbidden

-

No - Content

- 404

Not Found

-

No - Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.2.24. - Delete a topic config by key

-
-
-
DELETE /kafka/topics/{topic}/conf/{key}
-
-
-
-
Parameters
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TypeNameDescriptionSchema

- Path

- key
- required

key

string

-

- Path

- topic
- required

topic

-

string

-
-
-
-
Responses
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

- 200

OK

boolean

-

- 204

No - Content

No - Content

- 401

- Unauthorized

No - Content

- 403

Forbidden

-

No - Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.2.25. - Create a topic config by key

-
-
-
POST /kafka/topics/{topic}/conf/{key}={value}
-
-
-
-
Parameters
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TypeNameDescriptionSchema

- Path

- key
- required

key

string

-

- Path

- topic
- required

topic

-

string

-

- Path

- value
- required

value

-

string

-
-
-
-
Responses
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

- 200

OK

< string, - object > map

- 201

Created

-

No - Content

- 401

- Unauthorized

No - Content

- 403

Forbidden

-

No - Content

- 404

Not Found

-

No - Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.2.26. - Update a topic config by key

-
-
-
PUT /kafka/topics/{topic}/conf/{key}={value}
-
-
-
-
Parameters
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TypeNameDescriptionSchema

- Path

- key
- required

key

string

-

- Path

- topic
- required

topic

-

string

-

- Path

- value
- required

value

-

string

-
-
-
-
Responses
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

- 200

OK

< string, - object > map

- 201

Created

-

No - Content

- 401

- Unauthorized

No - Content

- 403

Forbidden

-

No - Content

- 404

Not Found

-

No - Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.2.27. - Tell if a topic exists

-
-
-
GET /kafka/topics/{topic}/exist
-
-
-
-
Parameters
- - - - - - - - - - - - - - - - - - - - - - - -
TypeNameDescriptionSchema

- Path

- topic
- required

topic

-

string

-
-
-
-
Responses
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

- 200

OK

boolean

-

- 401

- Unauthorized

No - Content

- 403

Forbidden

-

No - Content

- 404

Not Found

-

No - Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.2.28. - Write a message to the topic, for testing purpose

-
-
-
POST /kafka/topics/{topic}/write
-
-
-
-
Parameters
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TypeNameDescriptionSchema

- Path

- topic
- required

topic

-

string

-

- Body

message
- required

message

-

string

-
-
-
-
Responses
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

- 201

Created

-

GeneralResponse

- 401

- Unauthorized

No - Content

- 403

Forbidden

-

No - Content

- 404

Not Found

-

No - Content

-
-
-
Consumes
-
-
    -
  • -

    text/plain

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.2.29. - List topics Brief

-
-
-
GET /kafka/topicsbrief
-
-
-
-
Responses
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

- 200

OK

< TopicBrief > array

- 401

- Unauthorized

No - Content

- 403

Forbidden

-

No - Content

- 404

Not Found

-

No - Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-
-

4.3. - User-controller

-
-

Security User Management Controller.

-
-
-

4.3.1. Add - user.

-
-
-
POST /users
-
-
-
-
Parameters
- - - - - - - - - - - - - - - - - - - - - - - -
TypeNameDescriptionSchema

- Body

- user
- required

user

User

-
-
-
Responses
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

- 200

OK

GeneralResponse

- 201

Created

-

No - Content

- 401

- Unauthorized

No - Content

- 403

Forbidden

-

No - Content

- 404

Not Found

-

No - Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.3.2. Get user - list.

-
-
-
GET /users
-
-
-
-
Responses
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

- 200

OK

< string - > array

- 401

- Unauthorized

No - Content

- 403

Forbidden

-

No - Content

- 404

Not Found

-

No - Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.3.3. - Modify user information.

-
-
-
PUT /users
-
-
-
-
Parameters
- - - - - - - - - - - - - - - - - - - - - - - -
TypeNameDescriptionSchema

- Body

- user
- required

user

User

-
-
-
Responses
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

- 200

OK

GeneralResponse

- 201

Created

-

No - Content

- 401

- Unauthorized

No - Content

- 403

Forbidden

-

No - Content

- 404

Not Found

-

No - Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.3.4. - Delete user.

-
-
-
DELETE /users/{username}
-
-
-
-
Parameters
- - - - - - - - - - - - - - - - - - - - - - - -
TypeNameDescriptionSchema

- Path

username
- required

username

-

string

-
-
-
-
Responses
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

- 200

OK

GeneralResponse

- 204

No - Content

No - Content

- 401

- Unauthorized

No - Content

- 403

Forbidden

-

No - Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-
-

4.4. - Zookeeper-controller

-
-

Zookeeper Controller

-
-
-

4.4.1. - Get the connection state of zookeeper

-
-
-
GET /zk/connstate
-
-
-
-
Responses
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

- 200

OK

string

-

- 401

- Unauthorized

No - Content

- 403

Forbidden

-

No - Content

- 404

Not Found

-

No - Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.4.2. Get the - environment information of zookeeper

-
-
-
GET /zk/env
-
-
-
-
Responses
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

- 200

OK

< string, - ZkServerEnvironment > map

- 401

- Unauthorized

No - Content

- 403

Forbidden

-

No - Content

- 404

Not Found

-

No - Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.4.3. List a zookeeper - path

-
-
-
GET /zk/ls/{path}
-
-
-
-
Parameters
- - - - - - - - - - - - - - - - - - - - - - - -
TypeNameDescriptionSchema

- Path

- path
- required

path

string

-
-
-
-
Responses
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

- 200

OK

< string - > array

- 401

- Unauthorized

No - Content

- 403

Forbidden

-

No - Content

- 404

Not Found

-

No - Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-

4.4.4. Get the - service state of zookeeper

-
-
-
GET /zk/stat
-
-
-
-
Responses
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

- 200

OK

< string, - ZkServerStat > map

- 401

- Unauthorized

No - Content

- 403

Forbidden

-

No - Content

- 404

Not Found

-

No - Content

-
-
-
Consumes
-
-
    -
  • -

    application/json

    -
  • -
-
-
-
-
Produces
-
-
    -
  • -

    /

    -
  • -
-
-
-
-
-
-
-
-

5. Definitions

-
-
-

5.1. AddPartition

- - - - - - - - - - - - - - - - - - - - - - - - - -
NameSchema

numPartitionsAdded
- optional

- integer(int32)

replicaAssignment
- optional

string

- topic
- optional

string

-
-
-

5.2. BrokerInfo

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameSchema

- endPoints
- optional

< string > - array

- host
- optional

string

- id
- optional

- integer(int32)

- jmxPort
- optional

- integer(int32)

- port
- optional

- integer(int32)

- rack
- optional

string

securityProtocol
- optional

object

- startTime
- optional

- string(date-time)

- version
- optional

- integer(int32)

-
-
-

5.3. - ConsumerGroupDesc

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameSchema

consumerId
- optional

string

currentOffset
- optional

- integer(int64)

- groupName
- optional

string

- host
- optional

string

- lag
- optional

- integer(int64)

logEndOffset
- optional

- integer(int64)

partitionId
- optional

- integer(int32)

- state
- optional

enum (RUNNING, - PENDING)

- topic
- optional

string

- type
- optional

enum (NEW, - OLD)

-
-
-

5.4. - GeneralResponse

- - - - - - - - - - - - - - - - - - - - - -
NameSchema

- msg
- optional

string

- state
- optional

enum (success, - failure)

-
-
-

5.5. - HashMap«string,object»

-
-

Type : < string, object > map

-
-
-
-

5.6. - HealthCheckResult

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionSchema

- msg
- optional

string

- status
- optional

string

- timestamp
- optional

- Example : "yyyy-MM-dd HH:mm:ss"

string

-
-
-

5.7. HostAndPort

- - - - - - - - - - - - - - - - - - - - - -
NameSchema

- hostText
- optional

string

- port
- optional

- integer(int32)

-
-
-

5.8. - JMXConfiguration

- - - - - - - - - - - - - - - - - - - - - -
NameSchema

- exclude
- optional

JMXFilter

- include
- optional

JMXFilter

-
-
-

5.9. JMXFilter

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameSchema

- attribute
- optional

object

- beanNames
- optional

< string > - array

beanRegexes
- optional

< Pattern > array

- domain
- optional

string

domainRegex
- optional

Pattern

emptyBeanName
- optional

boolean

- filter
- optional

< string, - object > map

-
-
-

5.10. JMXMetricData -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionSchema

- collected
- optional

boolean

- host
- optional

string

- metrics
- optional

< HashMap«string,object» > array

-

- msg
- optional

string

- timestamp
- optional

- Example : "yyyy-MM-dd HH:mm:ss"

string

-
-
-

5.11. - JMXMetricDataV1

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionSchema

- collected
- optional

boolean

- host
- optional

string

- mbeanInfo
- optional

object

- msg
- optional

string

- timestamp
- optional

- Example : "yyyy-MM-dd HH:mm:ss"

string

-
-
-

5.12. JMXQuery

- - - - - - - - - - - - - - - - - -
NameSchema

- filters
- optional

< JMXConfiguration > array

-
-
-

5.13. - Map«int,long»

-
-

Type : < string, integer(int64) > map

-
-
-
-

5.14. Pattern

- - - - - - - - - - - - - - - - - -
NameSchema

- cursor
- optional

- integer(int32)

-
-
-

5.15. - ReassignWrapper

- - - - - - - - - - - - - - - - - - - - - -
NameSchema

- brokers
- optional

< - integer(int32) > array

- topics
- optional

< string > - array

-
-
-

5.16. - TopicAndPartition

-
-

Type : object

-
-
-
-

5.17. TopicBrief

- - - - - - - - - - - - - - - - - - - - - - - - - -
NameSchema

- isrRate
- optional

- number(double)

numPartition
- optional

- integer(int32)

- topic
- optional

string

-
-
-

5.18. TopicDetail

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameSchema

- factor
- optional

- integer(int32)

- name
- optional

string

partitions
- optional

- integer(int32)

- prop
- optional

< string, - object > map

-
-
-

5.19. TopicMeta

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameSchema

partitionCount
- optional

- integer(int32)

replicationFactor
- optional

- integer(int32)

topicCustomConfigs
- optional

< string, - object > map

- topicName
- optional

string

topicPartitionInfos
- optional

< TopicPartitionInfo > array

-
-
-

5.20. - TopicPartitionInfo

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameSchema

- endOffset
- optional

- integer(int64)

- in_sync
- optional

boolean

- isr
- optional

< string > - array

- leader
- optional

string

messageAvailable
- optional

- integer(int64)

partitionId
- optional

- integer(int32)

- replicas
- optional

< string > - array

startOffset
- optional

- integer(int64)

-
-
-

5.21. User

- - - - - - - - - - - - - - - - - - - - - - - - - -
NameSchema

- password
- optional

string

- role
- optional

string

- username
- optional

string

-
-
-

5.22. ZkServerClient -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameSchema

- host
- optional

string

- ops
- optional

- integer(int32)

- port
- optional

- integer(int32)

- queued
- optional

- integer(int32)

- received
- optional

- integer(int32)

- sent
- optional

- integer(int32)

-
-
-

5.23. - ZkServerEnvironment

- - - - - - - - - - - - - - - - - -
NameSchema

attributes
- optional

< string, - string > map

-
-
-

5.24. ZkServerStat

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameSchema

avgLatency
- optional

- integer(int32)

- buildDate
- optional

string

- clients
- optional

< ZkServerClient > array

connections
- optional

- integer(int32)

maxLatency
- optional

- integer(int32)

minLatency
- optional

- integer(int32)

- mode
- optional

enum (Leader, - Follower, Observer)

- nodes
- optional

- integer(int32)

outstanding
- optional

- integer(int32)

- received
- optional

- integer(int32)

- sent
- optional

- integer(int32)

- version
- optional

string

- zxId
- optional

string

-
-
-
+
+

1. Overview

+
+
+

Kafka REST API SwaggerUI

+
+
+

1.1. Version information

+
+

Version : 0.1.0

+
+
+
+

1.2. Contact information

+
+

Contact : gnuhpc
+Contact Email : gnuhpc@gmail.com

+
+
+
+

1.3. URI scheme

+
+

Host : localhost:8080
+BasePath : /

+
+
+
+

1.4. Tags

+
+
    +
  • +

    collector-controller : Rest API for Collecting JMX Metric Data

    +
  • +
  • +

    kafka-controller : Kafka Controller

    +
  • +
  • +

    user-controller : Security User Management Controller.

    +
  • +
  • +

    zookeeper-controller : Zookeeper Controller

    +
  • +
+
+
+
+
+
+

2. Chapter of manual content 1

+
+
+

This is some dummy text

+
+
+

2.1. Sub chapter

+
+

Dummy text of sub chapter

+
+
+
+
+
+

3. Chapter of manual content 2

+
+
+

This is some dummy text

+
+
+
+
+

4. Resources

+
+
+

4.1. Collector-controller

+
+

Rest API for Collecting JMX Metric Data

+
+
+

4.1.1. Fetch all JMX metric data

+
+
+
GET /jmx/v1
+
+
+
+
Parameters
+ ++++++ + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

Query

jmxurl
+optional

Parameter jmxurl should be a comma-separated list of {IP:Port} or set to 'default'

string

+
+
+
Responses
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

200

OK

< JMXMetricDataV1 > array

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.1.2. Fetch JMX metric data with query filter. You can get the query filter template through the API /jmx/v2/filters.

+
+
+
POST /jmx/v2
+
+
+
+
Parameters
+ ++++++ + + + + + + + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

Query

jmxurl
+optional

Parameter jmxurl should be a comma-separated list of {IP:Port} or set to 'default'

string

Body

jmxQuery
+required

jmxQuery

JMXQuery

+
+
+
Responses
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

200

OK

< JMXMetricData > array

201

Created

No Content

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.1.3. List the query filter templates with the filterKey. If filterKey is set to empty, it will return all the templates.

+
+
+
GET /jmx/v2/filters
+
+
+
+
Parameters
+ ++++++ + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

Query

filterKey
+required

filterKey

string

+
+
+
Responses
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

200

OK

< string, object > map

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+
+

4.2. Kafka-controller

+
+

Kafka Controller

+
+
+

4.2.1. List brokers in this cluster

+
+
+
GET /kafka/brokers
+
+
+
+
Responses
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

200

OK

< BrokerInfo > array

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.2.2. List log dirs by broker list

+
+
+
GET /kafka/brokers/logdirs
+
+
+
+
Parameters
+ ++++++ + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

Query

brokerList
+optional

brokerList

< integer(int32) > array(multi)

+
+
+
Responses
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

200

OK

< string, < string > array > map

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.2.3. Describe log dirs by broker list and topic list

+
+
+
GET /kafka/brokers/logdirs/detail
+
+
+
+
Parameters
+ ++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

Query

brokerList
+optional

brokerList

< integer(int32) > array(multi)

Query

logDirList
+optional

logDirList

< string > array(multi)

Query

topicList
+optional

topicList

< string > array(multi)

+
+
+
Responses
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

200

OK

< string, < string, LogDirInfo > map > map

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.2.4. Describe replica log dirs.

+
+
+
GET /kafka/brokers/replicalogdirs
+
+
+
+
Parameters
+ ++++++ + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

Query

replicas
+required

replicas

string

+
+
+
Responses
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

200

OK

< string, ReplicaLogDirInfo > map

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.2.5. Get broker configs, including dynamic configs

+
+
+
GET /kafka/brokers/{brokerId}/conf
+
+
+
+
Parameters
+ ++++++ + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

Path

brokerId
+required

brokerId

integer(int32)

+
+
+
Responses
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

200

OK

< CustomConfigEntry > array

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.2.6. Get broker dynamic configs

+
+
+
GET /kafka/brokers/{brokerId}/dynconf
+
+
+
+
Parameters
+ ++++++ + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

Path

brokerId
+required

brokerId

integer(int32)

+
+
+
Responses
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

200

OK

< string, object > map

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.2.7. Update broker configs

+
+
+
PUT /kafka/brokers/{brokerId}/dynconf
+
+
+
+
Parameters
+ ++++++ + + + + + + + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

Path

brokerId
+required

brokerId

integer(int32)

Body

props
+required

props

< string, object > map

+
+
+
Responses
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

200

OK

< string, object > map

201

Created

No Content

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.2.8. Remove broker dynamic configs

+
+
+
DELETE /kafka/brokers/{brokerId}/dynconf
+
+
+
+
Parameters
+ ++++++ + + + + + + + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

Path

brokerId
+required

brokerId

integer(int32)

Query

configKeysToBeRemoved
+required

configKeysToBeRemoved

< string > array(multi)

+
+
+
Responses
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

200

OK

No Content

204

No Content

No Content

401

Unauthorized

No Content

403

Forbidden

No Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.2.9. Describe cluster, nodes, controller info.

+
+
+
GET /kafka/cluster
+
+
+
+
Responses
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

200

OK

ClusterInfo

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.2.10. Get the message from the offset of the partition in the topic, decoder is not supported yet

+
+
+
GET /kafka/consumer/{topic}/{partition}/{offset}
+
+
+
+
Parameters
+ ++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

Path

offset
+required

offset

integer(int64)

Path

partition
+required

partition

integer(int32)

Path

topic
+required

topic

string

Query

decoder
+optional

decoder

string

+
+
+
Responses
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

200

OK

Record

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.2.11. Delete Consumer Group

+
+
+
DELETE /kafka/consumergroup/{consumergroup}/{type}
+
+
+
+
Parameters
+ ++++++ + + + + + + + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

Path

consumergroup
+required

consumergroup

string

Path

type
+required

type

enum (NEW, OLD)

+
+
+
Responses
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

200

OK

GeneralResponse

204

No Content

No Content

401

Unauthorized

No Content

403

Forbidden

No Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.2.12. getLastCommitTimestamp

+
+
+
GET /kafka/consumergroup/{consumergroup}/{type}/topic/{topic}/lastcommittime
+
+
+
+
Parameters
+ ++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

Path

consumergroup
+required

consumergroup

string

Path

topic
+required

topic

string

Path

type
+required

type

enum (NEW, OLD)

+
+
+
Responses
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

200

OK

< string, < string, integer(int64) > map > map

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.2.13. Reset consumer group offset, earliest/latest can be used. Support reset by time for new consumer group, pass a parameter that satisfies yyyy-MM-dd HH:mm:ss.SSS to offset.

+
+
+
PUT /kafka/consumergroup/{consumergroup}/{type}/topic/{topic}/{partition}/{offset}
+
+
+
+
Parameters
+ ++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

Path

consumergroup
+required

consumergroup

string

Path

offset
+optional

[earliest/latest/{long}/yyyy-MM-dd HH:mm:ss.SSS] can be supported. The date type is only valid for new consumer group.

string

Path

partition
+required

partition

integer(int32)

Path

topic
+required

topic

string

Path

type
+required

type

enum (NEW, OLD)

+
+
+
Responses
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

200

OK

GeneralResponse

201

Created

No Content

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.2.14. List all consumer groups from zk and kafka

+
+
+
GET /kafka/consumergroups
+
+
+
+
Parameters
+ ++++++ + + + + + + + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

Query

topic
+optional

topic

string

Query

type
+optional

type

enum (NEW, OLD)

+
+
+
Responses
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

200

OK

< string, < string > array > map

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.2.15. Get the meta data of the specify new consumer group, including state, coordinator, assignmentStrategy, members

+
+
+
GET /kafka/consumergroups/{consumerGroup}/meta
+
+
+
+
Parameters
+ ++++++ + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

Path

consumerGroup
+required

consumerGroup

string

+
+
+
Responses
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

200

OK

ConsumerGroupMeta

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.2.16. Describe consumer group, showing lag and offset, may be slow if multi topics are listened

+
+
+
GET /kafka/consumergroups/{consumerGroup}/{type}
+
+
+
+
Parameters
+ ++++++ + + + + + + + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

Path

consumerGroup
+required

consumerGroup

string

Path

type
+required

type

enum (NEW, OLD)

+
+
+
Responses
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

200

OK

< string, < ConsumerGroupDesc > array > map

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.2.17. Get the topics involved of the specify consumer group

+
+
+
GET /kafka/consumergroups/{consumerGroup}/{type}/topic
+
+
+
+
Parameters
+ ++++++ + + + + + + + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

Path

consumerGroup
+required

consumerGroup

string

Path

type
+required

type

enum (NEW, OLD)

+
+
+
Responses
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

200

OK

< string > array

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.2.18. Describe consumer groups by topic, showing lag and offset

+
+
+
GET /kafka/consumergroups/{type}/topic/{topic}
+
+
+
+
Parameters
+ ++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

Path

topic
+required

topic

string

Path

type
+required

type

enum (NEW, OLD)

Query

consumerGroup
+optional

consumerGroup

string

+
+
+
Responses
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

200

OK

< ConsumerGroupDesc > array

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.2.19. Get controller in this cluster

+
+
+
GET /kafka/controller
+
+
+
+
Responses
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

200

OK

Node

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.2.20. Check the cluster health.

+
+
+
GET /kafka/health
+
+
+
+
Responses
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

200

OK

HealthCheckResult

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.2.21. Add partitions to the topics

+
+
+
POST /kafka/partitions/add
+
+
+
+
Parameters
+ ++++++ + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

Body

addPartitions
+required

addPartitions

< AddPartition > array

+
+
+
Responses
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

200

OK

< string, GeneralResponse > map

201

Created

No Content

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.2.22. Check the partition reassignment process

+
+
+
PUT /kafka/partitions/reassign/check
+
+
+
+
Parameters
+ ++++++ + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

Body

reassign
+required

reassign

ReassignModel

+
+
+
Responses
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

-1

Reassignment Failed

No Content

0

Reassignment In Progress

No Content

1

Reassignment Completed

No Content

200

OK

ReassignStatus

201

Created

No Content

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.2.23. Execute the partition reassignment

+
+
+
PUT /kafka/partitions/reassign/execute
+
+
+
+
Parameters
+ ++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

Query

interBrokerThrottle
+optional

interBrokerThrottle

integer(int64)

Query

replicaAlterLogDirsThrottle
+optional

replicaAlterLogDirsThrottle

integer(int64)

Query

timeoutMs
+optional

timeoutMs

integer(int64)

Body

reassign
+required

reassign

ReassignModel

+
+
+
Responses
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

200

OK

ReassignStatus

201

Created

No Content

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.2.24. Generate plan for the partition reassignment

+
+
+
POST /kafka/partitions/reassign/generate
+
+
+
+
Parameters
+ ++++++ + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

Body

reassignWrapper
+required

reassignWrapper

ReassignWrapper

+
+
+
Responses
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

200

OK

< ReassignModel > array

201

Created

No Content

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.2.25. List topics

+
+
+
GET /kafka/topics
+
+
+
+
Responses
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

200

OK

< string > array

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.2.26. Delete a topic list (you should enable topic deletion

+
+
+
DELETE /kafka/topics
+
+
+
+
Parameters
+ ++++++ + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

Query

topicList
+required

topicList

< string > array(multi)

+
+
+
Responses
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

200

OK

< string, GeneralResponse > map

204

No Content

No Content

401

Unauthorized

No Content

403

Forbidden

No Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.2.27. Create topics

+
+
+
POST /kafka/topics/create
+
+
+
+
Parameters
+ ++++++ + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

Body

topicList
+required

topicList

< TopicDetail > array

+
+
+
Responses
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

201

Created

< string, GeneralResponse > map

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.2.28. Describe a topic by fetching the metadata and config

+
+
+
GET /kafka/topics/{topic}
+
+
+
+
Parameters
+ ++++++ + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

Path

topic
+required

topic

string

+
+
+
Responses
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

200

OK

TopicMeta

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.2.29. Get topic configs

+
+
+
GET /kafka/topics/{topic}/conf
+
+
+
+
Parameters
+ ++++++ + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

Path

topic
+required

topic

string

+
+
+
Responses
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

200

OK

< CustomConfigEntry > array

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.2.30. Update topic configs

+
+
+
PUT /kafka/topics/{topic}/conf
+
+
+
+
Parameters
+ ++++++ + + + + + + + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

Path

topic
+required

topic

string

Body

props
+required

props

< string, object > map

+
+
+
Responses
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

200

OK

< CustomConfigEntry > array

201

Created

No Content

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.2.31. Get topic config by key

+
+
+
GET /kafka/topics/{topic}/conf/{key}
+
+
+
+
Parameters
+ ++++++ + + + + + + + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

Path

key
+required

key

string

Path

topic
+required

topic

string

+
+
+
Responses
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

200

OK

< string, object > map

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.2.32. Update a topic config by key

+
+
+
PUT /kafka/topics/{topic}/conf/{key}={value}
+
+
+
+
Parameters
+ ++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

Path

key
+required

key

string

Path

topic
+required

topic

string

Path

value
+required

value

string

+
+
+
Responses
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

200

OK

< CustomConfigEntry > array

201

Created

No Content

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.2.33. Get topic dyn configs

+
+
+
GET /kafka/topics/{topic}/dynconf
+
+
+
+
Parameters
+ ++++++ + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

Path

topic
+required

topic

string

+
+
+
Responses
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

200

OK

< string, object > map

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.2.34. Tell if a topic exists

+
+
+
GET /kafka/topics/{topic}/exist
+
+
+
+
Parameters
+ ++++++ + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

Path

topic
+required

topic

string

+
+
+
Responses
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

200

OK

boolean

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.2.35. List topics Brief

+
+
+
GET /kafka/topicsbrief
+
+
+
+
Responses
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

200

OK

< TopicBrief > array

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+
+

4.3. User-controller

+
+

Security User Management Controller.

+
+
+

4.3.1. Add user.

+
+
+
POST /users
+
+
+
+
Parameters
+ ++++++ + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

Body

user
+required

user

User

+
+
+
Responses
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

200

OK

GeneralResponse

201

Created

No Content

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.3.2. Get user list.

+
+
+
GET /users
+
+
+
+
Responses
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

200

OK

< string > array

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.3.3. Modify user information.

+
+
+
PUT /users
+
+
+
+
Parameters
+ ++++++ + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

Body

user
+required

user

User

+
+
+
Responses
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

200

OK

GeneralResponse

201

Created

No Content

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.3.4. Delete user.

+
+
+
DELETE /users/{username}
+
+
+
+
Parameters
+ ++++++ + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

Path

username
+required

username

string

+
+
+
Responses
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

200

OK

GeneralResponse

204

No Content

No Content

401

Unauthorized

No Content

403

Forbidden

No Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+
+

4.4. Zookeeper-controller

+
+

Zookeeper Controller

+
+
+

4.4.1. Get the connection state of zookeeper

+
+
+
GET /zk/connstate
+
+
+
+
Responses
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

200

OK

string

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.4.2. Get the environment information of zookeeper

+
+
+
GET /zk/env
+
+
+
+
Responses
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

200

OK

< string, ZkServerEnvironment > map

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.4.3. Get data of a zookeeper path

+
+
+
GET /zk/get/path
+
+
+
+
Parameters
+ ++++++ + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

Query

path
+required

path

string

+
+
+
Responses
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

200

OK

< string, string > map

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.4.4. List a zookeeper path

+
+
+
GET /zk/ls/path
+
+
+
+
Parameters
+ ++++++ + + + + + + + + + + + + + + + + +
TypeNameDescriptionSchema

Query

path
+required

path

string

+
+
+
Responses
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

200

OK

< string > array

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+

4.4.5. Get the service state of zookeeper

+
+
+
GET /zk/stat
+
+
+
+
Responses
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

200

OK

< string, ZkServerStat > map

401

Unauthorized

No Content

403

Forbidden

No Content

404

Not Found

No Content

+
+
+
Consumes
+
+
    +
  • +

    application/json

    +
  • +
+
+
+
+
Produces
+
+
    +
  • +

    /

    +
  • +
+
+
+
+
+
+
+
+

5. Definitions

+
+
+

5.1. AddPartition

+ ++++ + + + + + + + + + + + + + + + + + + + + +
NameSchema

numPartitionsAdded
+optional

integer(int32)

replicaAssignment
+optional

< < integer(int32) > array > array

topic
+optional

string

+
+
+

5.2. BrokerInfo

+ ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameSchema

endpoints
+optional

< string > array

host
+optional

string

id
+optional

integer(int32)

jmxPort
+optional

integer(int32)

port
+optional

integer(int32)

rack
+optional

string

securityProtocol
+optional

object

startTime
+optional

string(date-time)

version
+optional

integer(int32)

+
+
+

5.3. ClusterInfo

+ ++++ + + + + + + + + + + + + + + + + + + + + +
NameSchema

clusterId
+optional

string

controller
+optional

Node

nodes
+optional

< Node > array

+
+
+

5.4. ConsumerGroupDesc

+ ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameSchema

assignmentStrategy
+optional

string

clientId
+optional

string

consumerId
+optional

string

coordinator
+optional

Node

currentOffset
+optional

integer(int64)

groupName
+optional

string

host
+optional

string

lag
+optional

integer(int64)

logEndOffset
+optional

integer(int64)

partitionId
+optional

integer(int32)

state
+optional

enum (Unknown, PreparingRebalance, CompletingRebalance, Stable, Dead, Empty)

topic
+optional

string

type
+optional

enum (NEW, OLD)

+
+
+

5.5. ConsumerGroupMeta

+ ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameSchema

assignmentStrategy
+optional

string

coordinator
+optional

Node

groupId
+optional

string

members
+optional

< MemberDescription > array

state
+optional

enum (Unknown, PreparingRebalance, CompletingRebalance, Stable, Dead, Empty)

+
+
+

5.6. CustomConfigEntry

+ ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameSchema

isReadOnly
+optional

boolean

isSensitive
+optional

boolean

name
+optional

string

readOnly
+optional

boolean

sensitive
+optional

boolean

source
+optional

enum (DYNAMIC_TOPIC_CONFIG, DYNAMIC_BROKER_CONFIG, DYNAMIC_DEFAULT_BROKER_CONFIG, STATIC_BROKER_CONFIG, DEFAULT_CONFIG, UNKNOWN)

value
+optional

string

+
+
+

5.7. CustomTopicPartitionInfo

+ ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameSchema

endOffset
+optional

integer(int64)

in_sync
+optional

boolean

messageAvailable
+optional

integer(int64)

startOffset
+optional

integer(int64)

topicPartitionInfo
+optional

TopicPartitionInfo

+
+
+

5.8. GeneralResponse

+ ++++ + + + + + + + + + + + + + + + + + + + + +
NameSchema

data
+optional

object

msg
+optional

string

state
+optional

enum (success, failure)

+
+
+

5.9. HashMap«string,object»

+
+

Type : < string, object > map

+
+
+
+

5.10. HealthCheckResult

+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + +
NameDescriptionSchema

msg
+optional

string

status
+optional

string

timestamp
+optional

Example : "yyyy-MM-dd HH:mm:ss"

string

+
+
+

5.11. HostAndPort

+ ++++ + + + + + + + + + + + + + + + + + + + + + + + + +
NameSchema

hasBracketlessColons
+optional

boolean

host
+optional

string

hostText
+optional

string

port
+optional

integer(int32)

+
+
+

5.12. JMXConfiguration

+ ++++ + + + + + + + + + + + + + + + + +
NameSchema

exclude
+optional

JMXFilter

include
+optional

JMXFilter

+
+
+

5.13. JMXFilter

+ ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameSchema

attribute
+optional

object

beanNames
+optional

< string > array

beanRegexes
+optional

< Pattern > array

domain
+optional

string

domainRegex
+optional

Pattern

emptyBeanName
+optional

boolean

filter
+optional

< string, object > map

+
+
+

5.14. JMXMetricData

+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameDescriptionSchema

collected
+optional

boolean

host
+optional

string

metrics
+optional

< HashMap«string,object» > array

msg
+optional

string

timestamp
+optional

Example : "yyyy-MM-dd HH:mm:ss"

string

+
+
+

5.15. JMXMetricDataV1

+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameDescriptionSchema

collected
+optional

boolean

host
+optional

string

mbeanInfo
+optional

object

msg
+optional

string

timestamp
+optional

Example : "yyyy-MM-dd HH:mm:ss"

string

+
+
+

5.16. JMXQuery

+ ++++ + + + + + + + + + + + + +
NameSchema

filters
+optional

< JMXConfiguration > array

+
+
+

5.17. LogDirInfo

+ ++++ + + + + + + + + + + + + + + + + +
NameSchema

error
+optional

enum (UNKNOWN_SERVER_ERROR, NONE, OFFSET_OUT_OF_RANGE, CORRUPT_MESSAGE, UNKNOWN_TOPIC_OR_PARTITION, INVALID_FETCH_SIZE, LEADER_NOT_AVAILABLE, NOT_LEADER_FOR_PARTITION, REQUEST_TIMED_OUT, BROKER_NOT_AVAILABLE, REPLICA_NOT_AVAILABLE, MESSAGE_TOO_LARGE, STALE_CONTROLLER_EPOCH, OFFSET_METADATA_TOO_LARGE, NETWORK_EXCEPTION, COORDINATOR_LOAD_IN_PROGRESS, COORDINATOR_NOT_AVAILABLE, NOT_COORDINATOR, INVALID_TOPIC_EXCEPTION, RECORD_LIST_TOO_LARGE, NOT_ENOUGH_REPLICAS, NOT_ENOUGH_REPLICAS_AFTER_APPEND, INVALID_REQUIRED_ACKS, ILLEGAL_GENERATION, INCONSISTENT_GROUP_PROTOCOL, INVALID_GROUP_ID, UNKNOWN_MEMBER_ID, INVALID_SESSION_TIMEOUT, REBALANCE_IN_PROGRESS, INVALID_COMMIT_OFFSET_SIZE, TOPIC_AUTHORIZATION_FAILED, GROUP_AUTHORIZATION_FAILED, CLUSTER_AUTHORIZATION_FAILED, INVALID_TIMESTAMP, UNSUPPORTED_SASL_MECHANISM, ILLEGAL_SASL_STATE, UNSUPPORTED_VERSION, TOPIC_ALREADY_EXISTS, INVALID_PARTITIONS, INVALID_REPLICATION_FACTOR, INVALID_REPLICA_ASSIGNMENT, INVALID_CONFIG, NOT_CONTROLLER, INVALID_REQUEST, UNSUPPORTED_FOR_MESSAGE_FORMAT, POLICY_VIOLATION, OUT_OF_ORDER_SEQUENCE_NUMBER, DUPLICATE_SEQUENCE_NUMBER, INVALID_PRODUCER_EPOCH, INVALID_TXN_STATE, INVALID_PRODUCER_ID_MAPPING, INVALID_TRANSACTION_TIMEOUT, CONCURRENT_TRANSACTIONS, TRANSACTION_COORDINATOR_FENCED, TRANSACTIONAL_ID_AUTHORIZATION_FAILED, SECURITY_DISABLED, OPERATION_NOT_ATTEMPTED, KAFKA_STORAGE_ERROR, LOG_DIR_NOT_FOUND, SASL_AUTHENTICATION_FAILED, UNKNOWN_PRODUCER_ID, REASSIGNMENT_IN_PROGRESS, DELEGATION_TOKEN_AUTH_DISABLED, DELEGATION_TOKEN_NOT_FOUND, DELEGATION_TOKEN_OWNER_MISMATCH, DELEGATION_TOKEN_REQUEST_NOT_ALLOWED, DELEGATION_TOKEN_AUTHORIZATION_FAILED, DELEGATION_TOKEN_EXPIRED, INVALID_PRINCIPAL_TYPE, NON_EMPTY_GROUP, GROUP_ID_NOT_FOUND, FETCH_SESSION_ID_NOT_FOUND, INVALID_FETCH_SESSION_EPOCH)

replicaInfos
+optional

< string, ReplicaInfo > map

+
+
+

5.18. Map«int,long»

+
+

Type : < string, integer(int64) > map

+
+
+
+

5.19. Map«string,LogDirInfo»

+
+

Type : < string, LogDirInfo > map

+
+
+
+

5.20. MemberDescription

+ ++++ + + + + + + + + + + + + + + + + + + + + + + + + +
NameSchema

assignment
+optional

< TopicPartition > array

clientId
+optional

string

host
+optional

string

memberId
+optional

string

+
+
+

5.21. Node

+ ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameSchema

empty
+optional

boolean

hash
+optional

integer(int32)

host
+optional

string

id
+optional

integer(int32)

idString
+optional

string

port
+optional

integer(int32)

rack
+optional

string

+
+
+

5.22. Pattern

+ ++++ + + + + + + + + + + + + + + + + + + + + +
NameSchema

cursor
+optional

integer(int32)

flags
+optional

integer(int32)

pattern
+optional

string

+
+
+

5.23. ReassignModel

+ ++++ + + + + + + + + + + + + + + + + +
NameSchema

partitions
+optional

< TopicPartitionReplicaAssignment > array

version
+optional

integer(int32)

+
+
+

5.24. ReassignStatus

+ ++++ + + + + + + + + + + + + + + + + + + + + + + + + +
NameSchema

msg
+optional

string

partitionsReassignStatus
+optional

< string, integer(int32) > map

removeThrottle
+optional

boolean

replicasReassignStatus
+optional

< string, integer(int32) > map

+
+
+

5.25. ReassignWrapper

+ ++++ + + + + + + + + + + + + + + + + +
NameSchema

brokers
+optional

< integer(int32) > array

topics
+optional

< string > array

+
+
+

5.26. Record

+ ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameSchema

decoder
+optional

string

key
+optional

object

offset
+optional

integer(int64)

timestamp
+optional

integer(int64)

topic
+optional

string

value
+optional

string

+
+
+

5.27. ReplicaInfo

+ ++++ + + + + + + + + + + + + + + + + + + + + +
NameSchema

isFuture
+optional

boolean

offsetLag
+optional

integer(int64)

size
+optional

integer(int64)

+
+
+

5.28. ReplicaLogDirInfo

+ ++++ + + + + + + + + + + + + + + + + + + + + + + + + +
NameSchema

currentReplicaLogDir
+optional

string

currentReplicaOffsetLag
+optional

integer(int64)

futureReplicaLogDir
+optional

string

futureReplicaOffsetLag
+optional

integer(int64)

+
+
+

5.29. TopicBrief

+ ++++ + + + + + + + + + + + + + + + + + + + + +
NameSchema

isrRate
+optional

number(double)

numPartition
+optional

integer(int32)

topic
+optional

string

+
+
+

5.30. TopicDetail

+ ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameSchema

factor
+optional

integer(int32)

name
+optional

string

partitions
+optional

integer(int32)

prop
+optional

< string, object > map

replicasAssignments
+optional

< string, < integer(int32) > array > map

+
+
+

5.31. TopicMeta

+ ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameSchema

internal
+optional

boolean

partitionCount
+optional

integer(int32)

replicationFactor
+optional

integer(int32)

topicName
+optional

string

topicPartitionInfos
+optional

< CustomTopicPartitionInfo > array

+
+
+

5.32. TopicPartition

+ ++++ + + + + + + + + + + + + + + + + + + + + +
NameSchema

hash
+optional

integer(int32)

partition
+optional

integer(int32)

topic
+optional

string

+
+
+

5.33. TopicPartitionInfo

+ ++++ + + + + + + + + + + + + + + + + + + + + + + + + +
NameSchema

isr
+optional

< Node > array

leader
+optional

Node

partition
+optional

integer(int32)

replicas
+optional

< Node > array

+
+
+

5.34. TopicPartitionReplica

+ ++++ + + + + + + + + + + + + + + + + + + + + + + + + +
NameSchema

brokerId
+optional

integer(int32)

hash
+optional

integer(int32)

partition
+optional

integer(int32)

topic
+optional

string

+
+
+

5.35. TopicPartitionReplicaAssignment

+ ++++ + + + + + + + + + + + + + + + + + + + + + + + + +
NameSchema

log_dirs
+optional

< string > array

partition
+optional

integer(int32)

replicas
+optional

< integer(int32) > array

topic
+optional

string

+
+
+

5.36. User

+ ++++ + + + + + + + + + + + + + + + + + + + + +
NameSchema

password
+optional

string

role
+optional

string

username
+optional

string

+
+
+

5.37. ZkServerClient

+ ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameSchema

host
+optional

string

ops
+optional

integer(int32)

port
+optional

integer(int32)

queued
+optional

integer(int32)

received
+optional

integer(int32)

sent
+optional

integer(int32)

+
+
+

5.38. ZkServerEnvironment

+ ++++ + + + + + + + + + + + + +
NameSchema

attributes
+optional

< string, string > map

+
+
+

5.39. ZkServerStat

+ ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameSchema

avgLatency
+optional

integer(int32)

buildDate
+optional

string

clients
+optional

< ZkServerClient > array

connections
+optional

integer(int32)

maxLatency
+optional

integer(int32)

minLatency
+optional

integer(int32)

mode
+optional

enum (Leader, Follower, Observer, Standalone, Down, Unknow)

msg
+optional

string

nodes
+optional

integer(int32)

outstanding
+optional

integer(int32)

received
+optional

integer(int32)

sent
+optional

integer(int32)

version
+optional

string

zxId
+optional

string

+
+
+
\ No newline at end of file diff --git a/docs/index.pdf b/docs/index.pdf index b2a7a2e94e7f85c328bf137b2fd9a3d00562d9d7..54be30704cf1579cce94f2c0db28b206218bbad5 100644 GIT binary patch literal 1289556 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�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ξ<)0I-#Ngas<#MOd_ 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 z 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∨@I?j}x~mTf6i82WZ0iYKtqs!d-{ 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 Yzsqg^&sqc!(t#c+Trx6RM%rBe3pm6?3IG5A literal 1023450 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 diff --git a/docs/paths.adoc b/docs/paths.adoc index 5709dd5..296ffb2 100644 --- a/docs/paths.adoc +++ b/docs/paths.adoc @@ -161,10 +161,10 @@ GET /kafka/brokers * `*/*` -[[_getmessageusingget]] -==== Get the message from the offset of the partition in the topic, decoder is not supported yet +[[_listlogdirsusingget]] +==== List log dirs by broker list .... -GET /kafka/consumer/{topic}/{partition}/{offset} +GET /kafka/brokers/logdirs .... @@ -173,14 +173,8 @@ GET /kafka/consumer/{topic}/{partition}/{offset} [options="header", cols=".^2,.^3,.^9,.^4"] |=== |Type|Name|Description|Schema -|**Path**|**offset** + -__required__|offset|integer(int64) -|**Path**|**partition** + -__required__|partition|integer(int32) -|**Path**|**topic** + -__required__|topic|string -|**Query**|**decoder** + -__optional__|decoder|string +|**Query**|**brokerList** + +__optional__|brokerList|< integer(int32) > array(multi) |=== @@ -189,7 +183,7 @@ __optional__|decoder|string [options="header", cols=".^2,.^14,.^4"] |=== |HTTP Code|Description|Schema -|**200**|OK|string +|**200**|OK|< string, < string > array > map |**401**|Unauthorized|No Content |**403**|Forbidden|No Content |**404**|Not Found|No Content @@ -206,10 +200,10 @@ __optional__|decoder|string * `*/*` -[[_deleteoldconsumergroupusingdelete]] -==== Delete old Consumer Group +[[_describelogdirsusingget]] +==== Describe log dirs by broker list and topic list .... -DELETE /kafka/consumergroup/{consumergroup} +GET /kafka/brokers/logdirs/detail .... @@ -218,8 +212,12 @@ DELETE /kafka/consumergroup/{consumergroup} [options="header", cols=".^2,.^3,.^9,.^4"] |=== |Type|Name|Description|Schema -|**Path**|**consumergroup** + -__required__|consumergroup|string +|**Query**|**brokerList** + +__optional__|brokerList|< integer(int32) > array(multi) +|**Query**|**logDirList** + +__optional__|logDirList|< string > array(multi) +|**Query**|**topicList** + +__optional__|topicList|< string > array(multi) |=== @@ -228,10 +226,10 @@ __required__|consumergroup|string [options="header", cols=".^2,.^14,.^4"] |=== |HTTP Code|Description|Schema -|**200**|OK|<<_generalresponse,GeneralResponse>> -|**204**|No Content|No Content +|**200**|OK|< string, < string, <<_logdirinfo,LogDirInfo>> > map > map |**401**|Unauthorized|No Content |**403**|Forbidden|No Content +|**404**|Not Found|No Content |=== @@ -245,10 +243,10 @@ __required__|consumergroup|string * `*/*` -[[_getlastcommittimestampusingget]] -==== getLastCommitTimestamp +[[_describereplicalogdirsusingget]] +==== Describe replica log dirs. .... -GET /kafka/consumergroup/{consumergroup}/{type}/topic/{topic}/lastcommittime +GET /kafka/brokers/replicalogdirs .... @@ -257,12 +255,8 @@ GET /kafka/consumergroup/{consumergroup}/{type}/topic/{topic}/lastcommittime [options="header", cols=".^2,.^3,.^9,.^4"] |=== |Type|Name|Description|Schema -|**Path**|**consumergroup** + -__required__|consumergroup|string -|**Path**|**topic** + -__required__|topic|string -|**Path**|**type** + -__required__|type|enum (NEW, OLD) +|**Query**|**replicas** + +__required__|replicas|string |=== @@ -271,7 +265,7 @@ __required__|type|enum (NEW, OLD) [options="header", cols=".^2,.^14,.^4"] |=== |HTTP Code|Description|Schema -|**200**|OK|< string, < string, integer(int64) > map > map +|**200**|OK|< string, <<_replicalogdirinfo,ReplicaLogDirInfo>> > map |**401**|Unauthorized|No Content |**403**|Forbidden|No Content |**404**|Not Found|No Content @@ -288,10 +282,10 @@ __required__|type|enum (NEW, OLD) * `*/*` -[[_resetoffsetusingput]] -==== Reset consumer group offset, earliest/latest can be used +[[_getbrokerconfigusingget]] +==== Get broker configs, including dynamic configs .... -PUT /kafka/consumergroup/{consumergroup}/{type}/topic/{topic}/{partition}/{offset} +GET /kafka/brokers/{brokerId}/conf .... @@ -300,16 +294,8 @@ PUT /kafka/consumergroup/{consumergroup}/{type}/topic/{topic}/{partition}/{offse [options="header", cols=".^2,.^3,.^9,.^4"] |=== |Type|Name|Description|Schema -|**Path**|**consumergroup** + -__required__|consumergroup|string -|**Path**|**offset** + -__required__|offset|string -|**Path**|**partition** + -__required__|partition|integer(int32) -|**Path**|**topic** + -__required__|topic|string -|**Path**|**type** + -__required__|type|enum (NEW, OLD) +|**Path**|**brokerId** + +__required__|brokerId|integer(int32) |=== @@ -318,7 +304,87 @@ __required__|type|enum (NEW, OLD) [options="header", cols=".^2,.^14,.^4"] |=== |HTTP Code|Description|Schema -|**200**|OK|<<_generalresponse,GeneralResponse>> +|**200**|OK|< <<_customconfigentry,CustomConfigEntry>> > array +|**401**|Unauthorized|No Content +|**403**|Forbidden|No Content +|**404**|Not Found|No Content +|=== + + +===== Consumes + +* `application/json` + + +===== Produces + +* `*/*` + + +[[_getbrokerdynconfigusingget]] +==== Get broker dynamic configs +.... +GET /kafka/brokers/{brokerId}/dynconf +.... + + +===== Parameters + +[options="header", cols=".^2,.^3,.^9,.^4"] +|=== +|Type|Name|Description|Schema +|**Path**|**brokerId** + +__required__|brokerId|integer(int32) +|=== + + +===== Responses + +[options="header", cols=".^2,.^14,.^4"] +|=== +|HTTP Code|Description|Schema +|**200**|OK|< string, object > map +|**401**|Unauthorized|No Content +|**403**|Forbidden|No Content +|**404**|Not Found|No Content +|=== + + +===== Consumes + +* `application/json` + + +===== Produces + +* `*/*` + + +[[_updatebrokerdynconfigusingput]] +==== Update broker configs +.... +PUT /kafka/brokers/{brokerId}/dynconf +.... + + +===== Parameters + +[options="header", cols=".^2,.^3,.^9,.^4"] +|=== +|Type|Name|Description|Schema +|**Path**|**brokerId** + +__required__|brokerId|integer(int32) +|**Body**|**props** + +__required__|props|< string, object > map +|=== + + +===== Responses + +[options="header", cols=".^2,.^14,.^4"] +|=== +|HTTP Code|Description|Schema +|**200**|OK|< string, object > map |**201**|Created|No Content |**401**|Unauthorized|No Content |**403**|Forbidden|No Content @@ -336,10 +402,10 @@ __required__|type|enum (NEW, OLD) * `*/*` -[[_listallconsumergroupsusingget]] -==== List all consumer groups from zk and kafka +[[_removebrokerdynconfigusingdelete]] +==== Remove broker dynamic configs .... -GET /kafka/consumergroups +DELETE /kafka/brokers/{brokerId}/dynconf .... @@ -348,10 +414,10 @@ GET /kafka/consumergroups [options="header", cols=".^2,.^3,.^9,.^4"] |=== |Type|Name|Description|Schema -|**Query**|**topic** + -__optional__|topic|string -|**Query**|**type** + -__optional__|type|enum (NEW, OLD) +|**Path**|**brokerId** + +__required__|brokerId|integer(int32) +|**Query**|**configKeysToBeRemoved** + +__required__|configKeysToBeRemoved|< string > array(multi) |=== @@ -360,7 +426,36 @@ __optional__|type|enum (NEW, OLD) [options="header", cols=".^2,.^14,.^4"] |=== |HTTP Code|Description|Schema -|**200**|OK|< string, < string > array > map +|**200**|OK|No Content +|**204**|No Content|No Content +|**401**|Unauthorized|No Content +|**403**|Forbidden|No Content +|=== + + +===== Consumes + +* `application/json` + + +===== Produces + +* `*/*` + + +[[_describeclusterusingget]] +==== Describe cluster, nodes, controller info. +.... +GET /kafka/cluster +.... + + +===== Responses + +[options="header", cols=".^2,.^14,.^4"] +|=== +|HTTP Code|Description|Schema +|**200**|OK|<<_clusterinfo,ClusterInfo>> |**401**|Unauthorized|No Content |**403**|Forbidden|No Content |**404**|Not Found|No Content @@ -377,10 +472,10 @@ __optional__|type|enum (NEW, OLD) * `*/*` -[[_describecgusingget]] -==== Describe consumer groups, showing lag and offset, may be slow if multi topic are listened +[[_getmessageusingget]] +==== Get the message from the offset of the partition in the topic, decoder is not supported yet .... -GET /kafka/consumergroups/{consumerGroup}/{type} +GET /kafka/consumer/{topic}/{partition}/{offset} .... @@ -389,8 +484,53 @@ GET /kafka/consumergroups/{consumerGroup}/{type} [options="header", cols=".^2,.^3,.^9,.^4"] |=== |Type|Name|Description|Schema -|**Path**|**consumerGroup** + -__required__|consumerGroup|string +|**Path**|**offset** + +__required__|offset|integer(int64) +|**Path**|**partition** + +__required__|partition|integer(int32) +|**Path**|**topic** + +__required__|topic|string +|**Query**|**decoder** + +__optional__|decoder|string +|=== + + +===== Responses + +[options="header", cols=".^2,.^14,.^4"] +|=== +|HTTP Code|Description|Schema +|**200**|OK|<<_record,Record>> +|**401**|Unauthorized|No Content +|**403**|Forbidden|No Content +|**404**|Not Found|No Content +|=== + + +===== Consumes + +* `application/json` + + +===== Produces + +* `*/*` + + +[[_deleteoldconsumergroupusingdelete]] +==== Delete Consumer Group +.... +DELETE /kafka/consumergroup/{consumergroup}/{type} +.... + + +===== Parameters + +[options="header", cols=".^2,.^3,.^9,.^4"] +|=== +|Type|Name|Description|Schema +|**Path**|**consumergroup** + +__required__|consumergroup|string |**Path**|**type** + __required__|type|enum (NEW, OLD) |=== @@ -401,10 +541,10 @@ __required__|type|enum (NEW, OLD) [options="header", cols=".^2,.^14,.^4"] |=== |HTTP Code|Description|Schema -|**200**|OK|< string, < <<_consumergroupdesc,ConsumerGroupDesc>> > array > map +|**200**|OK|<<_generalresponse,GeneralResponse>> +|**204**|No Content|No Content |**401**|Unauthorized|No Content |**403**|Forbidden|No Content -|**404**|Not Found|No Content |=== @@ -418,10 +558,10 @@ __required__|type|enum (NEW, OLD) * `*/*` -[[_listtopicbycgusingget]] -==== Get the topics involved of the specify consumer group +[[_getlastcommittimestampusingget]] +==== getLastCommitTimestamp .... -GET /kafka/consumergroups/{consumerGroup}/{type}/topic +GET /kafka/consumergroup/{consumergroup}/{type}/topic/{topic}/lastcommittime .... @@ -430,8 +570,10 @@ GET /kafka/consumergroups/{consumerGroup}/{type}/topic [options="header", cols=".^2,.^3,.^9,.^4"] |=== |Type|Name|Description|Schema -|**Path**|**consumerGroup** + -__required__|consumerGroup|string +|**Path**|**consumergroup** + +__required__|consumergroup|string +|**Path**|**topic** + +__required__|topic|string |**Path**|**type** + __required__|type|enum (NEW, OLD) |=== @@ -442,7 +584,7 @@ __required__|type|enum (NEW, OLD) [options="header", cols=".^2,.^14,.^4"] |=== |HTTP Code|Description|Schema -|**200**|OK|< string > array +|**200**|OK|< string, < string, integer(int64) > map > map |**401**|Unauthorized|No Content |**403**|Forbidden|No Content |**404**|Not Found|No Content @@ -459,10 +601,10 @@ __required__|type|enum (NEW, OLD) * `*/*` -[[_describecgbytopicusingget]] -==== Describe consumer groups by topic, showing lag and offset +[[_resetoffsetusingput]] +==== Reset consumer group offset, earliest/latest can be used. Support reset by time for new consumer group, pass a parameter that satisfies yyyy-MM-dd HH:mm:ss.SSS to offset. .... -GET /kafka/consumergroups/{consumerGroup}/{type}/topic/{topic} +PUT /kafka/consumergroup/{consumergroup}/{type}/topic/{topic}/{partition}/{offset} .... @@ -471,8 +613,12 @@ GET /kafka/consumergroups/{consumerGroup}/{type}/topic/{topic} [options="header", cols=".^2,.^3,.^9,.^4"] |=== |Type|Name|Description|Schema -|**Path**|**consumerGroup** + -__required__|consumerGroup|string +|**Path**|**consumergroup** + +__required__|consumergroup|string +|**Path**|**offset** + +__optional__|[earliest/latest/{long}/yyyy-MM-dd HH:mm:ss.SSS] can be supported. The date type is only valid for new consumer group.|string +|**Path**|**partition** + +__required__|partition|integer(int32) |**Path**|**topic** + __required__|topic|string |**Path**|**type** + @@ -485,7 +631,8 @@ __required__|type|enum (NEW, OLD) [options="header", cols=".^2,.^14,.^4"] |=== |HTTP Code|Description|Schema -|**200**|OK|< <<_consumergroupdesc,ConsumerGroupDesc>> > array +|**200**|OK|<<_generalresponse,GeneralResponse>> +|**201**|Created|No Content |**401**|Unauthorized|No Content |**403**|Forbidden|No Content |**404**|Not Found|No Content @@ -502,19 +649,31 @@ __required__|type|enum (NEW, OLD) * `*/*` -[[_healthcheckusingget]] -==== Check the cluster health. +[[_listallconsumergroupsusingget]] +==== List all consumer groups from zk and kafka .... -GET /kafka/health +GET /kafka/consumergroups .... +===== Parameters + +[options="header", cols=".^2,.^3,.^9,.^4"] +|=== +|Type|Name|Description|Schema +|**Query**|**topic** + +__optional__|topic|string +|**Query**|**type** + +__optional__|type|enum (NEW, OLD) +|=== + + ===== Responses [options="header", cols=".^2,.^14,.^4"] |=== |HTTP Code|Description|Schema -|**200**|OK|<<_healthcheckresult,HealthCheckResult>> +|**200**|OK|< string, < string > array > map |**401**|Unauthorized|No Content |**403**|Forbidden|No Content |**404**|Not Found|No Content @@ -531,10 +690,10 @@ GET /kafka/health * `*/*` -[[_addpartitionusingpost]] -==== Add a partition to the topic +[[_getconsumergroupmetausingget]] +==== Get the meta data of the specify new consumer group, including state, coordinator, assignmentStrategy, members .... -POST /kafka/partitions/add +GET /kafka/consumergroups/{consumerGroup}/meta .... @@ -543,8 +702,8 @@ POST /kafka/partitions/add [options="header", cols=".^2,.^3,.^9,.^4"] |=== |Type|Name|Description|Schema -|**Body**|**addPartition** + -__required__|addPartition|<<_addpartition,AddPartition>> +|**Path**|**consumerGroup** + +__required__|consumerGroup|string |=== @@ -553,8 +712,7 @@ __required__|addPartition|<<_addpartition,AddPartition>> [options="header", cols=".^2,.^14,.^4"] |=== |HTTP Code|Description|Schema -|**200**|OK|<<_topicmeta,TopicMeta>> -|**201**|Created|No Content +|**200**|OK|<<_consumergroupmeta,ConsumerGroupMeta>> |**401**|Unauthorized|No Content |**403**|Forbidden|No Content |**404**|Not Found|No Content @@ -571,10 +729,10 @@ __required__|addPartition|<<_addpartition,AddPartition>> * `*/*` -[[_checkreassignpartitionsusingput]] -==== Check the partition reassignment process +[[_describeconsumergroupusingget]] +==== Describe consumer group, showing lag and offset, may be slow if multi topics are listened .... -PUT /kafka/partitions/reassign/check +GET /kafka/consumergroups/{consumerGroup}/{type} .... @@ -583,8 +741,10 @@ PUT /kafka/partitions/reassign/check [options="header", cols=".^2,.^3,.^9,.^4"] |=== |Type|Name|Description|Schema -|**Body**|**reassignStr** + -__required__|reassignStr|string +|**Path**|**consumerGroup** + +__required__|consumerGroup|string +|**Path**|**type** + +__required__|type|enum (NEW, OLD) |=== @@ -593,11 +753,7 @@ __required__|reassignStr|string [options="header", cols=".^2,.^14,.^4"] |=== |HTTP Code|Description|Schema -|**-1**|Reassignment Failed|No Content -|**0**|Reassignment In Progress|No Content -|**1**|Reassignment Completed|No Content -|**200**|OK|< string, integer(int32) > map -|**201**|Created|No Content +|**200**|OK|< string, < <<_consumergroupdesc,ConsumerGroupDesc>> > array > map |**401**|Unauthorized|No Content |**403**|Forbidden|No Content |**404**|Not Found|No Content @@ -614,10 +770,10 @@ __required__|reassignStr|string * `*/*` -[[_executereassignpartitionsusingput]] -==== Execute the partition reassignment +[[_listtopicbyconsumergroupusingget]] +==== Get the topics involved of the specify consumer group .... -PUT /kafka/partitions/reassign/execute +GET /kafka/consumergroups/{consumerGroup}/{type}/topic .... @@ -626,8 +782,10 @@ PUT /kafka/partitions/reassign/execute [options="header", cols=".^2,.^3,.^9,.^4"] |=== |Type|Name|Description|Schema -|**Body**|**reassignStr** + -__required__|reassignStr|string +|**Path**|**consumerGroup** + +__required__|consumerGroup|string +|**Path**|**type** + +__required__|type|enum (NEW, OLD) |=== @@ -636,8 +794,7 @@ __required__|reassignStr|string [options="header", cols=".^2,.^14,.^4"] |=== |HTTP Code|Description|Schema -|**200**|OK|< string, integer(int32) > map -|**201**|Created|No Content +|**200**|OK|< string > array |**401**|Unauthorized|No Content |**403**|Forbidden|No Content |**404**|Not Found|No Content @@ -654,10 +811,10 @@ __required__|reassignStr|string * `*/*` -[[_generatereassignpartitionsusingpost]] -==== Generate plan for the partition reassignment +[[_describeconsumergroupbytopicusingget]] +==== Describe consumer groups by topic, showing lag and offset .... -POST /kafka/partitions/reassign/generate +GET /kafka/consumergroups/{type}/topic/{topic} .... @@ -666,8 +823,12 @@ POST /kafka/partitions/reassign/generate [options="header", cols=".^2,.^3,.^9,.^4"] |=== |Type|Name|Description|Schema -|**Body**|**reassignWrapper** + -__required__|reassignWrapper|<<_reassignwrapper,ReassignWrapper>> +|**Path**|**topic** + +__required__|topic|string +|**Path**|**type** + +__required__|type|enum (NEW, OLD) +|**Query**|**consumerGroup** + +__optional__|consumerGroup|string |=== @@ -676,8 +837,7 @@ __required__|reassignWrapper|<<_reassignwrapper,ReassignWrapper>> [options="header", cols=".^2,.^14,.^4"] |=== |HTTP Code|Description|Schema -|**200**|OK|< string > array -|**201**|Created|No Content +|**200**|OK|< <<_consumergroupdesc,ConsumerGroupDesc>> > array |**401**|Unauthorized|No Content |**403**|Forbidden|No Content |**404**|Not Found|No Content @@ -694,10 +854,10 @@ __required__|reassignWrapper|<<_reassignwrapper,ReassignWrapper>> * `*/*` -[[_listtopicsusingget]] -==== List topics +[[_getcontrolleridusingget]] +==== Get controller in this cluster .... -GET /kafka/topics +GET /kafka/controller .... @@ -706,7 +866,7 @@ GET /kafka/topics [options="header", cols=".^2,.^14,.^4"] |=== |HTTP Code|Description|Schema -|**200**|OK|< string > array +|**200**|OK|<<_node,Node>> |**401**|Unauthorized|No Content |**403**|Forbidden|No Content |**404**|Not Found|No Content @@ -723,10 +883,39 @@ GET /kafka/topics * `*/*` -[[_createtopicusingpost]] -==== Create a topic +[[_healthcheckusingget]] +==== Check the cluster health. .... -POST /kafka/topics/create +GET /kafka/health +.... + + +===== Responses + +[options="header", cols=".^2,.^14,.^4"] +|=== +|HTTP Code|Description|Schema +|**200**|OK|<<_healthcheckresult,HealthCheckResult>> +|**401**|Unauthorized|No Content +|**403**|Forbidden|No Content +|**404**|Not Found|No Content +|=== + + +===== Consumes + +* `application/json` + + +===== Produces + +* `*/*` + + +[[_addpartitionusingpost]] +==== Add partitions to the topics +.... +POST /kafka/partitions/add .... @@ -735,10 +924,8 @@ POST /kafka/topics/create [options="header", cols=".^2,.^3,.^9,.^4"] |=== |Type|Name|Description|Schema -|**Query**|**reassignStr** + -__optional__|reassignStr|string -|**Body**|**topic** + -__required__|topic|<<_topicdetail,TopicDetail>> +|**Body**|**addPartitions** + +__required__|addPartitions|< <<_addpartition,AddPartition>> > array |=== @@ -747,7 +934,8 @@ __required__|topic|<<_topicdetail,TopicDetail>> [options="header", cols=".^2,.^14,.^4"] |=== |HTTP Code|Description|Schema -|**201**|Created|<<_topicmeta,TopicMeta>> +|**200**|OK|< string, <<_generalresponse,GeneralResponse>> > map +|**201**|Created|No Content |**401**|Unauthorized|No Content |**403**|Forbidden|No Content |**404**|Not Found|No Content @@ -764,10 +952,10 @@ __required__|topic|<<_topicdetail,TopicDetail>> * `*/*` -[[_describetopicusingget]] -==== Describe a topic by fetching the metadata and config +[[_checkreassignpartitionsusingput]] +==== Check the partition reassignment process .... -GET /kafka/topics/{topic} +PUT /kafka/partitions/reassign/check .... @@ -776,8 +964,8 @@ GET /kafka/topics/{topic} [options="header", cols=".^2,.^3,.^9,.^4"] |=== |Type|Name|Description|Schema -|**Path**|**topic** + -__required__|topic|string +|**Body**|**reassign** + +__required__|reassign|<<_reassignmodel,ReassignModel>> |=== @@ -786,7 +974,11 @@ __required__|topic|string [options="header", cols=".^2,.^14,.^4"] |=== |HTTP Code|Description|Schema -|**200**|OK|<<_topicmeta,TopicMeta>> +|**-1**|Reassignment Failed|No Content +|**0**|Reassignment In Progress|No Content +|**1**|Reassignment Completed|No Content +|**200**|OK|<<_reassignstatus,ReassignStatus>> +|**201**|Created|No Content |**401**|Unauthorized|No Content |**403**|Forbidden|No Content |**404**|Not Found|No Content @@ -803,10 +995,10 @@ __required__|topic|string * `*/*` -[[_deletetopicusingdelete]] -==== Delete a topic (you should enable topic deletion +[[_executereassignpartitionsusingput]] +==== Execute the partition reassignment .... -DELETE /kafka/topics/{topic} +PUT /kafka/partitions/reassign/execute .... @@ -815,8 +1007,14 @@ DELETE /kafka/topics/{topic} [options="header", cols=".^2,.^3,.^9,.^4"] |=== |Type|Name|Description|Schema -|**Path**|**topic** + -__required__|topic|string +|**Query**|**interBrokerThrottle** + +__optional__|interBrokerThrottle|integer(int64) +|**Query**|**replicaAlterLogDirsThrottle** + +__optional__|replicaAlterLogDirsThrottle|integer(int64) +|**Query**|**timeoutMs** + +__optional__|timeoutMs|integer(int64) +|**Body**|**reassign** + +__required__|reassign|<<_reassignmodel,ReassignModel>> |=== @@ -825,10 +1023,11 @@ __required__|topic|string [options="header", cols=".^2,.^14,.^4"] |=== |HTTP Code|Description|Schema -|**200**|OK|<<_generalresponse,GeneralResponse>> -|**204**|No Content|No Content +|**200**|OK|<<_reassignstatus,ReassignStatus>> +|**201**|Created|No Content |**401**|Unauthorized|No Content |**403**|Forbidden|No Content +|**404**|Not Found|No Content |=== @@ -842,10 +1041,10 @@ __required__|topic|string * `*/*` -[[_createtopicconfigusingpost]] -==== Create topic configs +[[_generatereassignpartitionsusingpost]] +==== Generate plan for the partition reassignment .... -POST /kafka/topics/{topic}/conf +POST /kafka/partitions/reassign/generate .... @@ -854,10 +1053,8 @@ POST /kafka/topics/{topic}/conf [options="header", cols=".^2,.^3,.^9,.^4"] |=== |Type|Name|Description|Schema -|**Path**|**topic** + -__required__|topic|string -|**Body**|**prop** + -__required__|prop|< string, object > map +|**Body**|**reassignWrapper** + +__required__|reassignWrapper|<<_reassignwrapper,ReassignWrapper>> |=== @@ -866,7 +1063,7 @@ __required__|prop|< string, object > map [options="header", cols=".^2,.^14,.^4"] |=== |HTTP Code|Description|Schema -|**200**|OK|< string, object > map +|**200**|OK|< <<_reassignmodel,ReassignModel>> > array |**201**|Created|No Content |**401**|Unauthorized|No Content |**403**|Forbidden|No Content @@ -884,10 +1081,39 @@ __required__|prop|< string, object > map * `*/*` -[[_gettopicconfigusingget]] -==== Get topic configs +[[_listtopicsusingget]] +==== List topics .... -GET /kafka/topics/{topic}/conf +GET /kafka/topics +.... + + +===== Responses + +[options="header", cols=".^2,.^14,.^4"] +|=== +|HTTP Code|Description|Schema +|**200**|OK|< string > array +|**401**|Unauthorized|No Content +|**403**|Forbidden|No Content +|**404**|Not Found|No Content +|=== + + +===== Consumes + +* `application/json` + + +===== Produces + +* `*/*` + + +[[_deletetopiclistusingdelete]] +==== Delete a topic list (you should enable topic deletion +.... +DELETE /kafka/topics .... @@ -896,8 +1122,8 @@ GET /kafka/topics/{topic}/conf [options="header", cols=".^2,.^3,.^9,.^4"] |=== |Type|Name|Description|Schema -|**Path**|**topic** + -__required__|topic|string +|**Query**|**topicList** + +__required__|topicList|< string > array(multi) |=== @@ -906,10 +1132,10 @@ __required__|topic|string [options="header", cols=".^2,.^14,.^4"] |=== |HTTP Code|Description|Schema -|**200**|OK|< string, object > map +|**200**|OK|< string, <<_generalresponse,GeneralResponse>> > map +|**204**|No Content|No Content |**401**|Unauthorized|No Content |**403**|Forbidden|No Content -|**404**|Not Found|No Content |=== @@ -923,10 +1149,10 @@ __required__|topic|string * `*/*` -[[_updatetopicconfigusingput]] -==== Update topic configs +[[_createtopicusingpost]] +==== Create topics .... -PUT /kafka/topics/{topic}/conf +POST /kafka/topics/create .... @@ -935,10 +1161,8 @@ PUT /kafka/topics/{topic}/conf [options="header", cols=".^2,.^3,.^9,.^4"] |=== |Type|Name|Description|Schema -|**Path**|**topic** + -__required__|topic|string -|**Body**|**prop** + -__required__|prop|< string, object > map +|**Body**|**topicList** + +__required__|topicList|< <<_topicdetail,TopicDetail>> > array |=== @@ -947,8 +1171,7 @@ __required__|prop|< string, object > map [options="header", cols=".^2,.^14,.^4"] |=== |HTTP Code|Description|Schema -|**200**|OK|< string, object > map -|**201**|Created|No Content +|**201**|Created|< string, <<_generalresponse,GeneralResponse>> > map |**401**|Unauthorized|No Content |**403**|Forbidden|No Content |**404**|Not Found|No Content @@ -965,10 +1188,10 @@ __required__|prop|< string, object > map * `*/*` -[[_deletetopicconfigusingdelete]] -==== Delete topic configs +[[_describetopicusingget]] +==== Describe a topic by fetching the metadata and config .... -DELETE /kafka/topics/{topic}/conf +GET /kafka/topics/{topic} .... @@ -979,8 +1202,6 @@ DELETE /kafka/topics/{topic}/conf |Type|Name|Description|Schema |**Path**|**topic** + __required__|topic|string -|**Body**|**delProps** + -__required__|delProps|< string > array |=== @@ -989,10 +1210,10 @@ __required__|delProps|< string > array [options="header", cols=".^2,.^14,.^4"] |=== |HTTP Code|Description|Schema -|**200**|OK|< string, object > map -|**204**|No Content|No Content +|**200**|OK|<<_topicmeta,TopicMeta>> |**401**|Unauthorized|No Content |**403**|Forbidden|No Content +|**404**|Not Found|No Content |=== @@ -1006,10 +1227,10 @@ __required__|delProps|< string > array * `*/*` -[[_gettopicconfigbykeyusingget]] -==== Get topic config by key +[[_gettopicconfigusingget]] +==== Get topic configs .... -GET /kafka/topics/{topic}/conf/{key} +GET /kafka/topics/{topic}/conf .... @@ -1018,8 +1239,6 @@ GET /kafka/topics/{topic}/conf/{key} [options="header", cols=".^2,.^3,.^9,.^4"] |=== |Type|Name|Description|Schema -|**Path**|**key** + -__required__|key|string |**Path**|**topic** + __required__|topic|string |=== @@ -1030,7 +1249,7 @@ __required__|topic|string [options="header", cols=".^2,.^14,.^4"] |=== |HTTP Code|Description|Schema -|**200**|OK|< string, object > map +|**200**|OK|< <<_customconfigentry,CustomConfigEntry>> > array |**401**|Unauthorized|No Content |**403**|Forbidden|No Content |**404**|Not Found|No Content @@ -1047,10 +1266,10 @@ __required__|topic|string * `*/*` -[[_deletetopicconfigbykeyusingdelete]] -==== Delete a topic config by key +[[_updatetopicconfigusingput]] +==== Update topic configs .... -DELETE /kafka/topics/{topic}/conf/{key} +PUT /kafka/topics/{topic}/conf .... @@ -1059,10 +1278,10 @@ DELETE /kafka/topics/{topic}/conf/{key} [options="header", cols=".^2,.^3,.^9,.^4"] |=== |Type|Name|Description|Schema -|**Path**|**key** + -__required__|key|string |**Path**|**topic** + __required__|topic|string +|**Body**|**props** + +__required__|props|< string, object > map |=== @@ -1071,10 +1290,11 @@ __required__|topic|string [options="header", cols=".^2,.^14,.^4"] |=== |HTTP Code|Description|Schema -|**200**|OK|boolean -|**204**|No Content|No Content +|**200**|OK|< <<_customconfigentry,CustomConfigEntry>> > array +|**201**|Created|No Content |**401**|Unauthorized|No Content |**403**|Forbidden|No Content +|**404**|Not Found|No Content |=== @@ -1088,10 +1308,10 @@ __required__|topic|string * `*/*` -[[_createtopicconfigbykeyusingpost]] -==== Create a topic config by key +[[_gettopicconfigbykeyusingget]] +==== Get topic config by key .... -POST /kafka/topics/{topic}/conf/{key}={value} +GET /kafka/topics/{topic}/conf/{key} .... @@ -1104,8 +1324,6 @@ POST /kafka/topics/{topic}/conf/{key}={value} __required__|key|string |**Path**|**topic** + __required__|topic|string -|**Path**|**value** + -__required__|value|string |=== @@ -1115,7 +1333,6 @@ __required__|value|string |=== |HTTP Code|Description|Schema |**200**|OK|< string, object > map -|**201**|Created|No Content |**401**|Unauthorized|No Content |**403**|Forbidden|No Content |**404**|Not Found|No Content @@ -1158,7 +1375,7 @@ __required__|value|string [options="header", cols=".^2,.^14,.^4"] |=== |HTTP Code|Description|Schema -|**200**|OK|< string, object > map +|**200**|OK|< <<_customconfigentry,CustomConfigEntry>> > array |**201**|Created|No Content |**401**|Unauthorized|No Content |**403**|Forbidden|No Content @@ -1176,10 +1393,10 @@ __required__|value|string * `*/*` -[[_existtopicusingget]] -==== Tell if a topic exists +[[_gettopicdynconfigusingget]] +==== Get topic dyn configs .... -GET /kafka/topics/{topic}/exist +GET /kafka/topics/{topic}/dynconf .... @@ -1198,7 +1415,7 @@ __required__|topic|string [options="header", cols=".^2,.^14,.^4"] |=== |HTTP Code|Description|Schema -|**200**|OK|boolean +|**200**|OK|< string, object > map |**401**|Unauthorized|No Content |**403**|Forbidden|No Content |**404**|Not Found|No Content @@ -1215,10 +1432,10 @@ __required__|topic|string * `*/*` -[[_writemessageusingpost]] -==== Write a message to the topic, for testing purpose +[[_existtopicusingget]] +==== Tell if a topic exists .... -POST /kafka/topics/{topic}/write +GET /kafka/topics/{topic}/exist .... @@ -1229,8 +1446,6 @@ POST /kafka/topics/{topic}/write |Type|Name|Description|Schema |**Path**|**topic** + __required__|topic|string -|**Body**|**message** + -__required__|message|string |=== @@ -1239,7 +1454,7 @@ __required__|message|string [options="header", cols=".^2,.^14,.^4"] |=== |HTTP Code|Description|Schema -|**201**|Created|<<_generalresponse,GeneralResponse>> +|**200**|OK|boolean |**401**|Unauthorized|No Content |**403**|Forbidden|No Content |**404**|Not Found|No Content @@ -1248,7 +1463,7 @@ __required__|message|string ===== Consumes -* `text/plain` +* `application/json` ===== Produces @@ -1501,10 +1716,49 @@ GET /zk/env * `*/*` +[[_getusingget]] +==== Get data of a zookeeper path +.... +GET /zk/get/path +.... + + +===== Parameters + +[options="header", cols=".^2,.^3,.^9,.^4"] +|=== +|Type|Name|Description|Schema +|**Query**|**path** + +__required__|path|string +|=== + + +===== Responses + +[options="header", cols=".^2,.^14,.^4"] +|=== +|HTTP Code|Description|Schema +|**200**|OK|< string, string > map +|**401**|Unauthorized|No Content +|**403**|Forbidden|No Content +|**404**|Not Found|No Content +|=== + + +===== Consumes + +* `application/json` + + +===== Produces + +* `*/*` + + [[_lsusingget]] ==== List a zookeeper path .... -GET /zk/ls/{path} +GET /zk/ls/path .... @@ -1513,7 +1767,7 @@ GET /zk/ls/{path} [options="header", cols=".^2,.^3,.^9,.^4"] |=== |Type|Name|Description|Schema -|**Path**|**path** + +|**Query**|**path** + __required__|path|string |=== diff --git a/pom.xml b/pom.xml index f752e88..8411136 100644 --- a/pom.xml +++ b/pom.xml @@ -129,7 +129,7 @@ org.springframework.boot spring-boot-starter-test - test + @@ -266,12 +266,6 @@ avro 1.8.2 - diff --git a/src/docs/swagger/swagger.json b/src/docs/swagger/swagger.json index 601e028..4999959 100644 --- a/src/docs/swagger/swagger.json +++ b/src/docs/swagger/swagger.json @@ -1,2470 +1 @@ -{ - "swagger": "2.0", - "info": { - "description": "Kafka REST API SwaggerUI", - "version": "0.1.0", - "title": "Kafka REST API SwaggerUI", - "contact": { - "name": "gnuhpc", - "url": "https://github.com/gnuhpc", - "email": "gnuhpc@gmail.com" - } - }, - "host": "localhost:8080", - "basePath": "/", - "tags": [ - { - "name": "collector-controller", - "description": "Rest API for Collecting JMX Metric Data" - }, - { - "name": "user-controller", - "description": "Security User Management Controller." - }, - { - "name": "zookeeper-controller", - "description": "Zookeeper Controller" - }, - { - "name": "kafka-controller", - "description": "Kafka Controller" - } - ], - "paths": { - "/jmx/v1": { - "get": { - "tags": [ - "collector-controller" - ], - "summary": "Fetch all JMX metric data", - "operationId": "collectJMXMetricUsingGET", - "consumes": [ - "application/json" - ], - "produces": [ - "*/*" - ], - "parameters": [ - { - "name": "jmxurl", - "in": "query", - "description": "Parameter jmxurl should be a comma-separated list of {IP:Port} or set to 'default'", - "required": false, - "type": "string" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/JMXMetricDataV1" - } - } - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - }, - "404": { - "description": "Not Found" - } - } - } - }, - "/jmx/v2": { - "post": { - "tags": [ - "collector-controller" - ], - "summary": "Fetch JMX metric data with query filter. You can get the query filter template through the API /jmx/v2/filters.", - "operationId": "collectJMXMetricUsingPOST", - "consumes": [ - "application/json" - ], - "produces": [ - "*/*" - ], - "parameters": [ - { - "name": "jmxurl", - "in": "query", - "description": "Parameter jmxurl should be a comma-separated list of {IP:Port} or set to 'default'", - "required": false, - "type": "string" - }, - { - "in": "body", - "name": "jmxQuery", - "description": "jmxQuery", - "required": true, - "schema": { - "$ref": "#/definitions/JMXQuery" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/JMXMetricData" - } - } - }, - "201": { - "description": "Created" - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - }, - "404": { - "description": "Not Found" - } - } - } - }, - "/jmx/v2/filters": { - "get": { - "tags": [ - "collector-controller" - ], - "summary": "List the query filter templates with the filterKey. If filterKey is set to empty, it will return all the templates.", - "operationId": "listJMXFilterTemplateUsingGET", - "consumes": [ - "application/json" - ], - "produces": [ - "*/*" - ], - "parameters": [ - { - "name": "filterKey", - "in": "query", - "description": "filterKey", - "required": true, - "type": "string" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "object", - "additionalProperties": { - "type": "object" - } - } - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - }, - "404": { - "description": "Not Found" - } - } - } - }, - "/kafka/brokers": { - "get": { - "tags": [ - "kafka-controller" - ], - "summary": "List brokers in this cluster", - "operationId": "listBrokersUsingGET", - "consumes": [ - "application/json" - ], - "produces": [ - "*/*" - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/BrokerInfo" - } - } - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - }, - "404": { - "description": "Not Found" - } - } - } - }, - "/kafka/consumer/{topic}/{partition}/{offset}": { - "get": { - "tags": [ - "kafka-controller" - ], - "summary": "Get the message from the offset of the partition in the topic, decoder is not supported yet", - "operationId": "getMessageUsingGET", - "consumes": [ - "application/json" - ], - "produces": [ - "*/*" - ], - "parameters": [ - { - "name": "topic", - "in": "path", - "description": "topic", - "required": true, - "type": "string" - }, - { - "name": "partition", - "in": "path", - "description": "partition", - "required": true, - "type": "integer", - "format": "int32" - }, - { - "name": "offset", - "in": "path", - "description": "offset", - "required": true, - "type": "integer", - "format": "int64" - }, - { - "name": "decoder", - "in": "query", - "description": "decoder", - "required": false, - "type": "string" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "string" - } - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - }, - "404": { - "description": "Not Found" - } - } - } - }, - "/kafka/consumergroup/{consumergroup}": { - "delete": { - "tags": [ - "kafka-controller" - ], - "summary": "Delete old Consumer Group", - "operationId": "deleteOldConsumerGroupUsingDELETE", - "consumes": [ - "application/json" - ], - "produces": [ - "*/*" - ], - "parameters": [ - { - "name": "consumergroup", - "in": "path", - "description": "consumergroup", - "required": true, - "type": "string" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/GeneralResponse" - } - }, - "204": { - "description": "No Content" - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - } - } - } - }, - "/kafka/consumergroup/{consumergroup}/{type}/topic/{topic}/lastcommittime": { - "get": { - "tags": [ - "kafka-controller" - ], - "summary": "getLastCommitTimestamp", - "operationId": "getLastCommitTimestampUsingGET", - "consumes": [ - "application/json" - ], - "produces": [ - "*/*" - ], - "parameters": [ - { - "name": "consumergroup", - "in": "path", - "description": "consumergroup", - "required": true, - "type": "string" - }, - { - "name": "topic", - "in": "path", - "description": "topic", - "required": true, - "type": "string" - }, - { - "name": "type", - "in": "path", - "description": "type", - "required": true, - "type": "string", - "enum": [ - "NEW", - "OLD" - ] - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "object", - "additionalProperties": { - "type": "object", - "additionalProperties": { - "type": "integer", - "format": "int64" - } - } - } - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - }, - "404": { - "description": "Not Found" - } - } - } - }, - "/kafka/consumergroup/{consumergroup}/{type}/topic/{topic}/{partition}/{offset}": { - "put": { - "tags": [ - "kafka-controller" - ], - "summary": "Reset consumer group offset, earliest/latest can be used", - "operationId": "resetOffsetUsingPUT", - "consumes": [ - "application/json" - ], - "produces": [ - "*/*" - ], - "parameters": [ - { - "name": "topic", - "in": "path", - "description": "topic", - "required": true, - "type": "string" - }, - { - "name": "partition", - "in": "path", - "description": "partition", - "required": true, - "type": "integer", - "format": "int32" - }, - { - "name": "consumergroup", - "in": "path", - "description": "consumergroup", - "required": true, - "type": "string" - }, - { - "name": "offset", - "in": "path", - "description": "offset", - "required": true, - "type": "string" - }, - { - "name": "type", - "in": "path", - "description": "type", - "required": true, - "type": "string", - "enum": [ - "NEW", - "OLD" - ] - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/GeneralResponse" - } - }, - "201": { - "description": "Created" - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - }, - "404": { - "description": "Not Found" - } - } - } - }, - "/kafka/consumergroups": { - "get": { - "tags": [ - "kafka-controller" - ], - "summary": "List all consumer groups from zk and kafka", - "operationId": "listAllConsumerGroupsUsingGET", - "consumes": [ - "application/json" - ], - "produces": [ - "*/*" - ], - "parameters": [ - { - "name": "type", - "in": "query", - "description": "type", - "required": false, - "type": "string", - "enum": [ - "NEW", - "OLD" - ] - }, - { - "name": "topic", - "in": "query", - "description": "topic", - "required": false, - "type": "string" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "object", - "additionalProperties": { - "type": "array", - "items": { - "type": "string" - } - } - } - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - }, - "404": { - "description": "Not Found" - } - } - } - }, - "/kafka/consumergroups/{consumerGroup}/{type}": { - "get": { - "tags": [ - "kafka-controller" - ], - "summary": "Describe consumer groups, showing lag and offset, may be slow if multi topic are listened", - "operationId": "describeCGUsingGET", - "consumes": [ - "application/json" - ], - "produces": [ - "*/*" - ], - "parameters": [ - { - "name": "consumerGroup", - "in": "path", - "description": "consumerGroup", - "required": true, - "type": "string" - }, - { - "name": "type", - "in": "path", - "description": "type", - "required": true, - "type": "string", - "enum": [ - "NEW", - "OLD" - ] - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "object", - "additionalProperties": { - "type": "array", - "items": { - "$ref": "#/definitions/ConsumerGroupDesc" - } - } - } - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - }, - "404": { - "description": "Not Found" - } - } - } - }, - "/kafka/consumergroups/{consumerGroup}/{type}/topic": { - "get": { - "tags": [ - "kafka-controller" - ], - "summary": "Get the topics involved of the specify consumer group", - "operationId": "listTopicByCGUsingGET", - "consumes": [ - "application/json" - ], - "produces": [ - "*/*" - ], - "parameters": [ - { - "name": "consumerGroup", - "in": "path", - "description": "consumerGroup", - "required": true, - "type": "string" - }, - { - "name": "type", - "in": "path", - "description": "type", - "required": true, - "type": "string", - "enum": [ - "NEW", - "OLD" - ] - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - }, - "404": { - "description": "Not Found" - } - } - } - }, - "/kafka/consumergroups/{consumerGroup}/{type}/topic/{topic}": { - "get": { - "tags": [ - "kafka-controller" - ], - "summary": "Describe consumer groups by topic, showing lag and offset", - "operationId": "describeCGByTopicUsingGET", - "consumes": [ - "application/json" - ], - "produces": [ - "*/*" - ], - "parameters": [ - { - "name": "consumerGroup", - "in": "path", - "description": "consumerGroup", - "required": true, - "type": "string" - }, - { - "name": "type", - "in": "path", - "description": "type", - "required": true, - "type": "string", - "enum": [ - "NEW", - "OLD" - ] - }, - { - "name": "topic", - "in": "path", - "description": "topic", - "required": true, - "type": "string" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/ConsumerGroupDesc" - } - } - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - }, - "404": { - "description": "Not Found" - } - } - } - }, - "/kafka/health": { - "get": { - "tags": [ - "kafka-controller" - ], - "summary": "Check the cluster health.", - "operationId": "healthCheckUsingGET", - "consumes": [ - "application/json" - ], - "produces": [ - "*/*" - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/HealthCheckResult" - } - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - }, - "404": { - "description": "Not Found" - } - } - } - }, - "/kafka/partitions/add": { - "post": { - "tags": [ - "kafka-controller" - ], - "summary": "Add a partition to the topic", - "operationId": "addPartitionUsingPOST", - "consumes": [ - "application/json" - ], - "produces": [ - "*/*" - ], - "parameters": [ - { - "in": "body", - "name": "addPartition", - "description": "addPartition", - "required": true, - "schema": { - "$ref": "#/definitions/AddPartition" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/TopicMeta" - } - }, - "201": { - "description": "Created" - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - }, - "404": { - "description": "Not Found" - } - } - } - }, - "/kafka/partitions/reassign/check": { - "put": { - "tags": [ - "kafka-controller" - ], - "summary": "Check the partition reassignment process", - "operationId": "checkReassignPartitionsUsingPUT", - "consumes": [ - "application/json" - ], - "produces": [ - "*/*" - ], - "parameters": [ - { - "in": "body", - "name": "reassignStr", - "description": "reassignStr", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "-1": { - "description": "Reassignment Failed" - }, - "0": { - "description": "Reassignment In Progress" - }, - "1": { - "description": "Reassignment Completed" - }, - "200": { - "description": "OK", - "schema": { - "type": "object", - "additionalProperties": { - "type": "integer", - "format": "int32" - } - } - }, - "201": { - "description": "Created" - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - }, - "404": { - "description": "Not Found" - } - } - } - }, - "/kafka/partitions/reassign/execute": { - "put": { - "tags": [ - "kafka-controller" - ], - "summary": "Execute the partition reassignment", - "operationId": "executeReassignPartitionsUsingPUT", - "consumes": [ - "application/json" - ], - "produces": [ - "*/*" - ], - "parameters": [ - { - "in": "body", - "name": "reassignStr", - "description": "reassignStr", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "object", - "additionalProperties": { - "type": "integer", - "format": "int32" - } - } - }, - "201": { - "description": "Created" - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - }, - "404": { - "description": "Not Found" - } - } - } - }, - "/kafka/partitions/reassign/generate": { - "post": { - "tags": [ - "kafka-controller" - ], - "summary": "Generate plan for the partition reassignment", - "operationId": "generateReassignPartitionsUsingPOST", - "consumes": [ - "application/json" - ], - "produces": [ - "*/*" - ], - "parameters": [ - { - "in": "body", - "name": "reassignWrapper", - "description": "reassignWrapper", - "required": true, - "schema": { - "$ref": "#/definitions/ReassignWrapper" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "201": { - "description": "Created" - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - }, - "404": { - "description": "Not Found" - } - } - } - }, - "/kafka/topics": { - "get": { - "tags": [ - "kafka-controller" - ], - "summary": "List topics", - "operationId": "listTopicsUsingGET", - "consumes": [ - "application/json" - ], - "produces": [ - "*/*" - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - }, - "404": { - "description": "Not Found" - } - } - } - }, - "/kafka/topics/create": { - "post": { - "tags": [ - "kafka-controller" - ], - "summary": "Create a topic", - "operationId": "createTopicUsingPOST", - "consumes": [ - "application/json" - ], - "produces": [ - "*/*" - ], - "parameters": [ - { - "in": "body", - "name": "topic", - "description": "topic", - "required": true, - "schema": { - "$ref": "#/definitions/TopicDetail" - } - }, - { - "name": "reassignStr", - "in": "query", - "description": "reassignStr", - "required": false, - "type": "string" - } - ], - "responses": { - "201": { - "description": "Created", - "schema": { - "$ref": "#/definitions/TopicMeta" - } - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - }, - "404": { - "description": "Not Found" - } - } - } - }, - "/kafka/topics/{topic}": { - "get": { - "tags": [ - "kafka-controller" - ], - "summary": "Describe a topic by fetching the metadata and config", - "operationId": "describeTopicUsingGET", - "consumes": [ - "application/json" - ], - "produces": [ - "*/*" - ], - "parameters": [ - { - "name": "topic", - "in": "path", - "description": "topic", - "required": true, - "type": "string" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/TopicMeta" - } - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - }, - "404": { - "description": "Not Found" - } - } - }, - "delete": { - "tags": [ - "kafka-controller" - ], - "summary": "Delete a topic (you should enable topic deletion", - "operationId": "deleteTopicUsingDELETE", - "consumes": [ - "application/json" - ], - "produces": [ - "*/*" - ], - "parameters": [ - { - "name": "topic", - "in": "path", - "description": "topic", - "required": true, - "type": "string" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/GeneralResponse" - } - }, - "204": { - "description": "No Content" - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - } - } - } - }, - "/kafka/topics/{topic}/conf": { - "get": { - "tags": [ - "kafka-controller" - ], - "summary": "Get topic configs", - "operationId": "getTopicConfigUsingGET", - "consumes": [ - "application/json" - ], - "produces": [ - "*/*" - ], - "parameters": [ - { - "name": "topic", - "in": "path", - "description": "topic", - "required": true, - "type": "string" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "object", - "additionalProperties": { - "type": "object" - } - } - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - }, - "404": { - "description": "Not Found" - } - } - }, - "post": { - "tags": [ - "kafka-controller" - ], - "summary": "Create topic configs", - "operationId": "createTopicConfigUsingPOST", - "consumes": [ - "application/json" - ], - "produces": [ - "*/*" - ], - "parameters": [ - { - "name": "topic", - "in": "path", - "description": "topic", - "required": true, - "type": "string" - }, - { - "in": "body", - "name": "prop", - "description": "prop", - "required": true, - "schema": { - "type": "object", - "additionalProperties": { - "type": "object" - } - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "object", - "additionalProperties": { - "type": "object" - } - } - }, - "201": { - "description": "Created" - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - }, - "404": { - "description": "Not Found" - } - } - }, - "put": { - "tags": [ - "kafka-controller" - ], - "summary": "Update topic configs", - "operationId": "updateTopicConfigUsingPUT", - "consumes": [ - "application/json" - ], - "produces": [ - "*/*" - ], - "parameters": [ - { - "name": "topic", - "in": "path", - "description": "topic", - "required": true, - "type": "string" - }, - { - "in": "body", - "name": "prop", - "description": "prop", - "required": true, - "schema": { - "type": "object", - "additionalProperties": { - "type": "object" - } - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "object", - "additionalProperties": { - "type": "object" - } - } - }, - "201": { - "description": "Created" - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - }, - "404": { - "description": "Not Found" - } - } - }, - "delete": { - "tags": [ - "kafka-controller" - ], - "summary": "Delete topic configs", - "operationId": "deleteTopicConfigUsingDELETE", - "consumes": [ - "application/json" - ], - "produces": [ - "*/*" - ], - "parameters": [ - { - "name": "topic", - "in": "path", - "description": "topic", - "required": true, - "type": "string" - }, - { - "in": "body", - "name": "delProps", - "description": "delProps", - "required": true, - "schema": { - "type": "array", - "items": { - "type": "string" - } - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "object", - "additionalProperties": { - "type": "object" - } - } - }, - "204": { - "description": "No Content" - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - } - } - } - }, - "/kafka/topics/{topic}/conf/{key}": { - "get": { - "tags": [ - "kafka-controller" - ], - "summary": "Get topic config by key", - "operationId": "getTopicConfigByKeyUsingGET", - "consumes": [ - "application/json" - ], - "produces": [ - "*/*" - ], - "parameters": [ - { - "name": "topic", - "in": "path", - "description": "topic", - "required": true, - "type": "string" - }, - { - "name": "key", - "in": "path", - "description": "key", - "required": true, - "type": "string" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "object", - "additionalProperties": { - "type": "object" - } - } - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - }, - "404": { - "description": "Not Found" - } - } - }, - "delete": { - "tags": [ - "kafka-controller" - ], - "summary": "Delete a topic config by key", - "operationId": "deleteTopicConfigByKeyUsingDELETE", - "consumes": [ - "application/json" - ], - "produces": [ - "*/*" - ], - "parameters": [ - { - "name": "topic", - "in": "path", - "description": "topic", - "required": true, - "type": "string" - }, - { - "name": "key", - "in": "path", - "description": "key", - "required": true, - "type": "string" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "boolean" - } - }, - "204": { - "description": "No Content" - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - } - } - } - }, - "/kafka/topics/{topic}/conf/{key}={value}": { - "post": { - "tags": [ - "kafka-controller" - ], - "summary": "Create a topic config by key", - "operationId": "createTopicConfigByKeyUsingPOST", - "consumes": [ - "application/json" - ], - "produces": [ - "*/*" - ], - "parameters": [ - { - "name": "topic", - "in": "path", - "description": "topic", - "required": true, - "type": "string" - }, - { - "name": "key", - "in": "path", - "description": "key", - "required": true, - "type": "string" - }, - { - "name": "value", - "in": "path", - "description": "value", - "required": true, - "type": "string" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "object", - "additionalProperties": { - "type": "object" - } - } - }, - "201": { - "description": "Created" - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - }, - "404": { - "description": "Not Found" - } - } - }, - "put": { - "tags": [ - "kafka-controller" - ], - "summary": "Update a topic config by key", - "operationId": "updateTopicConfigByKeyUsingPUT", - "consumes": [ - "application/json" - ], - "produces": [ - "*/*" - ], - "parameters": [ - { - "name": "topic", - "in": "path", - "description": "topic", - "required": true, - "type": "string" - }, - { - "name": "key", - "in": "path", - "description": "key", - "required": true, - "type": "string" - }, - { - "name": "value", - "in": "path", - "description": "value", - "required": true, - "type": "string" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "object", - "additionalProperties": { - "type": "object" - } - } - }, - "201": { - "description": "Created" - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - }, - "404": { - "description": "Not Found" - } - } - } - }, - "/kafka/topics/{topic}/exist": { - "get": { - "tags": [ - "kafka-controller" - ], - "summary": "Tell if a topic exists", - "operationId": "existTopicUsingGET", - "consumes": [ - "application/json" - ], - "produces": [ - "*/*" - ], - "parameters": [ - { - "name": "topic", - "in": "path", - "description": "topic", - "required": true, - "type": "string" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "boolean" - } - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - }, - "404": { - "description": "Not Found" - } - } - } - }, - "/kafka/topics/{topic}/write": { - "post": { - "tags": [ - "kafka-controller" - ], - "summary": "Write a message to the topic, for testing purpose", - "operationId": "writeMessageUsingPOST", - "consumes": [ - "text/plain" - ], - "produces": [ - "*/*" - ], - "parameters": [ - { - "name": "topic", - "in": "path", - "description": "topic", - "required": true, - "type": "string" - }, - { - "in": "body", - "name": "message", - "description": "message", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "201": { - "description": "Created", - "schema": { - "$ref": "#/definitions/GeneralResponse" - } - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - }, - "404": { - "description": "Not Found" - } - } - } - }, - "/kafka/topicsbrief": { - "get": { - "tags": [ - "kafka-controller" - ], - "summary": "List topics Brief", - "operationId": "listTopicBriefUsingGET", - "consumes": [ - "application/json" - ], - "produces": [ - "*/*" - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/TopicBrief" - } - } - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - }, - "404": { - "description": "Not Found" - } - } - } - }, - "/users": { - "get": { - "tags": [ - "user-controller" - ], - "summary": "Get user list.", - "operationId": "listUserUsingGET", - "consumes": [ - "application/json" - ], - "produces": [ - "*/*" - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - }, - "404": { - "description": "Not Found" - } - } - }, - "post": { - "tags": [ - "user-controller" - ], - "summary": "Add user.", - "operationId": "addUserUsingPOST", - "consumes": [ - "application/json" - ], - "produces": [ - "*/*" - ], - "parameters": [ - { - "in": "body", - "name": "user", - "description": "user", - "required": true, - "schema": { - "$ref": "#/definitions/User" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/GeneralResponse" - } - }, - "201": { - "description": "Created" - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - }, - "404": { - "description": "Not Found" - } - } - }, - "put": { - "tags": [ - "user-controller" - ], - "summary": "Modify user information.", - "operationId": "modifyUserUsingPUT", - "consumes": [ - "application/json" - ], - "produces": [ - "*/*" - ], - "parameters": [ - { - "in": "body", - "name": "user", - "description": "user", - "required": true, - "schema": { - "$ref": "#/definitions/User" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/GeneralResponse" - } - }, - "201": { - "description": "Created" - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - }, - "404": { - "description": "Not Found" - } - } - } - }, - "/users/{username}": { - "delete": { - "tags": [ - "user-controller" - ], - "summary": "Delete user.", - "operationId": "delUserUsingDELETE", - "consumes": [ - "application/json" - ], - "produces": [ - "*/*" - ], - "parameters": [ - { - "name": "username", - "in": "path", - "description": "username", - "required": true, - "type": "string" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/GeneralResponse" - } - }, - "204": { - "description": "No Content" - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - } - } - } - }, - "/zk/connstate": { - "get": { - "tags": [ - "zookeeper-controller" - ], - "summary": "Get the connection state of zookeeper", - "operationId": "zkConnStateUsingGET", - "consumes": [ - "application/json" - ], - "produces": [ - "*/*" - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "string" - } - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - }, - "404": { - "description": "Not Found" - } - } - } - }, - "/zk/env": { - "get": { - "tags": [ - "zookeeper-controller" - ], - "summary": "Get the environment information of zookeeper", - "operationId": "getEnvUsingGET", - "consumes": [ - "application/json" - ], - "produces": [ - "*/*" - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/ZkServerEnvironment" - } - } - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - }, - "404": { - "description": "Not Found" - } - } - } - }, - "/zk/ls/{path}": { - "get": { - "tags": [ - "zookeeper-controller" - ], - "summary": "List a zookeeper path", - "operationId": "lsUsingGET", - "consumes": [ - "application/json" - ], - "produces": [ - "*/*" - ], - "parameters": [ - { - "name": "path", - "in": "path", - "description": "path", - "required": true, - "type": "string" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - }, - "404": { - "description": "Not Found" - } - } - } - }, - "/zk/stat": { - "get": { - "tags": [ - "zookeeper-controller" - ], - "summary": "Get the service state of zookeeper", - "operationId": "getStatUsingGET", - "consumes": [ - "application/json" - ], - "produces": [ - "*/*" - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/ZkServerStat" - } - } - }, - "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Forbidden" - }, - "404": { - "description": "Not Found" - } - } - } - } - }, - "definitions": { - "AddPartition": { - "type": "object", - "properties": { - "numPartitionsAdded": { - "type": "integer", - "format": "int32" - }, - "replicaAssignment": { - "type": "string" - }, - "topic": { - "type": "string" - } - } - }, - "BrokerInfo": { - "type": "object", - "properties": { - "endPoints": { - "type": "array", - "items": { - "type": "string" - } - }, - "host": { - "type": "string" - }, - "id": { - "type": "integer", - "format": "int32" - }, - "jmxPort": { - "type": "integer", - "format": "int32" - }, - "port": { - "type": "integer", - "format": "int32" - }, - "rack": { - "type": "string" - }, - "securityProtocol": { - "type": "object" - }, - "startTime": { - "type": "string", - "format": "date-time" - }, - "version": { - "type": "integer", - "format": "int32" - } - } - }, - "ConsumerGroupDesc": { - "type": "object", - "properties": { - "consumerId": { - "type": "string" - }, - "currentOffset": { - "type": "integer", - "format": "int64" - }, - "groupName": { - "type": "string" - }, - "host": { - "type": "string" - }, - "lag": { - "type": "integer", - "format": "int64" - }, - "logEndOffset": { - "type": "integer", - "format": "int64" - }, - "partitionId": { - "type": "integer", - "format": "int32" - }, - "state": { - "type": "string", - "enum": [ - "RUNNING", - "PENDING" - ] - }, - "topic": { - "type": "string" - }, - "type": { - "type": "string", - "enum": [ - "NEW", - "OLD" - ] - } - } - }, - "GeneralResponse": { - "type": "object", - "properties": { - "msg": { - "type": "string" - }, - "state": { - "type": "string", - "enum": [ - "success", - "failure" - ] - } - } - }, - "HashMap«string,object»": { - "type": "object", - "additionalProperties": { - "type": "object" - } - }, - "HealthCheckResult": { - "type": "object", - "properties": { - "msg": { - "type": "string" - }, - "status": { - "type": "string" - }, - "timestamp": { - "type": "string", - "example": "yyyy-MM-dd HH:mm:ss" - } - } - }, - "HostAndPort": { - "type": "object", - "properties": { - "hostText": { - "type": "string" - }, - "port": { - "type": "integer", - "format": "int32" - } - } - }, - "JMXConfiguration": { - "type": "object", - "properties": { - "include": { - "$ref": "#/definitions/JMXFilter" - }, - "exclude": { - "$ref": "#/definitions/JMXFilter" - } - } - }, - "JMXFilter": { - "type": "object", - "properties": { - "attribute": { - "type": "object" - }, - "beanNames": { - "type": "array", - "items": { - "type": "string" - } - }, - "beanRegexes": { - "type": "array", - "items": { - "$ref": "#/definitions/Pattern" - } - }, - "domain": { - "type": "string" - }, - "domainRegex": { - "$ref": "#/definitions/Pattern" - }, - "emptyBeanName": { - "type": "boolean" - }, - "filter": { - "type": "object", - "additionalProperties": { - "type": "object" - } - } - } - }, - "JMXMetricData": { - "type": "object", - "properties": { - "collected": { - "type": "boolean" - }, - "host": { - "type": "string" - }, - "metrics": { - "type": "array", - "items": { - "$ref": "#/definitions/HashMap«string,object»" - } - }, - "msg": { - "type": "string" - }, - "timestamp": { - "type": "string", - "example": "yyyy-MM-dd HH:mm:ss" - } - } - }, - "JMXMetricDataV1": { - "type": "object", - "properties": { - "collected": { - "type": "boolean" - }, - "host": { - "type": "string" - }, - "mbeanInfo": { - "type": "object" - }, - "msg": { - "type": "string" - }, - "timestamp": { - "type": "string", - "example": "yyyy-MM-dd HH:mm:ss" - } - } - }, - "JMXQuery": { - "type": "object", - "properties": { - "filters": { - "type": "array", - "items": { - "$ref": "#/definitions/JMXConfiguration" - } - } - } - }, - "Map«int,long»": { - "type": "object", - "additionalProperties": { - "type": "integer", - "format": "int64" - } - }, - "Pattern": { - "type": "object", - "properties": { - "cursor": { - "type": "integer", - "format": "int32" - } - } - }, - "ReassignWrapper": { - "type": "object", - "properties": { - "brokers": { - "type": "array", - "items": { - "type": "integer", - "format": "int32" - } - }, - "topics": { - "type": "array", - "items": { - "type": "string" - } - } - } - }, - "TopicAndPartition": { - "type": "object" - }, - "TopicBrief": { - "type": "object", - "properties": { - "isrRate": { - "type": "number", - "format": "double" - }, - "numPartition": { - "type": "integer", - "format": "int32" - }, - "topic": { - "type": "string" - } - } - }, - "TopicDetail": { - "type": "object", - "properties": { - "factor": { - "type": "integer", - "format": "int32" - }, - "name": { - "type": "string" - }, - "partitions": { - "type": "integer", - "format": "int32" - }, - "prop": { - "type": "object", - "additionalProperties": { - "type": "object" - } - } - } - }, - "TopicMeta": { - "type": "object", - "properties": { - "partitionCount": { - "type": "integer", - "format": "int32" - }, - "replicationFactor": { - "type": "integer", - "format": "int32" - }, - "topicCustomConfigs": { - "type": "object", - "additionalProperties": { - "type": "object" - } - }, - "topicName": { - "type": "string" - }, - "topicPartitionInfos": { - "type": "array", - "items": { - "$ref": "#/definitions/TopicPartitionInfo" - } - } - } - }, - "TopicPartitionInfo": { - "type": "object", - "properties": { - "endOffset": { - "type": "integer", - "format": "int64" - }, - "in_sync": { - "type": "boolean" - }, - "isr": { - "type": "array", - "items": { - "type": "string" - } - }, - "leader": { - "type": "string" - }, - "messageAvailable": { - "type": "integer", - "format": "int64" - }, - "partitionId": { - "type": "integer", - "format": "int32" - }, - "replicas": { - "type": "array", - "items": { - "type": "string" - } - }, - "startOffset": { - "type": "integer", - "format": "int64" - } - } - }, - "User": { - "type": "object", - "properties": { - "username": { - "type": "string" - }, - "password": { - "type": "string" - }, - "role": { - "type": "string" - } - } - }, - "ZkServerClient": { - "type": "object", - "properties": { - "host": { - "type": "string" - }, - "ops": { - "type": "integer", - "format": "int32" - }, - "port": { - "type": "integer", - "format": "int32" - }, - "queued": { - "type": "integer", - "format": "int32" - }, - "received": { - "type": "integer", - "format": "int32" - }, - "sent": { - "type": "integer", - "format": "int32" - } - } - }, - "ZkServerEnvironment": { - "type": "object", - "properties": { - "attributes": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - } - }, - "ZkServerStat": { - "type": "object", - "properties": { - "avgLatency": { - "type": "integer", - "format": "int32" - }, - "buildDate": { - "type": "string" - }, - "clients": { - "type": "array", - "items": { - "$ref": "#/definitions/ZkServerClient" - } - }, - "connections": { - "type": "integer", - "format": "int32" - }, - "maxLatency": { - "type": "integer", - "format": "int32" - }, - "minLatency": { - "type": "integer", - "format": "int32" - }, - "mode": { - "type": "string", - "enum": [ - "Leader", - "Follower", - "Observer" - ] - }, - "nodes": { - "type": "integer", - "format": "int32" - }, - "outstanding": { - "type": "integer", - "format": "int32" - }, - "received": { - "type": "integer", - "format": "int32" - }, - "sent": { - "type": "integer", - "format": "int32" - }, - "version": { - "type": "string" - }, - "zxId": { - "type": "string" - } - } - } - } -} \ No newline at end of file +{"swagger":"2.0","info":{"description":"Kafka REST API SwaggerUI","version":"0.1.0","title":"Kafka REST API SwaggerUI","contact":{"name":"gnuhpc","url":"https://github.com/gnuhpc","email":"gnuhpc@gmail.com"}},"host":"localhost:8080","basePath":"/","tags":[{"name":"collector-controller","description":"Rest API for Collecting JMX Metric Data"},{"name":"user-controller","description":"Security User Management Controller."},{"name":"zookeeper-controller","description":"Zookeeper Controller"},{"name":"kafka-controller","description":"Kafka Controller"}],"paths":{"/jmx/v1":{"get":{"tags":["collector-controller"],"summary":"Fetch all JMX metric data","operationId":"collectJMXMetricUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"jmxurl","in":"query","description":"Parameter jmxurl should be a comma-separated list of {IP:Port} or set to 'default'","required":false,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/JMXMetricDataV1"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/jmx/v2":{"post":{"tags":["collector-controller"],"summary":"Fetch JMX metric data with query filter. You can get the query filter template through the API /jmx/v2/filters.","operationId":"collectJMXMetricUsingPOST","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"jmxurl","in":"query","description":"Parameter jmxurl should be a comma-separated list of {IP:Port} or set to 'default'","required":false,"type":"string"},{"in":"body","name":"jmxQuery","description":"jmxQuery","required":true,"schema":{"$ref":"#/definitions/JMXQuery"}}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/JMXMetricData"}}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/jmx/v2/filters":{"get":{"tags":["collector-controller"],"summary":"List the query filter templates with the filterKey. If filterKey is set to empty, it will return all the templates.","operationId":"listJMXFilterTemplateUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"filterKey","in":"query","description":"filterKey","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"object"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/brokers":{"get":{"tags":["kafka-controller"],"summary":"List brokers in this cluster","operationId":"listBrokersUsingGET","consumes":["application/json"],"produces":["*/*"],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/BrokerInfo"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/brokers/logdirs":{"get":{"tags":["kafka-controller"],"summary":"List log dirs by broker list","operationId":"listLogDirsUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"brokerList","in":"query","description":"brokerList","required":false,"type":"array","items":{"type":"integer","format":"int32"},"collectionFormat":"multi"}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/brokers/logdirs/detail":{"get":{"tags":["kafka-controller"],"summary":"Describe log dirs by broker list and topic list","operationId":"describeLogDirsUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"brokerList","in":"query","description":"brokerList","required":false,"type":"array","items":{"type":"integer","format":"int32"},"collectionFormat":"multi"},{"name":"logDirList","in":"query","description":"logDirList","required":false,"type":"array","items":{"type":"string"},"collectionFormat":"multi"},{"name":"topicList","in":"query","description":"topicList","required":false,"type":"array","items":{"type":"string"},"collectionFormat":"multi"}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"object","additionalProperties":{"$ref":"#/definitions/LogDirInfo"}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/brokers/replicalogdirs":{"get":{"tags":["kafka-controller"],"summary":"Describe replica log dirs.","operationId":"describeReplicaLogDirsUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"replicas","in":"query","description":"replicas","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"$ref":"#/definitions/ReplicaLogDirInfo"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/brokers/{brokerId}/conf":{"get":{"tags":["kafka-controller"],"summary":"Get broker configs, including dynamic configs","operationId":"getBrokerConfigUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"brokerId","in":"path","description":"brokerId","required":true,"type":"integer","format":"int32"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/CustomConfigEntry"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/brokers/{brokerId}/dynconf":{"get":{"tags":["kafka-controller"],"summary":"Get broker dynamic configs","operationId":"getBrokerDynConfigUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"brokerId","in":"path","description":"brokerId","required":true,"type":"integer","format":"int32"}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"object"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}},"put":{"tags":["kafka-controller"],"summary":"Update broker configs","operationId":"updateBrokerDynConfigUsingPUT","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"brokerId","in":"path","description":"brokerId","required":true,"type":"integer","format":"int32"},{"in":"body","name":"props","description":"props","required":true,"schema":{"type":"object","additionalProperties":{"type":"object"}}}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"object"}}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}},"delete":{"tags":["kafka-controller"],"summary":"Remove broker dynamic configs","operationId":"removeBrokerDynConfigUsingDELETE","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"brokerId","in":"path","description":"brokerId","required":true,"type":"integer","format":"int32"},{"name":"configKeysToBeRemoved","in":"query","description":"configKeysToBeRemoved","required":true,"type":"array","items":{"type":"string"},"collectionFormat":"multi"}],"responses":{"200":{"description":"OK"},"204":{"description":"No Content"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"}}}},"/kafka/cluster":{"get":{"tags":["kafka-controller"],"summary":"Describe cluster, nodes, controller info.","operationId":"describeClusterUsingGET","consumes":["application/json"],"produces":["*/*"],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/ClusterInfo"}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/consumer/{topic}/{partition}/{offset}":{"get":{"tags":["kafka-controller"],"summary":"Get the message from the offset of the partition in the topic, decoder is not supported yet","operationId":"getMessageUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"topic","in":"path","description":"topic","required":true,"type":"string"},{"name":"partition","in":"path","description":"partition","required":true,"type":"integer","format":"int32"},{"name":"offset","in":"path","description":"offset","required":true,"type":"integer","format":"int64"},{"name":"decoder","in":"query","description":"decoder","required":false,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/Record"}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/consumergroup/{consumergroup}/{type}":{"delete":{"tags":["kafka-controller"],"summary":"Delete Consumer Group","operationId":"deleteOldConsumerGroupUsingDELETE","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"consumergroup","in":"path","description":"consumergroup","required":true,"type":"string"},{"name":"type","in":"path","description":"type","required":true,"type":"string","enum":["NEW","OLD"]}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/GeneralResponse"}},"204":{"description":"No Content"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"}}}},"/kafka/consumergroup/{consumergroup}/{type}/topic/{topic}/lastcommittime":{"get":{"tags":["kafka-controller"],"summary":"getLastCommitTimestamp","operationId":"getLastCommitTimestampUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"consumergroup","in":"path","description":"consumergroup","required":true,"type":"string"},{"name":"topic","in":"path","description":"topic","required":true,"type":"string"},{"name":"type","in":"path","description":"type","required":true,"type":"string","enum":["NEW","OLD"]}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"object","additionalProperties":{"type":"integer","format":"int64"}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/consumergroup/{consumergroup}/{type}/topic/{topic}/{partition}/{offset}":{"put":{"tags":["kafka-controller"],"summary":"Reset consumer group offset, earliest/latest can be used. Support reset by time for new consumer group, pass a parameter that satisfies yyyy-MM-dd HH:mm:ss.SSS to offset.","operationId":"resetOffsetUsingPUT","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"topic","in":"path","description":"topic","required":true,"type":"string"},{"name":"partition","in":"path","description":"partition","required":true,"type":"integer","format":"int32"},{"name":"consumergroup","in":"path","description":"consumergroup","required":true,"type":"string"},{"name":"offset","in":"path","description":"[earliest/latest/{long}/yyyy-MM-dd HH:mm:ss.SSS] can be supported. The date type is only valid for new consumer group.","required":false,"type":"string"},{"name":"type","in":"path","description":"type","required":true,"type":"string","enum":["NEW","OLD"]}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/GeneralResponse"}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/consumergroups":{"get":{"tags":["kafka-controller"],"summary":"List all consumer groups from zk and kafka","operationId":"listAllConsumerGroupsUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"type","in":"query","description":"type","required":false,"type":"string","enum":["NEW","OLD"]},{"name":"topic","in":"query","description":"topic","required":false,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/consumergroups/{consumerGroup}/meta":{"get":{"tags":["kafka-controller"],"summary":"Get the meta data of the specify new consumer group, including state, coordinator, assignmentStrategy, members","operationId":"getConsumerGroupMetaUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"consumerGroup","in":"path","description":"consumerGroup","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/ConsumerGroupMeta"}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/consumergroups/{consumerGroup}/{type}":{"get":{"tags":["kafka-controller"],"summary":"Describe consumer group, showing lag and offset, may be slow if multi topics are listened","operationId":"describeConsumerGroupUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"consumerGroup","in":"path","description":"consumerGroup","required":true,"type":"string"},{"name":"type","in":"path","description":"type","required":true,"type":"string","enum":["NEW","OLD"]}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"array","items":{"$ref":"#/definitions/ConsumerGroupDesc"}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/consumergroups/{consumerGroup}/{type}/topic":{"get":{"tags":["kafka-controller"],"summary":"Get the topics involved of the specify consumer group","operationId":"listTopicByConsumerGroupUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"consumerGroup","in":"path","description":"consumerGroup","required":true,"type":"string"},{"name":"type","in":"path","description":"type","required":true,"type":"string","enum":["NEW","OLD"]}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"type":"string"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/consumergroups/{type}/topic/{topic}":{"get":{"tags":["kafka-controller"],"summary":"Describe consumer groups by topic, showing lag and offset","operationId":"describeConsumerGroupByTopicUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"consumerGroup","in":"query","description":"consumerGroup","required":false,"type":"string"},{"name":"type","in":"path","description":"type","required":true,"type":"string","enum":["NEW","OLD"]},{"name":"topic","in":"path","description":"topic","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/ConsumerGroupDesc"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/controller":{"get":{"tags":["kafka-controller"],"summary":"Get controller in this cluster","operationId":"getControllerIdUsingGET","consumes":["application/json"],"produces":["*/*"],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/Node"}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/health":{"get":{"tags":["kafka-controller"],"summary":"Check the cluster health.","operationId":"healthCheckUsingGET","consumes":["application/json"],"produces":["*/*"],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/HealthCheckResult"}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/partitions/add":{"post":{"tags":["kafka-controller"],"summary":"Add partitions to the topics","operationId":"addPartitionUsingPOST","consumes":["application/json"],"produces":["*/*"],"parameters":[{"in":"body","name":"addPartitions","description":"addPartitions","required":true,"schema":{"type":"array","items":{"$ref":"#/definitions/AddPartition"}}}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"$ref":"#/definitions/GeneralResponse"}}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/partitions/reassign/check":{"put":{"tags":["kafka-controller"],"summary":"Check the partition reassignment process","operationId":"checkReassignPartitionsUsingPUT","consumes":["application/json"],"produces":["*/*"],"parameters":[{"in":"body","name":"reassign","description":"reassign","required":true,"schema":{"$ref":"#/definitions/ReassignModel"}}],"responses":{"-1":{"description":"Reassignment Failed"},"0":{"description":"Reassignment In Progress"},"1":{"description":"Reassignment Completed"},"200":{"description":"OK","schema":{"$ref":"#/definitions/ReassignStatus"}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/partitions/reassign/execute":{"put":{"tags":["kafka-controller"],"summary":"Execute the partition reassignment","operationId":"executeReassignPartitionsUsingPUT","consumes":["application/json"],"produces":["*/*"],"parameters":[{"in":"body","name":"reassign","description":"reassign","required":true,"schema":{"$ref":"#/definitions/ReassignModel"}},{"name":"interBrokerThrottle","in":"query","description":"interBrokerThrottle","required":false,"type":"integer","format":"int64"},{"name":"replicaAlterLogDirsThrottle","in":"query","description":"replicaAlterLogDirsThrottle","required":false,"type":"integer","format":"int64"},{"name":"timeoutMs","in":"query","description":"timeoutMs","required":false,"type":"integer","format":"int64"}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/ReassignStatus"}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/partitions/reassign/generate":{"post":{"tags":["kafka-controller"],"summary":"Generate plan for the partition reassignment","operationId":"generateReassignPartitionsUsingPOST","consumes":["application/json"],"produces":["*/*"],"parameters":[{"in":"body","name":"reassignWrapper","description":"reassignWrapper","required":true,"schema":{"$ref":"#/definitions/ReassignWrapper"}}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/ReassignModel"}}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/topics":{"get":{"tags":["kafka-controller"],"summary":"List topics","operationId":"listTopicsUsingGET","consumes":["application/json"],"produces":["*/*"],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"type":"string"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}},"delete":{"tags":["kafka-controller"],"summary":"Delete a topic list (you should enable topic deletion","operationId":"deleteTopicListUsingDELETE","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"topicList","in":"query","description":"topicList","required":true,"type":"array","items":{"type":"string"},"collectionFormat":"multi"}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"$ref":"#/definitions/GeneralResponse"}}},"204":{"description":"No Content"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"}}}},"/kafka/topics/create":{"post":{"tags":["kafka-controller"],"summary":"Create topics","operationId":"createTopicUsingPOST","consumes":["application/json"],"produces":["*/*"],"parameters":[{"in":"body","name":"topicList","description":"topicList","required":true,"schema":{"type":"array","items":{"$ref":"#/definitions/TopicDetail"}}}],"responses":{"201":{"description":"Created","schema":{"type":"object","additionalProperties":{"$ref":"#/definitions/GeneralResponse"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/topics/{topic}":{"get":{"tags":["kafka-controller"],"summary":"Describe a topic by fetching the metadata and config","operationId":"describeTopicUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"topic","in":"path","description":"topic","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/TopicMeta"}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/topics/{topic}/conf":{"get":{"tags":["kafka-controller"],"summary":"Get topic configs","operationId":"getTopicConfigUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"topic","in":"path","description":"topic","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/CustomConfigEntry"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}},"put":{"tags":["kafka-controller"],"summary":"Update topic configs","operationId":"updateTopicConfigUsingPUT","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"topic","in":"path","description":"topic","required":true,"type":"string"},{"in":"body","name":"props","description":"props","required":true,"schema":{"type":"object","additionalProperties":{"type":"object"}}}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/CustomConfigEntry"}}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/topics/{topic}/conf/{key}":{"get":{"tags":["kafka-controller"],"summary":"Get topic config by key","operationId":"getTopicConfigByKeyUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"topic","in":"path","description":"topic","required":true,"type":"string"},{"name":"key","in":"path","description":"key","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"object"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/topics/{topic}/conf/{key}={value}":{"put":{"tags":["kafka-controller"],"summary":"Update a topic config by key","operationId":"updateTopicConfigByKeyUsingPUT","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"topic","in":"path","description":"topic","required":true,"type":"string"},{"name":"key","in":"path","description":"key","required":true,"type":"string"},{"name":"value","in":"path","description":"value","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/CustomConfigEntry"}}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/topics/{topic}/dynconf":{"get":{"tags":["kafka-controller"],"summary":"Get topic dyn configs","operationId":"getTopicDynConfigUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"topic","in":"path","description":"topic","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"object"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/topics/{topic}/exist":{"get":{"tags":["kafka-controller"],"summary":"Tell if a topic exists","operationId":"existTopicUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"topic","in":"path","description":"topic","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"boolean"}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/topicsbrief":{"get":{"tags":["kafka-controller"],"summary":"List topics Brief","operationId":"listTopicBriefUsingGET","consumes":["application/json"],"produces":["*/*"],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/TopicBrief"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/users":{"get":{"tags":["user-controller"],"summary":"Get user list.","operationId":"listUserUsingGET","consumes":["application/json"],"produces":["*/*"],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"type":"string"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}},"post":{"tags":["user-controller"],"summary":"Add user.","operationId":"addUserUsingPOST","consumes":["application/json"],"produces":["*/*"],"parameters":[{"in":"body","name":"user","description":"user","required":true,"schema":{"$ref":"#/definitions/User"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/GeneralResponse"}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}},"put":{"tags":["user-controller"],"summary":"Modify user information.","operationId":"modifyUserUsingPUT","consumes":["application/json"],"produces":["*/*"],"parameters":[{"in":"body","name":"user","description":"user","required":true,"schema":{"$ref":"#/definitions/User"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/GeneralResponse"}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/users/{username}":{"delete":{"tags":["user-controller"],"summary":"Delete user.","operationId":"delUserUsingDELETE","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"username","in":"path","description":"username","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/GeneralResponse"}},"204":{"description":"No Content"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"}}}},"/zk/connstate":{"get":{"tags":["zookeeper-controller"],"summary":"Get the connection state of zookeeper","operationId":"zkConnStateUsingGET","consumes":["application/json"],"produces":["*/*"],"responses":{"200":{"description":"OK","schema":{"type":"string"}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/zk/env":{"get":{"tags":["zookeeper-controller"],"summary":"Get the environment information of zookeeper","operationId":"getEnvUsingGET","consumes":["application/json"],"produces":["*/*"],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"$ref":"#/definitions/ZkServerEnvironment"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/zk/get/path":{"get":{"tags":["zookeeper-controller"],"summary":"Get data of a zookeeper path","operationId":"getUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"path","in":"query","description":"path","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"string"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/zk/ls/path":{"get":{"tags":["zookeeper-controller"],"summary":"List a zookeeper path","operationId":"lsUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"path","in":"query","description":"path","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"type":"string"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/zk/stat":{"get":{"tags":["zookeeper-controller"],"summary":"Get the service state of zookeeper","operationId":"getStatUsingGET","consumes":["application/json"],"produces":["*/*"],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"$ref":"#/definitions/ZkServerStat"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}}},"definitions":{"AddPartition":{"type":"object","properties":{"topic":{"type":"string"},"numPartitionsAdded":{"type":"integer","format":"int32"},"replicaAssignment":{"type":"array","items":{"type":"array","items":{"type":"integer","format":"int32"}}}}},"BrokerInfo":{"type":"object","properties":{"endpoints":{"type":"array","items":{"type":"string"}},"host":{"type":"string"},"id":{"type":"integer","format":"int32"},"jmxPort":{"type":"integer","format":"int32"},"port":{"type":"integer","format":"int32"},"rack":{"type":"string"},"securityProtocol":{"type":"object"},"startTime":{"type":"string","format":"date-time"},"version":{"type":"integer","format":"int32"}}},"ClusterInfo":{"type":"object","properties":{"clusterId":{"type":"string"},"controller":{"$ref":"#/definitions/Node"},"nodes":{"type":"array","items":{"$ref":"#/definitions/Node"}}}},"ConsumerGroupDesc":{"type":"object","properties":{"assignmentStrategy":{"type":"string"},"clientId":{"type":"string"},"consumerId":{"type":"string"},"coordinator":{"$ref":"#/definitions/Node"},"currentOffset":{"type":"integer","format":"int64"},"groupName":{"type":"string"},"host":{"type":"string"},"lag":{"type":"integer","format":"int64"},"logEndOffset":{"type":"integer","format":"int64"},"partitionId":{"type":"integer","format":"int32"},"state":{"type":"string","enum":["Unknown","PreparingRebalance","CompletingRebalance","Stable","Dead","Empty"]},"topic":{"type":"string"},"type":{"type":"string","enum":["NEW","OLD"]}}},"ConsumerGroupMeta":{"type":"object","properties":{"assignmentStrategy":{"type":"string"},"coordinator":{"$ref":"#/definitions/Node"},"groupId":{"type":"string"},"members":{"type":"array","items":{"$ref":"#/definitions/MemberDescription"}},"state":{"type":"string","enum":["Unknown","PreparingRebalance","CompletingRebalance","Stable","Dead","Empty"]}}},"CustomConfigEntry":{"type":"object","properties":{"isReadOnly":{"type":"boolean"},"isSensitive":{"type":"boolean"},"name":{"type":"string"},"readOnly":{"type":"boolean"},"sensitive":{"type":"boolean"},"source":{"type":"string","enum":["DYNAMIC_TOPIC_CONFIG","DYNAMIC_BROKER_CONFIG","DYNAMIC_DEFAULT_BROKER_CONFIG","STATIC_BROKER_CONFIG","DEFAULT_CONFIG","UNKNOWN"]},"value":{"type":"string"}}},"CustomTopicPartitionInfo":{"type":"object","properties":{"endOffset":{"type":"integer","format":"int64"},"in_sync":{"type":"boolean"},"messageAvailable":{"type":"integer","format":"int64"},"startOffset":{"type":"integer","format":"int64"},"topicPartitionInfo":{"$ref":"#/definitions/TopicPartitionInfo"}}},"GeneralResponse":{"type":"object","properties":{"data":{"type":"object"},"msg":{"type":"string"},"state":{"type":"string","enum":["success","failure"]}}},"HashMap«string,object»":{"type":"object","additionalProperties":{"type":"object"}},"HealthCheckResult":{"type":"object","properties":{"msg":{"type":"string"},"status":{"type":"string"},"timestamp":{"type":"string","example":"yyyy-MM-dd HH:mm:ss"}}},"HostAndPort":{"type":"object","properties":{"hasBracketlessColons":{"type":"boolean"},"host":{"type":"string"},"hostText":{"type":"string"},"port":{"type":"integer","format":"int32"}}},"JMXConfiguration":{"type":"object","properties":{"include":{"$ref":"#/definitions/JMXFilter"},"exclude":{"$ref":"#/definitions/JMXFilter"}}},"JMXFilter":{"type":"object","properties":{"attribute":{"type":"object"},"beanNames":{"type":"array","items":{"type":"string"}},"beanRegexes":{"type":"array","items":{"$ref":"#/definitions/Pattern"}},"domain":{"type":"string"},"domainRegex":{"$ref":"#/definitions/Pattern"},"emptyBeanName":{"type":"boolean"},"filter":{"type":"object","additionalProperties":{"type":"object"}}}},"JMXMetricData":{"type":"object","properties":{"collected":{"type":"boolean"},"host":{"type":"string"},"metrics":{"type":"array","items":{"$ref":"#/definitions/HashMap«string,object»"}},"msg":{"type":"string"},"timestamp":{"type":"string","example":"yyyy-MM-dd HH:mm:ss"}}},"JMXMetricDataV1":{"type":"object","properties":{"collected":{"type":"boolean"},"host":{"type":"string"},"mbeanInfo":{"type":"object"},"msg":{"type":"string"},"timestamp":{"type":"string","example":"yyyy-MM-dd HH:mm:ss"}}},"JMXQuery":{"type":"object","properties":{"filters":{"type":"array","items":{"$ref":"#/definitions/JMXConfiguration"}}}},"LogDirInfo":{"type":"object","properties":{"error":{"type":"string","enum":["UNKNOWN_SERVER_ERROR","NONE","OFFSET_OUT_OF_RANGE","CORRUPT_MESSAGE","UNKNOWN_TOPIC_OR_PARTITION","INVALID_FETCH_SIZE","LEADER_NOT_AVAILABLE","NOT_LEADER_FOR_PARTITION","REQUEST_TIMED_OUT","BROKER_NOT_AVAILABLE","REPLICA_NOT_AVAILABLE","MESSAGE_TOO_LARGE","STALE_CONTROLLER_EPOCH","OFFSET_METADATA_TOO_LARGE","NETWORK_EXCEPTION","COORDINATOR_LOAD_IN_PROGRESS","COORDINATOR_NOT_AVAILABLE","NOT_COORDINATOR","INVALID_TOPIC_EXCEPTION","RECORD_LIST_TOO_LARGE","NOT_ENOUGH_REPLICAS","NOT_ENOUGH_REPLICAS_AFTER_APPEND","INVALID_REQUIRED_ACKS","ILLEGAL_GENERATION","INCONSISTENT_GROUP_PROTOCOL","INVALID_GROUP_ID","UNKNOWN_MEMBER_ID","INVALID_SESSION_TIMEOUT","REBALANCE_IN_PROGRESS","INVALID_COMMIT_OFFSET_SIZE","TOPIC_AUTHORIZATION_FAILED","GROUP_AUTHORIZATION_FAILED","CLUSTER_AUTHORIZATION_FAILED","INVALID_TIMESTAMP","UNSUPPORTED_SASL_MECHANISM","ILLEGAL_SASL_STATE","UNSUPPORTED_VERSION","TOPIC_ALREADY_EXISTS","INVALID_PARTITIONS","INVALID_REPLICATION_FACTOR","INVALID_REPLICA_ASSIGNMENT","INVALID_CONFIG","NOT_CONTROLLER","INVALID_REQUEST","UNSUPPORTED_FOR_MESSAGE_FORMAT","POLICY_VIOLATION","OUT_OF_ORDER_SEQUENCE_NUMBER","DUPLICATE_SEQUENCE_NUMBER","INVALID_PRODUCER_EPOCH","INVALID_TXN_STATE","INVALID_PRODUCER_ID_MAPPING","INVALID_TRANSACTION_TIMEOUT","CONCURRENT_TRANSACTIONS","TRANSACTION_COORDINATOR_FENCED","TRANSACTIONAL_ID_AUTHORIZATION_FAILED","SECURITY_DISABLED","OPERATION_NOT_ATTEMPTED","KAFKA_STORAGE_ERROR","LOG_DIR_NOT_FOUND","SASL_AUTHENTICATION_FAILED","UNKNOWN_PRODUCER_ID","REASSIGNMENT_IN_PROGRESS","DELEGATION_TOKEN_AUTH_DISABLED","DELEGATION_TOKEN_NOT_FOUND","DELEGATION_TOKEN_OWNER_MISMATCH","DELEGATION_TOKEN_REQUEST_NOT_ALLOWED","DELEGATION_TOKEN_AUTHORIZATION_FAILED","DELEGATION_TOKEN_EXPIRED","INVALID_PRINCIPAL_TYPE","NON_EMPTY_GROUP","GROUP_ID_NOT_FOUND","FETCH_SESSION_ID_NOT_FOUND","INVALID_FETCH_SESSION_EPOCH"]},"replicaInfos":{"type":"object","additionalProperties":{"$ref":"#/definitions/ReplicaInfo"}}}},"Map«int,long»":{"type":"object","additionalProperties":{"type":"integer","format":"int64"}},"Map«string,LogDirInfo»":{"type":"object","additionalProperties":{"$ref":"#/definitions/LogDirInfo"}},"MemberDescription":{"type":"object","properties":{"assignment":{"type":"array","items":{"$ref":"#/definitions/TopicPartition"}},"clientId":{"type":"string"},"host":{"type":"string"},"memberId":{"type":"string"}}},"Node":{"type":"object","properties":{"empty":{"type":"boolean"},"hash":{"type":"integer","format":"int32"},"host":{"type":"string"},"id":{"type":"integer","format":"int32"},"idString":{"type":"string"},"port":{"type":"integer","format":"int32"},"rack":{"type":"string"}}},"Pattern":{"type":"object","properties":{"cursor":{"type":"integer","format":"int32"},"flags":{"type":"integer","format":"int32"},"pattern":{"type":"string"}}},"ReassignModel":{"type":"object","properties":{"partitions":{"type":"array","items":{"$ref":"#/definitions/TopicPartitionReplicaAssignment"}},"version":{"type":"integer","format":"int32"}}},"ReassignStatus":{"type":"object","properties":{"msg":{"type":"string"},"partitionsReassignStatus":{"type":"object","additionalProperties":{"type":"integer","format":"int32"}},"removeThrottle":{"type":"boolean"},"replicasReassignStatus":{"type":"object","additionalProperties":{"type":"integer","format":"int32"}}}},"ReassignWrapper":{"type":"object","properties":{"brokers":{"type":"array","items":{"type":"integer","format":"int32"}},"topics":{"type":"array","items":{"type":"string"}}}},"Record":{"type":"object","properties":{"decoder":{"type":"string"},"key":{"type":"object"},"offset":{"type":"integer","format":"int64"},"timestamp":{"type":"integer","format":"int64"},"topic":{"type":"string"},"value":{"type":"string"}}},"ReplicaInfo":{"type":"object","properties":{"isFuture":{"type":"boolean"},"offsetLag":{"type":"integer","format":"int64"},"size":{"type":"integer","format":"int64"}}},"ReplicaLogDirInfo":{"type":"object","properties":{"currentReplicaLogDir":{"type":"string"},"currentReplicaOffsetLag":{"type":"integer","format":"int64"},"futureReplicaLogDir":{"type":"string"},"futureReplicaOffsetLag":{"type":"integer","format":"int64"}}},"TopicBrief":{"type":"object","properties":{"isrRate":{"type":"number","format":"double"},"numPartition":{"type":"integer","format":"int32"},"topic":{"type":"string"}}},"TopicDetail":{"type":"object","properties":{"partitions":{"type":"integer","format":"int32"},"factor":{"type":"integer","format":"int32"},"name":{"type":"string"},"prop":{"type":"object","additionalProperties":{"type":"object"}},"replicasAssignments":{"type":"object","additionalProperties":{"type":"array","items":{"type":"integer","format":"int32"}}}}},"TopicMeta":{"type":"object","properties":{"internal":{"type":"boolean"},"partitionCount":{"type":"integer","format":"int32"},"replicationFactor":{"type":"integer","format":"int32"},"topicName":{"type":"string"},"topicPartitionInfos":{"type":"array","items":{"$ref":"#/definitions/CustomTopicPartitionInfo"}}}},"TopicPartition":{"type":"object","properties":{"hash":{"type":"integer","format":"int32"},"partition":{"type":"integer","format":"int32"},"topic":{"type":"string"}}},"TopicPartitionInfo":{"type":"object","properties":{"isr":{"type":"array","items":{"$ref":"#/definitions/Node"}},"leader":{"$ref":"#/definitions/Node"},"partition":{"type":"integer","format":"int32"},"replicas":{"type":"array","items":{"$ref":"#/definitions/Node"}}}},"TopicPartitionReplica":{"type":"object","properties":{"brokerId":{"type":"integer","format":"int32"},"hash":{"type":"integer","format":"int32"},"partition":{"type":"integer","format":"int32"},"topic":{"type":"string"}}},"TopicPartitionReplicaAssignment":{"type":"object","properties":{"topic":{"type":"string"},"partition":{"type":"integer","format":"int32"},"replicas":{"type":"array","items":{"type":"integer","format":"int32"}},"log_dirs":{"type":"array","items":{"type":"string"}}}},"User":{"type":"object","properties":{"username":{"type":"string"},"password":{"type":"string"},"role":{"type":"string"}}},"ZkServerClient":{"type":"object","properties":{"host":{"type":"string"},"ops":{"type":"integer","format":"int32"},"port":{"type":"integer","format":"int32"},"queued":{"type":"integer","format":"int32"},"received":{"type":"integer","format":"int32"},"sent":{"type":"integer","format":"int32"}}},"ZkServerEnvironment":{"type":"object","properties":{"attributes":{"type":"object","additionalProperties":{"type":"string"}}}},"ZkServerStat":{"type":"object","properties":{"avgLatency":{"type":"integer","format":"int32"},"buildDate":{"type":"string"},"clients":{"type":"array","items":{"$ref":"#/definitions/ZkServerClient"}},"connections":{"type":"integer","format":"int32"},"maxLatency":{"type":"integer","format":"int32"},"minLatency":{"type":"integer","format":"int32"},"mode":{"type":"string","enum":["Leader","Follower","Observer","Standalone","Down","Unknow"]},"msg":{"type":"string"},"nodes":{"type":"integer","format":"int32"},"outstanding":{"type":"integer","format":"int32"},"received":{"type":"integer","format":"int32"},"sent":{"type":"integer","format":"int32"},"version":{"type":"string"},"zxId":{"type":"string"}}}}} \ No newline at end of file diff --git a/src/test/java/org/gnuhpc/bigdata/KafkaRestSpringbootApplicationTests.java b/src/test/java/org/gnuhpc/bigdata/KafkaRestSpringbootApplicationTests.java deleted file mode 100644 index 7769684..0000000 --- a/src/test/java/org/gnuhpc/bigdata/KafkaRestSpringbootApplicationTests.java +++ /dev/null @@ -1,50 +0,0 @@ -package org.gnuhpc.bigdata; - -import kafka.utils.ZkUtils; -import lombok.extern.log4j.Log4j; -import org.apache.curator.framework.CuratorFramework; -import org.gnuhpc.bigdata.service.KafkaAdminService; -import org.gnuhpc.bigdata.utils.KafkaUtils; -import org.gnuhpc.bigdata.utils.ZookeeperUtils; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import org.springframework.test.context.web.WebAppConfiguration; - -@SpringBootTest -@RunWith(SpringJUnit4ClassRunner.class) -@WebAppConfiguration -@Log4j -public class KafkaRestSpringbootApplicationTests { - - @Autowired private KafkaAdminService kafkaAdminService; - - @Autowired private ZookeeperUtils zookeeperUtils; - - @Autowired private KafkaUtils kafkaUtils; - - private ZkUtils zkUtils; - - private CuratorFramework zkCuratorClient; - - @Before - public void before() { - this.zkUtils = zookeeperUtils.getZkUtils(); -// this.zkCuratorClient = zookeeperUtils.getCuratorClient(); - } - - @Test - public void contextLoads() { - return; - } - - @Test - public void testListBrokers() throws Exception { - // List brokerInfoList = kafkaAdminService.listBrokers(); - // brokerInfoList.stream().forEach(log::info); - System.out.println(kafkaAdminService.listTopics()); - } -} From 4d869dccdb84b74816d2fc266cf1a6da468a3590 Mon Sep 17 00:00:00 2001 From: wenqiao <315524513@qq.com> Date: Wed, 24 Apr 2019 16:53:20 +0800 Subject: [PATCH 17/24] Add leader check in function getBeginningOffset and getEndOffset. --- docs/index.html | 406 +- docs/index.pdf | Bin 1289556 -> 1312630 bytes docs/paths.adoc | 39 + src/docs/swagger/swagger.json | 2 +- .../bigdata/controller/KafkaController.java | 19 + .../bigdata/service/KafkaAdminService.java | 5165 +++++++++-------- src/main/resources/application-tina.yml | 4 +- .../service/KafkaAdminServiceTest.java | 6 +- 8 files changed, 2946 insertions(+), 2695 deletions(-) diff --git a/docs/index.html b/docs/index.html index c3cf3ec..d26165d 100644 --- a/docs/index.html +++ b/docs/index.html @@ -462,27 +462,28 @@

    Kafka REST API SwaggerUI

  1. 4.2.12. getLastCommitTimestamp
  2. 4.2.13. Reset consumer group offset, earliest/latest can be used. Support reset by time for new consumer group, pass a parameter that satisfies yyyy-MM-dd HH:mm:ss.SSS to offset.
  3. 4.2.14. List all consumer groups from zk and kafka
  4. -
  5. 4.2.15. Get the meta data of the specify new consumer group, including state, coordinator, assignmentStrategy, members
  6. -
  7. 4.2.16. Describe consumer group, showing lag and offset, may be slow if multi topics are listened
  8. -
  9. 4.2.17. Get the topics involved of the specify consumer group
  10. -
  11. 4.2.18. Describe consumer groups by topic, showing lag and offset
  12. -
  13. 4.2.19. Get controller in this cluster
  14. -
  15. 4.2.20. Check the cluster health.
  16. -
  17. 4.2.21. Add partitions to the topics
  18. -
  19. 4.2.22. Check the partition reassignment process
  20. -
  21. 4.2.23. Execute the partition reassignment
  22. -
  23. 4.2.24. Generate plan for the partition reassignment
  24. -
  25. 4.2.25. List topics
  26. -
  27. 4.2.26. Delete a topic list (you should enable topic deletion
  28. -
  29. 4.2.27. Create topics
  30. -
  31. 4.2.28. Describe a topic by fetching the metadata and config
  32. -
  33. 4.2.29. Get topic configs
  34. -
  35. 4.2.30. Update topic configs
  36. -
  37. 4.2.31. Get topic config by key
  38. -
  39. 4.2.32. Update a topic config by key
  40. -
  41. 4.2.33. Get topic dyn configs
  42. -
  43. 4.2.34. Tell if a topic exists
  44. -
  45. 4.2.35. List topics Brief
  46. +
  47. 4.2.15. Get the meta data of the specified new consumer groups, including state, coordinator, assignmentStrategy, members
  48. +
  49. 4.2.16. Get the meta data of the specify new consumer group, including state, coordinator, assignmentStrategy, members
  50. +
  51. 4.2.17. Describe consumer group, showing lag and offset, may be slow if multi topics are listened
  52. +
  53. 4.2.18. Get the topics involved of the specify consumer group
  54. +
  55. 4.2.19. Describe consumer groups by topic, showing lag and offset
  56. +
  57. 4.2.20. Get controller in this cluster
  58. +
  59. 4.2.21. Check the cluster health.
  60. +
  61. 4.2.22. Add partitions to the topics
  62. +
  63. 4.2.23. Check the partition reassignment process
  64. +
  65. 4.2.24. Execute the partition reassignment
  66. +
  67. 4.2.25. Generate plan for the partition reassignment
  68. +
  69. 4.2.26. List topics
  70. +
  71. 4.2.27. Delete a topic list (you should enable topic deletion
  72. +
  73. 4.2.28. Create topics
  74. +
  75. 4.2.29. Describe a topic by fetching the metadata and config
  76. +
  77. 4.2.30. Get topic configs
  78. +
  79. 4.2.31. Update topic configs
  80. +
  81. 4.2.32. Get topic config by key
  82. +
  83. 4.2.33. Update a topic config by key
  84. +
  85. 4.2.34. Get topic dyn configs
  86. +
  87. 4.2.35. Tell if a topic exists
  88. +
  89. 4.2.36. List topics Brief
  90. 4.3. User-controller @@ -2321,10 +2322,10 @@
    Produces
    -

    4.2.15. Get the meta data of the specify new consumer group, including state, coordinator, assignmentStrategy, members

    +

    4.2.15. Get the meta data of the specified new consumer groups, including state, coordinator, assignmentStrategy, members

    -
    GET /kafka/consumergroups/{consumerGroup}/meta
    +
    GET /kafka/consumergroups/meta
    @@ -2346,6 +2347,101 @@
    Parameters< +

    Query

    +

    consumerGroupList
    +required

    +

    consumerGroupList

    +

    < string > array(multi)

    + + + +
    +
    +
    Responses
    + +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    HTTP CodeDescriptionSchema

    200

    OK

    < ConsumerGroupMeta > array

    401

    Unauthorized

    No Content

    403

    Forbidden

    No Content

    404

    Not Found

    No Content

    +
    +
    +
    Consumes
    +
    +
      +
    • +

      application/json

      +
    • +
    +
    +
    +
    +
    Produces
    +
    +
      +
    • +

      /

      +
    • +
    +
    +
    +
    +
    +

    4.2.16. Get the meta data of the specify new consumer group, including state, coordinator, assignmentStrategy, members

    +
    +
    +
    GET /kafka/consumergroups/{consumerGroup}/meta
    +
    +
    +
    +
    Parameters
    + ++++++ + + + + + + + + + + @@ -2356,7 +2452,7 @@
    Parameters<
    TypeNameDescriptionSchema

    Path

    consumerGroup
    required

    -
    Responses
    +
    Responses
    @@ -2395,7 +2491,7 @@
    Responses
    -
    Consumes
    +
    Consumes
    • @@ -2405,7 +2501,7 @@
      Consumes
    -
    Produces
    +
    Produces
    • @@ -2416,14 +2512,14 @@
      Produces
    -

    4.2.16. Describe consumer group, showing lag and offset, may be slow if multi topics are listened

    +

    4.2.17. Describe consumer group, showing lag and offset, may be slow if multi topics are listened

    GET /kafka/consumergroups/{consumerGroup}/{type}
    -
    Parameters
    +
    Parameters
    @@ -2458,7 +2554,7 @@
    Parameters<
    -
    Responses
    +
    Responses
    @@ -2497,7 +2593,7 @@
    Responses
    -
    Consumes
    +
    Consumes
    • @@ -2507,7 +2603,7 @@
      Consumes
    -
    Produces
    +
    Produces
    • @@ -2518,14 +2614,14 @@
      Produces
    -

    4.2.17. Get the topics involved of the specify consumer group

    +

    4.2.18. Get the topics involved of the specify consumer group

    GET /kafka/consumergroups/{consumerGroup}/{type}/topic
    -
    Parameters
    +
    Parameters
    @@ -2560,7 +2656,7 @@
    Parameters<
    -
    Responses
    +
    Responses
    @@ -2599,7 +2695,7 @@
    Responses
    -
    Consumes
    +
    Consumes
    • @@ -2609,7 +2705,7 @@
      Consumes
    -
    Produces
    +
    Produces
    • @@ -2620,14 +2716,14 @@
      Produces
    -

    4.2.18. Describe consumer groups by topic, showing lag and offset

    +

    4.2.19. Describe consumer groups by topic, showing lag and offset

    GET /kafka/consumergroups/{type}/topic/{topic}
    -
    Parameters
    +
    Parameters
    @@ -2669,7 +2765,7 @@
    Parameters<
    -
    Responses
    +
    Responses
    @@ -2708,7 +2804,7 @@
    Responses
    -
    Consumes
    +
    Consumes
    • @@ -2718,7 +2814,7 @@
      Consumes
    -
    Produces
    +
    Produces
    • @@ -2729,14 +2825,14 @@
      Produces
    -

    4.2.19. Get controller in this cluster

    +

    4.2.20. Get controller in this cluster

    GET /kafka/controller
    -
    Responses
    +
    Responses
    @@ -2775,7 +2871,7 @@
    Responses
    -
    Consumes
    +
    Consumes
    • @@ -2785,7 +2881,7 @@
      Consumes
    -
    Produces
    +
    Produces
    • @@ -2796,14 +2892,14 @@
      Produces
    -

    4.2.20. Check the cluster health.

    +

    4.2.21. Check the cluster health.

    GET /kafka/health
    -
    Responses
    +
    Responses
    @@ -2842,7 +2938,7 @@
    Responses
    -
    Consumes
    +
    Consumes
    • @@ -2852,7 +2948,7 @@
      Consumes
    -
    Produces
    +
    Produces
    • @@ -2863,14 +2959,14 @@
      Produces
    -

    4.2.21. Add partitions to the topics

    +

    4.2.22. Add partitions to the topics

    POST /kafka/partitions/add
    -
    Parameters
    +
    Parameters
    @@ -2898,7 +2994,7 @@
    Parameters<
    -
    Responses
    +
    Responses
    @@ -2942,7 +3038,7 @@
    Responses
    -
    Consumes
    +
    Consumes
    • @@ -2952,7 +3048,7 @@
      Consumes
    -
    Produces
    +
    Produces
    • @@ -2963,14 +3059,14 @@
      Produces
    -

    4.2.22. Check the partition reassignment process

    +

    4.2.23. Check the partition reassignment process

    PUT /kafka/partitions/reassign/check
    -
    Parameters
    +
    Parameters
    @@ -2998,7 +3094,7 @@
    Parameters<
    -
    Responses
    +
    Responses
    @@ -3057,7 +3153,7 @@
    Responses
    -
    Consumes
    +
    Consumes
    • @@ -3067,7 +3163,7 @@
      Consumes
    -
    Produces
    +
    Produces
    • @@ -3078,14 +3174,14 @@
      Produces
    -

    4.2.23. Execute the partition reassignment

    +

    4.2.24. Execute the partition reassignment

    PUT /kafka/partitions/reassign/execute
    -
    Parameters
    +
    Parameters
    @@ -3134,7 +3230,7 @@
    Parameters<
    -
    Responses
    +
    Responses
    @@ -3178,7 +3274,7 @@
    Responses
    -
    Consumes
    +
    Consumes
    • @@ -3188,7 +3284,7 @@
      Consumes
    -
    Produces
    +
    Produces
    • @@ -3199,14 +3295,14 @@
      Produces
    -

    4.2.24. Generate plan for the partition reassignment

    +

    4.2.25. Generate plan for the partition reassignment

    POST /kafka/partitions/reassign/generate
    -
    Parameters
    +
    Parameters
    @@ -3234,7 +3330,7 @@
    Parameters<
    -
    Responses
    +
    Responses
    @@ -3278,7 +3374,7 @@
    Responses
    -
    Consumes
    +
    Consumes
    • @@ -3288,7 +3384,7 @@
      Consumes
    -
    Produces
    +
    Produces
    • @@ -3299,14 +3395,14 @@
      Produces
    -

    4.2.25. List topics

    +

    4.2.26. List topics

    GET /kafka/topics
    -
    Responses
    +
    Responses
    @@ -3345,7 +3441,7 @@
    Responses
    -
    Consumes
    +
    Consumes
    • @@ -3355,7 +3451,7 @@
      Consumes
    -
    Produces
    +
    Produces
    • @@ -3366,14 +3462,14 @@
      Produces
    -

    4.2.26. Delete a topic list (you should enable topic deletion

    +

    4.2.27. Delete a topic list (you should enable topic deletion

    DELETE /kafka/topics
    -
    Parameters
    +
    Parameters
    @@ -3401,7 +3497,7 @@
    Parameters<
    -
    Responses
    +
    Responses
    @@ -3440,7 +3536,7 @@
    Responses
    -
    Consumes
    +
    Consumes
    • @@ -3450,7 +3546,7 @@
      Consumes
    -
    Produces
    +
    Produces
    • @@ -3461,14 +3557,14 @@
      Produces
    -

    4.2.27. Create topics

    +

    4.2.28. Create topics

    POST /kafka/topics/create
    -
    Parameters
    +
    Parameters
    @@ -3496,7 +3592,7 @@
    Parameters<
    -
    Responses
    +
    Responses
    @@ -3535,7 +3631,7 @@
    Responses
    -
    Consumes
    +
    Consumes
    • @@ -3545,7 +3641,7 @@
      Consumes
    -
    Produces
    +
    Produces
    • @@ -3556,14 +3652,14 @@
      Produces
    -

    4.2.28. Describe a topic by fetching the metadata and config

    +

    4.2.29. Describe a topic by fetching the metadata and config

    GET /kafka/topics/{topic}
    -
    Parameters
    +
    Parameters
    @@ -3591,7 +3687,7 @@
    Parameters<
    -
    Responses
    +
    Responses
    @@ -3630,7 +3726,7 @@
    Responses
    -
    Consumes
    +
    Consumes
    • @@ -3640,7 +3736,7 @@
      Consumes
    -
    Produces
    +
    Produces
    • @@ -3651,14 +3747,14 @@
      Produces
    -

    4.2.29. Get topic configs

    +

    4.2.30. Get topic configs

    GET /kafka/topics/{topic}/conf
    -
    Parameters
    +
    Parameters
    @@ -3686,7 +3782,7 @@
    Parameters<
    -
    Responses
    +
    Responses
    @@ -3725,7 +3821,7 @@
    Responses
    -
    Consumes
    +
    Consumes
    • @@ -3735,7 +3831,7 @@
      Consumes
    -
    Produces
    +
    Produces
    • @@ -3746,14 +3842,14 @@
      Produces
    -

    4.2.30. Update topic configs

    +

    4.2.31. Update topic configs

    PUT /kafka/topics/{topic}/conf
    -
    Parameters
    +
    Parameters
    @@ -3788,7 +3884,7 @@
    Parameters<
    -
    Responses
    +
    Responses
    @@ -3832,7 +3928,7 @@
    Responses
    -
    Consumes
    +
    Consumes
    • @@ -3842,7 +3938,7 @@
      Consumes
    -
    Produces
    +
    Produces
    • @@ -3853,14 +3949,14 @@
      Produces
    -

    4.2.31. Get topic config by key

    +

    4.2.32. Get topic config by key

    GET /kafka/topics/{topic}/conf/{key}
    -
    Parameters
    +
    Parameters
    @@ -3895,7 +3991,7 @@
    Parameters<
    -
    Responses
    +
    Responses
    @@ -3934,7 +4030,7 @@
    Responses
    -
    Consumes
    +
    Consumes
    • @@ -3944,7 +4040,7 @@
      Consumes
    -
    Produces
    +
    Produces
    • @@ -3955,14 +4051,14 @@
      Produces
    -

    4.2.32. Update a topic config by key

    +

    4.2.33. Update a topic config by key

    PUT /kafka/topics/{topic}/conf/{key}={value}
    -
    Parameters
    +
    Parameters
    @@ -4004,7 +4100,7 @@
    Parameters<
    -
    Responses
    +
    Responses
    @@ -4048,7 +4144,7 @@
    Responses
    -
    Consumes
    +
    Consumes
    • @@ -4058,7 +4154,7 @@
      Consumes
    -
    Produces
    +
    Produces
    • @@ -4069,14 +4165,14 @@
      Produces
    -

    4.2.33. Get topic dyn configs

    +

    4.2.34. Get topic dyn configs

    GET /kafka/topics/{topic}/dynconf
    -
    Parameters
    +
    Parameters
    @@ -4104,7 +4200,7 @@
    Parameters<
    -
    Responses
    +
    Responses
    @@ -4143,7 +4239,7 @@
    Responses
    -
    Consumes
    +
    Consumes
    • @@ -4153,7 +4249,7 @@
      Consumes
    -
    Produces
    +
    Produces
    • @@ -4164,14 +4260,14 @@
      Produces
    -

    4.2.34. Tell if a topic exists

    +

    4.2.35. Tell if a topic exists

    GET /kafka/topics/{topic}/exist
    -
    Parameters
    +
    Parameters
    @@ -4199,7 +4295,7 @@
    Parameters<
    -
    Responses
    +
    Responses
    @@ -4238,7 +4334,7 @@
    Responses
    -
    Consumes
    +
    Consumes
    • @@ -4248,7 +4344,7 @@
      Consumes
    -
    Produces
    +
    Produces
    • @@ -4259,14 +4355,14 @@
      Produces
    -

    4.2.35. List topics Brief

    +

    4.2.36. List topics Brief

    GET /kafka/topicsbrief
    -
    Responses
    +
    Responses
    @@ -4305,7 +4401,7 @@
    Responses
    -
    Consumes
    +
    Consumes
    • @@ -4315,7 +4411,7 @@
      Consumes
    -
    Produces
    +
    Produces
    • @@ -4339,7 +4435,7 @@

      4.3.1

    -
    Parameters
    +
    Parameters
    @@ -4367,7 +4463,7 @@
    Parameters<
    -
    Responses
    +
    Responses
    @@ -4411,7 +4507,7 @@
    Responses
    -
    Consumes
    +
    Consumes
    • @@ -4421,7 +4517,7 @@
      Consumes
    -
    Produces
    +
    Produces
    • @@ -4439,7 +4535,7 @@

      4.3.2

    -
    Responses
    +
    Responses
    @@ -4478,7 +4574,7 @@
    Responses
    -
    Consumes
    +
    Consumes
    • @@ -4488,7 +4584,7 @@
      Consumes
    -
    Produces
    +
    Produces
    • @@ -4506,7 +4602,7 @@

      4

    -
    Parameters
    +
    Parameters
    @@ -4534,7 +4630,7 @@
    Parameters<
    -
    Responses
    +
    Responses
    @@ -4578,7 +4674,7 @@
    Responses
    -
    Consumes
    +
    Consumes
    • @@ -4588,7 +4684,7 @@
      Consumes
    -
    Produces
    +
    Produces
    • @@ -4606,7 +4702,7 @@

      4

    -
    Parameters
    +
    Parameters
    @@ -4634,7 +4730,7 @@
    Parameters<
    -
    Responses
    +
    Responses
    @@ -4673,7 +4769,7 @@
    Responses
    -
    Consumes
    +
    Consumes
    • @@ -4683,7 +4779,7 @@
      Consumes
    -
    Produces
    +
    Produces
    • @@ -4707,7 +4803,7 @@

    -
    Responses
    +
    Responses
    @@ -4746,7 +4842,7 @@
    Responses
    -
    Consumes
    +
    Consumes
    • @@ -4756,7 +4852,7 @@
      Consumes
    -
    Produces
    +
    Produces
    • @@ -4774,7 +4870,7 @@

      4.4.2. Ge

    -
    Responses
    +
    Responses
    @@ -4813,7 +4909,7 @@
    Responses
    -
    Consumes
    +
    Consumes
    • @@ -4823,7 +4919,7 @@
      Consumes
    -
    Produces
    +
    Produces
    • @@ -4841,7 +4937,7 @@

      4.4.3. Get data

    -
    Parameters
    +
    Parameters
    @@ -4869,7 +4965,7 @@
    Parameters<
    -
    Responses
    +
    Responses
    @@ -4908,7 +5004,7 @@
    Responses
    -
    Consumes
    +
    Consumes
    • @@ -4918,7 +5014,7 @@
      Consumes
    -
    Produces
    +
    Produces
    • @@ -4936,7 +5032,7 @@

      4.4.4. List a zoo

    -
    Parameters
    +
    Parameters
    @@ -4964,7 +5060,7 @@
    Parameters<
    -
    Responses
    +
    Responses
    @@ -5003,7 +5099,7 @@
    Responses
    -
    Consumes
    +
    Consumes
    • @@ -5013,7 +5109,7 @@
      Consumes
    -
    Produces
    +
    Produces
    • @@ -5031,7 +5127,7 @@

      4.4.5.

    -
    Responses
    +
    Responses
    @@ -5070,7 +5166,7 @@
    Responses
    -
    Consumes
    +
    Consumes
    • @@ -5080,7 +5176,7 @@
      Consumes
    -
    Produces
    +
    Produces
    • @@ -6570,7 +6666,7 @@

      5.39. ZkServe

    diff --git a/docs/index.pdf b/docs/index.pdf index 54be30704cf1579cce94f2c0db28b206218bbad5..869084625a9bb6ec55a817f12b97d57cc51c3edd 100644 GIT binary patch delta 73457 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 N-I|+c95S=p{{x(}Pxb%+ delta 68013 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

    -XIzM5Tx;d?HQmF{&BOVV?uf5``(Q?bb9s*t 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 array(multi) +|=== + + +===== Responses + +[options="header", cols=".^2,.^14,.^4"] +|=== +|HTTP Code|Description|Schema +|**200**|OK|< <<_consumergroupmeta,ConsumerGroupMeta>> > array +|**401**|Unauthorized|No Content +|**403**|Forbidden|No Content +|**404**|Not Found|No Content +|=== + + +===== Consumes + +* `application/json` + + +===== Produces + +* `*/*` + + [[_getconsumergroupmetausingget]] ==== Get the meta data of the specify new consumer group, including state, coordinator, assignmentStrategy, members .... diff --git a/src/docs/swagger/swagger.json b/src/docs/swagger/swagger.json index 4999959..53bb8f5 100644 --- a/src/docs/swagger/swagger.json +++ b/src/docs/swagger/swagger.json @@ -1 +1 @@ -{"swagger":"2.0","info":{"description":"Kafka REST API SwaggerUI","version":"0.1.0","title":"Kafka REST API SwaggerUI","contact":{"name":"gnuhpc","url":"https://github.com/gnuhpc","email":"gnuhpc@gmail.com"}},"host":"localhost:8080","basePath":"/","tags":[{"name":"collector-controller","description":"Rest API for Collecting JMX Metric Data"},{"name":"user-controller","description":"Security User Management Controller."},{"name":"zookeeper-controller","description":"Zookeeper Controller"},{"name":"kafka-controller","description":"Kafka Controller"}],"paths":{"/jmx/v1":{"get":{"tags":["collector-controller"],"summary":"Fetch all JMX metric data","operationId":"collectJMXMetricUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"jmxurl","in":"query","description":"Parameter jmxurl should be a comma-separated list of {IP:Port} or set to 'default'","required":false,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/JMXMetricDataV1"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/jmx/v2":{"post":{"tags":["collector-controller"],"summary":"Fetch JMX metric data with query filter. You can get the query filter template through the API /jmx/v2/filters.","operationId":"collectJMXMetricUsingPOST","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"jmxurl","in":"query","description":"Parameter jmxurl should be a comma-separated list of {IP:Port} or set to 'default'","required":false,"type":"string"},{"in":"body","name":"jmxQuery","description":"jmxQuery","required":true,"schema":{"$ref":"#/definitions/JMXQuery"}}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/JMXMetricData"}}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/jmx/v2/filters":{"get":{"tags":["collector-controller"],"summary":"List the query filter templates with the filterKey. If filterKey is set to empty, it will return all the templates.","operationId":"listJMXFilterTemplateUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"filterKey","in":"query","description":"filterKey","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"object"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/brokers":{"get":{"tags":["kafka-controller"],"summary":"List brokers in this cluster","operationId":"listBrokersUsingGET","consumes":["application/json"],"produces":["*/*"],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/BrokerInfo"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/brokers/logdirs":{"get":{"tags":["kafka-controller"],"summary":"List log dirs by broker list","operationId":"listLogDirsUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"brokerList","in":"query","description":"brokerList","required":false,"type":"array","items":{"type":"integer","format":"int32"},"collectionFormat":"multi"}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/brokers/logdirs/detail":{"get":{"tags":["kafka-controller"],"summary":"Describe log dirs by broker list and topic list","operationId":"describeLogDirsUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"brokerList","in":"query","description":"brokerList","required":false,"type":"array","items":{"type":"integer","format":"int32"},"collectionFormat":"multi"},{"name":"logDirList","in":"query","description":"logDirList","required":false,"type":"array","items":{"type":"string"},"collectionFormat":"multi"},{"name":"topicList","in":"query","description":"topicList","required":false,"type":"array","items":{"type":"string"},"collectionFormat":"multi"}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"object","additionalProperties":{"$ref":"#/definitions/LogDirInfo"}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/brokers/replicalogdirs":{"get":{"tags":["kafka-controller"],"summary":"Describe replica log dirs.","operationId":"describeReplicaLogDirsUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"replicas","in":"query","description":"replicas","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"$ref":"#/definitions/ReplicaLogDirInfo"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/brokers/{brokerId}/conf":{"get":{"tags":["kafka-controller"],"summary":"Get broker configs, including dynamic configs","operationId":"getBrokerConfigUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"brokerId","in":"path","description":"brokerId","required":true,"type":"integer","format":"int32"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/CustomConfigEntry"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/brokers/{brokerId}/dynconf":{"get":{"tags":["kafka-controller"],"summary":"Get broker dynamic configs","operationId":"getBrokerDynConfigUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"brokerId","in":"path","description":"brokerId","required":true,"type":"integer","format":"int32"}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"object"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}},"put":{"tags":["kafka-controller"],"summary":"Update broker configs","operationId":"updateBrokerDynConfigUsingPUT","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"brokerId","in":"path","description":"brokerId","required":true,"type":"integer","format":"int32"},{"in":"body","name":"props","description":"props","required":true,"schema":{"type":"object","additionalProperties":{"type":"object"}}}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"object"}}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}},"delete":{"tags":["kafka-controller"],"summary":"Remove broker dynamic configs","operationId":"removeBrokerDynConfigUsingDELETE","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"brokerId","in":"path","description":"brokerId","required":true,"type":"integer","format":"int32"},{"name":"configKeysToBeRemoved","in":"query","description":"configKeysToBeRemoved","required":true,"type":"array","items":{"type":"string"},"collectionFormat":"multi"}],"responses":{"200":{"description":"OK"},"204":{"description":"No Content"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"}}}},"/kafka/cluster":{"get":{"tags":["kafka-controller"],"summary":"Describe cluster, nodes, controller info.","operationId":"describeClusterUsingGET","consumes":["application/json"],"produces":["*/*"],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/ClusterInfo"}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/consumer/{topic}/{partition}/{offset}":{"get":{"tags":["kafka-controller"],"summary":"Get the message from the offset of the partition in the topic, decoder is not supported yet","operationId":"getMessageUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"topic","in":"path","description":"topic","required":true,"type":"string"},{"name":"partition","in":"path","description":"partition","required":true,"type":"integer","format":"int32"},{"name":"offset","in":"path","description":"offset","required":true,"type":"integer","format":"int64"},{"name":"decoder","in":"query","description":"decoder","required":false,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/Record"}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/consumergroup/{consumergroup}/{type}":{"delete":{"tags":["kafka-controller"],"summary":"Delete Consumer Group","operationId":"deleteOldConsumerGroupUsingDELETE","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"consumergroup","in":"path","description":"consumergroup","required":true,"type":"string"},{"name":"type","in":"path","description":"type","required":true,"type":"string","enum":["NEW","OLD"]}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/GeneralResponse"}},"204":{"description":"No Content"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"}}}},"/kafka/consumergroup/{consumergroup}/{type}/topic/{topic}/lastcommittime":{"get":{"tags":["kafka-controller"],"summary":"getLastCommitTimestamp","operationId":"getLastCommitTimestampUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"consumergroup","in":"path","description":"consumergroup","required":true,"type":"string"},{"name":"topic","in":"path","description":"topic","required":true,"type":"string"},{"name":"type","in":"path","description":"type","required":true,"type":"string","enum":["NEW","OLD"]}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"object","additionalProperties":{"type":"integer","format":"int64"}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/consumergroup/{consumergroup}/{type}/topic/{topic}/{partition}/{offset}":{"put":{"tags":["kafka-controller"],"summary":"Reset consumer group offset, earliest/latest can be used. Support reset by time for new consumer group, pass a parameter that satisfies yyyy-MM-dd HH:mm:ss.SSS to offset.","operationId":"resetOffsetUsingPUT","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"topic","in":"path","description":"topic","required":true,"type":"string"},{"name":"partition","in":"path","description":"partition","required":true,"type":"integer","format":"int32"},{"name":"consumergroup","in":"path","description":"consumergroup","required":true,"type":"string"},{"name":"offset","in":"path","description":"[earliest/latest/{long}/yyyy-MM-dd HH:mm:ss.SSS] can be supported. The date type is only valid for new consumer group.","required":false,"type":"string"},{"name":"type","in":"path","description":"type","required":true,"type":"string","enum":["NEW","OLD"]}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/GeneralResponse"}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/consumergroups":{"get":{"tags":["kafka-controller"],"summary":"List all consumer groups from zk and kafka","operationId":"listAllConsumerGroupsUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"type","in":"query","description":"type","required":false,"type":"string","enum":["NEW","OLD"]},{"name":"topic","in":"query","description":"topic","required":false,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/consumergroups/{consumerGroup}/meta":{"get":{"tags":["kafka-controller"],"summary":"Get the meta data of the specify new consumer group, including state, coordinator, assignmentStrategy, members","operationId":"getConsumerGroupMetaUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"consumerGroup","in":"path","description":"consumerGroup","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/ConsumerGroupMeta"}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/consumergroups/{consumerGroup}/{type}":{"get":{"tags":["kafka-controller"],"summary":"Describe consumer group, showing lag and offset, may be slow if multi topics are listened","operationId":"describeConsumerGroupUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"consumerGroup","in":"path","description":"consumerGroup","required":true,"type":"string"},{"name":"type","in":"path","description":"type","required":true,"type":"string","enum":["NEW","OLD"]}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"array","items":{"$ref":"#/definitions/ConsumerGroupDesc"}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/consumergroups/{consumerGroup}/{type}/topic":{"get":{"tags":["kafka-controller"],"summary":"Get the topics involved of the specify consumer group","operationId":"listTopicByConsumerGroupUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"consumerGroup","in":"path","description":"consumerGroup","required":true,"type":"string"},{"name":"type","in":"path","description":"type","required":true,"type":"string","enum":["NEW","OLD"]}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"type":"string"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/consumergroups/{type}/topic/{topic}":{"get":{"tags":["kafka-controller"],"summary":"Describe consumer groups by topic, showing lag and offset","operationId":"describeConsumerGroupByTopicUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"consumerGroup","in":"query","description":"consumerGroup","required":false,"type":"string"},{"name":"type","in":"path","description":"type","required":true,"type":"string","enum":["NEW","OLD"]},{"name":"topic","in":"path","description":"topic","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/ConsumerGroupDesc"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/controller":{"get":{"tags":["kafka-controller"],"summary":"Get controller in this cluster","operationId":"getControllerIdUsingGET","consumes":["application/json"],"produces":["*/*"],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/Node"}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/health":{"get":{"tags":["kafka-controller"],"summary":"Check the cluster health.","operationId":"healthCheckUsingGET","consumes":["application/json"],"produces":["*/*"],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/HealthCheckResult"}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/partitions/add":{"post":{"tags":["kafka-controller"],"summary":"Add partitions to the topics","operationId":"addPartitionUsingPOST","consumes":["application/json"],"produces":["*/*"],"parameters":[{"in":"body","name":"addPartitions","description":"addPartitions","required":true,"schema":{"type":"array","items":{"$ref":"#/definitions/AddPartition"}}}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"$ref":"#/definitions/GeneralResponse"}}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/partitions/reassign/check":{"put":{"tags":["kafka-controller"],"summary":"Check the partition reassignment process","operationId":"checkReassignPartitionsUsingPUT","consumes":["application/json"],"produces":["*/*"],"parameters":[{"in":"body","name":"reassign","description":"reassign","required":true,"schema":{"$ref":"#/definitions/ReassignModel"}}],"responses":{"-1":{"description":"Reassignment Failed"},"0":{"description":"Reassignment In Progress"},"1":{"description":"Reassignment Completed"},"200":{"description":"OK","schema":{"$ref":"#/definitions/ReassignStatus"}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/partitions/reassign/execute":{"put":{"tags":["kafka-controller"],"summary":"Execute the partition reassignment","operationId":"executeReassignPartitionsUsingPUT","consumes":["application/json"],"produces":["*/*"],"parameters":[{"in":"body","name":"reassign","description":"reassign","required":true,"schema":{"$ref":"#/definitions/ReassignModel"}},{"name":"interBrokerThrottle","in":"query","description":"interBrokerThrottle","required":false,"type":"integer","format":"int64"},{"name":"replicaAlterLogDirsThrottle","in":"query","description":"replicaAlterLogDirsThrottle","required":false,"type":"integer","format":"int64"},{"name":"timeoutMs","in":"query","description":"timeoutMs","required":false,"type":"integer","format":"int64"}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/ReassignStatus"}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/partitions/reassign/generate":{"post":{"tags":["kafka-controller"],"summary":"Generate plan for the partition reassignment","operationId":"generateReassignPartitionsUsingPOST","consumes":["application/json"],"produces":["*/*"],"parameters":[{"in":"body","name":"reassignWrapper","description":"reassignWrapper","required":true,"schema":{"$ref":"#/definitions/ReassignWrapper"}}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/ReassignModel"}}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/topics":{"get":{"tags":["kafka-controller"],"summary":"List topics","operationId":"listTopicsUsingGET","consumes":["application/json"],"produces":["*/*"],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"type":"string"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}},"delete":{"tags":["kafka-controller"],"summary":"Delete a topic list (you should enable topic deletion","operationId":"deleteTopicListUsingDELETE","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"topicList","in":"query","description":"topicList","required":true,"type":"array","items":{"type":"string"},"collectionFormat":"multi"}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"$ref":"#/definitions/GeneralResponse"}}},"204":{"description":"No Content"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"}}}},"/kafka/topics/create":{"post":{"tags":["kafka-controller"],"summary":"Create topics","operationId":"createTopicUsingPOST","consumes":["application/json"],"produces":["*/*"],"parameters":[{"in":"body","name":"topicList","description":"topicList","required":true,"schema":{"type":"array","items":{"$ref":"#/definitions/TopicDetail"}}}],"responses":{"201":{"description":"Created","schema":{"type":"object","additionalProperties":{"$ref":"#/definitions/GeneralResponse"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/topics/{topic}":{"get":{"tags":["kafka-controller"],"summary":"Describe a topic by fetching the metadata and config","operationId":"describeTopicUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"topic","in":"path","description":"topic","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/TopicMeta"}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/topics/{topic}/conf":{"get":{"tags":["kafka-controller"],"summary":"Get topic configs","operationId":"getTopicConfigUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"topic","in":"path","description":"topic","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/CustomConfigEntry"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}},"put":{"tags":["kafka-controller"],"summary":"Update topic configs","operationId":"updateTopicConfigUsingPUT","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"topic","in":"path","description":"topic","required":true,"type":"string"},{"in":"body","name":"props","description":"props","required":true,"schema":{"type":"object","additionalProperties":{"type":"object"}}}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/CustomConfigEntry"}}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/topics/{topic}/conf/{key}":{"get":{"tags":["kafka-controller"],"summary":"Get topic config by key","operationId":"getTopicConfigByKeyUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"topic","in":"path","description":"topic","required":true,"type":"string"},{"name":"key","in":"path","description":"key","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"object"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/topics/{topic}/conf/{key}={value}":{"put":{"tags":["kafka-controller"],"summary":"Update a topic config by key","operationId":"updateTopicConfigByKeyUsingPUT","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"topic","in":"path","description":"topic","required":true,"type":"string"},{"name":"key","in":"path","description":"key","required":true,"type":"string"},{"name":"value","in":"path","description":"value","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/CustomConfigEntry"}}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/topics/{topic}/dynconf":{"get":{"tags":["kafka-controller"],"summary":"Get topic dyn configs","operationId":"getTopicDynConfigUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"topic","in":"path","description":"topic","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"object"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/topics/{topic}/exist":{"get":{"tags":["kafka-controller"],"summary":"Tell if a topic exists","operationId":"existTopicUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"topic","in":"path","description":"topic","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"boolean"}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/topicsbrief":{"get":{"tags":["kafka-controller"],"summary":"List topics Brief","operationId":"listTopicBriefUsingGET","consumes":["application/json"],"produces":["*/*"],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/TopicBrief"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/users":{"get":{"tags":["user-controller"],"summary":"Get user list.","operationId":"listUserUsingGET","consumes":["application/json"],"produces":["*/*"],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"type":"string"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}},"post":{"tags":["user-controller"],"summary":"Add user.","operationId":"addUserUsingPOST","consumes":["application/json"],"produces":["*/*"],"parameters":[{"in":"body","name":"user","description":"user","required":true,"schema":{"$ref":"#/definitions/User"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/GeneralResponse"}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}},"put":{"tags":["user-controller"],"summary":"Modify user information.","operationId":"modifyUserUsingPUT","consumes":["application/json"],"produces":["*/*"],"parameters":[{"in":"body","name":"user","description":"user","required":true,"schema":{"$ref":"#/definitions/User"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/GeneralResponse"}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/users/{username}":{"delete":{"tags":["user-controller"],"summary":"Delete user.","operationId":"delUserUsingDELETE","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"username","in":"path","description":"username","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/GeneralResponse"}},"204":{"description":"No Content"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"}}}},"/zk/connstate":{"get":{"tags":["zookeeper-controller"],"summary":"Get the connection state of zookeeper","operationId":"zkConnStateUsingGET","consumes":["application/json"],"produces":["*/*"],"responses":{"200":{"description":"OK","schema":{"type":"string"}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/zk/env":{"get":{"tags":["zookeeper-controller"],"summary":"Get the environment information of zookeeper","operationId":"getEnvUsingGET","consumes":["application/json"],"produces":["*/*"],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"$ref":"#/definitions/ZkServerEnvironment"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/zk/get/path":{"get":{"tags":["zookeeper-controller"],"summary":"Get data of a zookeeper path","operationId":"getUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"path","in":"query","description":"path","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"string"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/zk/ls/path":{"get":{"tags":["zookeeper-controller"],"summary":"List a zookeeper path","operationId":"lsUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"path","in":"query","description":"path","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"type":"string"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/zk/stat":{"get":{"tags":["zookeeper-controller"],"summary":"Get the service state of zookeeper","operationId":"getStatUsingGET","consumes":["application/json"],"produces":["*/*"],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"$ref":"#/definitions/ZkServerStat"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}}},"definitions":{"AddPartition":{"type":"object","properties":{"topic":{"type":"string"},"numPartitionsAdded":{"type":"integer","format":"int32"},"replicaAssignment":{"type":"array","items":{"type":"array","items":{"type":"integer","format":"int32"}}}}},"BrokerInfo":{"type":"object","properties":{"endpoints":{"type":"array","items":{"type":"string"}},"host":{"type":"string"},"id":{"type":"integer","format":"int32"},"jmxPort":{"type":"integer","format":"int32"},"port":{"type":"integer","format":"int32"},"rack":{"type":"string"},"securityProtocol":{"type":"object"},"startTime":{"type":"string","format":"date-time"},"version":{"type":"integer","format":"int32"}}},"ClusterInfo":{"type":"object","properties":{"clusterId":{"type":"string"},"controller":{"$ref":"#/definitions/Node"},"nodes":{"type":"array","items":{"$ref":"#/definitions/Node"}}}},"ConsumerGroupDesc":{"type":"object","properties":{"assignmentStrategy":{"type":"string"},"clientId":{"type":"string"},"consumerId":{"type":"string"},"coordinator":{"$ref":"#/definitions/Node"},"currentOffset":{"type":"integer","format":"int64"},"groupName":{"type":"string"},"host":{"type":"string"},"lag":{"type":"integer","format":"int64"},"logEndOffset":{"type":"integer","format":"int64"},"partitionId":{"type":"integer","format":"int32"},"state":{"type":"string","enum":["Unknown","PreparingRebalance","CompletingRebalance","Stable","Dead","Empty"]},"topic":{"type":"string"},"type":{"type":"string","enum":["NEW","OLD"]}}},"ConsumerGroupMeta":{"type":"object","properties":{"assignmentStrategy":{"type":"string"},"coordinator":{"$ref":"#/definitions/Node"},"groupId":{"type":"string"},"members":{"type":"array","items":{"$ref":"#/definitions/MemberDescription"}},"state":{"type":"string","enum":["Unknown","PreparingRebalance","CompletingRebalance","Stable","Dead","Empty"]}}},"CustomConfigEntry":{"type":"object","properties":{"isReadOnly":{"type":"boolean"},"isSensitive":{"type":"boolean"},"name":{"type":"string"},"readOnly":{"type":"boolean"},"sensitive":{"type":"boolean"},"source":{"type":"string","enum":["DYNAMIC_TOPIC_CONFIG","DYNAMIC_BROKER_CONFIG","DYNAMIC_DEFAULT_BROKER_CONFIG","STATIC_BROKER_CONFIG","DEFAULT_CONFIG","UNKNOWN"]},"value":{"type":"string"}}},"CustomTopicPartitionInfo":{"type":"object","properties":{"endOffset":{"type":"integer","format":"int64"},"in_sync":{"type":"boolean"},"messageAvailable":{"type":"integer","format":"int64"},"startOffset":{"type":"integer","format":"int64"},"topicPartitionInfo":{"$ref":"#/definitions/TopicPartitionInfo"}}},"GeneralResponse":{"type":"object","properties":{"data":{"type":"object"},"msg":{"type":"string"},"state":{"type":"string","enum":["success","failure"]}}},"HashMap«string,object»":{"type":"object","additionalProperties":{"type":"object"}},"HealthCheckResult":{"type":"object","properties":{"msg":{"type":"string"},"status":{"type":"string"},"timestamp":{"type":"string","example":"yyyy-MM-dd HH:mm:ss"}}},"HostAndPort":{"type":"object","properties":{"hasBracketlessColons":{"type":"boolean"},"host":{"type":"string"},"hostText":{"type":"string"},"port":{"type":"integer","format":"int32"}}},"JMXConfiguration":{"type":"object","properties":{"include":{"$ref":"#/definitions/JMXFilter"},"exclude":{"$ref":"#/definitions/JMXFilter"}}},"JMXFilter":{"type":"object","properties":{"attribute":{"type":"object"},"beanNames":{"type":"array","items":{"type":"string"}},"beanRegexes":{"type":"array","items":{"$ref":"#/definitions/Pattern"}},"domain":{"type":"string"},"domainRegex":{"$ref":"#/definitions/Pattern"},"emptyBeanName":{"type":"boolean"},"filter":{"type":"object","additionalProperties":{"type":"object"}}}},"JMXMetricData":{"type":"object","properties":{"collected":{"type":"boolean"},"host":{"type":"string"},"metrics":{"type":"array","items":{"$ref":"#/definitions/HashMap«string,object»"}},"msg":{"type":"string"},"timestamp":{"type":"string","example":"yyyy-MM-dd HH:mm:ss"}}},"JMXMetricDataV1":{"type":"object","properties":{"collected":{"type":"boolean"},"host":{"type":"string"},"mbeanInfo":{"type":"object"},"msg":{"type":"string"},"timestamp":{"type":"string","example":"yyyy-MM-dd HH:mm:ss"}}},"JMXQuery":{"type":"object","properties":{"filters":{"type":"array","items":{"$ref":"#/definitions/JMXConfiguration"}}}},"LogDirInfo":{"type":"object","properties":{"error":{"type":"string","enum":["UNKNOWN_SERVER_ERROR","NONE","OFFSET_OUT_OF_RANGE","CORRUPT_MESSAGE","UNKNOWN_TOPIC_OR_PARTITION","INVALID_FETCH_SIZE","LEADER_NOT_AVAILABLE","NOT_LEADER_FOR_PARTITION","REQUEST_TIMED_OUT","BROKER_NOT_AVAILABLE","REPLICA_NOT_AVAILABLE","MESSAGE_TOO_LARGE","STALE_CONTROLLER_EPOCH","OFFSET_METADATA_TOO_LARGE","NETWORK_EXCEPTION","COORDINATOR_LOAD_IN_PROGRESS","COORDINATOR_NOT_AVAILABLE","NOT_COORDINATOR","INVALID_TOPIC_EXCEPTION","RECORD_LIST_TOO_LARGE","NOT_ENOUGH_REPLICAS","NOT_ENOUGH_REPLICAS_AFTER_APPEND","INVALID_REQUIRED_ACKS","ILLEGAL_GENERATION","INCONSISTENT_GROUP_PROTOCOL","INVALID_GROUP_ID","UNKNOWN_MEMBER_ID","INVALID_SESSION_TIMEOUT","REBALANCE_IN_PROGRESS","INVALID_COMMIT_OFFSET_SIZE","TOPIC_AUTHORIZATION_FAILED","GROUP_AUTHORIZATION_FAILED","CLUSTER_AUTHORIZATION_FAILED","INVALID_TIMESTAMP","UNSUPPORTED_SASL_MECHANISM","ILLEGAL_SASL_STATE","UNSUPPORTED_VERSION","TOPIC_ALREADY_EXISTS","INVALID_PARTITIONS","INVALID_REPLICATION_FACTOR","INVALID_REPLICA_ASSIGNMENT","INVALID_CONFIG","NOT_CONTROLLER","INVALID_REQUEST","UNSUPPORTED_FOR_MESSAGE_FORMAT","POLICY_VIOLATION","OUT_OF_ORDER_SEQUENCE_NUMBER","DUPLICATE_SEQUENCE_NUMBER","INVALID_PRODUCER_EPOCH","INVALID_TXN_STATE","INVALID_PRODUCER_ID_MAPPING","INVALID_TRANSACTION_TIMEOUT","CONCURRENT_TRANSACTIONS","TRANSACTION_COORDINATOR_FENCED","TRANSACTIONAL_ID_AUTHORIZATION_FAILED","SECURITY_DISABLED","OPERATION_NOT_ATTEMPTED","KAFKA_STORAGE_ERROR","LOG_DIR_NOT_FOUND","SASL_AUTHENTICATION_FAILED","UNKNOWN_PRODUCER_ID","REASSIGNMENT_IN_PROGRESS","DELEGATION_TOKEN_AUTH_DISABLED","DELEGATION_TOKEN_NOT_FOUND","DELEGATION_TOKEN_OWNER_MISMATCH","DELEGATION_TOKEN_REQUEST_NOT_ALLOWED","DELEGATION_TOKEN_AUTHORIZATION_FAILED","DELEGATION_TOKEN_EXPIRED","INVALID_PRINCIPAL_TYPE","NON_EMPTY_GROUP","GROUP_ID_NOT_FOUND","FETCH_SESSION_ID_NOT_FOUND","INVALID_FETCH_SESSION_EPOCH"]},"replicaInfos":{"type":"object","additionalProperties":{"$ref":"#/definitions/ReplicaInfo"}}}},"Map«int,long»":{"type":"object","additionalProperties":{"type":"integer","format":"int64"}},"Map«string,LogDirInfo»":{"type":"object","additionalProperties":{"$ref":"#/definitions/LogDirInfo"}},"MemberDescription":{"type":"object","properties":{"assignment":{"type":"array","items":{"$ref":"#/definitions/TopicPartition"}},"clientId":{"type":"string"},"host":{"type":"string"},"memberId":{"type":"string"}}},"Node":{"type":"object","properties":{"empty":{"type":"boolean"},"hash":{"type":"integer","format":"int32"},"host":{"type":"string"},"id":{"type":"integer","format":"int32"},"idString":{"type":"string"},"port":{"type":"integer","format":"int32"},"rack":{"type":"string"}}},"Pattern":{"type":"object","properties":{"cursor":{"type":"integer","format":"int32"},"flags":{"type":"integer","format":"int32"},"pattern":{"type":"string"}}},"ReassignModel":{"type":"object","properties":{"partitions":{"type":"array","items":{"$ref":"#/definitions/TopicPartitionReplicaAssignment"}},"version":{"type":"integer","format":"int32"}}},"ReassignStatus":{"type":"object","properties":{"msg":{"type":"string"},"partitionsReassignStatus":{"type":"object","additionalProperties":{"type":"integer","format":"int32"}},"removeThrottle":{"type":"boolean"},"replicasReassignStatus":{"type":"object","additionalProperties":{"type":"integer","format":"int32"}}}},"ReassignWrapper":{"type":"object","properties":{"brokers":{"type":"array","items":{"type":"integer","format":"int32"}},"topics":{"type":"array","items":{"type":"string"}}}},"Record":{"type":"object","properties":{"decoder":{"type":"string"},"key":{"type":"object"},"offset":{"type":"integer","format":"int64"},"timestamp":{"type":"integer","format":"int64"},"topic":{"type":"string"},"value":{"type":"string"}}},"ReplicaInfo":{"type":"object","properties":{"isFuture":{"type":"boolean"},"offsetLag":{"type":"integer","format":"int64"},"size":{"type":"integer","format":"int64"}}},"ReplicaLogDirInfo":{"type":"object","properties":{"currentReplicaLogDir":{"type":"string"},"currentReplicaOffsetLag":{"type":"integer","format":"int64"},"futureReplicaLogDir":{"type":"string"},"futureReplicaOffsetLag":{"type":"integer","format":"int64"}}},"TopicBrief":{"type":"object","properties":{"isrRate":{"type":"number","format":"double"},"numPartition":{"type":"integer","format":"int32"},"topic":{"type":"string"}}},"TopicDetail":{"type":"object","properties":{"partitions":{"type":"integer","format":"int32"},"factor":{"type":"integer","format":"int32"},"name":{"type":"string"},"prop":{"type":"object","additionalProperties":{"type":"object"}},"replicasAssignments":{"type":"object","additionalProperties":{"type":"array","items":{"type":"integer","format":"int32"}}}}},"TopicMeta":{"type":"object","properties":{"internal":{"type":"boolean"},"partitionCount":{"type":"integer","format":"int32"},"replicationFactor":{"type":"integer","format":"int32"},"topicName":{"type":"string"},"topicPartitionInfos":{"type":"array","items":{"$ref":"#/definitions/CustomTopicPartitionInfo"}}}},"TopicPartition":{"type":"object","properties":{"hash":{"type":"integer","format":"int32"},"partition":{"type":"integer","format":"int32"},"topic":{"type":"string"}}},"TopicPartitionInfo":{"type":"object","properties":{"isr":{"type":"array","items":{"$ref":"#/definitions/Node"}},"leader":{"$ref":"#/definitions/Node"},"partition":{"type":"integer","format":"int32"},"replicas":{"type":"array","items":{"$ref":"#/definitions/Node"}}}},"TopicPartitionReplica":{"type":"object","properties":{"brokerId":{"type":"integer","format":"int32"},"hash":{"type":"integer","format":"int32"},"partition":{"type":"integer","format":"int32"},"topic":{"type":"string"}}},"TopicPartitionReplicaAssignment":{"type":"object","properties":{"topic":{"type":"string"},"partition":{"type":"integer","format":"int32"},"replicas":{"type":"array","items":{"type":"integer","format":"int32"}},"log_dirs":{"type":"array","items":{"type":"string"}}}},"User":{"type":"object","properties":{"username":{"type":"string"},"password":{"type":"string"},"role":{"type":"string"}}},"ZkServerClient":{"type":"object","properties":{"host":{"type":"string"},"ops":{"type":"integer","format":"int32"},"port":{"type":"integer","format":"int32"},"queued":{"type":"integer","format":"int32"},"received":{"type":"integer","format":"int32"},"sent":{"type":"integer","format":"int32"}}},"ZkServerEnvironment":{"type":"object","properties":{"attributes":{"type":"object","additionalProperties":{"type":"string"}}}},"ZkServerStat":{"type":"object","properties":{"avgLatency":{"type":"integer","format":"int32"},"buildDate":{"type":"string"},"clients":{"type":"array","items":{"$ref":"#/definitions/ZkServerClient"}},"connections":{"type":"integer","format":"int32"},"maxLatency":{"type":"integer","format":"int32"},"minLatency":{"type":"integer","format":"int32"},"mode":{"type":"string","enum":["Leader","Follower","Observer","Standalone","Down","Unknow"]},"msg":{"type":"string"},"nodes":{"type":"integer","format":"int32"},"outstanding":{"type":"integer","format":"int32"},"received":{"type":"integer","format":"int32"},"sent":{"type":"integer","format":"int32"},"version":{"type":"string"},"zxId":{"type":"string"}}}}} \ No newline at end of file +{"swagger":"2.0","info":{"description":"Kafka REST API SwaggerUI","version":"0.1.0","title":"Kafka REST API SwaggerUI","contact":{"name":"gnuhpc","url":"https://github.com/gnuhpc","email":"gnuhpc@gmail.com"}},"host":"localhost:8080","basePath":"/","tags":[{"name":"collector-controller","description":"Rest API for Collecting JMX Metric Data"},{"name":"user-controller","description":"Security User Management Controller."},{"name":"zookeeper-controller","description":"Zookeeper Controller"},{"name":"kafka-controller","description":"Kafka Controller"}],"paths":{"/jmx/v1":{"get":{"tags":["collector-controller"],"summary":"Fetch all JMX metric data","operationId":"collectJMXMetricUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"jmxurl","in":"query","description":"Parameter jmxurl should be a comma-separated list of {IP:Port} or set to 'default'","required":false,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/JMXMetricDataV1"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/jmx/v2":{"post":{"tags":["collector-controller"],"summary":"Fetch JMX metric data with query filter. You can get the query filter template through the API /jmx/v2/filters.","operationId":"collectJMXMetricUsingPOST","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"jmxurl","in":"query","description":"Parameter jmxurl should be a comma-separated list of {IP:Port} or set to 'default'","required":false,"type":"string"},{"in":"body","name":"jmxQuery","description":"jmxQuery","required":true,"schema":{"$ref":"#/definitions/JMXQuery"}}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/JMXMetricData"}}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/jmx/v2/filters":{"get":{"tags":["collector-controller"],"summary":"List the query filter templates with the filterKey. If filterKey is set to empty, it will return all the templates.","operationId":"listJMXFilterTemplateUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"filterKey","in":"query","description":"filterKey","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"object"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/brokers":{"get":{"tags":["kafka-controller"],"summary":"List brokers in this cluster","operationId":"listBrokersUsingGET","consumes":["application/json"],"produces":["*/*"],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/BrokerInfo"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/brokers/logdirs":{"get":{"tags":["kafka-controller"],"summary":"List log dirs by broker list","operationId":"listLogDirsUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"brokerList","in":"query","description":"brokerList","required":false,"type":"array","items":{"type":"integer","format":"int32"},"collectionFormat":"multi"}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/brokers/logdirs/detail":{"get":{"tags":["kafka-controller"],"summary":"Describe log dirs by broker list and topic list","operationId":"describeLogDirsUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"brokerList","in":"query","description":"brokerList","required":false,"type":"array","items":{"type":"integer","format":"int32"},"collectionFormat":"multi"},{"name":"logDirList","in":"query","description":"logDirList","required":false,"type":"array","items":{"type":"string"},"collectionFormat":"multi"},{"name":"topicList","in":"query","description":"topicList","required":false,"type":"array","items":{"type":"string"},"collectionFormat":"multi"}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"object","additionalProperties":{"$ref":"#/definitions/LogDirInfo"}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/brokers/replicalogdirs":{"get":{"tags":["kafka-controller"],"summary":"Describe replica log dirs.","operationId":"describeReplicaLogDirsUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"replicas","in":"query","description":"replicas","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"$ref":"#/definitions/ReplicaLogDirInfo"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/brokers/{brokerId}/conf":{"get":{"tags":["kafka-controller"],"summary":"Get broker configs, including dynamic configs","operationId":"getBrokerConfigUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"brokerId","in":"path","description":"brokerId","required":true,"type":"integer","format":"int32"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/CustomConfigEntry"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/brokers/{brokerId}/dynconf":{"get":{"tags":["kafka-controller"],"summary":"Get broker dynamic configs","operationId":"getBrokerDynConfigUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"brokerId","in":"path","description":"brokerId","required":true,"type":"integer","format":"int32"}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"object"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}},"put":{"tags":["kafka-controller"],"summary":"Update broker configs","operationId":"updateBrokerDynConfigUsingPUT","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"brokerId","in":"path","description":"brokerId","required":true,"type":"integer","format":"int32"},{"in":"body","name":"props","description":"props","required":true,"schema":{"type":"object","additionalProperties":{"type":"object"}}}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"object"}}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}},"delete":{"tags":["kafka-controller"],"summary":"Remove broker dynamic configs","operationId":"removeBrokerDynConfigUsingDELETE","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"brokerId","in":"path","description":"brokerId","required":true,"type":"integer","format":"int32"},{"name":"configKeysToBeRemoved","in":"query","description":"configKeysToBeRemoved","required":true,"type":"array","items":{"type":"string"},"collectionFormat":"multi"}],"responses":{"200":{"description":"OK"},"204":{"description":"No Content"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"}}}},"/kafka/cluster":{"get":{"tags":["kafka-controller"],"summary":"Describe cluster, nodes, controller info.","operationId":"describeClusterUsingGET","consumes":["application/json"],"produces":["*/*"],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/ClusterInfo"}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/consumer/{topic}/{partition}/{offset}":{"get":{"tags":["kafka-controller"],"summary":"Get the message from the offset of the partition in the topic, decoder is not supported yet","operationId":"getMessageUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"topic","in":"path","description":"topic","required":true,"type":"string"},{"name":"partition","in":"path","description":"partition","required":true,"type":"integer","format":"int32"},{"name":"offset","in":"path","description":"offset","required":true,"type":"integer","format":"int64"},{"name":"decoder","in":"query","description":"decoder","required":false,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/Record"}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/consumergroup/{consumergroup}/{type}":{"delete":{"tags":["kafka-controller"],"summary":"Delete Consumer Group","operationId":"deleteOldConsumerGroupUsingDELETE","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"consumergroup","in":"path","description":"consumergroup","required":true,"type":"string"},{"name":"type","in":"path","description":"type","required":true,"type":"string","enum":["NEW","OLD"]}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/GeneralResponse"}},"204":{"description":"No Content"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"}}}},"/kafka/consumergroup/{consumergroup}/{type}/topic/{topic}/lastcommittime":{"get":{"tags":["kafka-controller"],"summary":"getLastCommitTimestamp","operationId":"getLastCommitTimestampUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"consumergroup","in":"path","description":"consumergroup","required":true,"type":"string"},{"name":"topic","in":"path","description":"topic","required":true,"type":"string"},{"name":"type","in":"path","description":"type","required":true,"type":"string","enum":["NEW","OLD"]}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"object","additionalProperties":{"type":"integer","format":"int64"}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/consumergroup/{consumergroup}/{type}/topic/{topic}/{partition}/{offset}":{"put":{"tags":["kafka-controller"],"summary":"Reset consumer group offset, earliest/latest can be used. Support reset by time for new consumer group, pass a parameter that satisfies yyyy-MM-dd HH:mm:ss.SSS to offset.","operationId":"resetOffsetUsingPUT","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"topic","in":"path","description":"topic","required":true,"type":"string"},{"name":"partition","in":"path","description":"partition","required":true,"type":"integer","format":"int32"},{"name":"consumergroup","in":"path","description":"consumergroup","required":true,"type":"string"},{"name":"offset","in":"path","description":"[earliest/latest/{long}/yyyy-MM-dd HH:mm:ss.SSS] can be supported. The date type is only valid for new consumer group.","required":false,"type":"string"},{"name":"type","in":"path","description":"type","required":true,"type":"string","enum":["NEW","OLD"]}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/GeneralResponse"}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/consumergroups":{"get":{"tags":["kafka-controller"],"summary":"List all consumer groups from zk and kafka","operationId":"listAllConsumerGroupsUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"type","in":"query","description":"type","required":false,"type":"string","enum":["NEW","OLD"]},{"name":"topic","in":"query","description":"topic","required":false,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/consumergroups/meta":{"get":{"tags":["kafka-controller"],"summary":"Get the meta data of the specified new consumer groups, including state, coordinator, assignmentStrategy, members","operationId":"getConsumerGroupsMetaUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"consumerGroupList","in":"query","description":"consumerGroupList","required":true,"type":"array","items":{"type":"string"},"collectionFormat":"multi"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/ConsumerGroupMeta"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/consumergroups/{consumerGroup}/meta":{"get":{"tags":["kafka-controller"],"summary":"Get the meta data of the specify new consumer group, including state, coordinator, assignmentStrategy, members","operationId":"getConsumerGroupMetaUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"consumerGroup","in":"path","description":"consumerGroup","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/ConsumerGroupMeta"}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/consumergroups/{consumerGroup}/{type}":{"get":{"tags":["kafka-controller"],"summary":"Describe consumer group, showing lag and offset, may be slow if multi topics are listened","operationId":"describeConsumerGroupUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"consumerGroup","in":"path","description":"consumerGroup","required":true,"type":"string"},{"name":"type","in":"path","description":"type","required":true,"type":"string","enum":["NEW","OLD"]}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"array","items":{"$ref":"#/definitions/ConsumerGroupDesc"}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/consumergroups/{consumerGroup}/{type}/topic":{"get":{"tags":["kafka-controller"],"summary":"Get the topics involved of the specify consumer group","operationId":"listTopicByConsumerGroupUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"consumerGroup","in":"path","description":"consumerGroup","required":true,"type":"string"},{"name":"type","in":"path","description":"type","required":true,"type":"string","enum":["NEW","OLD"]}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"type":"string"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/consumergroups/{type}/topic/{topic}":{"get":{"tags":["kafka-controller"],"summary":"Describe consumer groups by topic, showing lag and offset","operationId":"describeConsumerGroupByTopicUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"consumerGroup","in":"query","description":"consumerGroup","required":false,"type":"string"},{"name":"type","in":"path","description":"type","required":true,"type":"string","enum":["NEW","OLD"]},{"name":"topic","in":"path","description":"topic","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/ConsumerGroupDesc"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/controller":{"get":{"tags":["kafka-controller"],"summary":"Get controller in this cluster","operationId":"getControllerIdUsingGET","consumes":["application/json"],"produces":["*/*"],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/Node"}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/health":{"get":{"tags":["kafka-controller"],"summary":"Check the cluster health.","operationId":"healthCheckUsingGET","consumes":["application/json"],"produces":["*/*"],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/HealthCheckResult"}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/partitions/add":{"post":{"tags":["kafka-controller"],"summary":"Add partitions to the topics","operationId":"addPartitionUsingPOST","consumes":["application/json"],"produces":["*/*"],"parameters":[{"in":"body","name":"addPartitions","description":"addPartitions","required":true,"schema":{"type":"array","items":{"$ref":"#/definitions/AddPartition"}}}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"$ref":"#/definitions/GeneralResponse"}}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/partitions/reassign/check":{"put":{"tags":["kafka-controller"],"summary":"Check the partition reassignment process","operationId":"checkReassignPartitionsUsingPUT","consumes":["application/json"],"produces":["*/*"],"parameters":[{"in":"body","name":"reassign","description":"reassign","required":true,"schema":{"$ref":"#/definitions/ReassignModel"}}],"responses":{"-1":{"description":"Reassignment Failed"},"0":{"description":"Reassignment In Progress"},"1":{"description":"Reassignment Completed"},"200":{"description":"OK","schema":{"$ref":"#/definitions/ReassignStatus"}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/partitions/reassign/execute":{"put":{"tags":["kafka-controller"],"summary":"Execute the partition reassignment","operationId":"executeReassignPartitionsUsingPUT","consumes":["application/json"],"produces":["*/*"],"parameters":[{"in":"body","name":"reassign","description":"reassign","required":true,"schema":{"$ref":"#/definitions/ReassignModel"}},{"name":"interBrokerThrottle","in":"query","description":"interBrokerThrottle","required":false,"type":"integer","format":"int64"},{"name":"replicaAlterLogDirsThrottle","in":"query","description":"replicaAlterLogDirsThrottle","required":false,"type":"integer","format":"int64"},{"name":"timeoutMs","in":"query","description":"timeoutMs","required":false,"type":"integer","format":"int64"}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/ReassignStatus"}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/partitions/reassign/generate":{"post":{"tags":["kafka-controller"],"summary":"Generate plan for the partition reassignment","operationId":"generateReassignPartitionsUsingPOST","consumes":["application/json"],"produces":["*/*"],"parameters":[{"in":"body","name":"reassignWrapper","description":"reassignWrapper","required":true,"schema":{"$ref":"#/definitions/ReassignWrapper"}}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/ReassignModel"}}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/topics":{"get":{"tags":["kafka-controller"],"summary":"List topics","operationId":"listTopicsUsingGET","consumes":["application/json"],"produces":["*/*"],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"type":"string"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}},"delete":{"tags":["kafka-controller"],"summary":"Delete a topic list (you should enable topic deletion","operationId":"deleteTopicListUsingDELETE","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"topicList","in":"query","description":"topicList","required":true,"type":"array","items":{"type":"string"},"collectionFormat":"multi"}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"$ref":"#/definitions/GeneralResponse"}}},"204":{"description":"No Content"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"}}}},"/kafka/topics/create":{"post":{"tags":["kafka-controller"],"summary":"Create topics","operationId":"createTopicUsingPOST","consumes":["application/json"],"produces":["*/*"],"parameters":[{"in":"body","name":"topicList","description":"topicList","required":true,"schema":{"type":"array","items":{"$ref":"#/definitions/TopicDetail"}}}],"responses":{"201":{"description":"Created","schema":{"type":"object","additionalProperties":{"$ref":"#/definitions/GeneralResponse"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/topics/{topic}":{"get":{"tags":["kafka-controller"],"summary":"Describe a topic by fetching the metadata and config","operationId":"describeTopicUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"topic","in":"path","description":"topic","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/TopicMeta"}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/topics/{topic}/conf":{"get":{"tags":["kafka-controller"],"summary":"Get topic configs","operationId":"getTopicConfigUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"topic","in":"path","description":"topic","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/CustomConfigEntry"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}},"put":{"tags":["kafka-controller"],"summary":"Update topic configs","operationId":"updateTopicConfigUsingPUT","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"topic","in":"path","description":"topic","required":true,"type":"string"},{"in":"body","name":"props","description":"props","required":true,"schema":{"type":"object","additionalProperties":{"type":"object"}}}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/CustomConfigEntry"}}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/topics/{topic}/conf/{key}":{"get":{"tags":["kafka-controller"],"summary":"Get topic config by key","operationId":"getTopicConfigByKeyUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"topic","in":"path","description":"topic","required":true,"type":"string"},{"name":"key","in":"path","description":"key","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"object"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/topics/{topic}/conf/{key}={value}":{"put":{"tags":["kafka-controller"],"summary":"Update a topic config by key","operationId":"updateTopicConfigByKeyUsingPUT","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"topic","in":"path","description":"topic","required":true,"type":"string"},{"name":"key","in":"path","description":"key","required":true,"type":"string"},{"name":"value","in":"path","description":"value","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/CustomConfigEntry"}}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/topics/{topic}/dynconf":{"get":{"tags":["kafka-controller"],"summary":"Get topic dyn configs","operationId":"getTopicDynConfigUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"topic","in":"path","description":"topic","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"object"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/topics/{topic}/exist":{"get":{"tags":["kafka-controller"],"summary":"Tell if a topic exists","operationId":"existTopicUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"topic","in":"path","description":"topic","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"boolean"}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/topicsbrief":{"get":{"tags":["kafka-controller"],"summary":"List topics Brief","operationId":"listTopicBriefUsingGET","consumes":["application/json"],"produces":["*/*"],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/TopicBrief"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/users":{"get":{"tags":["user-controller"],"summary":"Get user list.","operationId":"listUserUsingGET","consumes":["application/json"],"produces":["*/*"],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"type":"string"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}},"post":{"tags":["user-controller"],"summary":"Add user.","operationId":"addUserUsingPOST","consumes":["application/json"],"produces":["*/*"],"parameters":[{"in":"body","name":"user","description":"user","required":true,"schema":{"$ref":"#/definitions/User"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/GeneralResponse"}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}},"put":{"tags":["user-controller"],"summary":"Modify user information.","operationId":"modifyUserUsingPUT","consumes":["application/json"],"produces":["*/*"],"parameters":[{"in":"body","name":"user","description":"user","required":true,"schema":{"$ref":"#/definitions/User"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/GeneralResponse"}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/users/{username}":{"delete":{"tags":["user-controller"],"summary":"Delete user.","operationId":"delUserUsingDELETE","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"username","in":"path","description":"username","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/GeneralResponse"}},"204":{"description":"No Content"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"}}}},"/zk/connstate":{"get":{"tags":["zookeeper-controller"],"summary":"Get the connection state of zookeeper","operationId":"zkConnStateUsingGET","consumes":["application/json"],"produces":["*/*"],"responses":{"200":{"description":"OK","schema":{"type":"string"}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/zk/env":{"get":{"tags":["zookeeper-controller"],"summary":"Get the environment information of zookeeper","operationId":"getEnvUsingGET","consumes":["application/json"],"produces":["*/*"],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"$ref":"#/definitions/ZkServerEnvironment"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/zk/get/path":{"get":{"tags":["zookeeper-controller"],"summary":"Get data of a zookeeper path","operationId":"getUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"path","in":"query","description":"path","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"string"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/zk/ls/path":{"get":{"tags":["zookeeper-controller"],"summary":"List a zookeeper path","operationId":"lsUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"path","in":"query","description":"path","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"type":"string"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/zk/stat":{"get":{"tags":["zookeeper-controller"],"summary":"Get the service state of zookeeper","operationId":"getStatUsingGET","consumes":["application/json"],"produces":["*/*"],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"$ref":"#/definitions/ZkServerStat"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}}},"definitions":{"AddPartition":{"type":"object","properties":{"topic":{"type":"string"},"numPartitionsAdded":{"type":"integer","format":"int32"},"replicaAssignment":{"type":"array","items":{"type":"array","items":{"type":"integer","format":"int32"}}}}},"BrokerInfo":{"type":"object","properties":{"endpoints":{"type":"array","items":{"type":"string"}},"host":{"type":"string"},"id":{"type":"integer","format":"int32"},"jmxPort":{"type":"integer","format":"int32"},"port":{"type":"integer","format":"int32"},"rack":{"type":"string"},"securityProtocol":{"type":"object"},"startTime":{"type":"string","format":"date-time"},"version":{"type":"integer","format":"int32"}}},"ClusterInfo":{"type":"object","properties":{"clusterId":{"type":"string"},"controller":{"$ref":"#/definitions/Node"},"nodes":{"type":"array","items":{"$ref":"#/definitions/Node"}}}},"ConsumerGroupDesc":{"type":"object","properties":{"assignmentStrategy":{"type":"string"},"clientId":{"type":"string"},"consumerId":{"type":"string"},"coordinator":{"$ref":"#/definitions/Node"},"currentOffset":{"type":"integer","format":"int64"},"groupName":{"type":"string"},"host":{"type":"string"},"lag":{"type":"integer","format":"int64"},"logEndOffset":{"type":"integer","format":"int64"},"partitionId":{"type":"integer","format":"int32"},"state":{"type":"string","enum":["Unknown","PreparingRebalance","CompletingRebalance","Stable","Dead","Empty"]},"topic":{"type":"string"},"type":{"type":"string","enum":["NEW","OLD"]}}},"ConsumerGroupMeta":{"type":"object","properties":{"assignmentStrategy":{"type":"string"},"coordinator":{"$ref":"#/definitions/Node"},"groupId":{"type":"string"},"members":{"type":"array","items":{"$ref":"#/definitions/MemberDescription"}},"state":{"type":"string","enum":["Unknown","PreparingRebalance","CompletingRebalance","Stable","Dead","Empty"]}}},"CustomConfigEntry":{"type":"object","properties":{"isReadOnly":{"type":"boolean"},"isSensitive":{"type":"boolean"},"name":{"type":"string"},"readOnly":{"type":"boolean"},"sensitive":{"type":"boolean"},"source":{"type":"string","enum":["DYNAMIC_TOPIC_CONFIG","DYNAMIC_BROKER_CONFIG","DYNAMIC_DEFAULT_BROKER_CONFIG","STATIC_BROKER_CONFIG","DEFAULT_CONFIG","UNKNOWN"]},"value":{"type":"string"}}},"CustomTopicPartitionInfo":{"type":"object","properties":{"endOffset":{"type":"integer","format":"int64"},"in_sync":{"type":"boolean"},"messageAvailable":{"type":"integer","format":"int64"},"startOffset":{"type":"integer","format":"int64"},"topicPartitionInfo":{"$ref":"#/definitions/TopicPartitionInfo"}}},"GeneralResponse":{"type":"object","properties":{"data":{"type":"object"},"msg":{"type":"string"},"state":{"type":"string","enum":["success","failure"]}}},"HashMap«string,object»":{"type":"object","additionalProperties":{"type":"object"}},"HealthCheckResult":{"type":"object","properties":{"msg":{"type":"string"},"status":{"type":"string"},"timestamp":{"type":"string","example":"yyyy-MM-dd HH:mm:ss"}}},"HostAndPort":{"type":"object","properties":{"hasBracketlessColons":{"type":"boolean"},"host":{"type":"string"},"hostText":{"type":"string"},"port":{"type":"integer","format":"int32"}}},"JMXConfiguration":{"type":"object","properties":{"include":{"$ref":"#/definitions/JMXFilter"},"exclude":{"$ref":"#/definitions/JMXFilter"}}},"JMXFilter":{"type":"object","properties":{"attribute":{"type":"object"},"beanNames":{"type":"array","items":{"type":"string"}},"beanRegexes":{"type":"array","items":{"$ref":"#/definitions/Pattern"}},"domain":{"type":"string"},"domainRegex":{"$ref":"#/definitions/Pattern"},"emptyBeanName":{"type":"boolean"},"filter":{"type":"object","additionalProperties":{"type":"object"}}}},"JMXMetricData":{"type":"object","properties":{"collected":{"type":"boolean"},"host":{"type":"string"},"metrics":{"type":"array","items":{"$ref":"#/definitions/HashMap«string,object»"}},"msg":{"type":"string"},"timestamp":{"type":"string","example":"yyyy-MM-dd HH:mm:ss"}}},"JMXMetricDataV1":{"type":"object","properties":{"collected":{"type":"boolean"},"host":{"type":"string"},"mbeanInfo":{"type":"object"},"msg":{"type":"string"},"timestamp":{"type":"string","example":"yyyy-MM-dd HH:mm:ss"}}},"JMXQuery":{"type":"object","properties":{"filters":{"type":"array","items":{"$ref":"#/definitions/JMXConfiguration"}}}},"LogDirInfo":{"type":"object","properties":{"error":{"type":"string","enum":["UNKNOWN_SERVER_ERROR","NONE","OFFSET_OUT_OF_RANGE","CORRUPT_MESSAGE","UNKNOWN_TOPIC_OR_PARTITION","INVALID_FETCH_SIZE","LEADER_NOT_AVAILABLE","NOT_LEADER_FOR_PARTITION","REQUEST_TIMED_OUT","BROKER_NOT_AVAILABLE","REPLICA_NOT_AVAILABLE","MESSAGE_TOO_LARGE","STALE_CONTROLLER_EPOCH","OFFSET_METADATA_TOO_LARGE","NETWORK_EXCEPTION","COORDINATOR_LOAD_IN_PROGRESS","COORDINATOR_NOT_AVAILABLE","NOT_COORDINATOR","INVALID_TOPIC_EXCEPTION","RECORD_LIST_TOO_LARGE","NOT_ENOUGH_REPLICAS","NOT_ENOUGH_REPLICAS_AFTER_APPEND","INVALID_REQUIRED_ACKS","ILLEGAL_GENERATION","INCONSISTENT_GROUP_PROTOCOL","INVALID_GROUP_ID","UNKNOWN_MEMBER_ID","INVALID_SESSION_TIMEOUT","REBALANCE_IN_PROGRESS","INVALID_COMMIT_OFFSET_SIZE","TOPIC_AUTHORIZATION_FAILED","GROUP_AUTHORIZATION_FAILED","CLUSTER_AUTHORIZATION_FAILED","INVALID_TIMESTAMP","UNSUPPORTED_SASL_MECHANISM","ILLEGAL_SASL_STATE","UNSUPPORTED_VERSION","TOPIC_ALREADY_EXISTS","INVALID_PARTITIONS","INVALID_REPLICATION_FACTOR","INVALID_REPLICA_ASSIGNMENT","INVALID_CONFIG","NOT_CONTROLLER","INVALID_REQUEST","UNSUPPORTED_FOR_MESSAGE_FORMAT","POLICY_VIOLATION","OUT_OF_ORDER_SEQUENCE_NUMBER","DUPLICATE_SEQUENCE_NUMBER","INVALID_PRODUCER_EPOCH","INVALID_TXN_STATE","INVALID_PRODUCER_ID_MAPPING","INVALID_TRANSACTION_TIMEOUT","CONCURRENT_TRANSACTIONS","TRANSACTION_COORDINATOR_FENCED","TRANSACTIONAL_ID_AUTHORIZATION_FAILED","SECURITY_DISABLED","OPERATION_NOT_ATTEMPTED","KAFKA_STORAGE_ERROR","LOG_DIR_NOT_FOUND","SASL_AUTHENTICATION_FAILED","UNKNOWN_PRODUCER_ID","REASSIGNMENT_IN_PROGRESS","DELEGATION_TOKEN_AUTH_DISABLED","DELEGATION_TOKEN_NOT_FOUND","DELEGATION_TOKEN_OWNER_MISMATCH","DELEGATION_TOKEN_REQUEST_NOT_ALLOWED","DELEGATION_TOKEN_AUTHORIZATION_FAILED","DELEGATION_TOKEN_EXPIRED","INVALID_PRINCIPAL_TYPE","NON_EMPTY_GROUP","GROUP_ID_NOT_FOUND","FETCH_SESSION_ID_NOT_FOUND","INVALID_FETCH_SESSION_EPOCH"]},"replicaInfos":{"type":"object","additionalProperties":{"$ref":"#/definitions/ReplicaInfo"}}}},"Map«int,long»":{"type":"object","additionalProperties":{"type":"integer","format":"int64"}},"Map«string,LogDirInfo»":{"type":"object","additionalProperties":{"$ref":"#/definitions/LogDirInfo"}},"MemberDescription":{"type":"object","properties":{"assignment":{"type":"array","items":{"$ref":"#/definitions/TopicPartition"}},"clientId":{"type":"string"},"host":{"type":"string"},"memberId":{"type":"string"}}},"Node":{"type":"object","properties":{"empty":{"type":"boolean"},"hash":{"type":"integer","format":"int32"},"host":{"type":"string"},"id":{"type":"integer","format":"int32"},"idString":{"type":"string"},"port":{"type":"integer","format":"int32"},"rack":{"type":"string"}}},"Pattern":{"type":"object","properties":{"cursor":{"type":"integer","format":"int32"},"flags":{"type":"integer","format":"int32"},"pattern":{"type":"string"}}},"ReassignModel":{"type":"object","properties":{"partitions":{"type":"array","items":{"$ref":"#/definitions/TopicPartitionReplicaAssignment"}},"version":{"type":"integer","format":"int32"}}},"ReassignStatus":{"type":"object","properties":{"msg":{"type":"string"},"partitionsReassignStatus":{"type":"object","additionalProperties":{"type":"integer","format":"int32"}},"removeThrottle":{"type":"boolean"},"replicasReassignStatus":{"type":"object","additionalProperties":{"type":"integer","format":"int32"}}}},"ReassignWrapper":{"type":"object","properties":{"brokers":{"type":"array","items":{"type":"integer","format":"int32"}},"topics":{"type":"array","items":{"type":"string"}}}},"Record":{"type":"object","properties":{"decoder":{"type":"string"},"key":{"type":"object"},"offset":{"type":"integer","format":"int64"},"timestamp":{"type":"integer","format":"int64"},"topic":{"type":"string"},"value":{"type":"string"}}},"ReplicaInfo":{"type":"object","properties":{"isFuture":{"type":"boolean"},"offsetLag":{"type":"integer","format":"int64"},"size":{"type":"integer","format":"int64"}}},"ReplicaLogDirInfo":{"type":"object","properties":{"currentReplicaLogDir":{"type":"string"},"currentReplicaOffsetLag":{"type":"integer","format":"int64"},"futureReplicaLogDir":{"type":"string"},"futureReplicaOffsetLag":{"type":"integer","format":"int64"}}},"TopicBrief":{"type":"object","properties":{"isrRate":{"type":"number","format":"double"},"numPartition":{"type":"integer","format":"int32"},"topic":{"type":"string"}}},"TopicDetail":{"type":"object","properties":{"partitions":{"type":"integer","format":"int32"},"factor":{"type":"integer","format":"int32"},"name":{"type":"string"},"prop":{"type":"object","additionalProperties":{"type":"object"}},"replicasAssignments":{"type":"object","additionalProperties":{"type":"array","items":{"type":"integer","format":"int32"}}}}},"TopicMeta":{"type":"object","properties":{"internal":{"type":"boolean"},"partitionCount":{"type":"integer","format":"int32"},"replicationFactor":{"type":"integer","format":"int32"},"topicName":{"type":"string"},"topicPartitionInfos":{"type":"array","items":{"$ref":"#/definitions/CustomTopicPartitionInfo"}}}},"TopicPartition":{"type":"object","properties":{"hash":{"type":"integer","format":"int32"},"partition":{"type":"integer","format":"int32"},"topic":{"type":"string"}}},"TopicPartitionInfo":{"type":"object","properties":{"isr":{"type":"array","items":{"$ref":"#/definitions/Node"}},"leader":{"$ref":"#/definitions/Node"},"partition":{"type":"integer","format":"int32"},"replicas":{"type":"array","items":{"$ref":"#/definitions/Node"}}}},"TopicPartitionReplica":{"type":"object","properties":{"brokerId":{"type":"integer","format":"int32"},"hash":{"type":"integer","format":"int32"},"partition":{"type":"integer","format":"int32"},"topic":{"type":"string"}}},"TopicPartitionReplicaAssignment":{"type":"object","properties":{"topic":{"type":"string"},"partition":{"type":"integer","format":"int32"},"replicas":{"type":"array","items":{"type":"integer","format":"int32"}},"log_dirs":{"type":"array","items":{"type":"string"}}}},"User":{"type":"object","properties":{"username":{"type":"string"},"password":{"type":"string"},"role":{"type":"string"}}},"ZkServerClient":{"type":"object","properties":{"host":{"type":"string"},"ops":{"type":"integer","format":"int32"},"port":{"type":"integer","format":"int32"},"queued":{"type":"integer","format":"int32"},"received":{"type":"integer","format":"int32"},"sent":{"type":"integer","format":"int32"}}},"ZkServerEnvironment":{"type":"object","properties":{"attributes":{"type":"object","additionalProperties":{"type":"string"}}}},"ZkServerStat":{"type":"object","properties":{"avgLatency":{"type":"integer","format":"int32"},"buildDate":{"type":"string"},"clients":{"type":"array","items":{"$ref":"#/definitions/ZkServerClient"}},"connections":{"type":"integer","format":"int32"},"maxLatency":{"type":"integer","format":"int32"},"minLatency":{"type":"integer","format":"int32"},"mode":{"type":"string","enum":["Leader","Follower","Observer","Standalone","Down","Unknow"]},"msg":{"type":"string"},"nodes":{"type":"integer","format":"int32"},"outstanding":{"type":"integer","format":"int32"},"received":{"type":"integer","format":"int32"},"sent":{"type":"integer","format":"int32"},"version":{"type":"string"},"zxId":{"type":"string"}}}}} \ No newline at end of file diff --git a/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java b/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java index 3c46a9d..c0ba446 100644 --- a/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java +++ b/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java @@ -4,6 +4,7 @@ import io.swagger.annotations.ApiParam; import io.swagger.annotations.ApiResponse; import io.swagger.annotations.ApiResponses; +import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; @@ -294,6 +295,24 @@ public ConsumerGroupMeta getConsumerGroupMeta(@PathVariable String consumerGroup throw new ApiException("New consumer group:" + consumerGroup + " non-exist."); } + @GetMapping(value = "/consumergroups/meta") + @ApiOperation( + value = + "Get the meta data of the specified new consumer groups, including state, coordinator," + + " assignmentStrategy, members") + public List getConsumerGroupsMeta(@RequestParam List consumerGroupList) { + List consumerGroupMetaList = new ArrayList<>(); + for (String consumerGroup:consumerGroupList) { + if (kafkaAdminService.isNewConsumerGroup(consumerGroup)) { + consumerGroupMetaList.add(kafkaAdminService.getConsumerGroupMeta(consumerGroup)); + } else { + throw new ApiException("New consumer group:" + consumerGroup + " non-exist."); + } + } + + return consumerGroupMetaList; + } + @GetMapping(value = "/consumergroups/{type}/topic/{topic}") @ApiOperation(value = "Describe consumer groups by topic, showing lag and offset") public List describeConsumerGroupByTopic( diff --git a/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java b/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java index f324dd6..42acfc9 100644 --- a/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java +++ b/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java @@ -1,2534 +1,2631 @@ -package org.gnuhpc.bigdata.service; - -import static java.lang.String.format; -import static java.util.stream.Collectors.toList; -import static java.util.stream.Collectors.toMap; -import static java.util.stream.Collectors.toSet; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.module.scala.DefaultScalaModule; -import com.google.common.collect.ImmutableMap; -import java.io.IOException; -import java.nio.ByteBuffer; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.TimeUnit; -import java.util.function.Function; -import java.util.regex.Pattern; -import java.util.stream.Collectors; -import javax.annotation.PostConstruct; -import kafka.admin.AdminClient; -import kafka.admin.AdminClient.ConsumerGroupSummary; -import kafka.admin.AdminClient.ConsumerSummary; -import kafka.admin.AdminUtils; -import kafka.admin.ConsumerGroupCommand; -import kafka.admin.ReassignPartitionsCommand; -import kafka.admin.ReassignPartitionsCommand.Throttle; -import kafka.api.PartitionOffsetRequestInfo; -import kafka.cluster.Broker; -import kafka.common.TopicAndPartition; -import kafka.coordinator.group.GroupOverview; -import kafka.coordinator.group.GroupTopicPartition; -import kafka.javaapi.OffsetRequest; -import kafka.javaapi.OffsetResponse; -import kafka.javaapi.consumer.SimpleConsumer; -import kafka.log.LogConfig; -import kafka.server.ConfigType; -import kafka.utils.ZkUtils; -import kafka.zk.AdminZkClient; -import kafka.zk.KafkaZkClient; -import lombok.extern.log4j.Log4j; -import org.apache.avro.Schema; -import org.apache.avro.generic.GenericDatumReader; -import org.apache.avro.generic.GenericRecord; -import org.apache.avro.io.DatumReader; -import org.apache.avro.io.DecoderFactory; -import org.apache.curator.framework.CuratorFramework; -import org.apache.kafka.clients.admin.AdminClientConfig; -import org.apache.kafka.clients.admin.AlterConfigsResult; -import org.apache.kafka.clients.admin.Config; -import org.apache.kafka.clients.admin.ConfigEntry; -import org.apache.kafka.clients.admin.CreatePartitionsResult; -import org.apache.kafka.clients.admin.CreateTopicsOptions; -import org.apache.kafka.clients.admin.CreateTopicsResult; -import org.apache.kafka.clients.admin.DeleteTopicsResult; -import org.apache.kafka.clients.admin.DescribeClusterOptions; -import org.apache.kafka.clients.admin.DescribeClusterResult; -import org.apache.kafka.clients.admin.DescribeConfigsResult; -import org.apache.kafka.clients.admin.DescribeLogDirsOptions; -import org.apache.kafka.clients.admin.DescribeLogDirsResult; -import org.apache.kafka.clients.admin.DescribeReplicaLogDirsOptions; -import org.apache.kafka.clients.admin.DescribeReplicaLogDirsResult; -import org.apache.kafka.clients.admin.DescribeReplicaLogDirsResult.ReplicaLogDirInfo; -import org.apache.kafka.clients.admin.DescribeTopicsResult; -import org.apache.kafka.clients.admin.KafkaAdminClient; -import org.apache.kafka.clients.admin.ListTopicsOptions; -import org.apache.kafka.clients.admin.ListTopicsResult; -import org.apache.kafka.clients.admin.NewPartitions; -import org.apache.kafka.clients.admin.NewTopic; -import org.apache.kafka.clients.admin.TopicDescription; -import org.apache.kafka.clients.consumer.ConsumerRecord; -import org.apache.kafka.clients.consumer.ConsumerRecords; -import org.apache.kafka.clients.consumer.KafkaConsumer; -import org.apache.kafka.clients.consumer.OffsetAndTimestamp; -import org.apache.kafka.clients.producer.KafkaProducer; -import org.apache.kafka.clients.producer.ProducerRecord; -import org.apache.kafka.clients.producer.RecordMetadata; -import org.apache.kafka.common.KafkaFuture; -import org.apache.kafka.common.Node; -import org.apache.kafka.common.TopicPartition; -import org.apache.kafka.common.TopicPartitionInfo; -import org.apache.kafka.common.TopicPartitionReplica; -import org.apache.kafka.common.config.ConfigResource; -import org.apache.kafka.common.config.ConfigResource.Type; -import org.apache.kafka.common.errors.ApiException; -import org.apache.kafka.common.errors.InvalidTopicException; -import org.apache.kafka.common.internals.Topic; -import org.apache.kafka.common.protocol.Errors; -import org.apache.kafka.common.requests.DescribeLogDirsResponse.LogDirInfo; -import org.apache.kafka.common.serialization.Serdes; -import org.gnuhpc.bigdata.CollectionConvertor; -import org.gnuhpc.bigdata.componet.OffsetStorage; -import org.gnuhpc.bigdata.config.KafkaConfig; -import org.gnuhpc.bigdata.constant.ConsumerGroupState; -import org.gnuhpc.bigdata.constant.ConsumerType; -import org.gnuhpc.bigdata.constant.GeneralResponseState; -import org.gnuhpc.bigdata.constant.ReassignmentState; -import org.gnuhpc.bigdata.model.AddPartition; -import org.gnuhpc.bigdata.model.BrokerInfo; -import org.gnuhpc.bigdata.model.ClusterInfo; -import org.gnuhpc.bigdata.model.ConsumerGroupDesc; -import org.gnuhpc.bigdata.model.ConsumerGroupMeta; -import org.gnuhpc.bigdata.model.CustomConfigEntry; -import org.gnuhpc.bigdata.model.CustomTopicPartitionInfo; -import org.gnuhpc.bigdata.model.GeneralResponse; -import org.gnuhpc.bigdata.model.HealthCheckResult; -import org.gnuhpc.bigdata.model.MemberDescription; -import org.gnuhpc.bigdata.model.PartitionAssignmentState; -import org.gnuhpc.bigdata.model.ReassignModel; -import org.gnuhpc.bigdata.model.ReassignStatus; -import org.gnuhpc.bigdata.model.ReassignWrapper; -import org.gnuhpc.bigdata.model.Record; -import org.gnuhpc.bigdata.model.TopicBrief; -import org.gnuhpc.bigdata.model.TopicDetail; -import org.gnuhpc.bigdata.model.TopicMeta; -import org.gnuhpc.bigdata.model.TwoTuple; -import org.gnuhpc.bigdata.utils.KafkaUtils; -import org.gnuhpc.bigdata.utils.ZookeeperUtils; -import org.gnuhpc.bigdata.validator.ConsumerGroupExistConstraint; -import org.gnuhpc.bigdata.validator.TopicExistConstraint; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -import org.springframework.validation.annotation.Validated; -import scala.Function0; -import scala.Tuple2; -import scala.collection.JavaConverters; -import scala.collection.Seq; -import scala.collection.mutable.ListBuffer; -import scala.runtime.AbstractFunction0; -import scala.runtime.BoxedUnit; - -/** Created by gnuhpc on 2017/7/17. */ -@Service -@Log4j -@Validated -public class KafkaAdminService { - - private static final int channelSocketTimeoutMs = 600; - private static final int channelRetryBackoffMs = 600; - private static final long kafkaAdminClientGetTimeoutMs = 30000; - private static final long kafkaAdminClientAlterTimeoutMs = 60000; - private static final String CONSUMERPATHPREFIX = "/consumers/"; - private static final String OFFSETSPATHPREFIX = "/offsets/"; - public static final String LeaderReplicationThrottledRateProp = - "leader.replication.throttled.rate"; - public static final String FollowerReplicationThrottledRateProp = - "follower.replication.throttled.rate"; - public static final String ReplicaAlterLogDirsIoMaxBytesPerSecondProp = - "replica.alter.log.dirs.io.max.bytes.per.second"; - - @Autowired private ZookeeperUtils zookeeperUtils; - - @Autowired private KafkaUtils kafkaUtils; - - @Autowired private KafkaConfig kafkaConfig; - - @Autowired private OffsetStorage storage; - - // For AdminUtils use - private ZkUtils zkUtils; - - private org.apache.kafka.clients.admin.AdminClient kafkaAdminClient = null; - - private AdminClient oldAdminClient = null; - - private scala.Option none = scala.Option.apply(null); - - @PostConstruct - private void init() {} - - private org.apache.kafka.clients.admin.AdminClient createKafkaAdminClient() { - if (this.kafkaAdminClient == null) { - Properties adminClientProp = new Properties(); - adminClientProp.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaConfig.getBrokers()); - this.kafkaAdminClient = KafkaAdminClient.create(adminClientProp); - } - - return this.kafkaAdminClient; - } - - public HashMap createTopic(List topicList) { - List newTopicList = new ArrayList<>(); - HashMap createResults = new HashMap<>(); - - for (TopicDetail topic : topicList) { - NewTopic newTopic; - Map> replicasAssignments = topic.getReplicasAssignments(); - - try { - Topic.validate(topic.getName()); - - if (Topic.hasCollisionChars(topic.getName())) { - throw new InvalidTopicException("Invalid topic name, it contains '.' or '_'."); - } - } catch (Exception exception) { - GeneralResponse generalResponse = - GeneralResponse.builder() - .state(GeneralResponseState.failure) - .msg(exception.getMessage()) - .build(); - createResults.put(topic.getName(), generalResponse); - continue; - } - - if (replicasAssignments != null && !replicasAssignments.isEmpty()) { - newTopic = new NewTopic(topic.getName(), replicasAssignments); - } else { - newTopic = new NewTopic(topic.getName(), topic.getPartitions(), (short) topic.getFactor()); - } - - if (topic.getProp() != null) { - newTopic.configs((Map) topic.getProp()); - } - - newTopicList.add(newTopic); - } - - org.apache.kafka.clients.admin.AdminClient kafkaAdminClient = createKafkaAdminClient(); - - CreateTopicsOptions createTopicsOptions = new CreateTopicsOptions(); - createTopicsOptions.timeoutMs((int) kafkaAdminClientAlterTimeoutMs); - CreateTopicsResult createTopicsResult = - kafkaAdminClient.createTopics(newTopicList, createTopicsOptions); - - try { - createTopicsResult.all().get(kafkaAdminClientGetTimeoutMs, TimeUnit.MILLISECONDS); - } catch (Exception exception) { - log.warn("Create topic exception:" + exception); - } finally { - createTopicsResult - .values() - .forEach( - (topicName, result) -> { - GeneralResponse generalResponse; - if (result.isDone() && !result.isCompletedExceptionally()) { - TopicMeta topicMeta = describeTopic(topicName); - generalResponse = - GeneralResponse.builder() - .state(GeneralResponseState.success) - .data(topicMeta) - .build(); - } else { - generalResponse = - GeneralResponse.builder() - .state(GeneralResponseState.failure) - .msg(result.toString()) - .build(); - } - createResults.put(topicName, generalResponse); - }); - } - - return createResults; - } - - public List listTopics() { - List topicNamesList = new ArrayList(); - topicNamesList.addAll(getAllTopics()); - - return topicNamesList; - } - - public Set getAllTopics() { - Set topicNames; - org.apache.kafka.clients.admin.AdminClient kafkaAdminClient = createKafkaAdminClient(); - ListTopicsOptions options = new ListTopicsOptions(); - // includes internal topics such as __consumer_offsets - options.listInternal(true); - - ListTopicsResult topics = kafkaAdminClient.listTopics(options); - try { - topicNames = topics.names().get(kafkaAdminClientGetTimeoutMs, TimeUnit.MILLISECONDS); - log.info("Current topics in this cluster: " + topicNames); - } catch (Exception exception) { - log.warn("List topic exception : " + exception); - throw new ApiException("List topic exception : " + exception); - } - - return topicNames; - } - - public List listTopicBrief() { - org.apache.kafka.clients.admin.AdminClient kafkaAdminClient = createKafkaAdminClient(); - - DescribeTopicsResult describeTopicsResult = kafkaAdminClient.describeTopics(listTopics()); - Map topicMap; - List result; - try { - topicMap = - describeTopicsResult.all().get(kafkaAdminClientGetTimeoutMs, TimeUnit.MILLISECONDS); - result = - topicMap - .entrySet() - .parallelStream() - .map( - e -> { - String topic = e.getKey(); - TopicDescription topicDescription = e.getValue(); - List topicPartitionInfoList = - topicDescription.partitions(); - int replicateCount = 0; - int isrCount = 0; - for (org.apache.kafka.common.TopicPartitionInfo topicPartitionInfo : - topicPartitionInfoList) { - replicateCount += topicPartitionInfo.replicas().size(); - isrCount += topicPartitionInfo.isr().size(); - } - if (replicateCount == 0) { - return new TopicBrief(topic, topicDescription.partitions().size(), 0); - } else { - return new TopicBrief( - topic, - topicDescription.partitions().size(), - ((double) isrCount / replicateCount)); - } - }) - .collect(toList()); - } catch (Exception exception) { - log.warn("Describe all topics exception:" + exception); - throw new ApiException("Describe all topics exception:" + exception); - } - - return result; - } - - public boolean existTopic(String topicName) { - KafkaZkClient kafkaZkClient = zookeeperUtils.createKafkaZkClient(); - boolean exists = kafkaZkClient.topicExists(topicName); - - return exists; - } - - public ClusterInfo describeCluster() { - // Map clusterDetail = new HashMap<>(); - ClusterInfo clusterInfo = new ClusterInfo(); - - org.apache.kafka.clients.admin.AdminClient kafkaAdminClient = createKafkaAdminClient(); - DescribeClusterOptions describeClusterOptions = - new DescribeClusterOptions().timeoutMs((int) kafkaAdminClientGetTimeoutMs); - - DescribeClusterResult describeClusterResult = - kafkaAdminClient.describeCluster(describeClusterOptions); - - KafkaFuture clusterIdFuture = describeClusterResult.clusterId(); - KafkaFuture controllerFuture = describeClusterResult.controller(); - KafkaFuture> nodesFuture = describeClusterResult.nodes(); - String clusterId = ""; - Node controller = null; - Collection nodes = new ArrayList<>(); - - try { - clusterId = clusterIdFuture.get(kafkaAdminClientGetTimeoutMs, TimeUnit.MILLISECONDS); - controller = controllerFuture.get(kafkaAdminClientGetTimeoutMs, TimeUnit.MILLISECONDS); - nodes = nodesFuture.get(kafkaAdminClientGetTimeoutMs, TimeUnit.MILLISECONDS); - } catch (Exception exception) { - log.warn("Describe cluster exception:" + exception); - throw new ApiException("Describe cluster exception:" + exception); - } finally { - if (clusterIdFuture.isDone() && !clusterIdFuture.isCompletedExceptionally()) { - // clusterDetail.put("clusterId", clusterId); - clusterInfo.setClusterId(clusterId); - } - if (controllerFuture.isDone() && !controllerFuture.isCompletedExceptionally()) { - // clusterDetail.put("controllerId", controller); - clusterInfo.setController(controller); - } - if (nodesFuture.isDone() && !nodesFuture.isCompletedExceptionally()) { - // clusterDetail.put("nodes", nodes); - clusterInfo.setNodes(nodes); - } - } - - return clusterInfo; - } - - public List listBrokers() { - CuratorFramework zkClient = zookeeperUtils.createZkClient(); - KafkaZkClient kafkaZkClient = zookeeperUtils.createKafkaZkClient(); - List brokerList = - CollectionConvertor.seqConvertJavaList(kafkaZkClient.getAllBrokersInCluster()); - - List brokerInfoList = - brokerList - .parallelStream() - .collect(Collectors.toMap(Broker::id, Broker::rack)) - .entrySet() - .parallelStream() - .map( - entry -> { - String brokerInfoStr = null; - try { - // TODO replace zkClient with kafkaZKClient - brokerInfoStr = - new String( - zkClient - .getData() - .forPath(ZkUtils.BrokerIdsPath() + "/" + entry.getKey())); - } catch (Exception e) { - e.printStackTrace(); - } - BrokerInfo brokerInfo; - try { - ObjectMapper objectMapper = new ObjectMapper(); - brokerInfo = objectMapper.readValue(brokerInfoStr, BrokerInfo.class); - } catch (Exception exception) { - throw new ApiException("List broker exception." + exception); - } - if (entry.getValue().isEmpty()) { - brokerInfo.setRack(""); - } else { - brokerInfo.setRack(entry.getValue().get()); - } - brokerInfo.setId(entry.getKey()); - return brokerInfo; - }) - .collect(toList()); - - return brokerInfoList; - } - - public Node getController() { - return describeCluster().getController(); - } - - public Map> listLogDirsByBroker(List brokerList) { - Map> logDirList = new HashMap<>(); - - Map> logDirInfosByBroker = - describeLogDirsByBrokerAndTopic(brokerList, null,null); - logDirInfosByBroker - .entrySet() - .forEach( - e -> { - List dirList = e.getValue().keySet().stream().collect(Collectors.toList()); - Collections.sort(dirList); - logDirList.put(e.getKey(), dirList); - }); - - return logDirList; - } - - public Map> describeLogDirsByBrokerAndTopic( - List brokerList, List logDirList, List topicList) { - org.apache.kafka.clients.admin.AdminClient kafkaAdminClient = createKafkaAdminClient(); - - List brokerIdsInCluster = - listBrokers().stream().map(brokerInfo -> brokerInfo.getId()).collect(Collectors.toList()); - - if (brokerList == null || brokerList.isEmpty()) { - brokerList = brokerIdsInCluster; - } else { - for (int brokerId : brokerList) { - if (!brokerIdsInCluster.contains(brokerId)) { - throw new ApiException("Bad Request. Broker Id:" + brokerId + " non-exist."); - } - } - } - // Delete reason: we want to delete the topic data that no longer found on zk, we can still use - // this - // function to get the topic log dir - // if (topicList != null && !topicList.isEmpty()) { - // for (String topic : topicList) { - // if (!existTopic(topic)) { - // throw new ApiException("Bad Request. Topic:" + topic + " non-exist."); - // } - // } - // } - - DescribeLogDirsOptions describeLogDirsOptions = - new DescribeLogDirsOptions().timeoutMs((int) kafkaAdminClientGetTimeoutMs); - DescribeLogDirsResult describeLogDirsResult = - kafkaAdminClient.describeLogDirs(brokerList, describeLogDirsOptions); - Map> logDirInfosByBroker = new HashMap<>(); - - try { - logDirInfosByBroker = - describeLogDirsResult.all().get(kafkaAdminClientGetTimeoutMs, TimeUnit.MILLISECONDS); - } catch (Exception exception) { - log.warn("Describe log dirs exception:" + exception); - throw new ApiException("Describe log dirs exception:" + exception); - } finally { - if (logDirList != null && !logDirList.isEmpty()) { - logDirInfosByBroker.entrySet().forEach(e->{ - e.getValue().entrySet().removeIf(m -> !logDirList.contains(m.getKey())); - }); - } - if (topicList != null && !topicList.isEmpty()) { - logDirInfosByBroker - .entrySet() - .forEach( - e -> { - e.getValue() - .entrySet() - .forEach( - m -> { - m.getValue() - .replicaInfos - .entrySet() - .removeIf(t -> !topicList.contains(t.getKey().topic())); - }); - }); - } - } - - return logDirInfosByBroker; - } - - public Map describeReplicaLogDirs( - List replicas) { - org.apache.kafka.clients.admin.AdminClient kafkaAdminClient = createKafkaAdminClient(); - Map replicaLogDirInfoMap; - - DescribeReplicaLogDirsOptions describeReplicaLogDirsOptions = - new DescribeReplicaLogDirsOptions().timeoutMs((int) kafkaAdminClientGetTimeoutMs); - DescribeReplicaLogDirsResult describeReplicaLogDirsResult = - kafkaAdminClient.describeReplicaLogDirs(replicas, describeReplicaLogDirsOptions); - - try { - replicaLogDirInfoMap = - describeReplicaLogDirsResult - .all() - .get(kafkaAdminClientGetTimeoutMs, TimeUnit.MILLISECONDS); - } catch (Exception exception) { - log.warn("Describe replica log dirs exception:" + exception); - throw new ApiException("Describe replica log dirs exception:" + exception); - } - - return replicaLogDirInfoMap; - } - - public Collection getBrokerConf(int brokerId) { - String broker = String.valueOf(brokerId); - Properties dynamicProps = getConfigInZk(Type.BROKER, broker); - - Collection configs = describeConfig(Type.BROKER, String.valueOf(brokerId)); - - return mergeConfigs(configs, dynamicProps); - } - - public Properties getConfigInZk(ConfigResource.Type type, String name) { - KafkaZkClient kafkaZkClient = zookeeperUtils.createKafkaZkClient(); - AdminZkClient adminZkClient = new AdminZkClient(kafkaZkClient); - Properties properties = new Properties(); - - if (type.equals(Type.BROKER)) { - properties = adminZkClient.fetchEntityConfig(ConfigType.Broker(), name); - } else if (type.equals(Type.TOPIC)) { - properties = adminZkClient.fetchEntityConfig(ConfigType.Topic(), name); - } - - return properties; - } - - private Collection mergeConfigs( - Collection configs, Properties dynamicProps) { - Collection mergedConfigs = new ArrayList<>(); - CustomConfigEntry newConfigEntry; - - for (ConfigEntry entry : configs) { - String key = entry.name(); - String value = entry.value(); - if (dynamicProps.containsKey(key)) { - value = dynamicProps.getProperty(key); - } - newConfigEntry = - new CustomConfigEntry( - key, value, entry.isSensitive(), entry.isReadOnly(), entry.source()); - mergedConfigs.add(newConfigEntry); - } - - return mergedConfigs; - } - - private void validateConfigs( - Type type, String name, Properties props, List keysToBeChecked) { - for (Object configKey : keysToBeChecked) { - if (!props.containsKey(configKey)) { - throw new ApiException(type.name() + ":" + name + " has no such property:" + configKey); - } - } - } - - public Properties updateBrokerDynConf(int brokerId, Properties propsToBeUpdated) { - Properties props = getConfigInZk(Type.BROKER, String.valueOf(brokerId)); - KafkaZkClient kafkaZkClient = zookeeperUtils.createKafkaZkClient(); - AdminZkClient adminZkClient = new AdminZkClient(kafkaZkClient); - - for (String key : propsToBeUpdated.stringPropertyNames()) { - if (props.containsKey(key)) { - props.setProperty(key, propsToBeUpdated.getProperty(key)); - } else { - props.put(key, propsToBeUpdated.getProperty(key)); - } - } - - adminZkClient.changeBrokerConfig( - JavaConverters.asScalaBufferConverter(Collections.singletonList((Object) brokerId)) - .asScala() - .toSeq(), - props); - - return getConfigInZk(Type.BROKER, String.valueOf(brokerId)); - } - - public void removeConfigInZk(Type type, String name, List configKeysToBeRemoved) { - KafkaZkClient kafkaZkClient = zookeeperUtils.createKafkaZkClient(); - AdminZkClient adminZkClient = new AdminZkClient(kafkaZkClient); - - Properties props = getConfigInZk(type, name); - - validateConfigs(Type.BROKER, String.valueOf(name), props, configKeysToBeRemoved); - - props.entrySet().removeIf(entry -> configKeysToBeRemoved.contains(entry.getKey())); - - if (type.equals(Type.BROKER)) { - int brokerId = Integer.parseInt(name); - adminZkClient.changeBrokerConfig( - JavaConverters.asScalaBufferConverter(Collections.singletonList((Object) brokerId)) - .asScala() - .toSeq(), - props); - } else if (type.equals(Type.TOPIC)) { - adminZkClient.changeTopicConfig(name, props); - } - } - - public TopicMeta describeTopic(@TopicExistConstraint String topicName) { - org.apache.kafka.clients.admin.AdminClient kafkaAdminClient = createKafkaAdminClient(); - - DescribeTopicsResult describeTopicsResult = - kafkaAdminClient.describeTopics(Collections.singletonList(topicName)); - TopicMeta topicMeta = new TopicMeta(topicName); - try { - Map topicMap = - describeTopicsResult.all().get(kafkaAdminClientGetTimeoutMs, TimeUnit.MILLISECONDS); - if (topicMap.containsKey(topicName)) { - TopicDescription topicDescription = topicMap.get(topicName); - List tmList = topicDescription.partitions(); - topicMeta.setInternal(topicDescription.isInternal()); - topicMeta.setPartitionCount(topicDescription.partitions().size()); - topicMeta.setReplicationFactor(tmList.get(0).replicas().size()); - topicMeta.setTopicPartitionInfos( - tmList - .parallelStream() - .map( - tm -> { - CustomTopicPartitionInfo customTopicPartitionInfo = - new CustomTopicPartitionInfo(); - customTopicPartitionInfo.setTopicPartitionInfo(tm); - customTopicPartitionInfo.setIn_sync(); - customTopicPartitionInfo.setStartOffset( - getBeginningOffset(topicName, tm.partition())); - customTopicPartitionInfo.setEndOffset( - getEndOffset(tm.leader(), topicName, tm.partition())); - customTopicPartitionInfo.setMessageAvailable(); - return customTopicPartitionInfo; - }) - .collect(toList())); - Collections.sort(topicMeta.getTopicPartitionInfos()); - } - } catch (Exception exception) { - exception.printStackTrace(); - throw new ApiException("Describe topic exception." + exception); - } - - return topicMeta; - } - - public Map deleteTopicList(List topicList) { - org.apache.kafka.clients.admin.AdminClient kafkaAdminClient = createKafkaAdminClient(); - HashMap deleteResults = new HashMap<>(); - - List topicListToBeDeleted = new ArrayList<>(topicList); - - log.warn("Delete topic " + topicList); - for (int i = 0; i < topicList.size(); i++) { - String topic = topicList.get(i); - try { - if (Topic.isInternal(topic)) { - throw new ApiException( - "Topic " + topic + " is a kafka internal topic and is not allowed to be deleted."); - } - if (!existTopic(topic)) { - throw new ApiException("Topic " + topic + " non-exists."); - } - } catch (Exception exception) { - topicListToBeDeleted.remove(topic); - GeneralResponse generalResponse = - GeneralResponse.builder() - .state(GeneralResponseState.failure) - .msg(exception.getMessage()) - .build(); - deleteResults.put(topic, generalResponse); - } - } - - DeleteTopicsResult deleteTopicsResult = kafkaAdminClient.deleteTopics(topicListToBeDeleted); - try { - deleteTopicsResult.all().get(kafkaAdminClientGetTimeoutMs, TimeUnit.MILLISECONDS); - } catch (Exception exception) { - log.warn("Delete topic exception:" + exception); - } finally { - deleteTopicsResult - .values() - .forEach( - (topic, result) -> { - GeneralResponse generalResponse; - if (result.isDone() && !result.isCompletedExceptionally()) { - generalResponse = - GeneralResponse.builder().state(GeneralResponseState.success).build(); - } else { - generalResponse = - GeneralResponse.builder() - .state(GeneralResponseState.failure) - .msg(result.toString()) - .build(); - } - deleteResults.put(topic, generalResponse); - }); - } - - return deleteResults; - } - - public Collection describeConfig(ConfigResource.Type type, String name) { - Map configs; - org.apache.kafka.clients.admin.AdminClient kafkaAdminClient = createKafkaAdminClient(); - ConfigResource configResource = new ConfigResource(type, name); - - DescribeConfigsResult ret = - kafkaAdminClient.describeConfigs(Collections.singleton(configResource)); - try { - configs = ret.all().get(kafkaAdminClientGetTimeoutMs, TimeUnit.MILLISECONDS); - } catch (Exception exception) { - log.warn("Describe config type:" + type + ", name:" + name + " exception:" + exception); - throw new ApiException("Describe config exception:" + exception.getLocalizedMessage()); - } - - return configs.get(configResource).entries(); - } - - public boolean alterConfig( - ConfigResource.Type type, String name, Collection configEntries) { - org.apache.kafka.clients.admin.AdminClient kafkaAdminClient = createKafkaAdminClient(); - Config config = new Config(configEntries); - AlterConfigsResult alterConfigsResult = - kafkaAdminClient.alterConfigs( - Collections.singletonMap(new ConfigResource(type, name), config)); - - try { - alterConfigsResult.all().get(kafkaAdminClientAlterTimeoutMs, TimeUnit.MILLISECONDS); - } catch (Exception exception) { - log.warn("Alter config type:" + type + ", name:" + name + " exception:" + exception); - return false; - } - - return true; - } - - public Collection updateTopicConf( - @TopicExistConstraint String topic, Properties props) { - Collection configEntries = - props - .entrySet() - .stream() - .map(e -> new ConfigEntry(e.getKey().toString(), e.getValue().toString())) - .collect(Collectors.toList()); - alterConfig(ConfigResource.Type.TOPIC, topic, configEntries); - - return getTopicConf(topic); - } - - public Collection getTopicConf(@TopicExistConstraint String topic) { - Properties dynamicProps = getConfigInZk(Type.TOPIC, topic); - - Collection configs = describeConfig(Type.TOPIC, String.valueOf(topic)); - - return mergeConfigs(configs, dynamicProps); - } - - public Properties getTopicConfByKey(@TopicExistConstraint String topic, String key) { - Collection configEntries = describeConfig(ConfigResource.Type.TOPIC, topic); - Properties returnProps = new Properties(); - for (ConfigEntry entry : configEntries) { - if (entry.name().equals(key)) { - returnProps.put(key, entry.value()); - return returnProps; - } - } - - return null; - } - - public Collection updateTopicConfByKey( - @TopicExistConstraint String topic, String key, String value) { - alterConfig( - ConfigResource.Type.TOPIC, topic, Collections.singletonList(new ConfigEntry(key, value))); - - return getTopicConf(topic); - } - - public Map> listAllConsumerGroups(ConsumerType type) { - Map> result = new HashMap<>(); - - if (type == null || type == ConsumerType.OLD) { - Set oldConsumerGroupList = listAllOldConsumerGroups(); - if (oldConsumerGroupList == null || oldConsumerGroupList.size() == 0) { - result.put("old", new HashSet<>()); - } else { - result.put("old", oldConsumerGroupList); - } - } - - if (type == null || type == ConsumerType.NEW) { - Set newConsumerGroupList = listAllNewConsumerGroups(); - if (newConsumerGroupList == null || newConsumerGroupList.size() == 0) { - result.put("new", new HashSet<>()); - } else { - result.put("new", newConsumerGroupList); - } - } - - return result; - } - - private Set listAllNewConsumerGroups() { - AdminClient adminClient = kafkaUtils.createAdminClient(); - log.info("Calling the listAllConsumerGroupsFlattened"); - // Send LIST_GROUPS Request to kafka - Set activeGroups = - CollectionConvertor.seqConvertJavaList(adminClient.listAllConsumerGroupsFlattened()) - .stream() - .map(GroupOverview::groupId) - .collect(toSet()); - - log.info("Finish getting new consumers"); - adminClient.close(); - return activeGroups; - } - - private Set listAllOldConsumerGroups() { - log.info("Finish getting old consumers"); - KafkaZkClient kafkaZkClient = zookeeperUtils.createKafkaZkClient(); - - Set oldConsumerGroups = - CollectionConvertor.seqConvertJavaList(kafkaZkClient.getChildren(ZkUtils.ConsumersPath())) - .stream() - .collect(toSet()); - - return oldConsumerGroups; - } - - public Map> listConsumerGroupsByTopic( - @TopicExistConstraint String topic, ConsumerType type) { - Map> result = new HashMap<>(); - - if (type == null || type == ConsumerType.OLD) { - Set oldConsumerGroupList = new HashSet<>(); - try { - oldConsumerGroupList = listOldConsumerGroupsByTopic(topic); - } catch (Exception e) { - e.printStackTrace(); - } - if (oldConsumerGroupList.size() != 0) { - result.put("old", oldConsumerGroupList); - } - } - - if (type == null || type == ConsumerType.NEW) { - Set newConsumerGroupList = listNewConsumerGroupsByTopic(topic); - - if (newConsumerGroupList.size() != 0) { - result.put("new", newConsumerGroupList); - } - } - - return result; - } - - private Set listNewConsumerGroupsByTopic(@TopicExistConstraint String topic) { - Set result = new HashSet(); - Set consumersList = listAllNewConsumerGroups(); - - for (String c : consumersList) { - List topics = getTopicListByConsumerGroup(c); - if (topics.contains(topic)) { - result.add(c); - } - } - - return result; - } - - private Set listOldConsumerGroupsByTopic(@TopicExistConstraint String topic) { - KafkaZkClient kafkaZkClient = zookeeperUtils.createKafkaZkClient(); - - List consumersFromZk = - CollectionConvertor.seqConvertJavaList(kafkaZkClient.getChildren(ZkUtils.ConsumersPath())); - Set consumerList = new HashSet<>(); - - for (String consumer : consumersFromZk) { - String path = ZkUtils.ConsumersPath() + "/" + consumer + "/offsets"; - List topics = CollectionConvertor.seqConvertJavaList(kafkaZkClient.getChildren(path)); - if (topics != null && topics.contains(topic)) { - consumerList.add(consumer); - } - } - - return consumerList; - } - - public Set listTopicsByConsumerGroup(String consumerGroup, ConsumerType type) { - KafkaZkClient kafkaZkClient = zookeeperUtils.createKafkaZkClient(); - Set topicList = new HashSet<>(); - - if (type == null) { - throw new ApiException("Bad Request since type is null."); - } - - if (type == ConsumerType.OLD) { - if (!isOldConsumerGroup(consumerGroup)) { - throw new ApiException("Consumer group:" + consumerGroup + " non-exist"); - } - String path = ZkUtils.ConsumersPath() + "/" + consumerGroup + "/offsets"; - topicList = - CollectionConvertor.seqConvertJavaList(kafkaZkClient.getChildren(path)) - .stream() - .collect(toSet()); - } else if (type == ConsumerType.NEW) { - if (!isNewConsumerGroup(consumerGroup)) { - throw new ApiException("Consumer group:" + consumerGroup + " non-exist!"); - } - topicList.addAll(getTopicListByConsumerGroup(consumerGroup)); - } else { - throw new ApiException("Unknown Type " + type); - } - - return topicList; - } - - private List getTopicListByConsumerGroup(String consumerGroup) { - AdminClient adminClient = kafkaUtils.createAdminClient(); - Map groupOffsets = - CollectionConvertor.mapConvertJavaMap(adminClient.listGroupOffsets(consumerGroup)); - - return groupOffsets - .entrySet() - .stream() - .map(topicPartitionObjectEntry -> topicPartitionObjectEntry.getKey().topic()) - .collect(toList()); - } - - public ConsumerGroupMeta getConsumerGroupMeta(String consumerGroup) { - List members = new ArrayList<>(); - AdminClient adminClient = kafkaUtils.createAdminClient(); - - ConsumerGroupSummary consumerGroupSummary = adminClient.describeConsumerGroup(consumerGroup, 0); - List consumerSummaryList = - CollectionConvertor.optionListConvertJavaList(consumerGroupSummary.consumers().get()); - - ConsumerGroupMeta consumerGroupMeta = - ConsumerGroupMeta.builder() - .groupId(consumerGroup) - .state(ConsumerGroupState.parse(consumerGroupSummary.state())) - .assignmentStrategy(consumerGroupSummary.assignmentStrategy()) - .coordinator(consumerGroupSummary.coordinator()) - .build(); - - consumerSummaryList.forEach( - consumerSummary -> { - List topicPartitions = - CollectionConvertor.listConvertJavaList(consumerSummary.assignment()); - members.add( - new MemberDescription( - consumerSummary.consumerId(), - consumerSummary.clientId(), - consumerSummary.host(), - topicPartitions)); - }); - - Collections.sort(members); - consumerGroupMeta.setMembers(members); - - adminClient.close(); - return consumerGroupMeta; - } - - public boolean isOldConsumerGroup(String consumerGroup) { - return listAllOldConsumerGroups().contains(consumerGroup); - } - - public boolean isNewConsumerGroup(String consumerGroup) { - // Active Consumergroup or Dead ConsumerGroup is OK - return (listAllNewConsumerGroups().contains(consumerGroup)); - } - - public Map> describeConsumerGroup( - String consumerGroup, ConsumerType type) { - Map> result = new HashMap<>(); - Set topicList = listTopicsByConsumerGroup(consumerGroup, type); - if (topicList == null) { - // Return empty result - return result; - } - if (type == ConsumerType.NEW) { - if (!isNewConsumerGroup(consumerGroup)) { - throw new ApiException("Consumer group:" + consumerGroup + " non-exist!"); - } - List partitionAssignmentStateList = - describeNewConsumerGroup(consumerGroup, false, null); - result = convertPasListToMap(consumerGroup, partitionAssignmentStateList, ConsumerType.NEW); - } else if (type == ConsumerType.OLD) { - if (!isOldConsumerGroup(consumerGroup)) { - throw new ApiException("Consumer group:" + consumerGroup + " non-exist"); - } - List partitionAssignmentStateList = - describeOldConsumerGroup(consumerGroup, false, null); - result = convertPasListToMap(consumerGroup, partitionAssignmentStateList, ConsumerType.OLD); - } - - return result; - } - - // Convert partition assignment to map, key is topic - private Map> convertPasListToMap( - String consumerGroup, List pasList, ConsumerType type) { - Map> result = new HashMap<>(); - ConsumerGroupSummary consumerGroupSummary; - - if (type.equals(ConsumerType.NEW)) { - AdminClient adminClient = kafkaUtils.createAdminClient(); - consumerGroupSummary = adminClient.describeConsumerGroup(consumerGroup, 0); - adminClient.close(); - } else { - // Old consumer group has no state, coordinator, assignmentStrategy info - consumerGroupSummary = null; - } - - pasList.forEach( - partitionAssignmentState -> { - String topic = partitionAssignmentState.getTopic(); - List consumerGroupDescs; - if (result.containsKey(topic)) { - consumerGroupDescs = result.get(topic); - } else { - consumerGroupDescs = new ArrayList<>(); - } - consumerGroupDescs.add( - convertParitionAssignmentStateToGroupDesc( - consumerGroup, consumerGroupSummary, partitionAssignmentState, type)); - result.put(topic, consumerGroupDescs); - }); - - return result; - } - - private ConsumerGroupDesc convertParitionAssignmentStateToGroupDesc( - String consumerGroup, - ConsumerGroupSummary consumerGroupSummary, - PartitionAssignmentState pas, - ConsumerType type) { - ConsumerGroupDesc.ConsumerGroupDescBuilder consumerGroupDescBuilder = - ConsumerGroupDesc.builder() - .groupName(consumerGroup) - .topic(pas.getTopic()) - .partitionId(pas.getPartition()) - .currentOffset(pas.getOffset()) - .logEndOffset(pas.getLogEndOffset()) - .lag(pas.getLag()) - .consumerId(pas.getConsumerId()) - .clientId(pas.getClientId()) - .host(pas.getHost()) - .type(type); - - if (consumerGroupSummary != null) { - consumerGroupDescBuilder = - consumerGroupDescBuilder - .state(ConsumerGroupState.parse(consumerGroupSummary.state())) - .assignmentStrategy(consumerGroupSummary.assignmentStrategy()) - .coordinator(consumerGroupSummary.coordinator()); - } - - return consumerGroupDescBuilder.build(); - } - - public List describeNewConsumerGroup( - String consumerGroup, boolean filtered, String topic) { - List partitionAssignmentStateList = new ArrayList<>(); - - if (filtered && !existTopic(topic)) { - return partitionAssignmentStateList; - } - - AdminClient adminClient = kafkaUtils.createAdminClient(); - ConsumerGroupSummary consumerGroupSummary = adminClient.describeConsumerGroup(consumerGroup, 0); - List consumerSummaryList = - CollectionConvertor.listConvertJavaList(consumerGroupSummary.consumers().get()); - if (consumerSummaryList != null) { - Map offsets = - CollectionConvertor.mapConvertJavaMap(adminClient.listGroupOffsets(consumerGroup)); - Map offsetsFiltered; - - adminClient.close(); - if (filtered && existTopic(topic)) { - offsetsFiltered = - offsets - .entrySet() - .stream() - .filter( - topicPartitionObjectEntry -> - topicPartitionObjectEntry.getKey().topic().equals(topic)) - .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); - } else { - offsetsFiltered = offsets; - } - if (offsetsFiltered.isEmpty()) { - return partitionAssignmentStateList; - } else { - ArrayList assignedTopicPartitions = new ArrayList<>(); - List rowsWithConsumer = - consumerSummaryList - .stream() - .flatMap( - consumerSummary -> { - List topicPartitions = - CollectionConvertor.listConvertJavaList(consumerSummary.assignment()); - List topicPartitionsFiltered = topicPartitions; - if (filtered) { - topicPartitionsFiltered = - topicPartitions - .stream() - .filter(topicPartition -> topicPartition.topic().equals(topic)) - .collect(toList()); - } - assignedTopicPartitions.addAll(topicPartitionsFiltered); - Map partitionOffsets = - topicPartitionsFiltered - .stream() - .collect( - toMap( - Function.identity(), - topicPartition -> offsetsFiltered.get(topicPartition))); - return collectConsumerAssignment( - consumerGroup, - consumerGroupSummary.coordinator(), - topicPartitionsFiltered, - partitionOffsets, - consumerSummary.consumerId(), - consumerSummary.host(), - consumerSummary.clientId()) - .stream(); - }) - .collect(toList()); - - List topicPartitionList = new ArrayList<>(); - Map partitionOffsets = new HashMap<>(); - - offsetsFiltered - .entrySet() - .forEach( - topicPartitionObjectEntry -> { - if (!assignedTopicPartitions.contains(topicPartitionObjectEntry.getKey())) { - topicPartitionList.add(topicPartitionObjectEntry.getKey()); - partitionOffsets.put( - topicPartitionObjectEntry.getKey(), topicPartitionObjectEntry.getValue()); - } - }); - List rowsWithoutConsumer = - collectConsumerAssignment( - consumerGroup, - consumerGroupSummary.coordinator(), - topicPartitionList, - partitionOffsets, - "-", - "-", - "-"); - - partitionAssignmentStateList.addAll(rowsWithConsumer); - partitionAssignmentStateList.addAll(rowsWithoutConsumer); - } - } - - Collections.sort(partitionAssignmentStateList); - return partitionAssignmentStateList; - } - - private List collectConsumerAssignment( - String group, - Node coordinator, - List topicPartitions, - Map partitionOffsets, - String consumerId, - String host, - String clientId) { - if (topicPartitions.size() == 0) { - return new ArrayList(); - } else { - List list = new ArrayList<>(); - topicPartitions.forEach( - topicPartition -> { - long logEndOffset = getEndOffset(topicPartition.topic(), topicPartition.partition()); - long offset = (Long) partitionOffsets.get(topicPartition); - long lag = logEndOffset - offset; - list.add( - new PartitionAssignmentState( - group, - coordinator, - topicPartition.topic(), - topicPartition.partition(), - offset, - lag, - consumerId, - host, - clientId, - logEndOffset)); - }); - - Collections.sort(list); - return list; - } - } - - public List describeOldConsumerGroup( - String consumerGroup, boolean filtered, String topic) { - List partitionAssignmentStateList = new ArrayList<>(); - - if (filtered && !existTopic(topic)) { - return partitionAssignmentStateList; - } - - String[] agrs = { - "--describe", - "--zookeeper", - zookeeperUtils.getZookeeperConfig().getUris(), - "--group", - consumerGroup - }; - ConsumerGroupCommand.ConsumerGroupCommandOptions options = - new ConsumerGroupCommand.ConsumerGroupCommandOptions(agrs); - ConsumerGroupCommand.ZkConsumerGroupService zkConsumerGroupService = - new ConsumerGroupCommand.ZkConsumerGroupService(options); - - ObjectMapper mapper = new ObjectMapper(); - mapper.registerModule(new DefaultScalaModule()); - mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - try { - String source = - mapper.writeValueAsString(zkConsumerGroupService.collectGroupOffsets()._2().get()); - partitionAssignmentStateList = - mapper.readValue( - source, getCollectionType(mapper, List.class, PartitionAssignmentState.class)); - List partitionAssignmentStateListFiltered; - if (filtered && existTopic(topic)) { - partitionAssignmentStateListFiltered = - partitionAssignmentStateList - .stream() - .filter( - partitionAssignmentState -> partitionAssignmentState.getTopic().equals(topic)) - .collect(Collectors.toList()); - } else { - partitionAssignmentStateListFiltered = partitionAssignmentStateList; - } - partitionAssignmentStateListFiltered.sort( - Comparator.comparing(PartitionAssignmentState::getTopic) - .thenComparing(PartitionAssignmentState::getPartition)); - } catch (JsonProcessingException jsonProcessingException) { - log.error("Describe old consumer group exception.", jsonProcessingException); - } catch (IOException ioexception) { - log.error("Describe old consumer group exception.", ioexception); - } - - return partitionAssignmentStateList; - } - - private JavaType getCollectionType( - ObjectMapper mapper, Class collectionClass, Class... elementClasses) { - return mapper.getTypeFactory().constructParametricType(collectionClass, elementClasses); - } - - private List getNewConsumerGroupDescByConsumerGroupAndTopic(String consumerGroup, String topic) { - List partitionAssignmentStateList = - describeNewConsumerGroup(consumerGroup, true, topic); - - AdminClient adminClient = kafkaUtils.createAdminClient(); - ConsumerGroupSummary consumerGroupSummary = adminClient.describeConsumerGroup(consumerGroup, 0); - adminClient.close(); - - return partitionAssignmentStateList - .stream() - .map( - partitionAssignmentState -> - convertParitionAssignmentStateToGroupDesc( - consumerGroup, - consumerGroupSummary, - partitionAssignmentState, - ConsumerType.NEW)) - .collect(Collectors.toList()); - } - - public List describeNewConsumerGroupByTopic( - String consumerGroup, @TopicExistConstraint String topic) { - if (consumerGroup != null && !isNewConsumerGroup(consumerGroup)) { - throw new ApiException("ConsumerGroup:" + consumerGroup + " non-exist!"); - } - - List consumerGroupDescList = new ArrayList<>(); - if (consumerGroup == null || consumerGroup.length() == 0) { - // To search all consumer groups - Set allNewConsumerGroups = listAllNewConsumerGroups(); - - for (String cg:allNewConsumerGroups) { - consumerGroupDescList.addAll(getNewConsumerGroupDescByConsumerGroupAndTopic(cg, topic)); - } - return consumerGroupDescList; - } else { - return getNewConsumerGroupDescByConsumerGroupAndTopic(consumerGroup, topic); - } - } - - private List getOldConsumerGroupDescByConsumerGroupAndTopic(String consumerGroup, String topic) { - List partitionAssignmentStateList = - describeOldConsumerGroup(consumerGroup, true, topic); - - ConsumerGroupSummary consumerGroupSummary = null; - - return partitionAssignmentStateList - .stream() - .map( - partitionAssignmentState -> - convertParitionAssignmentStateToGroupDesc( - consumerGroup, - consumerGroupSummary, - partitionAssignmentState, - ConsumerType.OLD)) - .collect(Collectors.toList()); - } - - public List describeOldConsumerGroupByTopic( - String consumerGroup, @TopicExistConstraint String topic) { - if (!isOldConsumerGroup(consumerGroup)) { - throw new ApiException("ConsumerGroup:" + consumerGroup + " non-exist"); - } - - List consumerGroupDescList = new ArrayList<>(); - if (consumerGroup == null || consumerGroup.length() == 0) { - // To search all consumer groups - Set allNewConsumerGroups = listAllNewConsumerGroups(); - - for (String cg:allNewConsumerGroups) { - consumerGroupDescList.addAll(getOldConsumerGroupDescByConsumerGroupAndTopic(cg, topic)); - } - return consumerGroupDescList; - } else { - return getOldConsumerGroupDescByConsumerGroupAndTopic(consumerGroup, topic); - } - } - - public Map addPartitions(List addPartitions) { - Map addPartitionsResult = new HashMap<>(); - org.apache.kafka.clients.admin.AdminClient kafkaAdminClient = createKafkaAdminClient(); - - Map newPartitionsMap = new HashMap<>(); - addPartitions.forEach( - addPartition -> { - String topic = addPartition.getTopic(); - if (!existTopic(topic)) { - addPartitionsResult.put( - topic, - GeneralResponse.builder() - .state(GeneralResponseState.failure) - .msg("Topic:" + topic + " non-exist.") - .build()); - } else { - TopicMeta topicMeta = describeTopic(topic); - int currentPartionCount = topicMeta.getPartitionCount(); - int numPartitionsAdded = addPartition.getNumPartitionsAdded(); - int totalCount = currentPartionCount + numPartitionsAdded; - List> newAssignments = addPartition.getReplicaAssignment(); - NewPartitions newPartitions; - if (newAssignments == null || newAssignments.isEmpty()) { - // The assignment of new replicas to brokers will be decided by the broker. - newPartitions = NewPartitions.increaseTo(totalCount); - } else { - newPartitions = NewPartitions.increaseTo(totalCount, newAssignments); - } - newPartitionsMap.put(topic, newPartitions); - } - }); - CreatePartitionsResult createPartitionsResult = - kafkaAdminClient.createPartitions(newPartitionsMap); - try { - createPartitionsResult.all().get(kafkaAdminClientAlterTimeoutMs, TimeUnit.MILLISECONDS); - } catch (Exception exception) { - log.warn("Add partitions exception: " + exception); - } finally { - Map> result = createPartitionsResult.values(); - result.forEach( - (topic, createResult) -> { - GeneralResponse generalResponse; - if (!createResult.isCompletedExceptionally() && createResult.isDone()) { - TopicMeta topicMeta = describeTopic(topic); - generalResponse = - GeneralResponse.builder() - .state(GeneralResponseState.success) - .data(topicMeta) - .build(); - } else { - generalResponse = - GeneralResponse.builder() - .state(GeneralResponseState.failure) - .msg(createResult.toString()) - .build(); - } - addPartitionsResult.put(topic, generalResponse); - }); - - return addPartitionsResult; - } - } - - // Return - public List generateReassignPartition(ReassignWrapper reassignWrapper) { - KafkaZkClient kafkaZkClient = zookeeperUtils.createKafkaZkClient(); - List result = new ArrayList<>(); - - Seq brokerSeq = - JavaConverters.asScalaBufferConverter(reassignWrapper.getBrokers()).asScala().toSeq(); - // - Tuple2 resultTuple2; - try { - resultTuple2 = - ReassignPartitionsCommand.generateAssignment( - kafkaZkClient, brokerSeq, reassignWrapper.generateReassignJsonString(), false); - } catch (Exception exception) { - throw new ApiException("Generate reassign plan exception." + exception); - } - HashMap emptyMap = new HashMap<>(); - ObjectMapper objectMapper = new ObjectMapper(); - try { - result.add( - objectMapper.readValue( - ReassignPartitionsCommand.formatAsReassignmentJson( - (scala.collection.Map>) resultTuple2._2(), - JavaConverters.mapAsScalaMapConverter(emptyMap).asScala()), - ReassignModel.class)); - result.add( - objectMapper.readValue( - ReassignPartitionsCommand.formatAsReassignmentJson( - (scala.collection.Map>) resultTuple2._1(), - JavaConverters.mapAsScalaMapConverter(emptyMap).asScala()), - ReassignModel.class)); - Collections.sort(result.get(0).getPartitions()); - Collections.sort(result.get(1).getPartitions()); - } catch (Exception exception) { - throw new ApiException("Generate reassign plan exception." + exception); - } - - return result; - } - - public ReassignStatus executeReassignPartition( - ReassignModel reassignModel, - Long interBrokerThrottle, - Long replicaAlterLogDirsThrottle, - Long timeoutMs) { - // Set default value - interBrokerThrottle = (interBrokerThrottle == null) ? Long.valueOf(-1) : interBrokerThrottle; - replicaAlterLogDirsThrottle = - (replicaAlterLogDirsThrottle == null) ? Long.valueOf(-1) : replicaAlterLogDirsThrottle; - timeoutMs = (timeoutMs == null) ? Long.valueOf(10000) : timeoutMs; - - org.apache.kafka.clients.admin.AdminClient kafkaAdminClient = createKafkaAdminClient(); - KafkaZkClient kafkaZkClient = zookeeperUtils.createKafkaZkClient(); - AdminZkClient adminZkClient = new AdminZkClient(kafkaZkClient); - - TwoTuple< - scala.collection.mutable.HashMap>, - scala.collection.Map> - reassignPlan; - String reassignStr = ""; - try { - reassignStr = new ObjectMapper().writeValueAsString(reassignModel); - } catch (Exception exception) { - throw new ApiException("Json processing exception." + exception); - } - reassignPlan = genReassignPlan(reassignStr); - scala.collection.mutable.HashMap> partitionsToBeReassignedMap = - reassignPlan.getFirst(); - scala.collection.Map replicatAssignment = - reassignPlan.getSecond(); - - ReassignPartitionsCommand reassignPartitionsCommand = - new ReassignPartitionsCommand( - kafkaZkClient, - scala.Option.apply(kafkaAdminClient), - partitionsToBeReassignedMap, - replicatAssignment, - adminZkClient); - - Function0 postUpdateAction = - new AbstractFunction0() { - @Override - public BoxedUnit apply() { - return null; - } - }; - Throttle throttle = - new Throttle(interBrokerThrottle, replicaAlterLogDirsThrottle, postUpdateAction); - - if (kafkaZkClient.reassignPartitionsInProgress()) { - // check whether zk node /admin/reassign_partitions exists - reassignPartitionsCommand.maybeLimit(throttle); - throw new ApiException( - "Failed to reassign partitions because there is an existing assignment running."); - } else { - try { - reassignPartitionsCommand.reassignPartitions(throttle, timeoutMs); - log.info("Successfully started reassignment of partitions."); - } catch (Exception exception) { - throw new ApiException( - "Failed to reassign partitions:" - + reassignPlan.getFirst() - + ". Exception:" - + exception.getLocalizedMessage()); - } - } - - return checkReassignStatus(partitionsToBeReassignedMap, replicatAssignment); - } - - private ReassignStatus checkReassignStatus( - scala.collection.Map> partitionsToBeReassigned, - scala.collection.Map replicaAssignement) { - ReassignStatus reassignStatus = new ReassignStatus(); - Map reassignedPartitionsStatus = - checkIfPartitionReassignmentSucceeded(partitionsToBeReassigned); - Map replicasReassignmentStatus = - checkIfReplicaReassignmentSucceeded( - CollectionConvertor.mapConvertJavaMap(replicaAssignement)); - - reassignStatus.setPartitionsReassignStatus(reassignedPartitionsStatus); - reassignStatus.setReplicasReassignStatus(replicasReassignmentStatus); - - if (removeThrottle(reassignedPartitionsStatus, replicasReassignmentStatus)) { - reassignStatus.setRemoveThrottle(true); - } else { - reassignStatus.setRemoveThrottle(false); - } - - reassignStatus.setMsg( - "If removeThrottle in response is false, please use check api to remove throttle."); - - return reassignStatus; - } - - private boolean removeThrottle( - Map reassignedPartitionsStatus, - Map replicasReassignmentStatus) { - for (Map.Entry entry : reassignedPartitionsStatus.entrySet()) { - // Partitions reassignemnt not all done - if (!entry.getValue().equals(ReassignmentState.ReassignmentCompleted.code())) { - return false; - } - } - - for (Map.Entry entry : replicasReassignmentStatus.entrySet()) { - // Replica reassignement not all done - if (!entry.getValue().equals(ReassignmentState.ReassignmentCompleted.code())) { - return false; - } - } - - KafkaZkClient kafkaZkClient = zookeeperUtils.createKafkaZkClient(); - - List brokerList = - CollectionConvertor.seqConvertJavaList(kafkaZkClient.getAllBrokersInCluster()); - for (Broker broker : brokerList) { - int brokerId = broker.id(); - List keysToBeRemoved = new ArrayList<>(); - // We can't access the scala object here - // keysToBeRemoved.add(DynamicConfig.Broker.LeaderReplicationThrottledRateProp()); - // keysToBeRemoved.add(DynamicConfig.Broker.FollowerReplicationThrottledRateProp()); - // - // keysToBeRemoved.add(DynamicConfig.Broker.ReplicaAlterLogDirsIoMaxBytesPerSecondProp()); - keysToBeRemoved.add(LeaderReplicationThrottledRateProp); - keysToBeRemoved.add(FollowerReplicationThrottledRateProp); - keysToBeRemoved.add(ReplicaAlterLogDirsIoMaxBytesPerSecondProp); - try { - removeConfigInZk(Type.BROKER, String.valueOf(brokerId), keysToBeRemoved); - } catch (ApiException apiException) { - log.info( - "Remove property on broker:" + brokerId + " failed since " + apiException.getMessage()); - } - } - - Set topics = - reassignedPartitionsStatus.keySet().stream().map(tp -> tp.topic()).collect(toSet()); - Set topicInReplicas = - replicasReassignmentStatus.keySet().stream().map(tpr -> tpr.topic()).collect(toSet()); - topics.addAll(topicInReplicas); - - for (String topic : topics) { - List keysToBeRemoved = new ArrayList<>(); - keysToBeRemoved.add(LogConfig.LeaderReplicationThrottledReplicasProp()); - keysToBeRemoved.add(LogConfig.FollowerReplicationThrottledReplicasProp()); - try { - removeConfigInZk(Type.TOPIC, topic, keysToBeRemoved); - } catch (ApiException apiException) { - log.info( - "Remove property on topic:" + topic + " failed since " + apiException.getMessage()); - } - } - - return true; - } - - private Map checkIfPartitionReassignmentSucceeded( - scala.collection.Map> partitionsToBeReassigned) { - Map reassignedPartitionsStatus = new HashMap<>(); - KafkaZkClient kafkaZkClient = zookeeperUtils.createKafkaZkClient(); - - scala.collection.immutable.Map> partitionsBeingReassigned = - kafkaZkClient.getPartitionReassignment(); - scala.collection.Iterator topicPartitionIterator = - partitionsToBeReassigned.keysIterator(); - while (topicPartitionIterator.hasNext()) { - TopicPartition topicPartition = topicPartitionIterator.next(); - reassignedPartitionsStatus.put( - topicPartition, - ReassignPartitionsCommand.checkIfPartitionReassignmentSucceeded( - kafkaZkClient, - topicPartition, - partitionsToBeReassigned, - partitionsBeingReassigned) - .status()); - } - - return reassignedPartitionsStatus; - } - - private Map checkIfReplicaReassignmentSucceeded( - Map replicaAssignement) { - Map replicasReassignmentStatus = new HashMap<>(); - Map replicaLogDirInfos = new HashMap<>(); - - if (!replicaAssignement.isEmpty()) { - List replicaList = new ArrayList<>(replicaAssignement.keySet()); - replicaLogDirInfos = describeReplicaLogDirs(replicaList); - } - - for (Map.Entry newLogDirEntry : replicaAssignement.entrySet()) { - TopicPartitionReplica tpr = newLogDirEntry.getKey(); - String newLogDir = newLogDirEntry.getValue(); - ReplicaLogDirInfo replicaLogDirInfo = replicaLogDirInfos.get(tpr); - if (replicaLogDirInfo.getCurrentReplicaLogDir() == null) { - // tpr log dir not found - replicasReassignmentStatus.put(tpr, ReassignmentState.ReassignmentFailed.code()); - } else if (replicaLogDirInfo.getFutureReplicaLogDir() != null - && replicaLogDirInfo.getFutureReplicaLogDir().equals(newLogDir)) { - replicasReassignmentStatus.put(tpr, ReassignmentState.ReassignmentInProgress.code()); - } else if (replicaLogDirInfo.getFutureReplicaLogDir() != null - && !replicaLogDirInfo.getFutureReplicaLogDir().equals(newLogDir)) { - // tpr is being moved to another logdir instead of newLogDir - replicasReassignmentStatus.put(tpr, ReassignmentState.ReassignmentFailed.code()); - } else if (replicaLogDirInfo.getCurrentReplicaLogDir() != null - && replicaLogDirInfo.getCurrentReplicaLogDir().equals(newLogDir)) { - replicasReassignmentStatus.put(tpr, ReassignmentState.ReassignmentCompleted.code()); - } else { - replicasReassignmentStatus.put(tpr, ReassignmentState.ReassignmentFailed.code()); - } - } - - return replicasReassignmentStatus; - } - - public ReassignStatus checkReassignStatus(ReassignModel reassignModel) { - String reassignJsonStr = ""; - try { - ObjectMapper objectMapper = new ObjectMapper(); - reassignJsonStr = objectMapper.writeValueAsString(reassignModel); - } catch (JsonProcessingException exception) { - throw new ApiException("Json processing exception." + exception); - } - TwoTuple< - scala.collection.mutable.HashMap>, - scala.collection.Map> - reassignPlan = genReassignPlan(reassignJsonStr); - scala.collection.mutable.HashMap> partitionsToBeReassignedMap = - reassignPlan.getFirst(); - scala.collection.Map replicatAssignment = - reassignPlan.getSecond(); - - return checkReassignStatus(partitionsToBeReassignedMap, replicatAssignment); - } - - private TwoTuple< - scala.collection.mutable.HashMap>, - scala.collection.Map> - genReassignPlan(String reassignJsonStr) { - Tuple2 resultTuple2; - - KafkaZkClient kafkaZkClient = zookeeperUtils.createKafkaZkClient(); - - try { - // Parse and validate reassignment json string, return (partitionsToBeReassigned, - // replicaAssignment) - resultTuple2 = ReassignPartitionsCommand.parseAndValidate(kafkaZkClient, reassignJsonStr); - } catch (Exception exception) { - throw new ApiException("Bad Request. " + exception.getMessage()); - } - // Change list buffer to map - ListBuffer partitionsToBeReassignedList = - (scala.collection.mutable.ListBuffer) resultTuple2._1(); - scala.collection.mutable.HashMap> partitionsToBeReassignedMap = - new scala.collection.mutable.HashMap<>(); - for (int i = 0; i < partitionsToBeReassignedList.size(); ++i) { - Tuple2 tup = (Tuple2) partitionsToBeReassignedList.apply(i); - partitionsToBeReassignedMap.put((TopicPartition) tup._1(), (Seq) tup._2()); - } - - scala.collection.Map replicatAssignment = - (scala.collection.Map) resultTuple2._2(); - - return new TwoTuple<>(partitionsToBeReassignedMap, replicatAssignment); - } - - public String getMessage( - @TopicExistConstraint String topic, - int partition, - long offset, - String decoder, - String avroSchema) { - KafkaConsumer consumer = - kafkaUtils.createNewConsumer(String.valueOf(System.currentTimeMillis())); - TopicPartition tp = new TopicPartition(topic, partition); - long beginningOffset = getBeginningOffset(topic, partition); - long endOffset = getEndOffset(topic, partition); - if (beginningOffset == endOffset) { - throw new ApiException("There is no message in this partition of this topic"); - } - if (offset < beginningOffset || offset >= endOffset) { - log.error(offset + " error"); - consumer.close(); - throw new ApiException( - "offsets must be between " + String.valueOf(beginningOffset + " and " + (endOffset - 1))); - } - consumer.assign(Collections.singletonList(tp)); - consumer.seek(tp, offset); - - String last = null; - - // ConsumerRecords crs = consumer.poll(channelRetryBackoffMs); - ConsumerRecords crs = consumer.poll(3000); - log.info( - "Seek to offset:" - + offset - + ", topic:" - + topic - + ", partition:" - + partition - + ", crs.count:" - + crs.count()); - if (crs.count() != 0) { - Iterator> it = crs.iterator(); - while (it.hasNext()) { - ConsumerRecord initCr = it.next(); - last = - "Value: " - + initCr.value() - + ", Offset: " - + String.valueOf(initCr.offset()) - + ", timestamp:" - + initCr.timestamp(); - log.info( - "Value: " + initCr.value() + ", initCr.Offset: " + String.valueOf(initCr.offset())); - if (last != null && initCr.offset() == offset) { - break; - } - } - } - log.info("last:" + last); - consumer.close(); - return last; - } - - public Record getRecordByOffset( - @TopicExistConstraint String topic, - int partition, - long offset, - String decoder, - String avroSchema) { - if (!isTopicPartitionValid(topic, partition)) { - throw new ApiException("Bad request. Topic:" + topic + " has no partition:" + partition); - } - - checkOffsetValid(topic, partition, offset); - - if (!kafkaUtils.DESERIALIZER_TYPE_MAP.containsKey(decoder)) { - throw new ApiException( - "Bad request. Decoder class:" - + decoder - + " not found. ByteArrayDeserializer, ByteBufferDeserializer, BytesDeserializer, " - + "DoubleDeserializer, FloatDeserializer, " - + "IntegerDeserializer, LongDeserializer, ShortDeserializer, StringDeserializer, AvroDeserializer " - + "are supported."); - } - - if (decoder != null && decoder.equals("AvroDeserializer")) { - if (avroSchema == null || avroSchema.isEmpty()) { - throw new ApiException("Bad request. Schema is needed when choosing AvroDeserializer."); - } else { - return getAvroRecordByOffset(topic, partition, offset, avroSchema); - } - } - - if (decoder == null || decoder.isEmpty()) { - // default decoder is StringDeserializer - decoder = "StringDeserializer"; - } - - Class type = kafkaUtils.DESERIALIZER_TYPE_MAP.get(decoder); - String dese = Serdes.serdeFrom(type).deserializer().getClass().getCanonicalName(); - - KafkaConsumer consumer; - try { - consumer = kafkaUtils.createNewConsumer(String.valueOf(System.currentTimeMillis()), dese); - } catch (ClassNotFoundException classNotFoundException) { - throw new ApiException("Class " + dese + "not found exception." + classNotFoundException); - } - - TopicPartition tp = new TopicPartition(topic, partition); - consumer.assign(Collections.singletonList(tp)); - consumer.seek(tp, offset); - - Record record = Record.builder().topic(topic).type(type).decoder(decoder).build(); - - try { - ConsumerRecords crs = consumer.poll(3000); - log.info( - "Seek to offset:" - + offset - + ", topic:" - + topic - + ", partition:" - + partition - + ", crs.count:" - + crs.count()); - if (crs.count() != 0) { - Iterator> it = crs.iterator(); - while (it.hasNext()) { - ConsumerRecord initCr = it.next(); - if (initCr.offset() == offset) { - record.setOffset(offset); - record.setTimestamp(initCr.timestamp()); - record.setKey(initCr.key()); - record.setValue(initCr.value()); - break; - } - log.info( - "Value: " - + initCr.value() - + ", initCr.Offset: " - + String.valueOf(initCr.offset()) - + ", timestamp:" - + initCr.timestamp()); - } - } - } catch (Exception exception) { - throw new ApiException( - "Consume " - + topic - + "-" - + partition - + " offset:" - + offset - + " using " - + decoder - + " exception. " - + exception.getLocalizedMessage()); - } finally { - consumer.close(); - } - - return record; - } - - private boolean isTopicPartitionValid(String topic, int partition) { - TopicMeta topicMeta = describeTopic(topic); - - for (CustomTopicPartitionInfo topicPartitionInfo : topicMeta.getTopicPartitionInfos()) { - if (topicPartitionInfo.getTopicPartitionInfo().partition() == partition) { - return true; - } - } - - return false; - } - - public void checkOffsetValid(String topic, int partition, long offset) { - long beginningOffset = getBeginningOffset(topic, partition); - long endOffset = getEndOffset(topic, partition); - - log.info( - "Topic:" - + topic - + ", partition:" - + partition - + " begin offset:" - + beginningOffset - + ", end offset:" - + endOffset); - if (beginningOffset == endOffset) { - throw new ApiException("There is no message in this partition of this topic"); - } - if (offset < beginningOffset || offset >= endOffset) { - log.error(offset + " error"); - throw new ApiException( - "offsets must be between " + String.valueOf(beginningOffset + " and " + (endOffset - 1))); - } - } - - public Record getAvroRecordByOffset(String topic, int partition, long offset, String avroSchema) { - TopicPartition tp = new TopicPartition(topic, partition); - KafkaConsumer consumer = null; - try { - consumer = - kafkaUtils.createNewConsumer( - String.valueOf(System.currentTimeMillis()), - "org.apache.kafka.common.serialization.ByteArrayDeserializer"); - } catch (Exception exception) { - log.error("Create consumer exception." + exception); - } - consumer.assign(Collections.singletonList(tp)); - consumer.seek(tp, offset); - - Record record = - Record.builder().topic(topic).type(byte[].class).decoder("AvorDeserializer").build(); - - try { - ConsumerRecords crs = consumer.poll(3000); - log.info( - "Seek to offset:" - + offset - + ", topic:" - + topic - + ", partition:" - + partition - + ", crs.count:" - + crs.count()); - if (crs.count() != 0) { - Iterator> it = crs.iterator(); - while (it.hasNext()) { - ConsumerRecord initCr = it.next(); - if (initCr.offset() == offset) { - record.setOffset(offset); - record.setTimestamp(initCr.timestamp()); - record.setKey(initCr.key()); - record.setValue(avroDeserialize(initCr.value(), avroSchema)); - break; - } - log.info( - "Value: " - + initCr.value() - + ", initCr.Offset: " - + String.valueOf(initCr.offset()) - + ", timestamp:" - + initCr.timestamp()); - } - } - } catch (Exception exception) { - throw new ApiException( - "Consume " - + topic - + "-" - + partition - + " offset:" - + offset - + " using " - + "ByteArrayDeserializer" - + " exception. " - + exception.getLocalizedMessage()); - } finally { - consumer.close(); - } - - return record; - } - - private Object avroDeserialize(byte[] bytes, String avroSchema) { - Schema schema = new Schema.Parser().parse(avroSchema); - DatumReader reader = new GenericDatumReader(schema); - ByteBuffer buffer = ByteBuffer.wrap(bytes); - Object object = null; - try { - object = - reader.read( - null, DecoderFactory.get().binaryDecoder(buffer.array(), 0, bytes.length, null)); - } catch (IOException exception) { - throw new ApiException("Avro Deserialize exception. " + exception); - } - - return object; - } - - public GeneralResponse resetOffset( - @TopicExistConstraint String topic, - int partition, - String consumerGroup, - ConsumerType type, - String offset) { - KafkaConsumer consumer = null; - if (type != null && type == ConsumerType.NEW) { - if (!isNewConsumerGroup(consumerGroup)) { - return GeneralResponse.builder() - .state(GeneralResponseState.failure) - .msg("New consumer group:" + consumerGroup + " non-exists!") - .build(); - } - } - - if (type != null && type == ConsumerType.OLD) { - if (!isOldConsumerGroup(consumerGroup)) { - return GeneralResponse.builder() - .state(GeneralResponseState.failure) - .msg("Old consumer group:" + consumerGroup + " non-exists!") - .build(); - } - } - - if (!isTopicPartitionValid(topic, partition)) { - return GeneralResponse.builder() - .state(GeneralResponseState.failure) - .msg("Topic:" + topic + " has no partition:" + partition) - .build(); - } - - long offsetToBeReset = -1; - long beginningOffset = getBeginningOffset(topic, partition); - long endOffset = getEndOffset(topic, partition); - - log.info("To tell the consumergroup " + consumerGroup + " is active now"); - if (isConsumerGroupActive(consumerGroup, type)) { - return GeneralResponse.builder() - .state(GeneralResponseState.failure) - .msg("Offsets can only be reset if the group " + consumerGroup + " is inactive") - .build(); - } - - if (type != null && type == ConsumerType.NEW && isNewConsumerGroup(consumerGroup)) { - try { - log.info("The consumergroup " + consumerGroup + " is new. Reset offset now"); - consumer = kafkaUtils.createNewConsumer(consumerGroup); - // if type is new or the consumergroup itself is new - TopicPartition tp = new TopicPartition(topic, partition); - consumer.assign(Arrays.asList(tp)); - consumer.poll(channelSocketTimeoutMs); - if (offset.equals("earliest")) { - consumer.seekToBeginning(Arrays.asList(tp)); - offsetToBeReset = beginningOffset; - log.info( - "Reset offset for consumer group:" - + consumerGroup - + " on " - + topic - + "-" - + partition - + " to " - + consumer.position(tp)); - } else if (offset.equals("latest")) { - consumer.seekToEnd(Arrays.asList(tp)); - offsetToBeReset = endOffset; - log.info( - "Reset offset for consumer group:" - + consumerGroup - + " on " - + topic - + "-" - + partition - + " to " - + consumer.position(tp)); - } else if (isDateTime(offset)) { - // Reset offset by time - SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); - Map timestampsToSearch = new HashMap<>(); - try { - timestampsToSearch.put(tp, sdf.parse(offset).getTime()); - Map results = - consumer.offsetsForTimes(timestampsToSearch); - OffsetAndTimestamp offsetAndTimestamp = results.get(tp); - if (offsetAndTimestamp != null) { - offsetToBeReset = offsetAndTimestamp.offset(); - log.info( - "Reset consumer group:" - + consumerGroup - + " offset by time. Reset to offset:" - + offsetAndTimestamp.offset() - + ", timestamp:" - + offsetAndTimestamp.timestamp() - + ", timestampToDate:" - + sdf.format(new Date(offsetAndTimestamp.timestamp()))); - consumer.seek(tp, offsetToBeReset); - } else { - return GeneralResponse.builder() - .state(GeneralResponseState.failure) - .msg( - "No offset's timestamp is greater than or equal to the given timestamp:" - + offset) - .build(); - } - } catch (ParseException parseException) { - return GeneralResponse.builder() - .state(GeneralResponseState.failure) - .msg("Invalid offset format. Date format should be yyyy-MM-dd HH:mm:ss.SSS .") - .build(); - } - } else { - if (Long.parseLong(offset) < beginningOffset || Long.parseLong(offset) > endOffset) { - log.warn(offset + " error"); - return GeneralResponse.builder() - .state(GeneralResponseState.failure) - .msg( - "Invalid request offset:" - + offset - + ". Topic " - + topic - + "'s beginning offset:" - + beginningOffset - + ", endoffset:" - + endOffset) - .build(); - } - offsetToBeReset = Long.parseLong(offset); - consumer.seek(tp, offsetToBeReset); - } - consumer.commitSync(); - } catch (IllegalStateException e) { - storage.getMap().remove(consumerGroup); - return GeneralResponse.builder() - .state(GeneralResponseState.failure) - .msg(e.getLocalizedMessage()) - .build(); - } finally { - if (consumer != null) { - consumer.close(); - } - } - } - - // if type is old or the consumer group itself is old - if (type != null && type == ConsumerType.OLD && isOldConsumerGroup(consumerGroup)) { - log.info("The consumergroup " + consumerGroup + " is old. Reset offset now"); - if (offset.equals("earliest")) { - offsetToBeReset = beginningOffset; - } else if (offset.equals("latest")) { - offsetToBeReset = endOffset; - } else { - try { - if (Long.parseLong(offset) < beginningOffset || Long.parseLong(offset) > endOffset) { - log.info("Setting offset to " + offset + " error"); - return GeneralResponse.builder() - .state(GeneralResponseState.failure) - .msg( - "Invalid request offset:" - + offset - + ". Topic " - + topic - + "'s beginning offset:" - + beginningOffset - + ", endoffset:" - + endOffset) - .build(); - } - log.info("Offset will be reset to " + offset); - zkUtils = zookeeperUtils.getZkUtils(); - offsetToBeReset = Long.parseLong(offset); - zkUtils - .zkClient() - .writeData( - "/consumers/" + consumerGroup + "/offsets/" + topic + "/" + partition, offset); - } catch (Exception e) { - return GeneralResponse.builder() - .state(GeneralResponseState.failure) - .msg(e.getLocalizedMessage()) - .build(); - } - } - } - return GeneralResponse.builder() - .state(GeneralResponseState.success) - .msg("Reset the offset successfully!") - .data(Long.toString(offsetToBeReset)) - .build(); - } - - private boolean isDateTime(String offset) { - String patternStr = "\\d\\d\\d\\d-[0-1]\\d-[0-3]\\d\\s+[0-2]\\d:[0-5]\\d:[0-5]\\d\\.\\d\\d\\d"; - Pattern timePattern = Pattern.compile(patternStr); - return timePattern.matcher(offset).find(); - } - - public Map> getLastCommitTime( - @ConsumerGroupExistConstraint String consumerGroup, - @TopicExistConstraint String topic, - ConsumerType type) { - Map> result = new ConcurrentHashMap<>(); - - if (type != null && type == ConsumerType.OLD) { - CuratorFramework zkClient = zookeeperUtils.createZkClient(); - // Get Old Consumer commit time - try { - Map oldConsumerOffsetMap = new ConcurrentHashMap<>(); - if (zkClient.checkExists().forPath(CONSUMERPATHPREFIX + consumerGroup) != null - && zkClient - .checkExists() - .forPath(CONSUMERPATHPREFIX + consumerGroup + OFFSETSPATHPREFIX + topic) - != null) { - List offsets = - zkClient - .getChildren() - .forPath(CONSUMERPATHPREFIX + consumerGroup + OFFSETSPATHPREFIX + topic); - for (String offset : offsets) { - Integer id = Integer.valueOf(offset); - long mtime = - zkClient - .checkExists() - .forPath( - CONSUMERPATHPREFIX - + consumerGroup - + OFFSETSPATHPREFIX - + topic - + "/" - + offset) - .getMtime(); - oldConsumerOffsetMap.put(id, mtime); - } - - result.put("old", oldConsumerOffsetMap); - } - } catch (Exception e) { - log.warn( - "Get last commit time for consumergroup:" - + consumerGroup - + " failed. " - + e.getLocalizedMessage()); - } - } else { - // Get New consumer commit time, from offset storage instance - // TODO find a solution a replace the storage - if (storage.get(consumerGroup) != null) { - Map storageResult = - storage.get(consumerGroup); - result.put( - "new", - (storageResult - .entrySet() - .parallelStream() - .filter(s -> s.getKey().topicPartition().topic().equals(topic)) - .collect( - Collectors.toMap( - s -> s.getKey().topicPartition().partition(), - s -> { - if (s.getValue() != null) { - return s.getValue().commitTimestamp(); - } else { - return -1L; - } - })))); - } - } - - return result; - } - - public GeneralResponse deleteConsumerGroup(String consumerGroup, ConsumerType type) { - if (type == ConsumerType.OLD && !isOldConsumerGroup(consumerGroup)) { - return GeneralResponse.builder() - .state(GeneralResponseState.failure) - .msg("Old consumer group:" + consumerGroup + " non-exist") - .build(); - } - if (type == ConsumerType.NEW && !isNewConsumerGroup(consumerGroup)) { - return GeneralResponse.builder() - .state(GeneralResponseState.failure) - .msg("New consumer group:" + consumerGroup + " non-exist") - .build(); - } - if (type == ConsumerType.OLD) { - zkUtils = zookeeperUtils.getZkUtils(); - if (!AdminUtils.deleteConsumerGroupInZK(zkUtils, consumerGroup)) { - return GeneralResponse.builder() - .state(GeneralResponseState.failure) - .msg("The consumer " + consumerGroup + " is still active.Please stop it first") - .build(); - } - } else if (type == ConsumerType.NEW) { - AdminClient adminClient = kafkaUtils.createAdminClient(); - List groups = new ArrayList<>(); - groups.add(consumerGroup); - - scala.collection.immutable.List groupsList = - JavaConverters.asScalaBufferConverter(groups).asScala().toList(); - scala.collection.immutable.Map stringErrorsMap = - adminClient.deleteConsumerGroups((scala.collection.immutable.List) (groupsList)); - - adminClient.close(); - if (!stringErrorsMap.get(consumerGroup).get().equals(Errors.NONE)) { - log.info("Consumer group:" - + consumerGroup - + " could not be deleted. Error Code:" - + stringErrorsMap.get(consumerGroup).get() - + ". Error msg:" - + stringErrorsMap.get(consumerGroup).get().exception()); - return GeneralResponse.builder() - .state(GeneralResponseState.failure) - .msg( - "Consumer group:" - + consumerGroup - + " could not be deleted. Error Code:" - + stringErrorsMap.get(consumerGroup).get() - + ". Error msg:" - + stringErrorsMap.get(consumerGroup).get().exception()) - .build(); - } - } - - log.info("Consumer group:" + consumerGroup + " has been deleted."); - - return GeneralResponse.builder() - .state(GeneralResponseState.success) - .msg("Consumer group:" + consumerGroup + " has been deleted.") - .data(consumerGroup) - .build(); - } - - private List getTopicPartitions(String t) { - List tpList = new ArrayList<>(); - List l = Arrays.asList(t); - zkUtils = zookeeperUtils.getZkUtils(); - java.util.Map> tpMap = - JavaConverters.mapAsJavaMapConverter( - zkUtils.getPartitionsForTopics( - JavaConverters.asScalaIteratorConverter(l.iterator()).asScala().toSeq())) - .asJava(); - if (tpMap != null) { - ArrayList partitionLists = - new ArrayList<>(JavaConverters.seqAsJavaListConverter(tpMap.get(t)).asJava()); - tpList = - partitionLists.stream().map(p -> new TopicAndPartition(t, (Integer) p)).collect(toList()); - } - return tpList; - } - - private long getOffsets(Node leader, String topic, int partitionId, long time) { - TopicAndPartition topicAndPartition = new TopicAndPartition(topic, partitionId); - - SimpleConsumer consumer = - new SimpleConsumer(leader.host(), leader.port(), 10000, 1024, "Kafka-zk-simpleconsumer"); - - PartitionOffsetRequestInfo partitionOffsetRequestInfo = - new PartitionOffsetRequestInfo(time, 10000); - OffsetRequest offsetRequest = - new OffsetRequest( - ImmutableMap.of(topicAndPartition, partitionOffsetRequestInfo), - kafka.api.OffsetRequest.CurrentVersion(), - consumer.clientId()); - OffsetResponse offsetResponse = consumer.getOffsetsBefore(offsetRequest); - - if (offsetResponse.hasError()) { - short errorCode = offsetResponse.errorCode(topic, partitionId); - log.warn(format("Offset response has error: %d", errorCode)); - throw new ApiException( - "could not fetch data from Kafka, error code is '" - + errorCode - + "'Exception Message:" - + offsetResponse.toString()); - } - - long[] offsets = offsetResponse.offsets(topic, partitionId); - consumer.close(); - return offsets[0]; - } - - public long getBeginningOffset(String topic, int partitionId) { - KafkaConsumer consumer = kafkaUtils.createNewConsumer(KafkaUtils.DEFAULTCP); - TopicPartition tp = new TopicPartition(topic, partitionId); - consumer.assign(Arrays.asList(tp)); - Map beginningOffsets = - consumer.beginningOffsets(Collections.singletonList(tp)); - consumer.close(); - - if (beginningOffsets != null) { - return beginningOffsets.get(tp); - } - - return -1; - } - - public long getEndOffset(String topic, int partitionId) { - KafkaConsumer consumer = kafkaUtils.createNewConsumer(KafkaUtils.DEFAULTCP); - TopicPartition tp = new TopicPartition(topic, partitionId); - consumer.assign(Arrays.asList(tp)); - Map endOffsets = consumer.endOffsets(Collections.singletonList(tp)); - consumer.close(); - - if (endOffsets != null) { - return endOffsets.get(tp); - } - - return -1; - } - - private long getEndOffset(Node leader, String topic, int partitionId) { - return getOffsets(leader, topic, partitionId, kafka.api.OffsetRequest.LatestTime()); - } - - private boolean isConsumerGroupActive(String consumerGroup, ConsumerType type) { - if (type == ConsumerType.NEW) { - ConsumerGroupMeta groupMeta = getConsumerGroupMeta(consumerGroup); - ConsumerGroupState groupState = groupMeta.getState(); - if (groupState.equals(ConsumerGroupState.STABLE) - || groupState.equals(ConsumerGroupState.PREPARING_REBALANCE) - || groupState.equals(ConsumerGroupState.COMPLETING_REBALANCE)) { - return true; - } - if (groupState.equals(ConsumerGroupState.EMPTY) - || groupState.equals(ConsumerGroupState.DEAD)) { - return false; - } - throw new ApiException( - "Consumer group:" + consumerGroup + " state:" + groupState + " unkown."); - } else if (type == ConsumerType.OLD) { - zkUtils = zookeeperUtils.getZkUtils(); - return zkUtils.getConsumersInGroup(consumerGroup).nonEmpty(); - } else { - throw new ApiException("Unknown type " + type); - } - } - - public HealthCheckResult healthCheck() { - String healthCheckTopic = kafkaConfig.getHealthCheckTopic(); - HealthCheckResult healthCheckResult = new HealthCheckResult(); - KafkaProducer producer = kafkaUtils.createProducer(); - int partitionId; - long offset; - - boolean healthCheckTopicExist = existTopic(healthCheckTopic); - log.info("HealthCheckTopic:" + healthCheckTopic + " existed:" + healthCheckTopicExist); - if (!healthCheckTopicExist) { - healthCheckResult.setStatus("unknown"); - healthCheckResult.setMsg( - "HealthCheckTopic: " - + healthCheckTopic - + " Non-Exist. Please create it before doing health check."); - return healthCheckResult; - } - - String message = "health check_" + System.currentTimeMillis(); - ProducerRecord record = new ProducerRecord(healthCheckTopic, null, message); - log.info("Generate message:" + message); - try { - RecordMetadata recordMetadata = (RecordMetadata) producer.send(record).get(); - partitionId = recordMetadata.partition(); - offset = recordMetadata.offset(); - log.info( - "Message:" - + message - + " has been sent to Partition:" - + partitionId - + ", offset:" - + offset); - } catch (Exception e) { - healthCheckResult.setStatus("error"); - healthCheckResult.setMsg( - "Health Check: Produce Message Failure. Exception: " + e.getMessage()); - log.error("Health Check: Produce Message Failure.", e); - return healthCheckResult; - } finally { - producer.close(); - } - - KafkaConsumer consumer = kafkaUtils.createNewConsumer(); - TopicPartition topicPartition = new TopicPartition(healthCheckTopic, partitionId); - consumer.assign(Arrays.asList(topicPartition)); - consumer.seek(topicPartition, offset); - int retries = 30; - int noRecordsCount = 0; - while (true) { - final ConsumerRecords consumerRecords = consumer.poll(1000); - if (consumerRecords.count() == 0) { - noRecordsCount++; - if (noRecordsCount > retries) { - break; - } else { - continue; - } - } - Iterator> iterator = consumerRecords.iterator(); - while (iterator.hasNext()) { - ConsumerRecord msg = iterator.next(); - log.info("Health Check: Fetch Message " + msg.value() + ", offset:" + msg.offset()); - if (msg.value().equals(message)) { - healthCheckResult.setStatus("ok"); - healthCheckResult.setMsg(message); - return healthCheckResult; - } - } - consumer.commitAsync(); - } - consumer.close(); - - if (healthCheckResult.getStatus() == null) { - healthCheckResult.setStatus("error"); - healthCheckResult.setMsg( - "Health Check: Consume Message Failure. Consumer can't fetch the message."); - } - return healthCheckResult; - } -} +package org.gnuhpc.bigdata.service; + +import static java.lang.String.format; +import static java.util.stream.Collectors.toList; +import static java.util.stream.Collectors.toMap; +import static java.util.stream.Collectors.toSet; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.module.scala.DefaultScalaModule; +import com.google.common.collect.ImmutableMap; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; +import java.util.function.Function; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import javax.annotation.PostConstruct; +import kafka.admin.AdminClient; +import kafka.admin.AdminClient.ConsumerGroupSummary; +import kafka.admin.AdminClient.ConsumerSummary; +import kafka.admin.AdminUtils; +import kafka.admin.ConsumerGroupCommand; +import kafka.admin.ReassignPartitionsCommand; +import kafka.admin.ReassignPartitionsCommand.Throttle; +import kafka.api.PartitionOffsetRequestInfo; +import kafka.cluster.Broker; +import kafka.common.TopicAndPartition; +import kafka.coordinator.group.GroupOverview; +import kafka.coordinator.group.GroupTopicPartition; +import kafka.javaapi.OffsetRequest; +import kafka.javaapi.OffsetResponse; +import kafka.javaapi.consumer.SimpleConsumer; +import kafka.log.LogConfig; +import kafka.server.ConfigType; +import kafka.utils.ZkUtils; +import kafka.zk.AdminZkClient; +import kafka.zk.KafkaZkClient; +import lombok.extern.log4j.Log4j; +import org.apache.avro.Schema; +import org.apache.avro.generic.GenericDatumReader; +import org.apache.avro.generic.GenericRecord; +import org.apache.avro.io.DatumReader; +import org.apache.avro.io.DecoderFactory; +import org.apache.curator.framework.CuratorFramework; +import org.apache.kafka.clients.admin.AdminClientConfig; +import org.apache.kafka.clients.admin.AlterConfigsResult; +import org.apache.kafka.clients.admin.Config; +import org.apache.kafka.clients.admin.ConfigEntry; +import org.apache.kafka.clients.admin.CreatePartitionsResult; +import org.apache.kafka.clients.admin.CreateTopicsOptions; +import org.apache.kafka.clients.admin.CreateTopicsResult; +import org.apache.kafka.clients.admin.DeleteTopicsResult; +import org.apache.kafka.clients.admin.DescribeClusterOptions; +import org.apache.kafka.clients.admin.DescribeClusterResult; +import org.apache.kafka.clients.admin.DescribeConfigsResult; +import org.apache.kafka.clients.admin.DescribeLogDirsOptions; +import org.apache.kafka.clients.admin.DescribeLogDirsResult; +import org.apache.kafka.clients.admin.DescribeReplicaLogDirsOptions; +import org.apache.kafka.clients.admin.DescribeReplicaLogDirsResult; +import org.apache.kafka.clients.admin.DescribeReplicaLogDirsResult.ReplicaLogDirInfo; +import org.apache.kafka.clients.admin.DescribeTopicsResult; +import org.apache.kafka.clients.admin.KafkaAdminClient; +import org.apache.kafka.clients.admin.ListTopicsOptions; +import org.apache.kafka.clients.admin.ListTopicsResult; +import org.apache.kafka.clients.admin.NewPartitions; +import org.apache.kafka.clients.admin.NewTopic; +import org.apache.kafka.clients.admin.TopicDescription; +import org.apache.kafka.clients.consumer.ConsumerRecord; +import org.apache.kafka.clients.consumer.ConsumerRecords; +import org.apache.kafka.clients.consumer.KafkaConsumer; +import org.apache.kafka.clients.consumer.OffsetAndTimestamp; +import org.apache.kafka.clients.producer.KafkaProducer; +import org.apache.kafka.clients.producer.ProducerRecord; +import org.apache.kafka.clients.producer.RecordMetadata; +import org.apache.kafka.common.KafkaFuture; +import org.apache.kafka.common.Node; +import org.apache.kafka.common.TopicPartition; +import org.apache.kafka.common.TopicPartitionInfo; +import org.apache.kafka.common.TopicPartitionReplica; +import org.apache.kafka.common.config.ConfigResource; +import org.apache.kafka.common.config.ConfigResource.Type; +import org.apache.kafka.common.errors.ApiException; +import org.apache.kafka.common.errors.InvalidTopicException; +import org.apache.kafka.common.internals.Topic; +import org.apache.kafka.common.protocol.Errors; +import org.apache.kafka.common.requests.DescribeLogDirsResponse.LogDirInfo; +import org.apache.kafka.common.serialization.Serdes; +import org.gnuhpc.bigdata.CollectionConvertor; +import org.gnuhpc.bigdata.componet.OffsetStorage; +import org.gnuhpc.bigdata.config.KafkaConfig; +import org.gnuhpc.bigdata.constant.ConsumerGroupState; +import org.gnuhpc.bigdata.constant.ConsumerType; +import org.gnuhpc.bigdata.constant.GeneralResponseState; +import org.gnuhpc.bigdata.constant.ReassignmentState; +import org.gnuhpc.bigdata.model.AddPartition; +import org.gnuhpc.bigdata.model.BrokerInfo; +import org.gnuhpc.bigdata.model.ClusterInfo; +import org.gnuhpc.bigdata.model.ConsumerGroupDesc; +import org.gnuhpc.bigdata.model.ConsumerGroupMeta; +import org.gnuhpc.bigdata.model.CustomConfigEntry; +import org.gnuhpc.bigdata.model.CustomTopicPartitionInfo; +import org.gnuhpc.bigdata.model.GeneralResponse; +import org.gnuhpc.bigdata.model.HealthCheckResult; +import org.gnuhpc.bigdata.model.MemberDescription; +import org.gnuhpc.bigdata.model.PartitionAssignmentState; +import org.gnuhpc.bigdata.model.ReassignModel; +import org.gnuhpc.bigdata.model.ReassignStatus; +import org.gnuhpc.bigdata.model.ReassignWrapper; +import org.gnuhpc.bigdata.model.Record; +import org.gnuhpc.bigdata.model.TopicBrief; +import org.gnuhpc.bigdata.model.TopicDetail; +import org.gnuhpc.bigdata.model.TopicMeta; +import org.gnuhpc.bigdata.model.TwoTuple; +import org.gnuhpc.bigdata.utils.KafkaUtils; +import org.gnuhpc.bigdata.utils.ZookeeperUtils; +import org.gnuhpc.bigdata.validator.ConsumerGroupExistConstraint; +import org.gnuhpc.bigdata.validator.TopicExistConstraint; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; +import scala.Function0; +import scala.Tuple2; +import scala.collection.JavaConverters; +import scala.collection.Seq; +import scala.collection.mutable.ListBuffer; +import scala.runtime.AbstractFunction0; +import scala.runtime.BoxedUnit; + +/** Created by gnuhpc on 2017/7/17. */ +@Service +@Log4j +@Validated +public class KafkaAdminService { + + private static final int channelSocketTimeoutMs = 600; + private static final int channelRetryBackoffMs = 600; + private static final long kafkaAdminClientGetTimeoutMs = 120000; + private static final long kafkaAdminClientAlterTimeoutMs = 120000; + private static final String CONSUMERPATHPREFIX = "/consumers/"; + private static final String OFFSETSPATHPREFIX = "/offsets/"; + public static final String LeaderReplicationThrottledRateProp = + "leader.replication.throttled.rate"; + public static final String FollowerReplicationThrottledRateProp = + "follower.replication.throttled.rate"; + public static final String ReplicaAlterLogDirsIoMaxBytesPerSecondProp = + "replica.alter.log.dirs.io.max.bytes.per.second"; + + @Autowired private ZookeeperUtils zookeeperUtils; + + @Autowired private KafkaUtils kafkaUtils; + + @Autowired private KafkaConfig kafkaConfig; + + @Autowired private OffsetStorage storage; + + // For AdminUtils use + private ZkUtils zkUtils; + + private org.apache.kafka.clients.admin.AdminClient kafkaAdminClient = null; + + private AdminClient oldAdminClient = null; + + private scala.Option none = scala.Option.apply(null); + + @PostConstruct + private void init() {} + + private org.apache.kafka.clients.admin.AdminClient createKafkaAdminClient() { + if (this.kafkaAdminClient == null) { + Properties adminClientProp = new Properties(); + adminClientProp.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaConfig.getBrokers()); + this.kafkaAdminClient = KafkaAdminClient.create(adminClientProp); + } + + return this.kafkaAdminClient; + } + + public HashMap createTopic(List topicList) { + List newTopicList = new ArrayList<>(); + HashMap createResults = new HashMap<>(); + + for (TopicDetail topic : topicList) { + NewTopic newTopic; + Map> replicasAssignments = topic.getReplicasAssignments(); + + try { + Topic.validate(topic.getName()); + + if (Topic.hasCollisionChars(topic.getName())) { + throw new InvalidTopicException("Invalid topic name, it contains '.' or '_'."); + } + } catch (Exception exception) { + GeneralResponse generalResponse = + GeneralResponse.builder() + .state(GeneralResponseState.failure) + .msg(exception.getMessage()) + .build(); + createResults.put(topic.getName(), generalResponse); + continue; + } + + if (replicasAssignments != null && !replicasAssignments.isEmpty()) { + newTopic = new NewTopic(topic.getName(), replicasAssignments); + } else { + newTopic = new NewTopic(topic.getName(), topic.getPartitions(), (short) topic.getFactor()); + } + + if (topic.getProp() != null) { + newTopic.configs((Map) topic.getProp()); + } + + newTopicList.add(newTopic); + } + + org.apache.kafka.clients.admin.AdminClient kafkaAdminClient = createKafkaAdminClient(); + + CreateTopicsOptions createTopicsOptions = new CreateTopicsOptions(); + createTopicsOptions.timeoutMs((int) kafkaAdminClientAlterTimeoutMs); + CreateTopicsResult createTopicsResult = + kafkaAdminClient.createTopics(newTopicList, createTopicsOptions); + + try { + createTopicsResult.all().get(kafkaAdminClientGetTimeoutMs, TimeUnit.MILLISECONDS); + } catch (Exception exception) { + log.warn("Create topic exception:" + exception); + } finally { + createTopicsResult + .values() + .forEach( + (topicName, result) -> { + GeneralResponse generalResponse; + if (result.isDone() && !result.isCompletedExceptionally()) { + TopicMeta topicMeta = describeTopic(topicName); + generalResponse = + GeneralResponse.builder() + .state(GeneralResponseState.success) + .data(topicMeta) + .build(); + } else { + generalResponse = + GeneralResponse.builder() + .state(GeneralResponseState.failure) + .msg(result.toString()) + .build(); + } + createResults.put(topicName, generalResponse); + }); + } + + return createResults; + } + + public List listTopics() { + List topicNamesList = new ArrayList(); + topicNamesList.addAll(getAllTopics()); + + return topicNamesList; + } + + public Set getAllTopics() { + Set topicNames; + org.apache.kafka.clients.admin.AdminClient kafkaAdminClient = createKafkaAdminClient(); + ListTopicsOptions options = new ListTopicsOptions(); + // includes internal topics such as __consumer_offsets + options.listInternal(true); + + ListTopicsResult topics = kafkaAdminClient.listTopics(options); + try { + topicNames = topics.names().get(kafkaAdminClientGetTimeoutMs, TimeUnit.MILLISECONDS); + log.info("Current topics in this cluster: " + topicNames); + } catch (Exception exception) { + log.warn("List topic exception : " + exception); + throw new ApiException("List topic exception : " + exception); + } + + return topicNames; + } + + public List listTopicBrief() { + org.apache.kafka.clients.admin.AdminClient kafkaAdminClient = createKafkaAdminClient(); + + DescribeTopicsResult describeTopicsResult = kafkaAdminClient.describeTopics(listTopics()); + Map topicMap; + List result; + try { + topicMap = + describeTopicsResult.all().get(kafkaAdminClientGetTimeoutMs, TimeUnit.MILLISECONDS); + result = + topicMap + .entrySet() + .parallelStream() + .map( + e -> { + String topic = e.getKey(); + TopicDescription topicDescription = e.getValue(); + List topicPartitionInfoList = + topicDescription.partitions(); + int replicateCount = 0; + int isrCount = 0; + for (org.apache.kafka.common.TopicPartitionInfo topicPartitionInfo : + topicPartitionInfoList) { + replicateCount += topicPartitionInfo.replicas().size(); + isrCount += topicPartitionInfo.isr().size(); + } + if (replicateCount == 0) { + return new TopicBrief(topic, topicDescription.partitions().size(), 0); + } else { + return new TopicBrief( + topic, + topicDescription.partitions().size(), + ((double) isrCount / replicateCount)); + } + }) + .collect(toList()); + } catch (Exception exception) { + log.warn("Describe all topics exception:" + exception); + throw new ApiException("Describe all topics exception:" + exception); + } + + return result; + } + + public boolean existTopic(String topicName) { + KafkaZkClient kafkaZkClient = zookeeperUtils.createKafkaZkClient(); + boolean exists = kafkaZkClient.topicExists(topicName); + + return exists; + } + + public ClusterInfo describeCluster() { + // Map clusterDetail = new HashMap<>(); + ClusterInfo clusterInfo = new ClusterInfo(); + + org.apache.kafka.clients.admin.AdminClient kafkaAdminClient = createKafkaAdminClient(); + DescribeClusterOptions describeClusterOptions = + new DescribeClusterOptions().timeoutMs((int) kafkaAdminClientGetTimeoutMs); + + DescribeClusterResult describeClusterResult = + kafkaAdminClient.describeCluster(describeClusterOptions); + + KafkaFuture clusterIdFuture = describeClusterResult.clusterId(); + KafkaFuture controllerFuture = describeClusterResult.controller(); + KafkaFuture> nodesFuture = describeClusterResult.nodes(); + String clusterId = ""; + Node controller = null; + Collection nodes = new ArrayList<>(); + + try { + clusterId = clusterIdFuture.get(kafkaAdminClientGetTimeoutMs, TimeUnit.MILLISECONDS); + controller = controllerFuture.get(kafkaAdminClientGetTimeoutMs, TimeUnit.MILLISECONDS); + nodes = nodesFuture.get(kafkaAdminClientGetTimeoutMs, TimeUnit.MILLISECONDS); + } catch (Exception exception) { + log.warn("Describe cluster exception:" + exception); + throw new ApiException("Describe cluster exception:" + exception); + } finally { + if (clusterIdFuture.isDone() && !clusterIdFuture.isCompletedExceptionally()) { + // clusterDetail.put("clusterId", clusterId); + clusterInfo.setClusterId(clusterId); + } + if (controllerFuture.isDone() && !controllerFuture.isCompletedExceptionally()) { + // clusterDetail.put("controllerId", controller); + clusterInfo.setController(controller); + } + if (nodesFuture.isDone() && !nodesFuture.isCompletedExceptionally()) { + // clusterDetail.put("nodes", nodes); + clusterInfo.setNodes(nodes); + } + } + + return clusterInfo; + } + + public List listBrokers() { + CuratorFramework zkClient = zookeeperUtils.createZkClient(); + KafkaZkClient kafkaZkClient = zookeeperUtils.createKafkaZkClient(); + List brokerList = + CollectionConvertor.seqConvertJavaList(kafkaZkClient.getAllBrokersInCluster()); + + List brokerInfoList = + brokerList + .parallelStream() + .collect(Collectors.toMap(Broker::id, Broker::rack)) + .entrySet() + .parallelStream() + .map( + entry -> { + String brokerInfoStr = null; + try { + // TODO replace zkClient with kafkaZKClient + brokerInfoStr = + new String( + zkClient + .getData() + .forPath(ZkUtils.BrokerIdsPath() + "/" + entry.getKey())); + } catch (Exception e) { + e.printStackTrace(); + } + BrokerInfo brokerInfo; + try { + ObjectMapper objectMapper = new ObjectMapper(); + brokerInfo = objectMapper.readValue(brokerInfoStr, BrokerInfo.class); + } catch (Exception exception) { + throw new ApiException("List broker exception." + exception); + } + if (entry.getValue().isEmpty()) { + brokerInfo.setRack(""); + } else { + brokerInfo.setRack(entry.getValue().get()); + } + brokerInfo.setId(entry.getKey()); + return brokerInfo; + }) + .collect(toList()); + + return brokerInfoList; + } + + public Node getController() { + return describeCluster().getController(); + } + + public Map> listLogDirsByBroker(List brokerList) { + Map> logDirList = new HashMap<>(); + + Map> logDirInfosByBroker = + describeLogDirsByBrokerAndTopic(brokerList, null,null); + logDirInfosByBroker + .entrySet() + .forEach( + e -> { + List dirList = e.getValue().keySet().stream().collect(Collectors.toList()); + Collections.sort(dirList); + logDirList.put(e.getKey(), dirList); + }); + + return logDirList; + } + + public Map> describeLogDirsByBrokerAndTopic( + List brokerList, List logDirList, List topicList) { + org.apache.kafka.clients.admin.AdminClient kafkaAdminClient = createKafkaAdminClient(); + + List brokerIdsInCluster = + listBrokers().stream().map(brokerInfo -> brokerInfo.getId()).collect(Collectors.toList()); + + if (brokerList == null || brokerList.isEmpty()) { + brokerList = brokerIdsInCluster; + } else { + for (int brokerId : brokerList) { + if (!brokerIdsInCluster.contains(brokerId)) { + throw new ApiException("Bad Request. Broker Id:" + brokerId + " non-exist."); + } + } + } + // Delete reason: we want to delete the topic data that no longer found on zk, we can still use + // this + // function to get the topic log dir + // if (topicList != null && !topicList.isEmpty()) { + // for (String topic : topicList) { + // if (!existTopic(topic)) { + // throw new ApiException("Bad Request. Topic:" + topic + " non-exist."); + // } + // } + // } + + DescribeLogDirsOptions describeLogDirsOptions = + new DescribeLogDirsOptions().timeoutMs((int) kafkaAdminClientGetTimeoutMs); + DescribeLogDirsResult describeLogDirsResult = + kafkaAdminClient.describeLogDirs(brokerList, describeLogDirsOptions); + Map> logDirInfosByBroker = new HashMap<>(); + + try { + logDirInfosByBroker = + describeLogDirsResult.all().get(kafkaAdminClientGetTimeoutMs, TimeUnit.MILLISECONDS); + } catch (Exception exception) { + log.warn("Describe log dirs exception:" + exception); + throw new ApiException("Describe log dirs exception:" + exception); + } finally { + if (logDirList != null && !logDirList.isEmpty()) { + logDirInfosByBroker.entrySet().forEach(e->{ + e.getValue().entrySet().removeIf(m -> !logDirList.contains(m.getKey())); + }); + } + if (topicList != null && !topicList.isEmpty()) { + logDirInfosByBroker + .entrySet() + .forEach( + e -> { + e.getValue() + .entrySet() + .forEach( + m -> { + m.getValue() + .replicaInfos + .entrySet() + .removeIf(t -> !topicList.contains(t.getKey().topic())); + }); + }); + } + } + + return logDirInfosByBroker; + } + + public Map describeReplicaLogDirs( + List replicas) { + org.apache.kafka.clients.admin.AdminClient kafkaAdminClient = createKafkaAdminClient(); + Map replicaLogDirInfoMap; + + DescribeReplicaLogDirsOptions describeReplicaLogDirsOptions = + new DescribeReplicaLogDirsOptions().timeoutMs((int) kafkaAdminClientGetTimeoutMs); + DescribeReplicaLogDirsResult describeReplicaLogDirsResult = + kafkaAdminClient.describeReplicaLogDirs(replicas, describeReplicaLogDirsOptions); + + try { + replicaLogDirInfoMap = + describeReplicaLogDirsResult + .all() + .get(kafkaAdminClientGetTimeoutMs, TimeUnit.MILLISECONDS); + } catch (Exception exception) { + log.warn("Describe replica log dirs exception:" + exception); + throw new ApiException("Describe replica log dirs exception:" + exception); + } + + return replicaLogDirInfoMap; + } + + public Collection getBrokerConf(int brokerId) { + String broker = String.valueOf(brokerId); + Properties dynamicProps = getConfigInZk(Type.BROKER, broker); + + Collection configs = describeConfig(Type.BROKER, String.valueOf(brokerId)); + + return mergeConfigs(configs, dynamicProps); + } + + public Properties getConfigInZk(ConfigResource.Type type, String name) { + KafkaZkClient kafkaZkClient = zookeeperUtils.createKafkaZkClient(); + AdminZkClient adminZkClient = new AdminZkClient(kafkaZkClient); + Properties properties = new Properties(); + + if (type.equals(Type.BROKER)) { + properties = adminZkClient.fetchEntityConfig(ConfigType.Broker(), name); + } else if (type.equals(Type.TOPIC)) { + properties = adminZkClient.fetchEntityConfig(ConfigType.Topic(), name); + } + + return properties; + } + + private Collection mergeConfigs( + Collection configs, Properties dynamicProps) { + Collection mergedConfigs = new ArrayList<>(); + CustomConfigEntry newConfigEntry; + + for (ConfigEntry entry : configs) { + String key = entry.name(); + String value = entry.value(); + if (dynamicProps.containsKey(key)) { + value = dynamicProps.getProperty(key); + } + newConfigEntry = + new CustomConfigEntry( + key, value, entry.isSensitive(), entry.isReadOnly(), entry.source()); + mergedConfigs.add(newConfigEntry); + } + + return mergedConfigs; + } + + private void validateConfigs( + Type type, String name, Properties props, List keysToBeChecked) { + for (Object configKey : keysToBeChecked) { + if (!props.containsKey(configKey)) { + throw new ApiException(type.name() + ":" + name + " has no such property:" + configKey); + } + } + } + + public Properties updateBrokerDynConf(int brokerId, Properties propsToBeUpdated) { + Properties props = getConfigInZk(Type.BROKER, String.valueOf(brokerId)); + KafkaZkClient kafkaZkClient = zookeeperUtils.createKafkaZkClient(); + AdminZkClient adminZkClient = new AdminZkClient(kafkaZkClient); + + for (String key : propsToBeUpdated.stringPropertyNames()) { + if (props.containsKey(key)) { + props.setProperty(key, propsToBeUpdated.getProperty(key)); + } else { + props.put(key, propsToBeUpdated.getProperty(key)); + } + } + + adminZkClient.changeBrokerConfig( + JavaConverters.asScalaBufferConverter(Collections.singletonList((Object) brokerId)) + .asScala() + .toSeq(), + props); + + return getConfigInZk(Type.BROKER, String.valueOf(brokerId)); + } + + public void removeConfigInZk(Type type, String name, List configKeysToBeRemoved) { + KafkaZkClient kafkaZkClient = zookeeperUtils.createKafkaZkClient(); + AdminZkClient adminZkClient = new AdminZkClient(kafkaZkClient); + + Properties props = getConfigInZk(type, name); + + validateConfigs(Type.BROKER, String.valueOf(name), props, configKeysToBeRemoved); + + props.entrySet().removeIf(entry -> configKeysToBeRemoved.contains(entry.getKey())); + + if (type.equals(Type.BROKER)) { + int brokerId = Integer.parseInt(name); + adminZkClient.changeBrokerConfig( + JavaConverters.asScalaBufferConverter(Collections.singletonList((Object) brokerId)) + .asScala() + .toSeq(), + props); + } else if (type.equals(Type.TOPIC)) { + adminZkClient.changeTopicConfig(name, props); + } + } + + public TopicDescription getTopicDescription(@TopicExistConstraint String topicName) { + TopicDescription topicDescription = null; + org.apache.kafka.clients.admin.AdminClient kafkaAdminClient = createKafkaAdminClient(); + + DescribeTopicsResult describeTopicsResult = + kafkaAdminClient.describeTopics(Collections.singletonList(topicName)); + try { + Map topicMap = + describeTopicsResult.all().get(kafkaAdminClientGetTimeoutMs, TimeUnit.MILLISECONDS); + if (topicMap.containsKey(topicName)) { + topicDescription = topicMap.get(topicName); + } + } catch (Exception exception) { + log.info("Get topic description exception:" + exception); + } + return topicDescription; + } + + public TopicMeta describeTopic(@TopicExistConstraint String topicName) { +// org.apache.kafka.clients.admin.AdminClient kafkaAdminClient = createKafkaAdminClient(); +// +// DescribeTopicsResult describeTopicsResult = +// kafkaAdminClient.describeTopics(Collections.singletonList(topicName)); + TopicMeta topicMeta = new TopicMeta(topicName); +// try { +// Map topicMap = +// describeTopicsResult.all().get(kafkaAdminClientGetTimeoutMs, TimeUnit.MILLISECONDS); +// if (topicMap.containsKey(topicName)) { +// TopicDescription topicDescription = topicMap.get(topicName); + TopicDescription topicDescription = getTopicDescription(topicName); + if (topicDescription != null) { + List tmList = topicDescription.partitions(); + topicMeta.setInternal(topicDescription.isInternal()); + topicMeta.setPartitionCount(topicDescription.partitions().size()); + topicMeta.setReplicationFactor(tmList.get(0).replicas().size()); + topicMeta.setTopicPartitionInfos( + tmList + .parallelStream() + .map( + tm -> { + CustomTopicPartitionInfo customTopicPartitionInfo = + new CustomTopicPartitionInfo(); + customTopicPartitionInfo.setTopicPartitionInfo(tm); + customTopicPartitionInfo.setIn_sync(); + if (tm.leader() != null) { + customTopicPartitionInfo.setStartOffset( + getBeginningOffset(topicName, tm.partition())); + customTopicPartitionInfo.setEndOffset( + getEndOffset(topicName, tm.partition())); + } else { + customTopicPartitionInfo.setStartOffset(-1); + customTopicPartitionInfo.setEndOffset(-1); + } + customTopicPartitionInfo.setMessageAvailable(); + return customTopicPartitionInfo; + }) + .collect(toList())); + Collections.sort(topicMeta.getTopicPartitionInfos()); + } +// } +// } catch (Exception exception) { +// exception.printStackTrace(); +// throw new ApiException("Describe topic exception." + exception); +// } + + return topicMeta; + } + + public Map deleteTopicList(List topicList) { + org.apache.kafka.clients.admin.AdminClient kafkaAdminClient = createKafkaAdminClient(); + HashMap deleteResults = new HashMap<>(); + + List topicListToBeDeleted = new ArrayList<>(topicList); + + log.warn("Delete topic " + topicList); + for (int i = 0; i < topicList.size(); i++) { + String topic = topicList.get(i); + try { + if (Topic.isInternal(topic)) { + throw new ApiException( + "Topic " + topic + " is a kafka internal topic and is not allowed to be deleted."); + } + if (!existTopic(topic)) { + throw new ApiException("Topic " + topic + " non-exists."); + } + } catch (Exception exception) { + topicListToBeDeleted.remove(topic); + GeneralResponse generalResponse = + GeneralResponse.builder() + .state(GeneralResponseState.failure) + .msg(exception.getMessage()) + .build(); + deleteResults.put(topic, generalResponse); + } + } + + DeleteTopicsResult deleteTopicsResult = kafkaAdminClient.deleteTopics(topicListToBeDeleted); + try { + deleteTopicsResult.all().get(kafkaAdminClientGetTimeoutMs, TimeUnit.MILLISECONDS); + } catch (Exception exception) { + log.warn("Delete topic exception:" + exception); + } finally { + deleteTopicsResult + .values() + .forEach( + (topic, result) -> { + GeneralResponse generalResponse; + if (result.isDone() && !result.isCompletedExceptionally()) { + generalResponse = + GeneralResponse.builder().state(GeneralResponseState.success).build(); + } else { + generalResponse = + GeneralResponse.builder() + .state(GeneralResponseState.failure) + .msg(result.toString()) + .build(); + } + deleteResults.put(topic, generalResponse); + }); + } + + return deleteResults; + } + + public Collection describeConfig(ConfigResource.Type type, String name) { + Map configs; + org.apache.kafka.clients.admin.AdminClient kafkaAdminClient = createKafkaAdminClient(); + ConfigResource configResource = new ConfigResource(type, name); + + DescribeConfigsResult ret = + kafkaAdminClient.describeConfigs(Collections.singleton(configResource)); + try { + configs = ret.all().get(kafkaAdminClientGetTimeoutMs, TimeUnit.MILLISECONDS); + } catch (Exception exception) { + log.warn("Describe config type:" + type + ", name:" + name + " exception:" + exception); + throw new ApiException("Describe config exception:" + exception.getLocalizedMessage()); + } + + return configs.get(configResource).entries(); + } + + public boolean alterConfig( + ConfigResource.Type type, String name, Collection configEntries) { + org.apache.kafka.clients.admin.AdminClient kafkaAdminClient = createKafkaAdminClient(); + Config config = new Config(configEntries); + AlterConfigsResult alterConfigsResult = + kafkaAdminClient.alterConfigs( + Collections.singletonMap(new ConfigResource(type, name), config)); + + try { + alterConfigsResult.all().get(kafkaAdminClientAlterTimeoutMs, TimeUnit.MILLISECONDS); + } catch (Exception exception) { + log.warn("Alter config type:" + type + ", name:" + name + " exception:" + exception); + return false; + } + + return true; + } + + public Collection updateTopicConf( + @TopicExistConstraint String topic, Properties props) { + Collection configEntries = + props + .entrySet() + .stream() + .map(e -> new ConfigEntry(e.getKey().toString(), e.getValue().toString())) + .collect(Collectors.toList()); + alterConfig(ConfigResource.Type.TOPIC, topic, configEntries); + + return getTopicConf(topic); + } + + public Collection getTopicConf(@TopicExistConstraint String topic) { + Properties dynamicProps = getConfigInZk(Type.TOPIC, topic); + + Collection configs = describeConfig(Type.TOPIC, String.valueOf(topic)); + + return mergeConfigs(configs, dynamicProps); + } + + public Properties getTopicConfByKey(@TopicExistConstraint String topic, String key) { + Collection configEntries = describeConfig(ConfigResource.Type.TOPIC, topic); + Properties returnProps = new Properties(); + for (ConfigEntry entry : configEntries) { + if (entry.name().equals(key)) { + returnProps.put(key, entry.value()); + return returnProps; + } + } + + return null; + } + + public Collection updateTopicConfByKey( + @TopicExistConstraint String topic, String key, String value) { + alterConfig( + ConfigResource.Type.TOPIC, topic, Collections.singletonList(new ConfigEntry(key, value))); + + return getTopicConf(topic); + } + + public Map> listAllConsumerGroups(ConsumerType type) { + Map> result = new HashMap<>(); + + if (type == null || type == ConsumerType.OLD) { + Set oldConsumerGroupList = listAllOldConsumerGroups(); + if (oldConsumerGroupList == null || oldConsumerGroupList.size() == 0) { + result.put("old", new HashSet<>()); + } else { + result.put("old", oldConsumerGroupList); + } + } + + if (type == null || type == ConsumerType.NEW) { + Set newConsumerGroupList = listAllNewConsumerGroups(); + if (newConsumerGroupList == null || newConsumerGroupList.size() == 0) { + result.put("new", new HashSet<>()); + } else { + result.put("new", newConsumerGroupList); + } + } + + return result; + } + + private Set listAllNewConsumerGroups() { + AdminClient adminClient = kafkaUtils.createAdminClient(); + log.info("Calling the listAllConsumerGroupsFlattened"); + // Send LIST_GROUPS Request to kafka + Set activeGroups = + CollectionConvertor.seqConvertJavaList(adminClient.listAllConsumerGroupsFlattened()) + .stream() + .map(GroupOverview::groupId) + .collect(toSet()); + + log.info("Finish getting new consumers"); + adminClient.close(); + return activeGroups; + } + + private Set listAllOldConsumerGroups() { + log.info("Finish getting old consumers"); + KafkaZkClient kafkaZkClient = zookeeperUtils.createKafkaZkClient(); + + Set oldConsumerGroups = + CollectionConvertor.seqConvertJavaList(kafkaZkClient.getChildren(ZkUtils.ConsumersPath())) + .stream() + .collect(toSet()); + + return oldConsumerGroups; + } + + public Map> listConsumerGroupsByTopic( + @TopicExistConstraint String topic, ConsumerType type) { + Map> result = new HashMap<>(); + + if (type == null || type == ConsumerType.OLD) { + Set oldConsumerGroupList = new HashSet<>(); + try { + oldConsumerGroupList = listOldConsumerGroupsByTopic(topic); + } catch (Exception e) { + e.printStackTrace(); + } + if (oldConsumerGroupList.size() != 0) { + result.put("old", oldConsumerGroupList); + } + } + + if (type == null || type == ConsumerType.NEW) { + Set newConsumerGroupList = listNewConsumerGroupsByTopic(topic); + + if (newConsumerGroupList.size() != 0) { + result.put("new", newConsumerGroupList); + } + } + + return result; + } + + private Set listNewConsumerGroupsByTopic(@TopicExistConstraint String topic) { + Set result = new HashSet(); + Set consumersList = listAllNewConsumerGroups(); + + for (String c : consumersList) { + List topics = getTopicListByConsumerGroup(c); + if (topics.contains(topic)) { + result.add(c); + } + } + + return result; + } + + private Set listOldConsumerGroupsByTopic(@TopicExistConstraint String topic) { + KafkaZkClient kafkaZkClient = zookeeperUtils.createKafkaZkClient(); + + List consumersFromZk = + CollectionConvertor.seqConvertJavaList(kafkaZkClient.getChildren(ZkUtils.ConsumersPath())); + Set consumerList = new HashSet<>(); + + for (String consumer : consumersFromZk) { + String path = ZkUtils.ConsumersPath() + "/" + consumer + "/offsets"; + List topics = CollectionConvertor.seqConvertJavaList(kafkaZkClient.getChildren(path)); + if (topics != null && topics.contains(topic)) { + consumerList.add(consumer); + } + } + + return consumerList; + } + + public Set listTopicsByConsumerGroup(String consumerGroup, ConsumerType type) { + KafkaZkClient kafkaZkClient = zookeeperUtils.createKafkaZkClient(); + Set topicList = new HashSet<>(); + + if (type == null) { + throw new ApiException("Bad Request since type is null."); + } + + if (type == ConsumerType.OLD) { + if (!isOldConsumerGroup(consumerGroup)) { + throw new ApiException("Consumer group:" + consumerGroup + " non-exist"); + } + String path = ZkUtils.ConsumersPath() + "/" + consumerGroup + "/offsets"; + topicList = + CollectionConvertor.seqConvertJavaList(kafkaZkClient.getChildren(path)) + .stream() + .collect(toSet()); + } else if (type == ConsumerType.NEW) { + if (!isNewConsumerGroup(consumerGroup)) { + throw new ApiException("Consumer group:" + consumerGroup + " non-exist!"); + } + topicList.addAll(getTopicListByConsumerGroup(consumerGroup)); + } else { + throw new ApiException("Unknown Type " + type); + } + + return topicList; + } + + private List getTopicListByConsumerGroup(String consumerGroup) { + AdminClient adminClient = kafkaUtils.createAdminClient(); + Map groupOffsets = + CollectionConvertor.mapConvertJavaMap(adminClient.listGroupOffsets(consumerGroup)); + + return groupOffsets + .entrySet() + .stream() + .map(topicPartitionObjectEntry -> topicPartitionObjectEntry.getKey().topic()) + .collect(toList()); + } + + public ConsumerGroupMeta getConsumerGroupMeta(String consumerGroup) { + List members = new ArrayList<>(); + AdminClient adminClient = kafkaUtils.createAdminClient(); + + ConsumerGroupSummary consumerGroupSummary = adminClient.describeConsumerGroup(consumerGroup, 0); + List consumerSummaryList = + CollectionConvertor.optionListConvertJavaList(consumerGroupSummary.consumers().get()); + + ConsumerGroupMeta consumerGroupMeta = + ConsumerGroupMeta.builder() + .groupId(consumerGroup) + .state(ConsumerGroupState.parse(consumerGroupSummary.state())) + .assignmentStrategy(consumerGroupSummary.assignmentStrategy()) + .coordinator(consumerGroupSummary.coordinator()) + .build(); + + consumerSummaryList.forEach( + consumerSummary -> { + List topicPartitions = + CollectionConvertor.listConvertJavaList(consumerSummary.assignment()); + members.add( + new MemberDescription( + consumerSummary.consumerId(), + consumerSummary.clientId(), + consumerSummary.host(), + topicPartitions)); + }); + + Collections.sort(members); + consumerGroupMeta.setMembers(members); + + adminClient.close(); + return consumerGroupMeta; + } + + public boolean isOldConsumerGroup(String consumerGroup) { + return listAllOldConsumerGroups().contains(consumerGroup); + } + + public boolean isNewConsumerGroup(String consumerGroup) { + // Active Consumergroup or Dead ConsumerGroup is OK + return (listAllNewConsumerGroups().contains(consumerGroup)); + } + + public Map> describeConsumerGroup( + String consumerGroup, ConsumerType type) { + Map> result = new HashMap<>(); + Set topicList = listTopicsByConsumerGroup(consumerGroup, type); + if (topicList == null) { + // Return empty result + return result; + } + if (type == ConsumerType.NEW) { + if (!isNewConsumerGroup(consumerGroup)) { + throw new ApiException("Consumer group:" + consumerGroup + " non-exist!"); + } + List partitionAssignmentStateList = + describeNewConsumerGroup(consumerGroup, false, null); + result = convertPasListToMap(consumerGroup, partitionAssignmentStateList, ConsumerType.NEW); + } else if (type == ConsumerType.OLD) { + if (!isOldConsumerGroup(consumerGroup)) { + throw new ApiException("Consumer group:" + consumerGroup + " non-exist"); + } + List partitionAssignmentStateList = + describeOldConsumerGroup(consumerGroup, false, null); + result = convertPasListToMap(consumerGroup, partitionAssignmentStateList, ConsumerType.OLD); + } + + return result; + } + + // Convert partition assignment to map, key is topic + private Map> convertPasListToMap( + String consumerGroup, List pasList, ConsumerType type) { + Map> result = new HashMap<>(); + ConsumerGroupSummary consumerGroupSummary; + + if (type.equals(ConsumerType.NEW)) { + AdminClient adminClient = kafkaUtils.createAdminClient(); + consumerGroupSummary = adminClient.describeConsumerGroup(consumerGroup, 0); + adminClient.close(); + } else { + // Old consumer group has no state, coordinator, assignmentStrategy info + consumerGroupSummary = null; + } + + pasList.forEach( + partitionAssignmentState -> { + String topic = partitionAssignmentState.getTopic(); + List consumerGroupDescs; + if (result.containsKey(topic)) { + consumerGroupDescs = result.get(topic); + } else { + consumerGroupDescs = new ArrayList<>(); + } + consumerGroupDescs.add( + convertParitionAssignmentStateToGroupDesc( + consumerGroup, consumerGroupSummary, partitionAssignmentState, type)); + result.put(topic, consumerGroupDescs); + }); + + return result; + } + + private ConsumerGroupDesc convertParitionAssignmentStateToGroupDesc( + String consumerGroup, + ConsumerGroupSummary consumerGroupSummary, + PartitionAssignmentState pas, + ConsumerType type) { + ConsumerGroupDesc.ConsumerGroupDescBuilder consumerGroupDescBuilder = + ConsumerGroupDesc.builder() + .groupName(consumerGroup) + .topic(pas.getTopic()) + .partitionId(pas.getPartition()) + .currentOffset(pas.getOffset()) + .logEndOffset(pas.getLogEndOffset()) + .lag(pas.getLag()) + .consumerId(pas.getConsumerId()) + .clientId(pas.getClientId()) + .host(pas.getHost()) + .type(type); + + if (consumerGroupSummary != null) { + consumerGroupDescBuilder = + consumerGroupDescBuilder + .state(ConsumerGroupState.parse(consumerGroupSummary.state())) + .assignmentStrategy(consumerGroupSummary.assignmentStrategy()) + .coordinator(consumerGroupSummary.coordinator()); + } + + return consumerGroupDescBuilder.build(); + } + + public List describeNewConsumerGroup( + String consumerGroup, boolean filtered, String topic) { + AdminClient adminClient = kafkaUtils.createAdminClient(); + ConsumerGroupSummary consumerGroupSummary = adminClient.describeConsumerGroup(consumerGroup, 0); + + adminClient.close(); + return describeNewConsumerGroup(consumerGroup, filtered, topic, consumerGroupSummary); + } + + public List describeNewConsumerGroup( + String consumerGroup, boolean filtered, String topic, ConsumerGroupSummary consumerGroupSummary) { + List partitionAssignmentStateList = new ArrayList<>(); + + if (filtered && !existTopic(topic)) { + return partitionAssignmentStateList; + } + + AdminClient adminClient = kafkaUtils.createAdminClient(); +// ConsumerGroupSummary consumerGroupSummary = adminClient.describeConsumerGroup(consumerGroup, 0); + List consumerSummaryList = + CollectionConvertor.listConvertJavaList(consumerGroupSummary.consumers().get()); + String consumersInfo = ""; + for (ConsumerSummary cs:consumerSummaryList) { + consumersInfo = consumersInfo + "{clientId:" + cs.clientId() + ", host:" + cs.host() + + ", consumerId:" + cs.consumerId() + "}\n"; + } + log.info("Describe consumer group:" + consumerGroup + " summary. ConsumerSummary List:" + consumersInfo); + if (consumerSummaryList != null) { + Map offsets = + CollectionConvertor.mapConvertJavaMap(adminClient.listGroupOffsets(consumerGroup)); + log.info("List group offsets for consumer:" + consumerGroup + ". Result is:\n"); + for(Map.Entry offset:offsets.entrySet()) { + log.info("Topic:" + offset.getKey().topic() + ", Partition:" + offset.getKey().partition() + ", Offset:" + offset.getValue()); + } + Map offsetsFiltered; + + adminClient.close(); + if (filtered && existTopic(topic)) { + offsetsFiltered = + offsets + .entrySet() + .stream() + .filter( + topicPartitionObjectEntry -> + topicPartitionObjectEntry.getKey().topic().equals(topic)) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + log.info("After filter by topic:" + topic + " for consumer:" + consumerGroup + ", offsets are:"); + for(Map.Entry offset:offsetsFiltered.entrySet()) { + log.info("Topic:" + offset.getKey().topic() + ", Partition:" + offset.getKey().partition() + ", Offset:" + offset.getValue()); + } + } else { + offsetsFiltered = offsets; + } + if (offsetsFiltered.isEmpty()) { + return partitionAssignmentStateList; + } else { + ArrayList assignedTopicPartitions = new ArrayList<>(); + List rowsWithConsumer = + consumerSummaryList + .stream() + .flatMap( + consumerSummary -> { + List topicPartitions = + CollectionConvertor.listConvertJavaList(consumerSummary.assignment()); + List topicPartitionsFiltered = topicPartitions; + if (filtered) { + topicPartitionsFiltered = + topicPartitions + .stream() + .filter(topicPartition -> topicPartition.topic().equals(topic)) + .collect(toList()); + } + Map partitionOffsets = new HashMap<>(); + log.info("topicPartitionsFiltered.size = " + topicPartitionsFiltered.size() + ". Detail is:"); + for (TopicPartition tp:topicPartitionsFiltered) { + log.info("topic:" + tp.topic() + ", partition:" + tp.partition() + ", offset:" + offsetsFiltered.get(tp)); + partitionOffsets.put(tp, offsetsFiltered.get(tp)); + } + assignedTopicPartitions.addAll(topicPartitionsFiltered); + /* + Map partitionOffsets = + topicPartitionsFiltered + .stream() + .collect( + toMap( + Function.identity(), + topicPartition -> offsetsFiltered.get(topicPartition))); + */ + log.info("Topic partitions with consumers. Detail is:"); + for(Map.Entry offset:partitionOffsets.entrySet()) { + log.info("Topic:" + offset.getKey().topic() + ", Partition:" + + offset.getKey().partition() + ", Offset:" + offset.getValue() + + ", consumerGroup:" + consumerGroup + ", consumerId:" + + consumerSummary.consumerId() + "," + ", consumerHost:" + + consumerSummary.host() + ", consumerClient:" + consumerSummary.clientId()); + } + return collectConsumerAssignment( + consumerGroup, + consumerGroupSummary.coordinator(), + topicPartitionsFiltered, + partitionOffsets, + consumerSummary.consumerId(), + consumerSummary.host(), + consumerSummary.clientId()) + .stream(); + }) + .collect(toList()); + + List topicPartitionsWithoutConsumer = new ArrayList<>(); + Map partitionOffsetsWithoutConsumer = new HashMap<>(); + + offsetsFiltered + .entrySet() + .forEach( + topicPartitionObjectEntry -> { + if (!assignedTopicPartitions.contains(topicPartitionObjectEntry.getKey())) { + topicPartitionsWithoutConsumer.add(topicPartitionObjectEntry.getKey()); + partitionOffsetsWithoutConsumer.put( + topicPartitionObjectEntry.getKey(), topicPartitionObjectEntry.getValue()); + } + }); + List rowsWithoutConsumer = + collectConsumerAssignment( + consumerGroup, + consumerGroupSummary.coordinator(), + topicPartitionsWithoutConsumer, + partitionOffsetsWithoutConsumer, + "-", + "-", + "-"); + if (rowsWithoutConsumer.size() > 0) { + log.info("Topic Partitions without consumer. Detail is :"); + for (PartitionAssignmentState pas : rowsWithoutConsumer) { + log.info("Topic:" + pas.getTopic() + ", Partition:" + pas.getPartition() + ", Offset:" + + pas.getOffset() + ", consumerGroup:" + consumerGroup + ", consumerId:" + + pas.getConsumerId() + "," + ", consumerHost:" + pas.getHost() + + ", consumerClient:" + pas.getClientId()); + } + } + partitionAssignmentStateList.addAll(rowsWithConsumer); + partitionAssignmentStateList.addAll(rowsWithoutConsumer); + log.info("rowsWithConsumer.size:" + rowsWithConsumer.size() + ", rowsWithoutConsumer.size:" + + rowsWithoutConsumer.size()); + } + } + + Collections.sort(partitionAssignmentStateList); + return partitionAssignmentStateList; + } + + private List collectConsumerAssignment( + String group, + Node coordinator, + List topicPartitions, + Map partitionOffsets, + String consumerId, + String host, + String clientId) { + if (topicPartitions.size() == 0) { + return new ArrayList(); + } else { + List list = new ArrayList<>(); + topicPartitions.forEach( + topicPartition -> { + long logEndOffset = getEndOffset(topicPartition.topic(), topicPartition.partition()); + long offset = (Long) partitionOffsets.get(topicPartition); + long lag = (logEndOffset < 0)?0:logEndOffset - offset; + list.add( + new PartitionAssignmentState( + group, + coordinator, + topicPartition.topic(), + topicPartition.partition(), + offset, + lag, + consumerId, + host, + clientId, + logEndOffset)); + }); + + Collections.sort(list); + return list; + } + } + + public List describeOldConsumerGroup( + String consumerGroup, boolean filtered, String topic) { + List partitionAssignmentStateList = new ArrayList<>(); + + if (filtered && !existTopic(topic)) { + return partitionAssignmentStateList; + } + + String[] agrs = { + "--describe", + "--zookeeper", + zookeeperUtils.getZookeeperConfig().getUris(), + "--group", + consumerGroup + }; + ConsumerGroupCommand.ConsumerGroupCommandOptions options = + new ConsumerGroupCommand.ConsumerGroupCommandOptions(agrs); + ConsumerGroupCommand.ZkConsumerGroupService zkConsumerGroupService = + new ConsumerGroupCommand.ZkConsumerGroupService(options); + + ObjectMapper mapper = new ObjectMapper(); + mapper.registerModule(new DefaultScalaModule()); + mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + try { + String source = + mapper.writeValueAsString(zkConsumerGroupService.collectGroupOffsets()._2().get()); + partitionAssignmentStateList = + mapper.readValue( + source, getCollectionType(mapper, List.class, PartitionAssignmentState.class)); + List partitionAssignmentStateListFiltered; + if (filtered && existTopic(topic)) { + partitionAssignmentStateListFiltered = + partitionAssignmentStateList + .stream() + .filter( + partitionAssignmentState -> partitionAssignmentState.getTopic().equals(topic)) + .collect(Collectors.toList()); + } else { + partitionAssignmentStateListFiltered = partitionAssignmentStateList; + } + partitionAssignmentStateListFiltered.sort( + Comparator.comparing(PartitionAssignmentState::getTopic) + .thenComparing(PartitionAssignmentState::getPartition)); + } catch (JsonProcessingException jsonProcessingException) { + log.error("Describe old consumer group exception.", jsonProcessingException); + } catch (IOException ioexception) { + log.error("Describe old consumer group exception.", ioexception); + } + + return partitionAssignmentStateList; + } + + private JavaType getCollectionType( + ObjectMapper mapper, Class collectionClass, Class... elementClasses) { + return mapper.getTypeFactory().constructParametricType(collectionClass, elementClasses); + } + + private List getNewConsumerGroupDescByConsumerGroupAndTopic(String consumerGroup, String topic) { + AdminClient adminClient = kafkaUtils.createAdminClient(); + ConsumerGroupSummary consumerGroupSummary = adminClient.describeConsumerGroup(consumerGroup, kafkaAdminClientGetTimeoutMs); + log.info("Describe consumer group:" + consumerGroup + ". Summary:" +consumerGroupSummary.consumers()); + List partitionAssignmentStateList = + describeNewConsumerGroup(consumerGroup, true, topic, consumerGroupSummary); + log.info("Partition Assignment State List for consumer:" + consumerGroup + " fitlered by topic:" + + topic + " size :" + partitionAssignmentStateList.size()); + + adminClient.close(); + + return partitionAssignmentStateList + .stream() + .map( + partitionAssignmentState -> + convertParitionAssignmentStateToGroupDesc( + consumerGroup, + consumerGroupSummary, + partitionAssignmentState, + ConsumerType.NEW)) + .collect(Collectors.toList()); + } + + public List describeNewConsumerGroupByTopic( + String consumerGroup, @TopicExistConstraint String topic) { + if (consumerGroup != null && !isNewConsumerGroup(consumerGroup)) { + throw new ApiException("ConsumerGroup:" + consumerGroup + " non-exist!"); + } + + List consumerGroupDescList = new ArrayList<>(); + if (consumerGroup == null || consumerGroup.length() == 0) { + // To search all consumer groups + Set allNewConsumerGroups = listAllNewConsumerGroups(); + + for (String cg:allNewConsumerGroups) { + consumerGroupDescList.addAll(getNewConsumerGroupDescByConsumerGroupAndTopic(cg, topic)); + } + return consumerGroupDescList; + } else { + return getNewConsumerGroupDescByConsumerGroupAndTopic(consumerGroup, topic); + } + } + + private List getOldConsumerGroupDescByConsumerGroupAndTopic(String consumerGroup, String topic) { + List partitionAssignmentStateList = + describeOldConsumerGroup(consumerGroup, true, topic); + + ConsumerGroupSummary consumerGroupSummary = null; + + return partitionAssignmentStateList + .stream() + .map( + partitionAssignmentState -> + convertParitionAssignmentStateToGroupDesc( + consumerGroup, + consumerGroupSummary, + partitionAssignmentState, + ConsumerType.OLD)) + .collect(Collectors.toList()); + } + + public List describeOldConsumerGroupByTopic( + String consumerGroup, @TopicExistConstraint String topic) { + if (!isOldConsumerGroup(consumerGroup)) { + throw new ApiException("ConsumerGroup:" + consumerGroup + " non-exist"); + } + + List consumerGroupDescList = new ArrayList<>(); + if (consumerGroup == null || consumerGroup.length() == 0) { + // To search all consumer groups + Set allNewConsumerGroups = listAllNewConsumerGroups(); + + for (String cg:allNewConsumerGroups) { + consumerGroupDescList.addAll(getOldConsumerGroupDescByConsumerGroupAndTopic(cg, topic)); + } + return consumerGroupDescList; + } else { + return getOldConsumerGroupDescByConsumerGroupAndTopic(consumerGroup, topic); + } + } + + public Map addPartitions(List addPartitions) { + Map addPartitionsResult = new HashMap<>(); + org.apache.kafka.clients.admin.AdminClient kafkaAdminClient = createKafkaAdminClient(); + + Map newPartitionsMap = new HashMap<>(); + addPartitions.forEach( + addPartition -> { + String topic = addPartition.getTopic(); + if (!existTopic(topic)) { + addPartitionsResult.put( + topic, + GeneralResponse.builder() + .state(GeneralResponseState.failure) + .msg("Topic:" + topic + " non-exist.") + .build()); + } else { + TopicMeta topicMeta = describeTopic(topic); + int currentPartionCount = topicMeta.getPartitionCount(); + int numPartitionsAdded = addPartition.getNumPartitionsAdded(); + int totalCount = currentPartionCount + numPartitionsAdded; + List> newAssignments = addPartition.getReplicaAssignment(); + NewPartitions newPartitions; + if (newAssignments == null || newAssignments.isEmpty()) { + // The assignment of new replicas to brokers will be decided by the broker. + newPartitions = NewPartitions.increaseTo(totalCount); + } else { + newPartitions = NewPartitions.increaseTo(totalCount, newAssignments); + } + newPartitionsMap.put(topic, newPartitions); + } + }); + CreatePartitionsResult createPartitionsResult = + kafkaAdminClient.createPartitions(newPartitionsMap); + try { + createPartitionsResult.all().get(kafkaAdminClientAlterTimeoutMs, TimeUnit.MILLISECONDS); + } catch (Exception exception) { + log.warn("Add partitions exception: " + exception); + } finally { + Map> result = createPartitionsResult.values(); + result.forEach( + (topic, createResult) -> { + GeneralResponse generalResponse; + if (!createResult.isCompletedExceptionally() && createResult.isDone()) { + TopicMeta topicMeta = describeTopic(topic); + generalResponse = + GeneralResponse.builder() + .state(GeneralResponseState.success) + .data(topicMeta) + .build(); + } else { + generalResponse = + GeneralResponse.builder() + .state(GeneralResponseState.failure) + .msg(createResult.toString()) + .build(); + } + addPartitionsResult.put(topic, generalResponse); + }); + + return addPartitionsResult; + } + } + + // Return + public List generateReassignPartition(ReassignWrapper reassignWrapper) { + KafkaZkClient kafkaZkClient = zookeeperUtils.createKafkaZkClient(); + List result = new ArrayList<>(); + + Seq brokerSeq = + JavaConverters.asScalaBufferConverter(reassignWrapper.getBrokers()).asScala().toSeq(); + // + Tuple2 resultTuple2; + try { + resultTuple2 = + ReassignPartitionsCommand.generateAssignment( + kafkaZkClient, brokerSeq, reassignWrapper.generateReassignJsonString(), false); + } catch (Exception exception) { + throw new ApiException("Generate reassign plan exception." + exception); + } + HashMap emptyMap = new HashMap<>(); + ObjectMapper objectMapper = new ObjectMapper(); + try { + result.add( + objectMapper.readValue( + ReassignPartitionsCommand.formatAsReassignmentJson( + (scala.collection.Map>) resultTuple2._2(), + JavaConverters.mapAsScalaMapConverter(emptyMap).asScala()), + ReassignModel.class)); + result.add( + objectMapper.readValue( + ReassignPartitionsCommand.formatAsReassignmentJson( + (scala.collection.Map>) resultTuple2._1(), + JavaConverters.mapAsScalaMapConverter(emptyMap).asScala()), + ReassignModel.class)); + Collections.sort(result.get(0).getPartitions()); + Collections.sort(result.get(1).getPartitions()); + } catch (Exception exception) { + throw new ApiException("Generate reassign plan exception." + exception); + } + + return result; + } + + public ReassignStatus executeReassignPartition( + ReassignModel reassignModel, + Long interBrokerThrottle, + Long replicaAlterLogDirsThrottle, + Long timeoutMs) { + // Set default value + interBrokerThrottle = (interBrokerThrottle == null) ? Long.valueOf(-1) : interBrokerThrottle; + replicaAlterLogDirsThrottle = + (replicaAlterLogDirsThrottle == null) ? Long.valueOf(-1) : replicaAlterLogDirsThrottle; + timeoutMs = (timeoutMs == null) ? Long.valueOf(10000) : timeoutMs; + + org.apache.kafka.clients.admin.AdminClient kafkaAdminClient = createKafkaAdminClient(); + KafkaZkClient kafkaZkClient = zookeeperUtils.createKafkaZkClient(); + AdminZkClient adminZkClient = new AdminZkClient(kafkaZkClient); + + TwoTuple< + scala.collection.mutable.HashMap>, + scala.collection.Map> + reassignPlan; + String reassignStr = ""; + try { + reassignStr = new ObjectMapper().writeValueAsString(reassignModel); + } catch (Exception exception) { + throw new ApiException("Json processing exception." + exception); + } + reassignPlan = genReassignPlan(reassignStr); + scala.collection.mutable.HashMap> partitionsToBeReassignedMap = + reassignPlan.getFirst(); + scala.collection.Map replicatAssignment = + reassignPlan.getSecond(); + + ReassignPartitionsCommand reassignPartitionsCommand = + new ReassignPartitionsCommand( + kafkaZkClient, + scala.Option.apply(kafkaAdminClient), + partitionsToBeReassignedMap, + replicatAssignment, + adminZkClient); + + Function0 postUpdateAction = + new AbstractFunction0() { + @Override + public BoxedUnit apply() { + return null; + } + }; + Throttle throttle = + new Throttle(interBrokerThrottle, replicaAlterLogDirsThrottle, postUpdateAction); + + if (kafkaZkClient.reassignPartitionsInProgress()) { + // check whether zk node /admin/reassign_partitions exists + reassignPartitionsCommand.maybeLimit(throttle); + throw new ApiException( + "Failed to reassign partitions because there is an existing assignment running."); + } else { + try { + reassignPartitionsCommand.reassignPartitions(throttle, timeoutMs); + log.info("Successfully started reassignment of partitions."); + } catch (Exception exception) { + throw new ApiException( + "Failed to reassign partitions:" + + reassignPlan.getFirst() + + ". Exception:" + + exception.getLocalizedMessage()); + } + } + + return checkReassignStatus(partitionsToBeReassignedMap, replicatAssignment); + } + + private ReassignStatus checkReassignStatus( + scala.collection.Map> partitionsToBeReassigned, + scala.collection.Map replicaAssignement) { + ReassignStatus reassignStatus = new ReassignStatus(); + Map reassignedPartitionsStatus = + checkIfPartitionReassignmentSucceeded(partitionsToBeReassigned); + Map replicasReassignmentStatus = + checkIfReplicaReassignmentSucceeded( + CollectionConvertor.mapConvertJavaMap(replicaAssignement)); + + reassignStatus.setPartitionsReassignStatus(reassignedPartitionsStatus); + reassignStatus.setReplicasReassignStatus(replicasReassignmentStatus); + + if (removeThrottle(reassignedPartitionsStatus, replicasReassignmentStatus)) { + reassignStatus.setRemoveThrottle(true); + } else { + reassignStatus.setRemoveThrottle(false); + } + + reassignStatus.setMsg( + "If removeThrottle in response is false, please use check api to remove throttle."); + + return reassignStatus; + } + + private boolean removeThrottle( + Map reassignedPartitionsStatus, + Map replicasReassignmentStatus) { + for (Map.Entry entry : reassignedPartitionsStatus.entrySet()) { + // Partitions reassignemnt not all done + if (!entry.getValue().equals(ReassignmentState.ReassignmentCompleted.code())) { + return false; + } + } + + for (Map.Entry entry : replicasReassignmentStatus.entrySet()) { + // Replica reassignement not all done + if (!entry.getValue().equals(ReassignmentState.ReassignmentCompleted.code())) { + return false; + } + } + + KafkaZkClient kafkaZkClient = zookeeperUtils.createKafkaZkClient(); + + List brokerList = + CollectionConvertor.seqConvertJavaList(kafkaZkClient.getAllBrokersInCluster()); + for (Broker broker : brokerList) { + int brokerId = broker.id(); + List keysToBeRemoved = new ArrayList<>(); + // We can't access the scala object here + // keysToBeRemoved.add(DynamicConfig.Broker.LeaderReplicationThrottledRateProp()); + // keysToBeRemoved.add(DynamicConfig.Broker.FollowerReplicationThrottledRateProp()); + // + // keysToBeRemoved.add(DynamicConfig.Broker.ReplicaAlterLogDirsIoMaxBytesPerSecondProp()); + keysToBeRemoved.add(LeaderReplicationThrottledRateProp); + keysToBeRemoved.add(FollowerReplicationThrottledRateProp); + keysToBeRemoved.add(ReplicaAlterLogDirsIoMaxBytesPerSecondProp); + try { + removeConfigInZk(Type.BROKER, String.valueOf(brokerId), keysToBeRemoved); + } catch (ApiException apiException) { + log.info( + "Remove property on broker:" + brokerId + " failed since " + apiException.getMessage()); + } + } + + Set topics = + reassignedPartitionsStatus.keySet().stream().map(tp -> tp.topic()).collect(toSet()); + Set topicInReplicas = + replicasReassignmentStatus.keySet().stream().map(tpr -> tpr.topic()).collect(toSet()); + topics.addAll(topicInReplicas); + + for (String topic : topics) { + List keysToBeRemoved = new ArrayList<>(); + keysToBeRemoved.add(LogConfig.LeaderReplicationThrottledReplicasProp()); + keysToBeRemoved.add(LogConfig.FollowerReplicationThrottledReplicasProp()); + try { + removeConfigInZk(Type.TOPIC, topic, keysToBeRemoved); + } catch (ApiException apiException) { + log.info( + "Remove property on topic:" + topic + " failed since " + apiException.getMessage()); + } + } + + return true; + } + + private Map checkIfPartitionReassignmentSucceeded( + scala.collection.Map> partitionsToBeReassigned) { + Map reassignedPartitionsStatus = new HashMap<>(); + KafkaZkClient kafkaZkClient = zookeeperUtils.createKafkaZkClient(); + + scala.collection.immutable.Map> partitionsBeingReassigned = + kafkaZkClient.getPartitionReassignment(); + scala.collection.Iterator topicPartitionIterator = + partitionsToBeReassigned.keysIterator(); + while (topicPartitionIterator.hasNext()) { + TopicPartition topicPartition = topicPartitionIterator.next(); + reassignedPartitionsStatus.put( + topicPartition, + ReassignPartitionsCommand.checkIfPartitionReassignmentSucceeded( + kafkaZkClient, + topicPartition, + partitionsToBeReassigned, + partitionsBeingReassigned) + .status()); + } + + return reassignedPartitionsStatus; + } + + private Map checkIfReplicaReassignmentSucceeded( + Map replicaAssignement) { + Map replicasReassignmentStatus = new HashMap<>(); + Map replicaLogDirInfos = new HashMap<>(); + + if (!replicaAssignement.isEmpty()) { + List replicaList = new ArrayList<>(replicaAssignement.keySet()); + replicaLogDirInfos = describeReplicaLogDirs(replicaList); + } + + for (Map.Entry newLogDirEntry : replicaAssignement.entrySet()) { + TopicPartitionReplica tpr = newLogDirEntry.getKey(); + String newLogDir = newLogDirEntry.getValue(); + ReplicaLogDirInfo replicaLogDirInfo = replicaLogDirInfos.get(tpr); + if (replicaLogDirInfo.getCurrentReplicaLogDir() == null) { + // tpr log dir not found + replicasReassignmentStatus.put(tpr, ReassignmentState.ReassignmentFailed.code()); + } else if (replicaLogDirInfo.getFutureReplicaLogDir() != null + && replicaLogDirInfo.getFutureReplicaLogDir().equals(newLogDir)) { + replicasReassignmentStatus.put(tpr, ReassignmentState.ReassignmentInProgress.code()); + } else if (replicaLogDirInfo.getFutureReplicaLogDir() != null + && !replicaLogDirInfo.getFutureReplicaLogDir().equals(newLogDir)) { + // tpr is being moved to another logdir instead of newLogDir + replicasReassignmentStatus.put(tpr, ReassignmentState.ReassignmentFailed.code()); + } else if (replicaLogDirInfo.getCurrentReplicaLogDir() != null + && replicaLogDirInfo.getCurrentReplicaLogDir().equals(newLogDir)) { + replicasReassignmentStatus.put(tpr, ReassignmentState.ReassignmentCompleted.code()); + } else { + replicasReassignmentStatus.put(tpr, ReassignmentState.ReassignmentFailed.code()); + } + } + + return replicasReassignmentStatus; + } + + public ReassignStatus checkReassignStatus(ReassignModel reassignModel) { + String reassignJsonStr = ""; + try { + ObjectMapper objectMapper = new ObjectMapper(); + reassignJsonStr = objectMapper.writeValueAsString(reassignModel); + } catch (JsonProcessingException exception) { + throw new ApiException("Json processing exception." + exception); + } + TwoTuple< + scala.collection.mutable.HashMap>, + scala.collection.Map> + reassignPlan = genReassignPlan(reassignJsonStr); + scala.collection.mutable.HashMap> partitionsToBeReassignedMap = + reassignPlan.getFirst(); + scala.collection.Map replicatAssignment = + reassignPlan.getSecond(); + + return checkReassignStatus(partitionsToBeReassignedMap, replicatAssignment); + } + + private TwoTuple< + scala.collection.mutable.HashMap>, + scala.collection.Map> + genReassignPlan(String reassignJsonStr) { + Tuple2 resultTuple2; + + KafkaZkClient kafkaZkClient = zookeeperUtils.createKafkaZkClient(); + + try { + // Parse and validate reassignment json string, return (partitionsToBeReassigned, + // replicaAssignment) + resultTuple2 = ReassignPartitionsCommand.parseAndValidate(kafkaZkClient, reassignJsonStr); + } catch (Exception exception) { + throw new ApiException("Bad Request. " + exception.getMessage()); + } + // Change list buffer to map + ListBuffer partitionsToBeReassignedList = + (scala.collection.mutable.ListBuffer) resultTuple2._1(); + scala.collection.mutable.HashMap> partitionsToBeReassignedMap = + new scala.collection.mutable.HashMap<>(); + for (int i = 0; i < partitionsToBeReassignedList.size(); ++i) { + Tuple2 tup = (Tuple2) partitionsToBeReassignedList.apply(i); + partitionsToBeReassignedMap.put((TopicPartition) tup._1(), (Seq) tup._2()); + } + + scala.collection.Map replicatAssignment = + (scala.collection.Map) resultTuple2._2(); + + return new TwoTuple<>(partitionsToBeReassignedMap, replicatAssignment); + } + + public String getMessage( + @TopicExistConstraint String topic, + int partition, + long offset, + String decoder, + String avroSchema) { + KafkaConsumer consumer = + kafkaUtils.createNewConsumer(String.valueOf(System.currentTimeMillis())); + TopicPartition tp = new TopicPartition(topic, partition); + long beginningOffset = getBeginningOffset(topic, partition); + long endOffset = getEndOffset(topic, partition); + if (beginningOffset == endOffset) { + throw new ApiException("There is no message in this partition of this topic"); + } + if (offset < beginningOffset || offset >= endOffset) { + log.error(offset + " error"); + consumer.close(); + throw new ApiException( + "offsets must be between " + String.valueOf(beginningOffset + " and " + (endOffset - 1))); + } + consumer.assign(Collections.singletonList(tp)); + consumer.seek(tp, offset); + + String last = null; + + // ConsumerRecords crs = consumer.poll(channelRetryBackoffMs); + ConsumerRecords crs = consumer.poll(3000); + log.info( + "Seek to offset:" + + offset + + ", topic:" + + topic + + ", partition:" + + partition + + ", crs.count:" + + crs.count()); + if (crs.count() != 0) { + Iterator> it = crs.iterator(); + while (it.hasNext()) { + ConsumerRecord initCr = it.next(); + last = + "Value: " + + initCr.value() + + ", Offset: " + + String.valueOf(initCr.offset()) + + ", timestamp:" + + initCr.timestamp(); + log.info( + "Value: " + initCr.value() + ", initCr.Offset: " + String.valueOf(initCr.offset())); + if (last != null && initCr.offset() == offset) { + break; + } + } + } + log.info("last:" + last); + consumer.close(); + return last; + } + + public Record getRecordByOffset( + @TopicExistConstraint String topic, + int partition, + long offset, + String decoder, + String avroSchema) { + if (!isTopicPartitionValid(topic, partition)) { + throw new ApiException("Bad request. Topic:" + topic + " has no partition:" + partition); + } + + checkOffsetValid(topic, partition, offset); + + if (!kafkaUtils.DESERIALIZER_TYPE_MAP.containsKey(decoder)) { + throw new ApiException( + "Bad request. Decoder class:" + + decoder + + " not found. ByteArrayDeserializer, ByteBufferDeserializer, BytesDeserializer, " + + "DoubleDeserializer, FloatDeserializer, " + + "IntegerDeserializer, LongDeserializer, ShortDeserializer, StringDeserializer, AvroDeserializer " + + "are supported."); + } + + if (decoder != null && decoder.equals("AvroDeserializer")) { + if (avroSchema == null || avroSchema.isEmpty()) { + throw new ApiException("Bad request. Schema is needed when choosing AvroDeserializer."); + } else { + return getAvroRecordByOffset(topic, partition, offset, avroSchema); + } + } + + if (decoder == null || decoder.isEmpty()) { + // default decoder is StringDeserializer + decoder = "StringDeserializer"; + } + + Class type = kafkaUtils.DESERIALIZER_TYPE_MAP.get(decoder); + String dese = Serdes.serdeFrom(type).deserializer().getClass().getCanonicalName(); + + KafkaConsumer consumer; + try { + consumer = kafkaUtils.createNewConsumer(String.valueOf(System.currentTimeMillis()), dese); + } catch (ClassNotFoundException classNotFoundException) { + throw new ApiException("Class " + dese + "not found exception." + classNotFoundException); + } + + TopicPartition tp = new TopicPartition(topic, partition); + consumer.assign(Collections.singletonList(tp)); + consumer.seek(tp, offset); + + Record record = Record.builder().topic(topic).type(type).decoder(decoder).build(); + + try { + ConsumerRecords crs = consumer.poll(3000); + log.info( + "Seek to offset:" + + offset + + ", topic:" + + topic + + ", partition:" + + partition + + ", crs.count:" + + crs.count()); + if (crs.count() != 0) { + Iterator> it = crs.iterator(); + while (it.hasNext()) { + ConsumerRecord initCr = it.next(); + if (initCr.offset() == offset) { + record.setOffset(offset); + record.setTimestamp(initCr.timestamp()); + record.setKey(initCr.key()); + record.setValue(initCr.value()); + break; + } + log.info( + "Value: " + + initCr.value() + + ", initCr.Offset: " + + String.valueOf(initCr.offset()) + + ", timestamp:" + + initCr.timestamp()); + } + } + } catch (Exception exception) { + throw new ApiException( + "Consume " + + topic + + "-" + + partition + + " offset:" + + offset + + " using " + + decoder + + " exception. " + + exception.getLocalizedMessage()); + } finally { + consumer.close(); + } + + return record; + } + + private boolean isTopicPartitionValid(String topic, int partition) { + TopicMeta topicMeta = describeTopic(topic); + + for (CustomTopicPartitionInfo topicPartitionInfo : topicMeta.getTopicPartitionInfos()) { + if (topicPartitionInfo.getTopicPartitionInfo().partition() == partition) { + return true; + } + } + + return false; + } + + public void checkOffsetValid(String topic, int partition, long offset) { + long beginningOffset = getBeginningOffset(topic, partition); + long endOffset = getEndOffset(topic, partition); + + log.info( + "Topic:" + + topic + + ", partition:" + + partition + + " begin offset:" + + beginningOffset + + ", end offset:" + + endOffset); + if (beginningOffset == endOffset) { + throw new ApiException("There is no message in this partition of this topic"); + } + if (offset < beginningOffset || offset >= endOffset) { + log.error(offset + " error"); + throw new ApiException( + "offsets must be between " + String.valueOf(beginningOffset + " and " + (endOffset - 1))); + } + } + + public Record getAvroRecordByOffset(String topic, int partition, long offset, String avroSchema) { + TopicPartition tp = new TopicPartition(topic, partition); + KafkaConsumer consumer = null; + try { + consumer = + kafkaUtils.createNewConsumer( + String.valueOf(System.currentTimeMillis()), + "org.apache.kafka.common.serialization.ByteArrayDeserializer"); + } catch (Exception exception) { + log.error("Create consumer exception." + exception); + } + consumer.assign(Collections.singletonList(tp)); + consumer.seek(tp, offset); + + Record record = + Record.builder().topic(topic).type(byte[].class).decoder("AvorDeserializer").build(); + + try { + ConsumerRecords crs = consumer.poll(3000); + log.info( + "Seek to offset:" + + offset + + ", topic:" + + topic + + ", partition:" + + partition + + ", crs.count:" + + crs.count()); + if (crs.count() != 0) { + Iterator> it = crs.iterator(); + while (it.hasNext()) { + ConsumerRecord initCr = it.next(); + if (initCr.offset() == offset) { + record.setOffset(offset); + record.setTimestamp(initCr.timestamp()); + record.setKey(initCr.key()); + record.setValue(avroDeserialize(initCr.value(), avroSchema)); + break; + } + log.info( + "Value: " + + initCr.value() + + ", initCr.Offset: " + + String.valueOf(initCr.offset()) + + ", timestamp:" + + initCr.timestamp()); + } + } + } catch (Exception exception) { + throw new ApiException( + "Consume " + + topic + + "-" + + partition + + " offset:" + + offset + + " using " + + "ByteArrayDeserializer" + + " exception. " + + exception.getLocalizedMessage()); + } finally { + consumer.close(); + } + + return record; + } + + private Object avroDeserialize(byte[] bytes, String avroSchema) { + Schema schema = new Schema.Parser().parse(avroSchema); + DatumReader reader = new GenericDatumReader(schema); + ByteBuffer buffer = ByteBuffer.wrap(bytes); + Object object = null; + try { + object = + reader.read( + null, DecoderFactory.get().binaryDecoder(buffer.array(), 0, bytes.length, null)); + } catch (IOException exception) { + throw new ApiException("Avro Deserialize exception. " + exception); + } + + return object; + } + + public GeneralResponse resetOffset( + @TopicExistConstraint String topic, + int partition, + String consumerGroup, + ConsumerType type, + String offset) { + KafkaConsumer consumer = null; + if (type != null && type == ConsumerType.NEW) { + if (!isNewConsumerGroup(consumerGroup)) { + return GeneralResponse.builder() + .state(GeneralResponseState.failure) + .msg("New consumer group:" + consumerGroup + " non-exists!") + .build(); + } + } + + if (type != null && type == ConsumerType.OLD) { + if (!isOldConsumerGroup(consumerGroup)) { + return GeneralResponse.builder() + .state(GeneralResponseState.failure) + .msg("Old consumer group:" + consumerGroup + " non-exists!") + .build(); + } + } + + if (!isTopicPartitionValid(topic, partition)) { + return GeneralResponse.builder() + .state(GeneralResponseState.failure) + .msg("Topic:" + topic + " has no partition:" + partition) + .build(); + } + + long offsetToBeReset = -1; + long beginningOffset = getBeginningOffset(topic, partition); + long endOffset = getEndOffset(topic, partition); + + log.info("To tell the consumergroup " + consumerGroup + " is active now"); + if (isConsumerGroupActive(consumerGroup, type)) { + return GeneralResponse.builder() + .state(GeneralResponseState.failure) + .msg("Offsets can only be reset if the group " + consumerGroup + " is inactive") + .build(); + } + + if (type != null && type == ConsumerType.NEW && isNewConsumerGroup(consumerGroup)) { + try { + log.info("The consumergroup " + consumerGroup + " is new. Reset offset now"); + consumer = kafkaUtils.createNewConsumer(consumerGroup); + // if type is new or the consumergroup itself is new + TopicPartition tp = new TopicPartition(topic, partition); + consumer.assign(Arrays.asList(tp)); + consumer.poll(channelSocketTimeoutMs); + if (offset.equals("earliest")) { + consumer.seekToBeginning(Arrays.asList(tp)); + offsetToBeReset = beginningOffset; + log.info( + "Reset offset for consumer group:" + + consumerGroup + + " on " + + topic + + "-" + + partition + + " to " + + consumer.position(tp)); + } else if (offset.equals("latest")) { + consumer.seekToEnd(Arrays.asList(tp)); + offsetToBeReset = endOffset; + log.info( + "Reset offset for consumer group:" + + consumerGroup + + " on " + + topic + + "-" + + partition + + " to " + + consumer.position(tp)); + } else if (isDateTime(offset)) { + // Reset offset by time + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); + Map timestampsToSearch = new HashMap<>(); + try { + timestampsToSearch.put(tp, sdf.parse(offset).getTime()); + Map results = + consumer.offsetsForTimes(timestampsToSearch); + OffsetAndTimestamp offsetAndTimestamp = results.get(tp); + if (offsetAndTimestamp != null) { + offsetToBeReset = offsetAndTimestamp.offset(); + log.info( + "Reset consumer group:" + + consumerGroup + + " offset by time. Reset to offset:" + + offsetAndTimestamp.offset() + + ", timestamp:" + + offsetAndTimestamp.timestamp() + + ", timestampToDate:" + + sdf.format(new Date(offsetAndTimestamp.timestamp()))); + consumer.seek(tp, offsetToBeReset); + } else { + return GeneralResponse.builder() + .state(GeneralResponseState.failure) + .msg( + "No offset's timestamp is greater than or equal to the given timestamp:" + + offset) + .build(); + } + } catch (ParseException parseException) { + return GeneralResponse.builder() + .state(GeneralResponseState.failure) + .msg("Invalid offset format. Date format should be yyyy-MM-dd HH:mm:ss.SSS .") + .build(); + } + } else { + if (Long.parseLong(offset) < beginningOffset || Long.parseLong(offset) > endOffset) { + log.warn(offset + " error"); + return GeneralResponse.builder() + .state(GeneralResponseState.failure) + .msg( + "Invalid request offset:" + + offset + + ". Topic " + + topic + + "'s beginning offset:" + + beginningOffset + + ", endoffset:" + + endOffset) + .build(); + } + offsetToBeReset = Long.parseLong(offset); + consumer.seek(tp, offsetToBeReset); + } + consumer.commitSync(); + } catch (IllegalStateException e) { + storage.getMap().remove(consumerGroup); + return GeneralResponse.builder() + .state(GeneralResponseState.failure) + .msg(e.getLocalizedMessage()) + .build(); + } finally { + if (consumer != null) { + consumer.close(); + } + } + } + + // if type is old or the consumer group itself is old + if (type != null && type == ConsumerType.OLD && isOldConsumerGroup(consumerGroup)) { + log.info("The consumergroup " + consumerGroup + " is old. Reset offset now"); + if (offset.equals("earliest")) { + offsetToBeReset = beginningOffset; + } else if (offset.equals("latest")) { + offsetToBeReset = endOffset; + } else { + try { + if (Long.parseLong(offset) < beginningOffset || Long.parseLong(offset) > endOffset) { + log.info("Setting offset to " + offset + " error"); + return GeneralResponse.builder() + .state(GeneralResponseState.failure) + .msg( + "Invalid request offset:" + + offset + + ". Topic " + + topic + + "'s beginning offset:" + + beginningOffset + + ", endoffset:" + + endOffset) + .build(); + } + log.info("Offset will be reset to " + offset); + zkUtils = zookeeperUtils.getZkUtils(); + offsetToBeReset = Long.parseLong(offset); + zkUtils + .zkClient() + .writeData( + "/consumers/" + consumerGroup + "/offsets/" + topic + "/" + partition, offset); + } catch (Exception e) { + return GeneralResponse.builder() + .state(GeneralResponseState.failure) + .msg(e.getLocalizedMessage()) + .build(); + } + } + } + return GeneralResponse.builder() + .state(GeneralResponseState.success) + .msg("Reset the offset successfully!") + .data(Long.toString(offsetToBeReset)) + .build(); + } + + private boolean isDateTime(String offset) { + String patternStr = "\\d\\d\\d\\d-[0-1]\\d-[0-3]\\d\\s+[0-2]\\d:[0-5]\\d:[0-5]\\d\\.\\d\\d\\d"; + Pattern timePattern = Pattern.compile(patternStr); + return timePattern.matcher(offset).find(); + } + + public Map> getLastCommitTime( + @ConsumerGroupExistConstraint String consumerGroup, + @TopicExistConstraint String topic, + ConsumerType type) { + Map> result = new ConcurrentHashMap<>(); + + if (type != null && type == ConsumerType.OLD) { + CuratorFramework zkClient = zookeeperUtils.createZkClient(); + // Get Old Consumer commit time + try { + Map oldConsumerOffsetMap = new ConcurrentHashMap<>(); + if (zkClient.checkExists().forPath(CONSUMERPATHPREFIX + consumerGroup) != null + && zkClient + .checkExists() + .forPath(CONSUMERPATHPREFIX + consumerGroup + OFFSETSPATHPREFIX + topic) + != null) { + List offsets = + zkClient + .getChildren() + .forPath(CONSUMERPATHPREFIX + consumerGroup + OFFSETSPATHPREFIX + topic); + for (String offset : offsets) { + Integer id = Integer.valueOf(offset); + long mtime = + zkClient + .checkExists() + .forPath( + CONSUMERPATHPREFIX + + consumerGroup + + OFFSETSPATHPREFIX + + topic + + "/" + + offset) + .getMtime(); + oldConsumerOffsetMap.put(id, mtime); + } + + result.put("old", oldConsumerOffsetMap); + } + } catch (Exception e) { + log.warn( + "Get last commit time for consumergroup:" + + consumerGroup + + " failed. " + + e.getLocalizedMessage()); + } + } else { + // Get New consumer commit time, from offset storage instance + // TODO find a solution a replace the storage + if (storage.get(consumerGroup) != null) { + Map storageResult = + storage.get(consumerGroup); + result.put( + "new", + (storageResult + .entrySet() + .parallelStream() + .filter(s -> s.getKey().topicPartition().topic().equals(topic)) + .collect( + Collectors.toMap( + s -> s.getKey().topicPartition().partition(), + s -> { + if (s.getValue() != null) { + return s.getValue().commitTimestamp(); + } else { + return -1L; + } + })))); + } + } + + return result; + } + + public GeneralResponse deleteConsumerGroup(String consumerGroup, ConsumerType type) { + if (type == ConsumerType.OLD && !isOldConsumerGroup(consumerGroup)) { + return GeneralResponse.builder() + .state(GeneralResponseState.failure) + .msg("Old consumer group:" + consumerGroup + " non-exist") + .build(); + } + if (type == ConsumerType.NEW && !isNewConsumerGroup(consumerGroup)) { + return GeneralResponse.builder() + .state(GeneralResponseState.failure) + .msg("New consumer group:" + consumerGroup + " non-exist") + .build(); + } + if (type == ConsumerType.OLD) { + zkUtils = zookeeperUtils.getZkUtils(); + if (!AdminUtils.deleteConsumerGroupInZK(zkUtils, consumerGroup)) { + return GeneralResponse.builder() + .state(GeneralResponseState.failure) + .msg("The consumer " + consumerGroup + " is still active.Please stop it first") + .build(); + } + } else if (type == ConsumerType.NEW) { + AdminClient adminClient = kafkaUtils.createAdminClient(); + List groups = new ArrayList<>(); + groups.add(consumerGroup); + + scala.collection.immutable.List groupsList = + JavaConverters.asScalaBufferConverter(groups).asScala().toList(); + scala.collection.immutable.Map stringErrorsMap = + adminClient.deleteConsumerGroups((scala.collection.immutable.List) (groupsList)); + + adminClient.close(); + if (!stringErrorsMap.get(consumerGroup).get().equals(Errors.NONE)) { + log.info("Consumer group:" + + consumerGroup + + " could not be deleted. Error Code:" + + stringErrorsMap.get(consumerGroup).get() + + ". Error msg:" + + stringErrorsMap.get(consumerGroup).get().exception()); + return GeneralResponse.builder() + .state(GeneralResponseState.failure) + .msg( + "Consumer group:" + + consumerGroup + + " could not be deleted. Error Code:" + + stringErrorsMap.get(consumerGroup).get() + + ". Error msg:" + + stringErrorsMap.get(consumerGroup).get().exception()) + .build(); + } + } + + log.info("Consumer group:" + consumerGroup + " has been deleted."); + + return GeneralResponse.builder() + .state(GeneralResponseState.success) + .msg("Consumer group:" + consumerGroup + " has been deleted.") + .data(consumerGroup) + .build(); + } + + private List getTopicPartitions(String t) { + List tpList = new ArrayList<>(); + List l = Arrays.asList(t); + zkUtils = zookeeperUtils.getZkUtils(); + java.util.Map> tpMap = + JavaConverters.mapAsJavaMapConverter( + zkUtils.getPartitionsForTopics( + JavaConverters.asScalaIteratorConverter(l.iterator()).asScala().toSeq())) + .asJava(); + if (tpMap != null) { + ArrayList partitionLists = + new ArrayList<>(JavaConverters.seqAsJavaListConverter(tpMap.get(t)).asJava()); + tpList = + partitionLists.stream().map(p -> new TopicAndPartition(t, (Integer) p)).collect(toList()); + } + return tpList; + } + + private long getOffsets(Node leader, String topic, int partitionId, long time) { + TopicAndPartition topicAndPartition = new TopicAndPartition(topic, partitionId); + + SimpleConsumer consumer = + new SimpleConsumer(leader.host(), leader.port(), 10000, 1024, "Kafka-zk-simpleconsumer"); + + PartitionOffsetRequestInfo partitionOffsetRequestInfo = + new PartitionOffsetRequestInfo(time, 10000); + OffsetRequest offsetRequest = + new OffsetRequest( + ImmutableMap.of(topicAndPartition, partitionOffsetRequestInfo), + kafka.api.OffsetRequest.CurrentVersion(), + consumer.clientId()); + OffsetResponse offsetResponse = consumer.getOffsetsBefore(offsetRequest); + + if (offsetResponse.hasError()) { + short errorCode = offsetResponse.errorCode(topic, partitionId); + log.warn(format("Offset response has error: %d", errorCode)); + throw new ApiException( + "could not fetch data from Kafka, error code is '" + + errorCode + + "'Exception Message:" + + offsetResponse.toString()); + } + + long[] offsets = offsetResponse.offsets(topic, partitionId); + consumer.close(); + return offsets[0]; + } + + boolean partitionLeaderExist(String topic, int partitionId) { + TopicDescription topicDes = getTopicDescription(topic); + if (topicDes != null) { + for(TopicPartitionInfo tpi:topicDes.partitions()) { + if (tpi.partition() == partitionId) { + return tpi.leader() != null; + } + } + } + return false; + } + + public long getBeginningOffset(String topic, int partitionId) { + if (partitionLeaderExist(topic, partitionId)) { + log.info("Getting beginning offset for topic:" + topic + ", partition:" + partitionId); + KafkaConsumer consumer = kafkaUtils.createNewConsumer(KafkaUtils.DEFAULTCP); + TopicPartition tp = new TopicPartition(topic, partitionId); + consumer.assign(Arrays.asList(tp)); + Map beginningOffsets = + consumer.beginningOffsets(Collections.singletonList(tp)); + consumer.close(); + + if (beginningOffsets != null) { + return beginningOffsets.get(tp); + } + + log.info("End Get beginning offset for topic:" + topic + ", partition:" + partitionId); + } + return -1; + } + + public long getEndOffset(String topic, int partitionId) { + if (partitionLeaderExist(topic, partitionId)) { + log.info("Getting end offset for topic:" + topic + ", partition:" + partitionId); + KafkaConsumer consumer = kafkaUtils.createNewConsumer(KafkaUtils.DEFAULTCP); + TopicPartition tp = new TopicPartition(topic, partitionId); + consumer.assign(Arrays.asList(tp)); + Map endOffsets = consumer.endOffsets(Collections.singletonList(tp)); + consumer.close(); + + log.info("End Get end offset for topic:" + topic + ", partition:" + partitionId); + if (endOffsets != null) { + return endOffsets.get(tp); + } + } + return -1; + } + + private long getEndOffset(Node leader, String topic, int partitionId) { + return getOffsets(leader, topic, partitionId, kafka.api.OffsetRequest.LatestTime()); + } + + private boolean isConsumerGroupActive(String consumerGroup, ConsumerType type) { + if (type == ConsumerType.NEW) { + ConsumerGroupMeta groupMeta = getConsumerGroupMeta(consumerGroup); + ConsumerGroupState groupState = groupMeta.getState(); + if (groupState.equals(ConsumerGroupState.STABLE) + || groupState.equals(ConsumerGroupState.PREPARING_REBALANCE) + || groupState.equals(ConsumerGroupState.COMPLETING_REBALANCE)) { + return true; + } + if (groupState.equals(ConsumerGroupState.EMPTY) + || groupState.equals(ConsumerGroupState.DEAD)) { + return false; + } + throw new ApiException( + "Consumer group:" + consumerGroup + " state:" + groupState + " unkown."); + } else if (type == ConsumerType.OLD) { + zkUtils = zookeeperUtils.getZkUtils(); + return zkUtils.getConsumersInGroup(consumerGroup).nonEmpty(); + } else { + throw new ApiException("Unknown type " + type); + } + } + + public HealthCheckResult healthCheck() { + String healthCheckTopic = kafkaConfig.getHealthCheckTopic(); + HealthCheckResult healthCheckResult = new HealthCheckResult(); + KafkaProducer producer = kafkaUtils.createProducer(); + int partitionId; + long offset; + + boolean healthCheckTopicExist = existTopic(healthCheckTopic); + log.info("HealthCheckTopic:" + healthCheckTopic + " existed:" + healthCheckTopicExist); + if (!healthCheckTopicExist) { + healthCheckResult.setStatus("unknown"); + healthCheckResult.setMsg( + "HealthCheckTopic: " + + healthCheckTopic + + " Non-Exist. Please create it before doing health check."); + return healthCheckResult; + } + + String message = "health check_" + System.currentTimeMillis(); + ProducerRecord record = new ProducerRecord(healthCheckTopic, null, message); + log.info("Generate message:" + message); + try { + RecordMetadata recordMetadata = (RecordMetadata) producer.send(record).get(); + partitionId = recordMetadata.partition(); + offset = recordMetadata.offset(); + log.info( + "Message:" + + message + + " has been sent to Partition:" + + partitionId + + ", offset:" + + offset); + } catch (Exception e) { + healthCheckResult.setStatus("error"); + healthCheckResult.setMsg( + "Health Check: Produce Message Failure. Exception: " + e.getMessage()); + log.error("Health Check: Produce Message Failure.", e); + return healthCheckResult; + } finally { + producer.close(); + } + + KafkaConsumer consumer = kafkaUtils.createNewConsumer(); + TopicPartition topicPartition = new TopicPartition(healthCheckTopic, partitionId); + consumer.assign(Arrays.asList(topicPartition)); + consumer.seek(topicPartition, offset); + int retries = 30; + int noRecordsCount = 0; + while (true) { + final ConsumerRecords consumerRecords = consumer.poll(1000); + if (consumerRecords.count() == 0) { + noRecordsCount++; + if (noRecordsCount > retries) { + break; + } else { + continue; + } + } + Iterator> iterator = consumerRecords.iterator(); + while (iterator.hasNext()) { + ConsumerRecord msg = iterator.next(); + log.info("Health Check: Fetch Message " + msg.value() + ", offset:" + msg.offset()); + if (msg.value().equals(message)) { + healthCheckResult.setStatus("ok"); + healthCheckResult.setMsg(message); + return healthCheckResult; + } + } + consumer.commitAsync(); + } + consumer.close(); + + if (healthCheckResult.getStatus() == null) { + healthCheckResult.setStatus("error"); + healthCheckResult.setMsg( + "Health Check: Consume Message Failure. Consumer can't fetch the message."); + } + return healthCheckResult; + } +} diff --git a/src/main/resources/application-tina.yml b/src/main/resources/application-tina.yml index fcbb51b..038f2da 100644 --- a/src/main/resources/application-tina.yml +++ b/src/main/resources/application-tina.yml @@ -1,5 +1,5 @@ kafka: - brokers: localhost:19092,localhost:19093,localhost:19095 + brokers: localhost:9092,localhost:9094,localhost:9096 offset: topic: "__consumer_offsets" partitions: 50 @@ -8,7 +8,7 @@ kafka: topic: "health" zookeeper: - uris: 127.0.0.1:2183 + uris: 127.0.0.1:2181 jmx: kafka: diff --git a/src/test/java/org/gnuhpc/bigdata/service/KafkaAdminServiceTest.java b/src/test/java/org/gnuhpc/bigdata/service/KafkaAdminServiceTest.java index 310b438..a7ecada 100644 --- a/src/test/java/org/gnuhpc/bigdata/service/KafkaAdminServiceTest.java +++ b/src/test/java/org/gnuhpc/bigdata/service/KafkaAdminServiceTest.java @@ -99,11 +99,11 @@ public class KafkaAdminServiceTest { @InjectMocks private KafkaAdminService kafkaAdminServiceUnderTest; private static final String TEST_KAFKA_BOOTSTRAP_SERVERS = - "localhost:19092,localhost:19093,localhost:19095"; + "localhost:9092,localhost:9094,localhost:9096"; private static final List TEST_KAFKA_BOOTSTRAP_SERVERS_ID = Arrays.asList(111, 113, 115); private static final int KAFKA_NODES_COUNT = TEST_KAFKA_BOOTSTRAP_SERVERS_ID.size(); - private static final String TEST_ZK = "localhost:2183"; - private static final int TEST_CONTROLLER_ID = 111; + private static final String TEST_ZK = "127.0.0.1:2181"; + private static final int TEST_CONTROLLER_ID = 0; private static final List TEST_KAFKA_LOG_DIRS = Arrays.asList( "/home/xiangli/bigdata/kafka_2.11-1.1.1/kafka-logs,/home/xiangli/bigdata/kafka_2.11-1.1.1/kafka111_2-logs,/home/xiangli/bigdata/kafka_2.11-1.1.1/kafka111_3-logs", From d5952c7f290412ccb81ada9f24902955ae82bc13 Mon Sep 17 00:00:00 2001 From: wenqiao <315524513@qq.com> Date: Wed, 24 Apr 2019 19:36:35 +0800 Subject: [PATCH 18/24] Add replicationfactor in gettopicbrief function. Add socket timeout in ZookeeperUtils. --- .../org/gnuhpc/bigdata/controller/KafkaController.java | 3 ++- src/main/java/org/gnuhpc/bigdata/model/TopicBrief.java | 1 + .../org/gnuhpc/bigdata/service/KafkaAdminService.java | 9 +++++---- .../java/org/gnuhpc/bigdata/utils/ZookeeperUtils.java | 4 +++- src/main/resources/application-tina.yml | 2 +- 5 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java b/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java index c0ba446..58420d9 100644 --- a/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java +++ b/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java @@ -300,7 +300,8 @@ public ConsumerGroupMeta getConsumerGroupMeta(@PathVariable String consumerGroup value = "Get the meta data of the specified new consumer groups, including state, coordinator," + " assignmentStrategy, members") - public List getConsumerGroupsMeta(@RequestParam List consumerGroupList) { + public List getConsumerGroupsMeta() { + Set consumerGroupList = kafkaAdminService.listAllNewConsumerGroups(); List consumerGroupMetaList = new ArrayList<>(); for (String consumerGroup:consumerGroupList) { if (kafkaAdminService.isNewConsumerGroup(consumerGroup)) { diff --git a/src/main/java/org/gnuhpc/bigdata/model/TopicBrief.java b/src/main/java/org/gnuhpc/bigdata/model/TopicBrief.java index eade921..630f3df 100644 --- a/src/main/java/org/gnuhpc/bigdata/model/TopicBrief.java +++ b/src/main/java/org/gnuhpc/bigdata/model/TopicBrief.java @@ -12,4 +12,5 @@ public class TopicBrief { private String topic; private int numPartition; private double isrRate; + private int replicationFactor; } diff --git a/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java b/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java index 42acfc9..dea86c7 100644 --- a/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java +++ b/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java @@ -322,12 +322,12 @@ public List listTopicBrief() { isrCount += topicPartitionInfo.isr().size(); } if (replicateCount == 0) { - return new TopicBrief(topic, topicDescription.partitions().size(), 0); + return new TopicBrief(topic, topicDescription.partitions().size(), 0, replicateCount); } else { return new TopicBrief( topic, topicDescription.partitions().size(), - ((double) isrCount / replicateCount)); + ((double) isrCount / replicateCount), replicateCount); } }) .collect(toList()); @@ -865,7 +865,7 @@ public Map> listAllConsumerGroups(ConsumerType type) { return result; } - private Set listAllNewConsumerGroups() { + public Set listAllNewConsumerGroups() { AdminClient adminClient = kafkaUtils.createAdminClient(); log.info("Calling the listAllConsumerGroupsFlattened"); // Send LIST_GROUPS Request to kafka @@ -996,7 +996,8 @@ public ConsumerGroupMeta getConsumerGroupMeta(String consumerGroup) { List members = new ArrayList<>(); AdminClient adminClient = kafkaUtils.createAdminClient(); - ConsumerGroupSummary consumerGroupSummary = adminClient.describeConsumerGroup(consumerGroup, 0); + ConsumerGroupSummary consumerGroupSummary = adminClient.describeConsumerGroup(consumerGroup, + kafkaAdminClientGetTimeoutMs); List consumerSummaryList = CollectionConvertor.optionListConvertJavaList(consumerGroupSummary.consumers().get()); diff --git a/src/main/java/org/gnuhpc/bigdata/utils/ZookeeperUtils.java b/src/main/java/org/gnuhpc/bigdata/utils/ZookeeperUtils.java index ce50f00..ff1aeb9 100644 --- a/src/main/java/org/gnuhpc/bigdata/utils/ZookeeperUtils.java +++ b/src/main/java/org/gnuhpc/bigdata/utils/ZookeeperUtils.java @@ -3,6 +3,7 @@ import com.google.common.base.Charsets; import java.io.IOException; import java.net.InetAddress; +import java.net.InetSocketAddress; import java.net.Socket; import java.util.HashMap; import java.util.Iterator; @@ -82,7 +83,8 @@ public List executeCommand(String host, Integer port, String command) throws ServiceNotAvailableException { Socket socket; try { - socket = new Socket(InetAddress.getByName(host), port); + socket = new Socket(); + socket.connect(new InetSocketAddress(host, port), CONNECTION_TIMEOUT); } catch (IOException e) { throw new ServiceNotAvailableException( "zookeeper", diff --git a/src/main/resources/application-tina.yml b/src/main/resources/application-tina.yml index 038f2da..26c149d 100644 --- a/src/main/resources/application-tina.yml +++ b/src/main/resources/application-tina.yml @@ -1,5 +1,5 @@ kafka: - brokers: localhost:9092,localhost:9094,localhost:9096 + brokers: localhost:19092,localhost:19094,localhost:19096 offset: topic: "__consumer_offsets" partitions: 50 From 991b8ef6e6395164c9ca43ba510d562b23f7f97c Mon Sep 17 00:00:00 2001 From: wenqiao <315524513@qq.com> Date: Thu, 25 Apr 2019 11:13:22 +0800 Subject: [PATCH 19/24] Change topiclist to topicparitionMap in get /brokers/logdirs/detail. --- .../bigdata/controller/KafkaController.java | 7 +- .../bigdata/service/KafkaAdminService.java | 11 +-- .../service/KafkaAdminServiceTest.java | 77 +++++++++++++++---- 3 files changed, 72 insertions(+), 23 deletions(-) diff --git a/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java b/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java index 58420d9..7ad771d 100644 --- a/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java +++ b/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java @@ -5,6 +5,7 @@ import io.swagger.annotations.ApiResponse; import io.swagger.annotations.ApiResponses; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.List; @@ -90,8 +91,10 @@ public Map> listLogDirs( public Map> describeLogDirs( @RequestParam(required = false) List brokerList, @RequestParam(required = false) List logDirList, - @RequestParam(required = false) List topicList) { - return kafkaAdminService.describeLogDirsByBrokerAndTopic(brokerList, logDirList, topicList); + @RequestParam(required = false) Map> topicPartitionMap) { + topicPartitionMap.clear(); + topicPartitionMap.put("__consumer_offsets", Arrays.asList(15, 16, 48)); + return kafkaAdminService.describeLogDirsByBrokerAndTopic(brokerList, logDirList, topicPartitionMap); } @GetMapping(value = "/brokers/replicalogdirs") diff --git a/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java b/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java index dea86c7..098f73a 100644 --- a/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java +++ b/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java @@ -456,7 +456,7 @@ public Map> listLogDirsByBroker(List brokerList) } public Map> describeLogDirsByBrokerAndTopic( - List brokerList, List logDirList, List topicList) { + List brokerList, List logDirList, Map> topicPartitionMap) { org.apache.kafka.clients.admin.AdminClient kafkaAdminClient = createKafkaAdminClient(); List brokerIdsInCluster = @@ -500,7 +500,7 @@ public Map> describeLogDirsByBrokerAndTopic( e.getValue().entrySet().removeIf(m -> !logDirList.contains(m.getKey())); }); } - if (topicList != null && !topicList.isEmpty()) { + if (topicPartitionMap != null && !topicPartitionMap.isEmpty()) { logDirInfosByBroker .entrySet() .forEach( @@ -510,9 +510,10 @@ public Map> describeLogDirsByBrokerAndTopic( .forEach( m -> { m.getValue() - .replicaInfos - .entrySet() - .removeIf(t -> !topicList.contains(t.getKey().topic())); + .replicaInfos.entrySet() + .removeIf(t -> !topicPartitionMap.keySet().contains(t.getKey().topic()) || + (topicPartitionMap.get(t.getKey().topic()) != null && + !topicPartitionMap.get(t.getKey().topic()).contains(t.getKey().partition()))); }); }); } diff --git a/src/test/java/org/gnuhpc/bigdata/service/KafkaAdminServiceTest.java b/src/test/java/org/gnuhpc/bigdata/service/KafkaAdminServiceTest.java index a7ecada..2d85678 100644 --- a/src/test/java/org/gnuhpc/bigdata/service/KafkaAdminServiceTest.java +++ b/src/test/java/org/gnuhpc/bigdata/service/KafkaAdminServiceTest.java @@ -99,16 +99,23 @@ public class KafkaAdminServiceTest { @InjectMocks private KafkaAdminService kafkaAdminServiceUnderTest; private static final String TEST_KAFKA_BOOTSTRAP_SERVERS = - "localhost:9092,localhost:9094,localhost:9096"; - private static final List TEST_KAFKA_BOOTSTRAP_SERVERS_ID = Arrays.asList(111, 113, 115); + "localhost:19092,localhost:19094,localhost:19096"; + private static final List TEST_KAFKA_BOOTSTRAP_SERVERS_ID = Arrays.asList(10, 11, 12); private static final int KAFKA_NODES_COUNT = TEST_KAFKA_BOOTSTRAP_SERVERS_ID.size(); private static final String TEST_ZK = "127.0.0.1:2181"; - private static final int TEST_CONTROLLER_ID = 0; + private static final int TEST_CONTROLLER_ID = 10; +// private static final List TEST_KAFKA_LOG_DIRS = +// Arrays.asList( +// "/home/xiangli/bigdata/kafka_2.11-1.1.1/kafka-logs,/home/xiangli/bigdata/kafka_2.11-1.1.1/kafka111_2-logs,/home/xiangli/bigdata/kafka_2.11-1.1.1/kafka111_3-logs", +// "/home/xiangli/bigdata/kafka_2.11-1.1.1/kafka113-logs,/home/xiangli/bigdata/kafka_2.11-1.1.1/kafka113_2-logs,/home/xiangli/bigdata/kafka_2.11-1.1.1/kafka113_3-logs", +// "/home/xiangli/bigdata/kafka_2.11-1.1.1/kafka115-logs,/home/xiangli/bigdata/kafka_2.11-1.1.1/kafka115_2-logs,/home/xiangli/bigdata/kafka_2.11-1.1.1/kafka115_3-logs"); + private static final List TEST_KAFKA_LOG_DIRS = Arrays.asList( - "/home/xiangli/bigdata/kafka_2.11-1.1.1/kafka-logs,/home/xiangli/bigdata/kafka_2.11-1.1.1/kafka111_2-logs,/home/xiangli/bigdata/kafka_2.11-1.1.1/kafka111_3-logs", - "/home/xiangli/bigdata/kafka_2.11-1.1.1/kafka113-logs,/home/xiangli/bigdata/kafka_2.11-1.1.1/kafka113_2-logs,/home/xiangli/bigdata/kafka_2.11-1.1.1/kafka113_3-logs", - "/home/xiangli/bigdata/kafka_2.11-1.1.1/kafka115-logs,/home/xiangli/bigdata/kafka_2.11-1.1.1/kafka115_2-logs,/home/xiangli/bigdata/kafka_2.11-1.1.1/kafka115_3-logs"); + "/Users/wenqiao/work/bigdata/kafka_2.11-1.1.1/data", + "/Users/wenqiao/work/bigdata/kafka_2.11-1.1.1_2/data", + "/Users/wenqiao/work/bigdata/kafka_2.11-1.1.1_3/data"); + private static final String FIRST_TOPIC_TO_TEST = "first"; private static final String SECOND_TOPIC_TO_TEST = "second"; @@ -2100,22 +2107,23 @@ public void testResetOffsetOnInvalidPartition() throws InterruptedException { } @Test - public void testDescribeLogDirsByBrokerAndTopic() { + public void testDescribeLogDirsByBrokerAndTopicPartition() { // Setup final String topic = FIRST_TOPIC_TO_TEST; - final List topicList = Arrays.asList(topic); + final Map> topicPartitionMap = new HashMap<>(); + topicPartitionMap.put(topic, Arrays.asList(0)); int brokerId = TEST_KAFKA_BOOTSTRAP_SERVERS_ID.get(0); final List brokerList = Arrays.asList(brokerId); Map> replicaAssignment = new HashMap<>(); replicaAssignment.put(0, brokerList); - // Create first topic with 1 partition on broker 111 + // Create first topic with 1 partition on first broker createOneTopic(topic, replicaAssignment); // Run the test final Map> result = - kafkaAdminServiceUnderTest.describeLogDirsByBrokerAndTopic(brokerList, null, topicList); + kafkaAdminServiceUnderTest.describeLogDirsByBrokerAndTopic(brokerList, null, topicPartitionMap); // Verify the results assertTrue(result.containsKey(brokerId)); @@ -2137,17 +2145,54 @@ public void testDescribeLogDirsByBrokerAndTopic() { } @Test - public void testDescribeLogDirsByBrokerLogDirAndTopic() { + public void testDescribeLogDirsByBrokerLogDirAndTopicPartition() { + // Setup + final String topic = FIRST_TOPIC_TO_TEST; + final Map> topicPartitionMap = new HashMap<>(); + topicPartitionMap.put(topic, Arrays.asList(0)); + + int brokerId = TEST_KAFKA_BOOTSTRAP_SERVERS_ID.get(0); + final List brokerList = Arrays.asList(brokerId); + Map> replicaAssignment = new HashMap<>(); + replicaAssignment.put(0, brokerList); + replicaAssignment.put(1, brokerList); + + // Create first topic with 2 partition on first broker + createOneTopic(topic, replicaAssignment); + + TopicPartitionReplica topicPartitionReplica = new TopicPartitionReplica(topic, 0, brokerId); + Map replicaReplicaLogDirInfoMap = kafkaAdminServiceUnderTest.describeReplicaLogDirs(Arrays.asList(topicPartitionReplica)); + String logDir = replicaReplicaLogDirInfoMap.get(topicPartitionReplica).getCurrentReplicaLogDir(); + + // Run the test + final Map> result = + kafkaAdminServiceUnderTest.describeLogDirsByBrokerAndTopic(brokerList, Arrays.asList(logDir), topicPartitionMap); + + // Verify the results + assertTrue(result.containsKey(brokerId)); + Map logDirInfoMap = result.get(brokerId); + assertEquals(1, logDirInfoMap.size()); + assertTrue(logDirInfoMap.containsKey(logDir)); + LogDirInfo logDirInfo = logDirInfoMap.get(logDir); + TopicPartition topicPartition = new TopicPartition(topic, 0); + assertTrue(logDirInfo.replicaInfos.containsKey(topicPartition)); + TopicPartition topicPartition1 = new TopicPartition(topic, 1); + assertFalse(logDirInfo.replicaInfos.containsKey(topicPartition1)); + } + + @Test + public void testDescribeLogDirsByBrokerLogDirAndNullTopicPartition() { // Setup final String topic = FIRST_TOPIC_TO_TEST; - final List topicList = Arrays.asList(topic); + final Map> topicPartitionMap = new HashMap<>(); + topicPartitionMap.put(topic, null); int brokerId = TEST_KAFKA_BOOTSTRAP_SERVERS_ID.get(0); final List brokerList = Arrays.asList(brokerId); Map> replicaAssignment = new HashMap<>(); replicaAssignment.put(0, brokerList); - // Create first topic with 1 partition on broker 111 + // Create first topic with 1 partition on first broker createOneTopic(topic, replicaAssignment); TopicPartitionReplica topicPartitionReplica = new TopicPartitionReplica(topic, 0, brokerId); @@ -2156,7 +2201,7 @@ public void testDescribeLogDirsByBrokerLogDirAndTopic() { // Run the test final Map> result = - kafkaAdminServiceUnderTest.describeLogDirsByBrokerAndTopic(brokerList, Arrays.asList(logDir), topicList); + kafkaAdminServiceUnderTest.describeLogDirsByBrokerAndTopic(brokerList, Arrays.asList(logDir), topicPartitionMap); // Verify the results assertTrue(result.containsKey(brokerId)); @@ -2177,7 +2222,7 @@ public void testDescribeReplicaLogDirs() { Map> replicaAssignment = new HashMap<>(); replicaAssignment.put(0, brokerList); - // Create first topic with 1 partition on broker 111 + // Create first topic with 1 partition on first broker createOneTopic(topic, replicaAssignment); TopicPartitionReplica topicPartitionReplica = new TopicPartitionReplica(topic, 0, brokerId); @@ -2190,7 +2235,7 @@ public void testDescribeReplicaLogDirs() { String currentLogDir = result.get(topicPartitionReplica).getCurrentReplicaLogDir(); final Map> logDirsByBrokerAndTopic = kafkaAdminServiceUnderTest.describeLogDirsByBrokerAndTopic( - brokerList, null, Arrays.asList(topic)); + brokerList, null, null); Map logDirInfoMap = logDirsByBrokerAndTopic.get(brokerId); assertTrue(logDirInfoMap.containsKey(currentLogDir)); TopicPartition topicPartition = new TopicPartition(topic, 0); From 1ddd053ddf3f1ee348a54de43fbff736ad5a3b8a Mon Sep 17 00:00:00 2001 From: wenqiao <315524513@qq.com> Date: Thu, 25 Apr 2019 17:17:59 +0800 Subject: [PATCH 20/24] Add keyDecoder,maxRecords,timeoutMs parameters when getting records by offset. Add api to stop reassign partitions. --- .../bigdata/controller/KafkaController.java | 20 +- .../java/org/gnuhpc/bigdata/model/Record.java | 37 ++- .../bigdata/service/KafkaAdminService.java | 232 ++++++++++-------- .../org/gnuhpc/bigdata/utils/KafkaUtils.java | 87 ++++--- src/main/resources/application-tina.yml | 2 +- .../service/KafkaAdminServiceTest.java | 159 +++++++++--- 6 files changed, 358 insertions(+), 179 deletions(-) diff --git a/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java b/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java index 7ad771d..a47c0bb 100644 --- a/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java +++ b/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java @@ -172,14 +172,18 @@ public boolean existTopic(@PathVariable String topic) { @GetMapping(value = "/consumer/{topic}/{partition}/{offset}") @ApiOperation( value = - "Get the message from the offset of the partition in the topic" - + ", decoder is not supported yet") - public Record getMessage( + "Get the message from the offset of the partition in the topic") + public List getMessage( @PathVariable String topic, @PathVariable int partition, @PathVariable long offset, - @RequestParam(required = false) String decoder) { - return kafkaAdminService.getRecordByOffset(topic, partition, offset, decoder, ""); + @RequestParam(required = false) int maxRecords, + @RequestParam(required = false, defaultValue = "StringDeserializer") String keyDecoder, + @RequestParam(required = false, defaultValue = "StringDeserializer") String valueDecoder, + @RequestParam(required = false) String avroSchema, + @RequestParam(required = false, defaultValue = "30000") long fetchTimeoutMs) throws ApiException { + return kafkaAdminService.getRecordsByOffset(topic, partition, offset, maxRecords, keyDecoder, + valueDecoder, avroSchema, fetchTimeoutMs); } @GetMapping(value = "/topics/{topic}") @@ -266,6 +270,12 @@ public ReassignStatus checkReassignPartitions(@RequestBody ReassignModel reassig return kafkaAdminService.checkReassignStatus(reassign); } + @PutMapping(value = "/partitions/reassign/stop") + @ApiOperation(value = "Stop the partition reassignment process") + public GeneralResponse stopReassignPartitions() { + return kafkaAdminService.stopReassignPartitions(); + } + @GetMapping(value = "/consumergroups") @ApiOperation(value = "List all consumer groups from zk and kafka") public Map> listAllConsumerGroups( diff --git a/src/main/java/org/gnuhpc/bigdata/model/Record.java b/src/main/java/org/gnuhpc/bigdata/model/Record.java index eaf6028..eb6236a 100644 --- a/src/main/java/org/gnuhpc/bigdata/model/Record.java +++ b/src/main/java/org/gnuhpc/bigdata/model/Record.java @@ -5,23 +5,28 @@ import lombok.Data; import lombok.Getter; import lombok.Setter; +import lombok.extern.log4j.Log4j; import org.apache.kafka.common.errors.ApiException; import org.apache.kafka.common.utils.Bytes; +import org.gnuhpc.bigdata.utils.KafkaUtils; @Data @Getter @Setter @Builder +@Log4j public class Record { public String topic; public long offset; public Object key = new Object(); public Object value = new Object(); public long timestamp; - public Class type; - String decoder; + String keyDecoder; + String valueDecoder; - public String getValue() { + public String getValueByDecoder(String decoder, Object value) { + if (value == null) return null; + Class type = KafkaUtils.DESERIALIZER_TYPE_MAP.get(decoder); try { if (String.class.isAssignableFrom(type)) { return value.toString(); @@ -53,7 +58,7 @@ public String getValue() { } if (byte[].class.isAssignableFrom(type)) { - if (decoder.equals("AvorDeserializer")) { + if (decoder.equals("AvroDeserializer")) { return value.toString(); } else { byte[] byteArray = (byte[]) value; @@ -74,6 +79,16 @@ public String getValue() { + "String, Short, Integer, Long, Float, Double, ByteArray, ByteBuffer, Bytes"); } + public String getValue() { + log.info("getValue for value:" + value + " by decoder:" + valueDecoder); + return getValueByDecoder(valueDecoder, value); + } + + public String getKey() { + log.info("getKeyValue for key:" + key + " by decoder:" + keyDecoder); + return getValueByDecoder(keyDecoder, key); + } + @Override public String toString() { if (value != null) { @@ -82,13 +97,15 @@ public String toString() { + ", offset:" + offset + ", key:" - + key + + getKey() + ", value:" + getValue() + ", timestamp:" + timestamp - + ", type:" - + type; + + ", keyDecoder:" + + keyDecoder + + ", valueDecoder:" + + valueDecoder; } else { return "topic:" + topic @@ -100,8 +117,10 @@ public String toString() { + value + ", timestamp:" + timestamp - + ", type:" - + type; + + ", keyDecoder:" + + keyDecoder + + ", valueDecoder:" + + valueDecoder; } } } diff --git a/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java b/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java index 0152571..4a3c833 100644 --- a/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java +++ b/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java @@ -2,7 +2,6 @@ import static java.lang.String.format; import static java.util.stream.Collectors.toList; -import static java.util.stream.Collectors.toMap; import static java.util.stream.Collectors.toSet; import com.fasterxml.jackson.core.JsonProcessingException; @@ -30,7 +29,6 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; -import java.util.function.Function; import java.util.regex.Pattern; import java.util.stream.Collectors; import javax.annotation.PostConstruct; @@ -145,7 +143,9 @@ import scala.runtime.AbstractFunction0; import scala.runtime.BoxedUnit; -/** Created by gnuhpc on 2017/7/17. */ +/** + * Created by gnuhpc on 2017/7/17. + */ @Service @Log4j @Validated @@ -164,13 +164,17 @@ public class KafkaAdminService { public static final String ReplicaAlterLogDirsIoMaxBytesPerSecondProp = "replica.alter.log.dirs.io.max.bytes.per.second"; - @Autowired private ZookeeperUtils zookeeperUtils; + @Autowired + private ZookeeperUtils zookeeperUtils; - @Autowired private KafkaUtils kafkaUtils; + @Autowired + private KafkaUtils kafkaUtils; - @Autowired private KafkaConfig kafkaConfig; + @Autowired + private KafkaConfig kafkaConfig; - @Autowired private OffsetStorage storage; + @Autowired + private OffsetStorage storage; // For AdminUtils use private ZkUtils zkUtils; @@ -182,7 +186,8 @@ public class KafkaAdminService { private scala.Option none = scala.Option.apply(null); @PostConstruct - private void init() {} + private void init() { + } private org.apache.kafka.clients.admin.AdminClient createKafkaAdminClient() { if (this.kafkaAdminClient == null) { @@ -322,7 +327,8 @@ public List listTopicBrief() { isrCount += topicPartitionInfo.isr().size(); } if (replicateCount == 0) { - return new TopicBrief(topic, topicDescription.partitions().size(), 0, replicateCount); + return new TopicBrief(topic, topicDescription.partitions().size(), 0, + replicateCount); } else { return new TopicBrief( topic, @@ -442,7 +448,7 @@ public Map> listLogDirsByBroker(List brokerList) Map> logDirList = new HashMap<>(); Map> logDirInfosByBroker = - describeLogDirsByBrokerAndTopic(brokerList, null,null); + describeLogDirsByBrokerAndTopic(brokerList, null, null); logDirInfosByBroker .entrySet() .forEach( @@ -456,7 +462,8 @@ public Map> listLogDirsByBroker(List brokerList) } public Map> describeLogDirsByBrokerAndTopic( - List brokerList, List logDirList, Map> topicPartitionMap) { + List brokerList, List logDirList, + Map> topicPartitionMap) { org.apache.kafka.clients.admin.AdminClient kafkaAdminClient = createKafkaAdminClient(); List brokerIdsInCluster = @@ -496,7 +503,7 @@ public Map> describeLogDirsByBrokerAndTopic( throw new ApiException("Describe log dirs exception:" + exception); } finally { if (logDirList != null && !logDirList.isEmpty()) { - logDirInfosByBroker.entrySet().forEach(e->{ + logDirInfosByBroker.entrySet().forEach(e -> { e.getValue().entrySet().removeIf(m -> !logDirList.contains(m.getKey())); }); } @@ -511,9 +518,11 @@ public Map> describeLogDirsByBrokerAndTopic( m -> { m.getValue() .replicaInfos.entrySet() - .removeIf(t -> !topicPartitionMap.keySet().contains(t.getKey().topic()) || - (topicPartitionMap.get(t.getKey().topic()) != null && - !topicPartitionMap.get(t.getKey().topic()).contains(t.getKey().partition()))); + .removeIf( + t -> !topicPartitionMap.keySet().contains(t.getKey().topic()) || + (topicPartitionMap.get(t.getKey().topic()) != null && + !topicPartitionMap.get(t.getKey().topic()) + .contains(t.getKey().partition()))); }); }); } @@ -660,16 +669,7 @@ public TopicDescription getTopicDescription(@TopicExistConstraint String topicNa } public TopicMeta describeTopic(@TopicExistConstraint String topicName) { -// org.apache.kafka.clients.admin.AdminClient kafkaAdminClient = createKafkaAdminClient(); -// -// DescribeTopicsResult describeTopicsResult = -// kafkaAdminClient.describeTopics(Collections.singletonList(topicName)); TopicMeta topicMeta = new TopicMeta(topicName); -// try { -// Map topicMap = -// describeTopicsResult.all().get(kafkaAdminClientGetTimeoutMs, TimeUnit.MILLISECONDS); -// if (topicMap.containsKey(topicName)) { -// TopicDescription topicDescription = topicMap.get(topicName); TopicDescription topicDescription = getTopicDescription(topicName); if (topicDescription != null) { List tmList = topicDescription.partitions(); @@ -700,11 +700,6 @@ public TopicMeta describeTopic(@TopicExistConstraint String topicName) { .collect(toList())); Collections.sort(topicMeta.getTopicPartitionInfos()); } -// } -// } catch (Exception exception) { -// exception.printStackTrace(); -// throw new ApiException("Describe topic exception." + exception); -// } return topicMeta; } @@ -1137,7 +1132,8 @@ public List describeNewConsumerGroup( } public List describeNewConsumerGroup( - String consumerGroup, boolean filtered, String topic, ConsumerGroupSummary consumerGroupSummary) { + String consumerGroup, boolean filtered, String topic, + ConsumerGroupSummary consumerGroupSummary) { List partitionAssignmentStateList = new ArrayList<>(); if (filtered && !existTopic(topic)) { @@ -1149,17 +1145,19 @@ public List describeNewConsumerGroup( List consumerSummaryList = CollectionConvertor.listConvertJavaList(consumerGroupSummary.consumers().get()); String consumersInfo = ""; - for (ConsumerSummary cs:consumerSummaryList) { + for (ConsumerSummary cs : consumerSummaryList) { consumersInfo = consumersInfo + "{clientId:" + cs.clientId() + ", host:" + cs.host() + ", consumerId:" + cs.consumerId() + "}\n"; } - log.info("Describe consumer group:" + consumerGroup + " summary. ConsumerSummary List:" + consumersInfo); + log.info("Describe consumer group:" + consumerGroup + " summary. ConsumerSummary List:" + + consumersInfo); if (consumerSummaryList != null) { Map offsets = CollectionConvertor.mapConvertJavaMap(adminClient.listGroupOffsets(consumerGroup)); log.info("List group offsets for consumer:" + consumerGroup + ". Result is:\n"); - for(Map.Entry offset:offsets.entrySet()) { - log.info("Topic:" + offset.getKey().topic() + ", Partition:" + offset.getKey().partition() + ", Offset:" + offset.getValue()); + for (Map.Entry offset : offsets.entrySet()) { + log.info("Topic:" + offset.getKey().topic() + ", Partition:" + offset.getKey().partition() + + ", Offset:" + offset.getValue()); } Map offsetsFiltered; @@ -1173,9 +1171,11 @@ public List describeNewConsumerGroup( topicPartitionObjectEntry -> topicPartitionObjectEntry.getKey().topic().equals(topic)) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); - log.info("After filter by topic:" + topic + " for consumer:" + consumerGroup + ", offsets are:"); - for(Map.Entry offset:offsetsFiltered.entrySet()) { - log.info("Topic:" + offset.getKey().topic() + ", Partition:" + offset.getKey().partition() + ", Offset:" + offset.getValue()); + log.info( + "After filter by topic:" + topic + " for consumer:" + consumerGroup + ", offsets are:"); + for (Map.Entry offset : offsetsFiltered.entrySet()) { + log.info("Topic:" + offset.getKey().topic() + ", Partition:" + offset.getKey().partition() + + ", Offset:" + offset.getValue()); } } else { offsetsFiltered = offsets; @@ -1200,9 +1200,12 @@ public List describeNewConsumerGroup( .collect(toList()); } Map partitionOffsets = new HashMap<>(); - log.info("topicPartitionsFiltered.size = " + topicPartitionsFiltered.size() + ". Detail is:"); - for (TopicPartition tp:topicPartitionsFiltered) { - log.info("topic:" + tp.topic() + ", partition:" + tp.partition() + ", offset:" + offsetsFiltered.get(tp)); + log.info("topicPartitionsFiltered.size = " + topicPartitionsFiltered.size() + + ". Detail is:"); + for (TopicPartition tp : topicPartitionsFiltered) { + log.info( + "topic:" + tp.topic() + ", partition:" + tp.partition() + ", offset:" + + offsetsFiltered.get(tp)); partitionOffsets.put(tp, offsetsFiltered.get(tp)); } assignedTopicPartitions.addAll(topicPartitionsFiltered); @@ -1216,12 +1219,13 @@ public List describeNewConsumerGroup( topicPartition -> offsetsFiltered.get(topicPartition))); */ log.info("Topic partitions with consumers. Detail is:"); - for(Map.Entry offset:partitionOffsets.entrySet()) { + for (Map.Entry offset : partitionOffsets.entrySet()) { log.info("Topic:" + offset.getKey().topic() + ", Partition:" + offset.getKey().partition() + ", Offset:" + offset.getValue() + ", consumerGroup:" + consumerGroup + ", consumerId:" + consumerSummary.consumerId() + "," + ", consumerHost:" + - consumerSummary.host() + ", consumerClient:" + consumerSummary.clientId()); + consumerSummary.host() + ", consumerClient:" + consumerSummary + .clientId()); } return collectConsumerAssignment( consumerGroup, @@ -1293,7 +1297,7 @@ private List collectConsumerAssignment( topicPartition -> { long logEndOffset = getEndOffset(topicPartition.topic(), topicPartition.partition()); long offset = (Long) partitionOffsets.get(topicPartition); - long lag = (logEndOffset < 0)?0:logEndOffset - offset; + long lag = (logEndOffset < 0) ? 0 : logEndOffset - offset; list.add( new PartitionAssignmentState( group, @@ -1370,10 +1374,13 @@ private JavaType getCollectionType( return mapper.getTypeFactory().constructParametricType(collectionClass, elementClasses); } - private List getNewConsumerGroupDescByConsumerGroupAndTopic(String consumerGroup, String topic) { + private List getNewConsumerGroupDescByConsumerGroupAndTopic( + String consumerGroup, String topic) { AdminClient adminClient = kafkaUtils.createAdminClient(); - ConsumerGroupSummary consumerGroupSummary = adminClient.describeConsumerGroup(consumerGroup, kafkaAdminClientGetTimeoutMs); - log.info("Describe consumer group:" + consumerGroup + ". Summary:" +consumerGroupSummary.consumers()); + ConsumerGroupSummary consumerGroupSummary = adminClient + .describeConsumerGroup(consumerGroup, kafkaAdminClientGetTimeoutMs); + log.info("Describe consumer group:" + consumerGroup + ". Summary:" + consumerGroupSummary + .consumers()); List partitionAssignmentStateList = describeNewConsumerGroup(consumerGroup, true, topic, consumerGroupSummary); log.info("Partition Assignment State List for consumer:" + consumerGroup + " fitlered by topic:" @@ -1404,7 +1411,7 @@ public List describeNewConsumerGroupByTopic( // To search all consumer groups Set allNewConsumerGroups = listAllNewConsumerGroups(); - for (String cg:allNewConsumerGroups) { + for (String cg : allNewConsumerGroups) { consumerGroupDescList.addAll(getNewConsumerGroupDescByConsumerGroupAndTopic(cg, topic)); } return consumerGroupDescList; @@ -1413,7 +1420,8 @@ public List describeNewConsumerGroupByTopic( } } - private List getOldConsumerGroupDescByConsumerGroupAndTopic(String consumerGroup, String topic) { + private List getOldConsumerGroupDescByConsumerGroupAndTopic( + String consumerGroup, String topic) { List partitionAssignmentStateList = describeOldConsumerGroup(consumerGroup, true, topic); @@ -1442,7 +1450,7 @@ public List describeOldConsumerGroupByTopic( // To search all consumer groups Set allNewConsumerGroups = listAllNewConsumerGroups(); - for (String cg:allNewConsumerGroups) { + for (String cg : allNewConsumerGroups) { consumerGroupDescList.addAll(getOldConsumerGroupDescByConsumerGroupAndTopic(cg, topic)); } return consumerGroupDescList; @@ -1820,6 +1828,24 @@ public ReassignStatus checkReassignStatus(ReassignModel reassignModel) { return new TwoTuple<>(partitionsToBeReassignedMap, replicatAssignment); } + public GeneralResponse stopReassignPartitions() { + GeneralResponse response = GeneralResponse.builder().build(); + + KafkaZkClient kafkaZkClient = zookeeperUtils.createKafkaZkClient(); + log.info("Deleting zk path /admin/reassign_partitions"); + try { + kafkaZkClient.deletePartitionReassignment(); + response.setState(GeneralResponseState.success); + log.info("Delete zk path /admin/reassign_partitions successfully."); + } catch (Exception exception) { + response.setState(GeneralResponseState.failure); + response.setMsg("Delete zk path /admin/reassign_partitions failed."); + log.info("Delete zk path /admin/reassign_partitions failed."); + } + + return response; + } + public String getMessage( @TopicExistConstraint String topic, int partition, @@ -1845,7 +1871,7 @@ public String getMessage( String last = null; - // ConsumerRecords crs = consumer.poll(channelRetryBackoffMs); +// ConsumerRecords crs = consumer.poll(channelRetryBackoffMs); ConsumerRecords crs = consumer.poll(3000); log.info( "Seek to offset:" @@ -1879,59 +1905,67 @@ public String getMessage( return last; } - public Record getRecordByOffset( + public List getRecordsByOffset( @TopicExistConstraint String topic, int partition, long offset, - String decoder, - String avroSchema) { + int maxRecords, + String keyDecoder, + String valueDecoder, + String avroSchema, + long timeoutMs) throws ApiException { if (!isTopicPartitionValid(topic, partition)) { throw new ApiException("Bad request. Topic:" + topic + " has no partition:" + partition); } checkOffsetValid(topic, partition, offset); - if (!kafkaUtils.DESERIALIZER_TYPE_MAP.containsKey(decoder)) { + if (!kafkaUtils.DESERIALIZER_TYPE_MAP.containsKey(valueDecoder)) { throw new ApiException( "Bad request. Decoder class:" - + decoder + + valueDecoder + " not found. ByteArrayDeserializer, ByteBufferDeserializer, BytesDeserializer, " + "DoubleDeserializer, FloatDeserializer, " + "IntegerDeserializer, LongDeserializer, ShortDeserializer, StringDeserializer, AvroDeserializer " + "are supported."); } - if (decoder != null && decoder.equals("AvroDeserializer")) { + if (valueDecoder == null || valueDecoder.isEmpty()) { + // default decoder is StringDeserializer + valueDecoder = "StringDeserializer"; + } + if (keyDecoder == null || keyDecoder.isEmpty()) { + // default decoder is StringDeserializer + keyDecoder = "StringDeserializer"; + } + + if (valueDecoder != null && valueDecoder.equals("AvroDeserializer")) { if (avroSchema == null || avroSchema.isEmpty()) { throw new ApiException("Bad request. Schema is needed when choosing AvroDeserializer."); } else { - return getAvroRecordByOffset(topic, partition, offset, avroSchema); + return getAvroRecordsByOffset(topic, partition, offset, keyDecoder, avroSchema, maxRecords, + timeoutMs); } } - if (decoder == null || decoder.isEmpty()) { - // default decoder is StringDeserializer - decoder = "StringDeserializer"; - } - - Class type = kafkaUtils.DESERIALIZER_TYPE_MAP.get(decoder); - String dese = Serdes.serdeFrom(type).deserializer().getClass().getCanonicalName(); - KafkaConsumer consumer; try { - consumer = kafkaUtils.createNewConsumer(String.valueOf(System.currentTimeMillis()), dese); + consumer = kafkaUtils + .createNewConsumer(String.valueOf(System.currentTimeMillis()), keyDecoder, valueDecoder, + maxRecords); } catch (ClassNotFoundException classNotFoundException) { - throw new ApiException("Class " + dese + "not found exception." + classNotFoundException); + throw new ApiException("Class for keyDecoder:" + keyDecoder + ", valueDecoder:" + valueDecoder + + "not found exception." + classNotFoundException); } TopicPartition tp = new TopicPartition(topic, partition); consumer.assign(Collections.singletonList(tp)); consumer.seek(tp, offset); - Record record = Record.builder().topic(topic).type(type).decoder(decoder).build(); + List recordList = new ArrayList<>(); try { - ConsumerRecords crs = consumer.poll(3000); + ConsumerRecords crs = consumer.poll(timeoutMs); log.info( "Seek to offset:" + offset @@ -1944,14 +1978,13 @@ public Record getRecordByOffset( if (crs.count() != 0) { Iterator> it = crs.iterator(); while (it.hasNext()) { + Record record = Record.builder().topic(topic).keyDecoder(keyDecoder) + .valueDecoder(valueDecoder).build(); ConsumerRecord initCr = it.next(); - if (initCr.offset() == offset) { - record.setOffset(offset); - record.setTimestamp(initCr.timestamp()); - record.setKey(initCr.key()); - record.setValue(initCr.value()); - break; - } + record.setOffset(offset); + record.setTimestamp(initCr.timestamp()); + record.setKey(initCr.key()); + record.setValue(initCr.value()); log.info( "Value: " + initCr.value() @@ -1959,6 +1992,7 @@ public Record getRecordByOffset( + String.valueOf(initCr.offset()) + ", timestamp:" + initCr.timestamp()); + recordList.add(record); } } } catch (Exception exception) { @@ -1969,15 +2003,15 @@ public Record getRecordByOffset( + partition + " offset:" + offset - + " using " - + decoder + + " using keyDecoder:" + keyDecoder + ", valueDecoder:" + + valueDecoder + " exception. " + exception.getLocalizedMessage()); } finally { consumer.close(); } - return record; + return recordList; } private boolean isTopicPartitionValid(String topic, int partition) { @@ -2015,25 +2049,26 @@ public void checkOffsetValid(String topic, int partition, long offset) { } } - public Record getAvroRecordByOffset(String topic, int partition, long offset, String avroSchema) { + public List getAvroRecordsByOffset(String topic, int partition, long offset, + String keyDecoder, + String avroSchema, int maxRecords, long timeoutMs) throws ApiException { TopicPartition tp = new TopicPartition(topic, partition); KafkaConsumer consumer = null; + final String valueDecoder = "AvroDeserializer"; try { - consumer = - kafkaUtils.createNewConsumer( - String.valueOf(System.currentTimeMillis()), - "org.apache.kafka.common.serialization.ByteArrayDeserializer"); + consumer = kafkaUtils.createNewConsumer( + String.valueOf(System.currentTimeMillis()), keyDecoder, + valueDecoder, maxRecords); } catch (Exception exception) { log.error("Create consumer exception." + exception); + throw new ApiException("Create consumer exception:" + exception.getMessage()); } consumer.assign(Collections.singletonList(tp)); consumer.seek(tp, offset); - Record record = - Record.builder().topic(topic).type(byte[].class).decoder("AvorDeserializer").build(); - + List recordList = new ArrayList<>(); try { - ConsumerRecords crs = consumer.poll(3000); + ConsumerRecords crs = consumer.poll(timeoutMs); log.info( "Seek to offset:" + offset @@ -2047,13 +2082,13 @@ public Record getAvroRecordByOffset(String topic, int partition, long offset, St Iterator> it = crs.iterator(); while (it.hasNext()) { ConsumerRecord initCr = it.next(); - if (initCr.offset() == offset) { - record.setOffset(offset); - record.setTimestamp(initCr.timestamp()); - record.setKey(initCr.key()); - record.setValue(avroDeserialize(initCr.value(), avroSchema)); - break; - } + Record record = + Record.builder().topic(topic).keyDecoder(keyDecoder).valueDecoder(valueDecoder) + .build(); + record.setOffset(offset); + record.setTimestamp(initCr.timestamp()); + record.setKey(initCr.key()); + record.setValue(avroDeserialize(initCr.value(), avroSchema)); log.info( "Value: " + initCr.value() @@ -2061,6 +2096,7 @@ public Record getAvroRecordByOffset(String topic, int partition, long offset, St + String.valueOf(initCr.offset()) + ", timestamp:" + initCr.timestamp()); + recordList.add(record); } } } catch (Exception exception) { @@ -2071,7 +2107,7 @@ public Record getAvroRecordByOffset(String topic, int partition, long offset, St + partition + " offset:" + offset - + " using " + + " using keyDecoder:" + keyDecoder + ", valueDecoder:" + "ByteArrayDeserializer" + " exception. " + exception.getLocalizedMessage()); @@ -2079,7 +2115,7 @@ public Record getAvroRecordByOffset(String topic, int partition, long offset, St consumer.close(); } - return record; + return recordList; } private Object avroDeserialize(byte[] bytes, String avroSchema) { @@ -2479,7 +2515,7 @@ private long getOffsets(Node leader, String topic, int partitionId, long time) { boolean partitionLeaderExist(String topic, int partitionId) { TopicDescription topicDes = getTopicDescription(topic); if (topicDes != null) { - for(TopicPartitionInfo tpi:topicDes.partitions()) { + for (TopicPartitionInfo tpi : topicDes.partitions()) { if (tpi.partition() == partitionId) { return tpi.leader() != null; } diff --git a/src/main/java/org/gnuhpc/bigdata/utils/KafkaUtils.java b/src/main/java/org/gnuhpc/bigdata/utils/KafkaUtils.java index 1cb18d6..c50ee9f 100644 --- a/src/main/java/org/gnuhpc/bigdata/utils/KafkaUtils.java +++ b/src/main/java/org/gnuhpc/bigdata/utils/KafkaUtils.java @@ -16,6 +16,7 @@ import org.apache.kafka.clients.producer.ProducerConfig; import org.apache.kafka.common.Node; import org.apache.kafka.common.PartitionInfo; +import org.apache.kafka.common.serialization.Serdes; import org.apache.kafka.common.serialization.StringDeserializer; import org.apache.kafka.common.serialization.StringSerializer; import org.apache.kafka.common.utils.Bytes; @@ -58,6 +59,21 @@ public class KafkaUtils { } }; + public static final Map> SERIALIZER_TYPE_MAP = new HashMap() { + { + put("StringSerializer", String.class); + put("ShortSerializer", Short.class); + put("IntegerSerializer", Integer.class); + put("LongSerializer", Long.class); + put("FloatSerializer", Float.class); + put("DoubleSerializer", Double.class); + put("ByteArraySerializer", byte[].class); + put("ByteBufferSerializer", ByteBuffer.class); + put("BytesSerializer", Bytes.class); + put("AvroSerializer", byte[].class); + } + }; + public void init() { } @@ -104,29 +120,42 @@ public KafkaConsumer createNewConsumerByClientId(String consumerGroup, String cl return new KafkaConsumer(properties); } - public KafkaConsumer createNewConsumer(String consumerGroup, String decoder) + public KafkaConsumer createNewConsumer(String consumerGroup, String keyDecoder, + String valueDecoder, int maxRecords) throws ClassNotFoundException { Properties properties = new Properties(); - properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, getKafkaConfig().getBrokers()); - properties.put(ConsumerConfig.GROUP_ID_CONFIG, consumerGroup); - properties.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "false"); - properties.put(ConsumerConfig.SESSION_TIMEOUT_MS_CONFIG, "30000"); - properties.put(ConsumerConfig.MAX_PARTITION_FETCH_BYTES_CONFIG, "100000000"); - properties.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, "5"); - if (decoder == null || decoder.isEmpty()) { + if (keyDecoder == null || keyDecoder.isEmpty()) { properties.put( ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getCanonicalName()); + } else { + Class keyType = KafkaUtils.DESERIALIZER_TYPE_MAP.get(keyDecoder); + String keyDese = Serdes.serdeFrom(keyType).deserializer().getClass().getCanonicalName(); + properties.put( + ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, + Class.forName(keyDese).getCanonicalName()); + } + + if (valueDecoder == null || valueDecoder.isEmpty()) { properties.put( ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getCanonicalName()); } else { - properties.put( - ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, Class.forName(decoder).getCanonicalName()); + Class valueType = KafkaUtils.DESERIALIZER_TYPE_MAP.get(valueDecoder); + String valDese = Serdes.serdeFrom(valueType).deserializer().getClass().getCanonicalName(); properties.put( ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, - Class.forName(decoder).getCanonicalName()); + Class.forName(valDese).getCanonicalName()); } + + properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, getKafkaConfig().getBrokers()); + properties.put(ConsumerConfig.GROUP_ID_CONFIG, consumerGroup); + properties.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "false"); + properties.put(ConsumerConfig.SESSION_TIMEOUT_MS_CONFIG, "30000"); + properties.put(ConsumerConfig.MAX_PARTITION_FETCH_BYTES_CONFIG, "100000000"); + properties.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, maxRecords); + + log.info("Consumer properties:" + properties); KafkaConsumer kafkaConsumer = new KafkaConsumer(properties); return kafkaConsumer; } @@ -159,33 +188,31 @@ public KafkaProducer createProducer() { return producer; } - public KafkaProducer createProducer(String encoder) throws ClassNotFoundException { + public KafkaProducer createProducer(String keyEncoder, String valueEncoder) throws ClassNotFoundException { Properties prop = new Properties(); prop.setProperty(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaConfig.getBrokers()); - prop.setProperty( - ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, - "org.apache.kafka.common.serialization.StringSerializer"); - prop.setProperty( - ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, - "org.apache.kafka.common.serialization.StringSerializer"); - prop.setProperty(ProducerConfig.RETRIES_CONFIG, "3"); - prop.setProperty(ProducerConfig.REQUEST_TIMEOUT_MS_CONFIG, "10000"); - producer = new KafkaProducer(prop); - - if (encoder == null || encoder.isEmpty()) { + if (keyEncoder == null || keyEncoder.isEmpty()) { + prop.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getCanonicalName()); + } else { + Class keyType = KafkaUtils.SERIALIZER_TYPE_MAP.get(keyEncoder); + String keySe = Serdes.serdeFrom(keyType).serializer().getClass().getCanonicalName(); prop.put( ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, - StringSerializer.class.getCanonicalName()); - prop.put( - ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, - StringSerializer.class.getCanonicalName()); + Class.forName(keySe).getCanonicalName()); + } + + if (valueEncoder == null || valueEncoder.isEmpty()) { + prop.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getCanonicalName()); } else { - prop.put( - ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, Class.forName(encoder).getCanonicalName()); + Class valueType = KafkaUtils.SERIALIZER_TYPE_MAP.get(valueEncoder); + String valSe = Serdes.serdeFrom(valueType).serializer().getClass().getCanonicalName(); prop.put( ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, - Class.forName(encoder).getCanonicalName()); + Class.forName(valSe).getCanonicalName()); } + + prop.setProperty(ProducerConfig.RETRIES_CONFIG, "3"); + prop.setProperty(ProducerConfig.REQUEST_TIMEOUT_MS_CONFIG, "10000"); producer = new KafkaProducer(prop); return producer; diff --git a/src/main/resources/application-tina.yml b/src/main/resources/application-tina.yml index 72a69ec..26c149d 100644 --- a/src/main/resources/application-tina.yml +++ b/src/main/resources/application-tina.yml @@ -8,7 +8,7 @@ kafka: topic: "health" zookeeper: - uris: 127.0.0.1:2183,127.0.0.1:2182 + uris: 127.0.0.1:2181 jmx: kafka: diff --git a/src/test/java/org/gnuhpc/bigdata/service/KafkaAdminServiceTest.java b/src/test/java/org/gnuhpc/bigdata/service/KafkaAdminServiceTest.java index 2d85678..ead083f 100644 --- a/src/test/java/org/gnuhpc/bigdata/service/KafkaAdminServiceTest.java +++ b/src/test/java/org/gnuhpc/bigdata/service/KafkaAdminServiceTest.java @@ -91,12 +91,18 @@ @FixMethodOrder(MethodSorters.JVM) @Log4j public class KafkaAdminServiceTest { - @Mock private static KafkaConfig mockKafkaConfig; - @Mock private static ZookeeperConfig mockZookeeperConfig; - @Spy private ZookeeperUtils mockZookeeperUtils = new ZookeeperUtils(); - @Spy private KafkaUtils mockKafkaUtils = new KafkaUtils(); - @InjectMocks private KafkaAdminService kafkaAdminServiceUnderTest; + @Mock + private static KafkaConfig mockKafkaConfig; + @Mock + private static ZookeeperConfig mockZookeeperConfig; + @Spy + private ZookeeperUtils mockZookeeperUtils = new ZookeeperUtils(); + @Spy + private KafkaUtils mockKafkaUtils = new KafkaUtils(); + + @InjectMocks + private KafkaAdminService kafkaAdminServiceUnderTest; private static final String TEST_KAFKA_BOOTSTRAP_SERVERS = "localhost:19092,localhost:19094,localhost:19096"; @@ -564,6 +570,7 @@ public void testDeleteInternalTopic() { } class ConsumerRunnable implements Runnable { + private final KafkaConsumer consumer; public ConsumerRunnable(String groupId, String clientId, List topicList) { @@ -583,6 +590,7 @@ public void close() { } class ConsumerGroup { + private List consumers; private List threadList; @@ -922,7 +930,9 @@ public void testDescribeConsumerGroup() throws InterruptedException { } private boolean isCollectionEqual(Collection collection1, Collection collection2) { - if (collection1.size() != collection2.size()) return false; + if (collection1.size() != collection2.size()) { + return false; + } Iterator iterator = collection2.iterator(); while (iterator.hasNext()) { @@ -1630,6 +1640,16 @@ private List produceRecords(String topic, int recordsCount) { return recordMetadataList; } + private String getEncoder(Class type) { + for (Map.Entry> entry:KafkaUtils.SERIALIZER_TYPE_MAP.entrySet()) { + if (entry.getValue().equals(type)) { + return entry.getKey(); + } + } + + return null; + } + private Map, List> produceRecords( String topic, Map, List> testData) { Map, List> dataOffsetMap = new HashMap<>(); @@ -1640,8 +1660,7 @@ private Map, List> produceRecords( List offsetList = new ArrayList<>(); try { kafkaProducer = - mockKafkaUtils.createProducer( - Serdes.serdeFrom(type).serializer().getClass().getCanonicalName()); + mockKafkaUtils.createProducer(null, getEncoder(type)); for (Object value : test.getValue()) { ProducerRecord record = new ProducerRecord(topic, value); try { @@ -1669,8 +1688,7 @@ private List produceAvroRecords(String topic, List testData) { List offsetList = new ArrayList<>(); try { kafkaProducer = - mockKafkaUtils.createProducer( - Serdes.serdeFrom(type).serializer().getClass().getCanonicalName()); + mockKafkaUtils.createProducer(null, "AvroSerializer"); for (byte[] value : testData) { ProducerRecord record = new ProducerRecord(topic, value); try { @@ -1697,6 +1715,25 @@ private List produceAvroRecords(String topic, List testData) { return offsetList; } + private RecordMetadata produceRecord(String topic, double key, String value) throws Exception{ + final String keySerializer = "DoubleSerializer"; + final String valueSerializer = "StringSerializer"; + KafkaProducer kafkaProducer = mockKafkaUtils.createProducer(keySerializer, valueSerializer); + RecordMetadata metadata = null; + + ProducerRecord record = new ProducerRecord(topic, key, value); + try { + metadata = (RecordMetadata) kafkaProducer.send(record).get(); + log.info("Record(key:" + key + ", value:" + value + ") has been sent to topic:" + + metadata.topic() + ", partition:" + metadata.partition() + ", offset:" + metadata.offset()); + } catch (Exception exception) { + log.error("Produce record:" + 0 + " error." + exception); + } + + kafkaProducer.close(); + return metadata; + } + @Test public void testResetOffsetToBeginning() throws InterruptedException { // Setup @@ -1736,7 +1773,9 @@ public void testResetOffsetToBeginning() throws InterruptedException { consumer.subscribe(Arrays.asList(topic)); while (true) { ConsumerRecords messages = consumer.poll(100); - if (messages.count() == 0) break; + if (messages.count() == 0) { + break; + } assertEquals(recordsCount, messages.count()); int i = 0; for (ConsumerRecord message : messages) { @@ -1789,7 +1828,9 @@ public void testResetOffsetToLatest() throws InterruptedException { consumer.subscribe(Arrays.asList(topic)); while (true) { ConsumerRecords messages = consumer.poll(100); - if (messages.count() == 0) break; + if (messages.count() == 0) { + break; + } assertEquals(recordsCount, messages.count()); int i = 0; for (ConsumerRecord message : messages) { @@ -1841,7 +1882,9 @@ public void testResetOffsetToSpecificOffset() throws InterruptedException { consumer.subscribe(Arrays.asList(topic)); while (true) { ConsumerRecords messages = consumer.poll(100); - if (messages.count() == 0) break; + if (messages.count() == 0) { + break; + } assertEquals(recordsCount - Long.parseLong(offset), messages.count()); int i = 0; for (ConsumerRecord message : messages) { @@ -1935,7 +1978,9 @@ public void testResetOffsetByTime() throws InterruptedException { consumer.subscribe(Arrays.asList(topic)); while (true) { ConsumerRecords messages = consumer.poll(100); - if (messages.count() == 0) break; + if (messages.count() == 0) { + break; + } assertEquals(recordsCount - recordIndexToReset, messages.count()); int i = 0; for (ConsumerRecord message : messages) { @@ -2123,7 +2168,8 @@ public void testDescribeLogDirsByBrokerAndTopicPartition() { // Run the test final Map> result = - kafkaAdminServiceUnderTest.describeLogDirsByBrokerAndTopic(brokerList, null, topicPartitionMap); + kafkaAdminServiceUnderTest + .describeLogDirsByBrokerAndTopic(brokerList, null, topicPartitionMap); // Verify the results assertTrue(result.containsKey(brokerId)); @@ -2161,12 +2207,15 @@ public void testDescribeLogDirsByBrokerLogDirAndTopicPartition() { createOneTopic(topic, replicaAssignment); TopicPartitionReplica topicPartitionReplica = new TopicPartitionReplica(topic, 0, brokerId); - Map replicaReplicaLogDirInfoMap = kafkaAdminServiceUnderTest.describeReplicaLogDirs(Arrays.asList(topicPartitionReplica)); - String logDir = replicaReplicaLogDirInfoMap.get(topicPartitionReplica).getCurrentReplicaLogDir(); + Map replicaReplicaLogDirInfoMap = kafkaAdminServiceUnderTest + .describeReplicaLogDirs(Arrays.asList(topicPartitionReplica)); + String logDir = replicaReplicaLogDirInfoMap.get(topicPartitionReplica) + .getCurrentReplicaLogDir(); // Run the test final Map> result = - kafkaAdminServiceUnderTest.describeLogDirsByBrokerAndTopic(brokerList, Arrays.asList(logDir), topicPartitionMap); + kafkaAdminServiceUnderTest + .describeLogDirsByBrokerAndTopic(brokerList, Arrays.asList(logDir), topicPartitionMap); // Verify the results assertTrue(result.containsKey(brokerId)); @@ -2196,12 +2245,15 @@ public void testDescribeLogDirsByBrokerLogDirAndNullTopicPartition() { createOneTopic(topic, replicaAssignment); TopicPartitionReplica topicPartitionReplica = new TopicPartitionReplica(topic, 0, brokerId); - Map replicaReplicaLogDirInfoMap = kafkaAdminServiceUnderTest.describeReplicaLogDirs(Arrays.asList(topicPartitionReplica)); - String logDir = replicaReplicaLogDirInfoMap.get(topicPartitionReplica).getCurrentReplicaLogDir(); + Map replicaReplicaLogDirInfoMap = kafkaAdminServiceUnderTest + .describeReplicaLogDirs(Arrays.asList(topicPartitionReplica)); + String logDir = replicaReplicaLogDirInfoMap.get(topicPartitionReplica) + .getCurrentReplicaLogDir(); // Run the test final Map> result = - kafkaAdminServiceUnderTest.describeLogDirsByBrokerAndTopic(brokerList, Arrays.asList(logDir), topicPartitionMap); + kafkaAdminServiceUnderTest + .describeLogDirsByBrokerAndTopic(brokerList, Arrays.asList(logDir), topicPartitionMap); // Verify the results assertTrue(result.containsKey(brokerId)); @@ -2335,6 +2387,8 @@ public void testGetRecordByOffset() { final String topic = FIRST_TOPIC_TO_TEST; final int partition = 0; final String avroSchema = "avroSchema"; + final int maxRecords = 1; + final long fetchTimeoutMs = 30000; // Create first topic createOneTopic(topic, 1, 1); @@ -2349,9 +2403,11 @@ public void testGetRecordByOffset() { for (int i = 0; i < offsetList.size(); i++) { Long offset = offsetList.get(i); String decoder = serde.deserializer().getClass().getSimpleName(); - Record result = - kafkaAdminServiceUnderTest.getRecordByOffset( - topic, partition, offset, decoder, avroSchema); + List result = + kafkaAdminServiceUnderTest.getRecordsByOffset( + topic, partition, offset, maxRecords, decoder, decoder, avroSchema, + fetchTimeoutMs); + Object exceptedValue = serde .deserializer() @@ -2359,11 +2415,11 @@ public void testGetRecordByOffset() { topic, serde.serializer().serialize(topic, testData.get(type).get(i))); if (type.equals(ByteBuffer.class)) { ByteBuffer byteBuffer = (ByteBuffer) exceptedValue; - assertEquals(new String(byteBuffer.array()), result.getValue()); + assertEquals(new String(byteBuffer.array()), result.get(0).getValue()); } else if (type.equals(byte[].class)) { - assertEquals(new String((byte[]) exceptedValue), result.getValue()); + assertEquals(new String((byte[]) exceptedValue), result.get(0).getValue()); } else { - assertEquals(exceptedValue.toString(), result.getValue()); + assertEquals(exceptedValue.toString(), result.get(0).getValue()); } } } @@ -2372,6 +2428,33 @@ public void testGetRecordByOffset() { } } + @Test + public void testGetRecordByOffsetWithDifferentKeyValueDecoder() throws Exception { + final String keyDecoder = "DoubleDeserializer"; + final String valueDecoder = "StringDeserializer"; + final double key = 0.01; + final String value = "test"; + + // Setup + final String topic = FIRST_TOPIC_TO_TEST; + final int partition = 0; + final String avroSchema = ""; + final int maxRecords = 1; + final long fetchTimeoutMs = 30000; + + // Create first topic + createOneTopic(topic, 1, 1); + + produceRecord(topic, key, value); + List result = kafkaAdminServiceUnderTest.getRecordsByOffset( + topic, partition, 0, maxRecords, keyDecoder, valueDecoder, avroSchema, + fetchTimeoutMs); + + assertEquals(1, result.size()); + assertEquals(String.valueOf(key), result.get(0).getKey()); + assertEquals(value, result.get(0).getValue()); + } + @Test public void testGetRecordByOffsetWithInvalidDecoder() { final Map, List> testData = @@ -2384,6 +2467,8 @@ public void testGetRecordByOffsetWithInvalidDecoder() { final String topic = FIRST_TOPIC_TO_TEST; final int partition = 0; final String avroSchema = "avroSchema"; + final int maxRecords = 1; + final long fetchTimeoutMs = 30000; // Create first topic createOneTopic(topic, 1, 1); @@ -2397,9 +2482,10 @@ public void testGetRecordByOffsetWithInvalidDecoder() { for (int i = 0; i < offsetList.size(); i++) { Long offset = offsetList.get(i); // Use DoubleDeserializer to dese string record - Record result = - kafkaAdminServiceUnderTest.getRecordByOffset( - topic, partition, offset, decoder, avroSchema); + List result = + kafkaAdminServiceUnderTest.getRecordsByOffset( + topic, partition, offset, maxRecords, decoder, decoder, avroSchema, + fetchTimeoutMs); } } } catch (ApiException apiException) { @@ -2413,7 +2499,7 @@ public void testGetRecordByOffsetWithInvalidDecoder() { + partition + " offset:" + 0 - + " using " + + " using keyDecoder:" + decoder + ", valueDecoder:" + decoder + " exception.")); } @@ -2424,7 +2510,7 @@ public void testGetRecordByAvroDeseriliazer() throws InterruptedException { String topic = FIRST_TOPIC_TO_TEST; // create first topic - createOneTopic(); + createOneTopic(topic, 1, 1); String schemaStr = "{\"namespace\": \"com.example.avro.model\",\n" @@ -2458,10 +2544,11 @@ public void testGetRecordByAvroDeseriliazer() throws InterruptedException { String decoder = "AvroDeserializer"; if (offsetList.size() > 0) { - Record result = - kafkaAdminServiceUnderTest.getRecordByOffset( - topic, 0, offsetList.get(0), decoder, schemaStr); - assertEquals(user.toString(), result.getValue()); + List result = + kafkaAdminServiceUnderTest.getRecordsByOffset( + topic, 0, offsetList.get(0), 1, null, decoder, schemaStr, 30000); + assertEquals(1, result.size()); + assertEquals(user.toString(), result.get(0).getValue()); } } From 8b1a9bd0df53c021b97dd4253bb21c3cc0563e02 Mon Sep 17 00:00:00 2001 From: wenqiao <315524513@qq.com> Date: Fri, 26 Apr 2019 09:14:50 +0800 Subject: [PATCH 21/24] Close kafkaZKClient in stop reassign partitions. Clear some test data. --- docs/index.pdf | Bin 1312630 -> 1312630 bytes .../bigdata/controller/KafkaController.java | 4 +--- .../bigdata/service/KafkaAdminService.java | 1 + 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/index.pdf b/docs/index.pdf index 7d7e5c58363cfdd24f83f0fb09929e01c16d27d2..8c4ad6bc594256900799bbecf64acd32af09436a 100644 GIT binary patch delta 89 zcmXZOyAgs=006-e> describeLogDirs( @RequestParam(required = false) List brokerList, @RequestParam(required = false) List logDirList, @RequestParam(required = false) Map> topicPartitionMap) { - topicPartitionMap.clear(); - topicPartitionMap.put("__consumer_offsets", Arrays.asList(15, 16, 48)); return kafkaAdminService.describeLogDirsByBrokerAndTopic(brokerList, logDirList, topicPartitionMap); } @@ -311,7 +309,7 @@ public ConsumerGroupMeta getConsumerGroupMeta(@PathVariable String consumerGroup @GetMapping(value = "/consumergroups/meta") @ApiOperation( value = - "Get the meta data of the specified new consumer groups, including state, coordinator," + "Get all the meta data of new consumer groups, including state, coordinator," + " assignmentStrategy, members") public List getConsumerGroupsMeta() { Set consumerGroupList = kafkaAdminService.listAllNewConsumerGroups(); diff --git a/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java b/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java index 4a3c833..265e745 100644 --- a/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java +++ b/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java @@ -1843,6 +1843,7 @@ public GeneralResponse stopReassignPartitions() { log.info("Delete zk path /admin/reassign_partitions failed."); } + kafkaZkClient.close(); return response; } From dcf9dbe503d8db12738ebde6a02b8f5e4582df91 Mon Sep 17 00:00:00 2001 From: wenqiao <315524513@qq.com> Date: Sun, 5 May 2019 15:46:34 +0800 Subject: [PATCH 22/24] Remove empty result in describeLogDirsByBrokerAndTopic --- .../org/gnuhpc/bigdata/controller/KafkaController.java | 6 +++--- .../java/org/gnuhpc/bigdata/service/KafkaAdminService.java | 7 ++++++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java b/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java index 6da948a..98afeb7 100644 --- a/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java +++ b/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java @@ -5,7 +5,6 @@ import io.swagger.annotations.ApiResponse; import io.swagger.annotations.ApiResponses; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.List; @@ -86,15 +85,16 @@ public Map> listLogDirs( return kafkaAdminService.listLogDirsByBroker(brokerList); } - @GetMapping(value = "/brokers/logdirs/detail") + @PostMapping(value = "/brokers/logdirs/detail") @ApiOperation(value = "Describe log dirs by broker list and topic list") public Map> describeLogDirs( @RequestParam(required = false) List brokerList, @RequestParam(required = false) List logDirList, - @RequestParam(required = false) Map> topicPartitionMap) { + @RequestBody(required = false) Map> topicPartitionMap) { return kafkaAdminService.describeLogDirsByBrokerAndTopic(brokerList, logDirList, topicPartitionMap); } + @GetMapping(value = "/brokers/replicalogdirs") @ApiOperation(value = "Describe replica log dirs.") public Map describeReplicaLogDirs( diff --git a/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java b/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java index 265e745..cc71317 100644 --- a/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java +++ b/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java @@ -101,7 +101,6 @@ import org.apache.kafka.common.internals.Topic; import org.apache.kafka.common.protocol.Errors; import org.apache.kafka.common.requests.DescribeLogDirsResponse.LogDirInfo; -import org.apache.kafka.common.serialization.Serdes; import org.gnuhpc.bigdata.CollectionConvertor; import org.gnuhpc.bigdata.componet.OffsetStorage; import org.gnuhpc.bigdata.config.KafkaConfig; @@ -502,11 +501,13 @@ public Map> describeLogDirsByBrokerAndTopic( log.warn("Describe log dirs exception:" + exception); throw new ApiException("Describe log dirs exception:" + exception); } finally { + log.info("After describe log dir, result is:" + logDirInfosByBroker); if (logDirList != null && !logDirList.isEmpty()) { logDirInfosByBroker.entrySet().forEach(e -> { e.getValue().entrySet().removeIf(m -> !logDirList.contains(m.getKey())); }); } + log.info("After describe log dir filtered by logdirList, result is:" + logDirInfosByBroker); if (topicPartitionMap != null && !topicPartitionMap.isEmpty()) { logDirInfosByBroker .entrySet() @@ -528,6 +529,10 @@ public Map> describeLogDirsByBrokerAndTopic( } } + log.info("After describe log dir filtered by topicPartitionMap, result is:" + logDirInfosByBroker); + logDirInfosByBroker.entrySet().forEach(e->e.getValue().entrySet().removeIf(m->m.getValue().replicaInfos.isEmpty())); + logDirInfosByBroker.entrySet().removeIf(e->e.getValue().isEmpty()); + return logDirInfosByBroker; } From baddd631eaf8b97e1e5e7a284be30d12a4a71d34 Mon Sep 17 00:00:00 2001 From: wenqiao <315524513@qq.com> Date: Sun, 5 May 2019 17:19:33 +0800 Subject: [PATCH 23/24] Modify readme. --- README.adoc | 24 +++++----- pics/ShowApi.png | Bin 990158 -> 832979 bytes .../bigdata/controller/KafkaController.java | 44 ++++++++++-------- .../bigdata/service/KafkaAdminService.java | 7 +++ 4 files changed, 45 insertions(+), 30 deletions(-) diff --git a/README.adoc b/README.adoc index ef78750..50f269d 100644 --- a/README.adoc +++ b/README.adoc @@ -8,13 +8,17 @@ Kafka/ZK REST API is to provide the production-ready endpoints to perform some a // tag::base-t[] .Following features are provided: +* cluster/nodes/controller info describe * Broker List -* Topic create/delete/describe -* Topic config create/update/delete/list -* Topic partition add and reassign -* Consumer group(old zookeeper based/new kafka based) list/describe -* Offset check/reset +* Broker config get/update, dynamic config get/update/delete +* Log dirs describe, filtered by brokers/topic/partition/logdirs +* Topic create/delete/describe/list +* Topic config create/update/list +* Topic partition add and reassign, replicas movement between log directories is supported +* Consumer group list/describe/delete * Consumer Group Lag check +* Offset reset by earliest/latest/timestamp +* Contents view of message by different decoder, avro is also supported * Collect JMX metrics from brokers that expose JMX metrics + More details refer to https://github.com/gnuhpc/Kafka-zk-restapi/blob/master/docs/JMXCollector.adoc[JMXCollector API Specification] * Secure the REST API with Spring Security @@ -82,7 +86,7 @@ Step 3: Use user controller API to add user to security file security/security.y * No need to restart server after adding new user or update user info. Timing thread introduced in Step 1 will refresh the user list according to your settings. === Support Kafka Version Information -Currently, this rest api (master branch) supports Kafka 0.10.x brokers. The master branch is the most active branch. We're going to get down to the work of supporting the Kafka 1.x version. +Currently, this rest api (master branch) supports Kafka 1.1.1 brokers. The master branch is the most active branch. *For 0.11.x, please checkout the branch 0.11.x by calling the command:* @@ -96,8 +100,7 @@ __BasePath__ : / You can access Swagger-UI by accessing http://127.0.0.1:8121/api -=== https://github.com/gnuhpc/Kafka-zk-restapi/blob/master/docs/paths.adoc[API LIST for 0.10] -=== https://github.com/gnuhpc/Kafka-zk-restapi/blob/0.11.x/docs/paths.adoc[API LIST for 0.11.x] +=== https://github.com/gnuhpc/Kafka-zk-restapi/blob/master/docs/paths.adoc[API LIST for 1.1.1] * kafka-controller : Kafka Api @@ -106,13 +109,12 @@ You can access Swagger-UI by accessing http://127.0.0.1:8121/api * user-controller : User management Api -=== https://github.com/gnuhpc/Kafka-zk-restapi/blob/master/docs/definitions.adoc[Data Model Definitions for 0.10] -=== https://github.com/gnuhpc/Kafka-zk-restapi/blob/0.11.x/docs/definitions.adoc[Data Model Definitions for 0.11.x] +=== https://github.com/gnuhpc/Kafka-zk-restapi/blob/master/docs/definitions.adoc[Data Model Definitions for 1.1.1] === Version information [%hardbreaks] -__Version__ : 0.1.0 +__Version__ : 1.1.1 === Contact information diff --git a/pics/ShowApi.png b/pics/ShowApi.png index 00ca838f2e2560a0e4b5aa0442fabe26c8c50040..f6519a9bd8278db17c030c8f1817863f54b55c1c 100644 GIT binary patch literal 832979 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 z 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�wJsPdu8s0F%& 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

    Kcpj^oF@1_Y9{U{m$UhEwvVqr}NAT!r9C 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 z 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

    BDQ=pz*NK=vtaLvJZr(qx$ND5KD4jY3Cra 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ℜ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 z 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

    ldmr>y^h&ep6 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

    GTb;{G>59=c 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�-Ek0 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%0AMjo3b~Bre0ia(kgS$I-y_XcF{y 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�c<_wC#aKL2<#)Kj)dt?}R(_YU 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
    XE;$PN_^Qeec;koEMeL z$#Hl3GW3<{yBU|{2FJWdW@Yzl)A#%^8cw3w$*xW5kdc9*2WSN}E)*ccL{Z}&A68mg zL85*Hq<`1X*M-Q0^-{Qpyg92~pIzx;@A<#$ROdb4d{v`Vs}a@Z<^5qxsfkUg3(6h~ zFHl!FoW@$pM^<#=0y)xLuAGP{n)*5a9s^paZx=`A|TjmQ`uF1FI29|I6Dz_>;p z5pmnOq~@JWR&jsJH#OPsfjNA*pWGQzY0>E#3PPbFcXD#V{>5b64V!DgXf_ABd09a0 zB`;^uJsYCX%n4*Z=ub3ydbElqlWBFo_t{GZn%MFy)yR@Cys-cl+*&t_?O5iHW`|6L zNfjD7z2Tm;jQh5*z&`A**+H$hEl6ILY{YV@#tQE@lpYCCYmZ^0Zmfiumu#k1y>9oc zoC6Ud2UUNrb$h*d3&VX0%nY$8SvcKxfGGHfX3t+BBcE_E`wl;Wi%qf)0DlquIl0;v zoXo6qpGNU|$RXZuON;!=@C_+~lzd4hrZT_^^O#_aZeRByNf>QrwP-~~^?-*@21!!J z#vnw&QPly2Jsit*k1qax0l2r@4w_7_!o`*gWNANztVf>opw-4mbBSVxb>#(iLz!5} z$xP3vtcOoc>o?R zA2Y7f-pmN^Q#_+wLT#mGEI*uh8Nv+x>Yn#TC)w9xUt9&=Ygm9uddy*)G5Bgi`i6_E z>p^!}VMrERM#W~8HI=A@&Uj-_n%;6ls?0R8bLo1ecfyq6Tyg(a{#&w0PG-u z$bAnDn?Kn%ixflm3Xm37fXaNnTi;Xgoz}N&GMR@}=T(w+XIoaHw9!doA#30$wam5I$wuj^1iN@Jn4p>Mk zK@mV%x-($XN4k?)Q|yciy<>L$DKi)!T^PqiobOa;Nz@xIfEhgpkUb+Xn1B9=xQxe8 zz7I#k=CsZX#a3XwK1$+}O6P||k;G;oH;N*hfR!gpv(hTglOQ+_ESNW2W>xK7IT8_PNs6Q++$T!EFIY0+^y5!76l|kh^=Lb?AdDO?Q`p){20@`gvWKi z^*&zj>6n)c^Q17dfh3cD1M3pJr}CWE1Lo^PNs)Bhm7@0-fhy#cB&^?yvuWkvjFZO9 zo|g~@R@6JOX_%2HQ|R&mG<`!Lm`15gPG}bPYFUCpNzl%EY`a*@EKsNKUDFZ2r@$T?!N!MQSiyEaaI@ShO~ym}T8H$~JyW-!__l1{me#r#oa;{bgTJ){22jroJ(GXREX0{6#wzj`=2?}o&! zZNrXdulG{Ppc2eYBro6E&{SOA;J5zRt;A<8CH!s2Jn|J-O^Nr#JexD1LiL9Wg{$Ys zS9TAB#OAT|4YLV+5M2g`7iH;mp$s33n8{)UO5dXy2pb&Wdv}GM-Y(RTpomU?A54fU zlgS+@`u@%#zMj8+Q10>e_He~x;qJUQmt1JK4UIt*^>eT#F=noCyg0*~W#ODkfMT7x zR40`tU6B0!qS|s_Hqi8)JHmZTwi%NZA-J0zt{XGFRd7OfVX|y8DD~__y$Djb|8MX9X2UH(emqL9*~%WM?CN}i@l2=lI( zNE?sFcx-~j&K{?qOZa0(PJ>9MH2QV=}K^g@MM79~1vMy+k&tJ2yB{4rUxyo~ikV7BjK-Ltu)j z&kCz;iees*kPtebYGn$MA* z9TlwiM`^@i`5&7=Zyec6oj~ahdaq3NNoA~&K`&G4`oT(@$>xj3(Vs{3)dP&Mje`SI zE2|7@9*B1*x?CLO%#l2$?st1gs@#Dx*p!I?12Z>pZHVQUHf%GTmyiQ<#&+x4?DoS8 zhdK2E2IoR(5rkJIP$;M^7@3ESjV((o6>eNix_hm(+6sFS6dd4q#GNP&DEXY_Tdg$n zNH#m4(;EE(YZIR?iG$;BZgxM$LV8uB+UwQ-*}!!;&j>^`zL-@353n3}~Az{YvbX;t(o>5^w78f2OAF(1Dq)6MMg zAyjjVn3)jZaJXJUxLm4Ik9{C`OQE@tnjzeU1i~bS;!VwhU7pG*FiFD5nYSGo;Tm_y z1o=K0=1#%9zuwxP#Q3BRAH%}>;M6|jMEsR6@2hMknC8oI;LTs5o!_6F4Z?Nzr;`Ai z@xbRBAmueIK;HEkRF-i&YbEeY@gvqoICKQ--Jpy$7%>PH3PYEkte{fjp9{_l^vw>7 z9gycP2za~8oj{;i;Dd|&DC&ogtuapN4#=Zd9~ zN@%la&r$QgJzgo4YsW!(K4M~i4|;Z3IGX1ZiinqdJ(wE@1a^Gw3XVelCJ`u%KL^V% zkz6p>*}q3=dGn=X#)Rwu76wm|vwDBHrn7_=Hy9a#3W&}@ay?9-W>Hz<8-ZY{pa9?RpAYK- z1#mI{Kla`-Dz9}(0}SqNK|-+L65L&bJA7z@ySo$IA-G!z?iSqL-66QU`|Nc0y|?F_ zb8mOgtXXSjR{zQR0{h#%``uObRz3Ap6_MY?)fJ*^?qy#vs-c6xSO&$-NvTF8A!8cM^46U9kjjUlf+#(TX*U_dDe*yCS{WbYY1AV7_5#vUFS4s-l zp!-B=pLYiEwt7XpXV9^^!58r~9EdXo2%^EguDh!(%CfS86GIHd3H;VNCYhgN$wJ=I zsLD%+TK?x@1b_59eA-~={hjhLyhjpxfvT43+&;#>--hiz8G07dT!$B3U8eG1_IrVi z1vGZ8rx_iK8kO9*?^A%>Z++vNORpyX7v=bW`xR{dZ`flhV7KPKQ!?L4@jDrBzrtz$ z7pKg>N9Od#Gvz+B1KJ-^KTtqWIem{^NoF(@$VCe{buUBg$s= z=zJNj=jM}^{{Mw$INgJ3t=$`@pCqyJr2p?U!x@VtMEP$u!^shX`kIa7BnaLi42XV5 zS8~fA<@FD4^S}RBAeac~or7A5Nbu(Z!oT_gBnp@tDm04mjnKbRzrVi|;;%r*iT}~v z;s0lA_x&hB3jy<2fdAju@b?eE&VaAKE;J$d6n%T~{6#|-Oo#16Q#ZeoZeDU+R=(1- zv$#@fUQ+8+ulHF$Oq;En zq!1Xqybm~L)*C2FfxrIf>q6ZA=1cQWq%_EXdd=UzsVD9O);VvxL;0`n`0rN-tfd{R+-e2?vVA;qA2}q8}nPm|M#~4<4s5s zu}cf00>t78);FuYU9DUP4Q2(?gej z$;rvuF?yn5iS5#>&9+@|Z@E+srlH54vSC(#Hb6l^LBqf-84Z}}q2IoC(d=MgV<#-I zg~cckcqoe8^L($aRM2Z`Xz@CFID1*)22~rD>l8;`$P*--cR$>@cwY)XKFml3J-ag- z4}5DAAl6b-riUbTb9WC24E*sal?}vnSdfMuWLc)w=(3r@{1Ts>oXT#s2>v>_X~RU2 z^3OynSefW#EkDvB^tTp}y#edX4Qi~h3_wV}9lJm98j<^77Nu?GOBO8jjH*B)> zQ?aXFfv(M@0za+C%5=23v+ahv3wa-|62wwH_2wmk2g7_BHyW1%iJ~>RVGZs zcP4qgZy6g^DcH947s>7VwGKU_dRO)9@SrBIT$ZuljIRNl;O*Yno6USbu1 z@>eH-mfoDfK3wU1u6x}JutL@Ssyv<-9C%BKbcxK)gb_j>&p@m6AK}QobDVZMe|(zg z&rslBg~pa|e_rhg+ejoSeAA^J4o6tfz!^$l@(fjXTcSuXeD)LafDn?7h$x9Ak7mDF zyNA@Vd;V+@N0t{~=sBtk){%nXPfd@Sh&$9w5D6Y4L*)B(Dxue8*{lQqqy@gJqZ27k z@X^@6d(yc7EqCne)qXBPfm)kye33E`p_Z0}jC>8g`rLiM%8FprxZGB6)Wd{nw>6BF zIu)%S5NPr;9NNXuGUnIy;z`04{Pti+`%x;cDv>$dzA}0o^`7HPx;kfN0ZwQlZLQN~ zq_JG4gUJfP#74dx;aDcG=k(n`pqtCy2Fce!Bz*SAy6ErO8QvDWdMyB?A*uu)`QdN9 z05bdEr7=3A4I1qj@jGGOi2aA{{=2=z9LIu1cr=`9z)^Bqo8$FQ=>)|_St=081tzU5 zik*%#YBW4-RriP!ehEhMJUXr-i<+p^%f5VEu|m*3icfjzPq! zma9`6Ro$)44DJz8Z)zb9`I0p6-mgQD#~yND+*dsQnp80_j;9Imx;b57wduDJ5$;hb z_6qgkqNY(TEaJi}*Lk?V*Kus6{ZV5zL{GbpU;d+S;EzX_fDo1d$;$hoG;6*~HV0ph zX}!^ao;IA~B!-A=Hnh3Zwu7779qu2!pOBG@qi4Bt*zLax32BZr-EEax*B>mn&ko|+ z)~~rvcPghU->xf-J3C?R=08FQO0R8_n?KJpM1 zqG>LCaf8iGD3xbS5X@oH%J8_)our*)Q#Z$StuY=;6Oz=EsCZG9wQzN@IVE(6r{k`Y zwijPCCSd!>#*`3N-n-7i!Scs*n2g$Ye7r(;SIcE*ouZ7(oBUAl+8(Dyh7cX^p|i_- zZd*;0p&2vip{%v_vD^+cMfMDxS-%RF{pCwXcANkDtLWYK`?n7de%=x$Nr`LqwA`3q z)}T@JcxN)7heoE}GYV;mBI6e)A0JiaS4&^q=TvN%G;X0m8G7HC?>*t~f`A{3QV8M*4YDeN}q8b7+ zTCP5pam=la@v}T3WD13{cu%aYp;+3IR2_aJuT?HR2`0i$Xu5K_#kQ1|mYMufE64|e zm7Cw0K;fa>}-l>7OT&=&3%P%{wZ^Nx?<~j_J&l5LuRq&qhOkxL*Ap(=_Tsh?+uV1>r63a z6oVVam_0U|z@NAEy!(}Sb|{(1*P^|ilgzY4-SlnS8nvq-365T#W2j6740f)ZYpuzh zdvYRXKqD*q9k_pgq38KV@~Zp8nQxf7<#j!BIvs86gLP5?3YoFX<1EtA11>s2_<-hz zYI^m4`5H*(8Hf(E-nlI5S9FE1}eJ!*`?EhzB> z0Z@Mm>e`**OZcv(#b6$1MrLX?oEE-N(=gN0FXpO7_A-xp!ov!VTLHOpC2txse3AOk z-|yv2cwf;TP|c;hv^wGe7-zk?pV zmlsg;0Ta^3n3X3&_w7KstfP(P6qd9*%+ z$}9OT{=2qIY}~7RCJpg%=`?&y^jXiInW&e_TM$``eWcZP3%7H{Cf+D86G7#Bj2|#W z#rbqn2hq1+KIBr(z-MbPFD$k%LdZbW8623L@X(?;7FVbkB0Tg8;&8kByl_;}^+IdP ziohwG!I(ev>elbJ(z(K2UzAv%crB}DHTXo=eE`ft`h8TpDr0NZACDWiXEt2!#IjcT zzz!xL9iE{tRTEzY+%qIGD-phXyp{LZ-0sjf4vnXzIzZFycs{zjtgfa>Gs#ly%8W&p zsccur=Rp><2z!v4oJBd#6j9ta`?I1Y?`br1e1AoL~wsrJni3q4Cd)_FCIlCZxy&z3Esq~c8Y5Hm>(@3grK-#8nmh8tPTOu= zPu3IZ8ya*!OZX1YQqjq5?}G_u-+iuy)g|t}8>M)I;GU9-x37IQ*~c*7$ItP!IoQVI zxf^r~2jM4F8tdgc_s)7dz2S6yAK$?8%h%i+P9?*M0h8{~!nZ4(%FF|E887v^F2>TP z=NB#B=LA$Xbar}_pZU}$FyWjUV>=#WakTiqOPC$(>O)lCd(wBg0iQu~VgU$4|;gk1a9uclS%cdfZ{UoX&`&3Nom<(F{vQMiP2J)JW0r zdY{6y`}{(M4Mq@Qw#Vq!Hnf*nk9nl9Zu|UQHs=!H>}N8F^P3b{1eKNXUVA*v;=~Jf zWZyG^a7I#uX5y=OhcG3E!F6BqsTpa`7O7B0eGRb5k-Mn3p546lp8~rw+V8A%8HdX4 z8Gg3jler0RvTa+?jkG;H3Of_MZzI~8&}W2P5iY`mgk{dJ`uX~hTf~>q#^Yg=ne-(< zGUZMXQg?~n8Ys4Qo_I6S(M2&fut!vPemL8Zo^AiziM1M$>|@4J%9D-2EO2+W!Z*Ve zd@jYGGgqOf!6Q;KyCoLKedn=f*QU6y7dX1o{DrO+C7na_ySl9k;MkXoieNkvkppwL zU44t7H}VieT-{x3t!CdO+qk95;Bnh+S9v3IWDG_###;P3EWWpg(PgDcK!chY6TLJ# zU23`X*)&ZC-`maC{wh3!UOCVfg!o{+ZKu-W!fAf+J$h$vSm8-GjcXw)haq7S&UeAlW2Ie2sW8bzT4rhP4*Zbw#SuRU?Eiu5w*sDF>+Ofg!&$LvRsFYbS6jSyxLr4=l~P?D--bqnedv5 zdDrP=QTygS=3BBs4%-EO1jnvWj?kY?dGRVuk=pV=5=U7K#*m-(k1os`C_UxRnbr7; z5R8ytKcyGFeNwh+;3q0d_GN#yxYPYyAF3Q#|PHDlio=btso8(HxbHmjCSp;eF&Cx4g23Kc%9~O=v zP$ur{zzk9CFyez5kQZzoyVL78^6o;EUd)nUtktM#9ETKmaKh(fCe|PDRZzT@M^@`) zWpl?6GFClKR&37=5nGPDd!kky)*Ma(s(8QVN;P4N^PpJOl4r)?`bXBUV!zu7K3i|? z*j6R*t~#rL_Et#Li@z{WtQMlIA3HdVTB)^e+@PGKGjgV6ayxvNIQKrDF8#zE@$NLR znN-h#MQ5T^#7NXdk&`BYDmn?O#@&nD2waC7Du>eNI5?d`EfH`S^q|@Q;^x4xH|-O?93&pI4tH zb^7Yay)C8i)8qUr<+SOzyt5uKoaim5m0}d%b7?GvICJ;zV!ai4uAhqe^rHRVT_J*+ zTIt$X?L!g%pTpP@2YQ?`=ji;7I5O|}9eZbaVT?z0!x3;sqC4*0A7}S;GxF*`x<44; zz7q*{xas`x7?q(>=hkur6V_yPXI;yIBd7%YN^J2ULX%NN`Yo1yV?6;ezW^}`c1qcHa|C!v$PktWFidPlzu_wB%A z09xNOuk`ij0-aFMK&IS#e)O?7gr8}O4RZJzA&6lVaOE?(%n47Bzjk+tMLTl2`; zje6JG3mJdm=Ad8-^dB!njCdz(mNjrYu+Xh!#n+D|W~zAX~Z{)Uz?6 zx}SQj+QQEAi@L6jGj26pk(TJG4`YmO5BzSJXJPAR(j3IF-ihe@voSv0gy{z0$ERlz z^w^*8P>`44zY{^Bk>xREyak`U6B#2(MiF+_-YE%1&gsY#e@}kg$e^)-E+Y6Mf((sq z&pI{W)_gmde?Dvv1XQve+x5xbXcL02T=0qSJ@*moHwR@iW2QIyo00cc=y7shjz(RSkq6J-*EcSnlj z0dW~;L6p&g;#?5@dryzJAxDy`pYDCC1f4ZuSPTJQ&GaHtYEcK&Is6&=NRjo>q~g%b z@K_A|)rZhxJ6-~cU$sMnk&%fdcPqSgr?BZ*cPU#x!johj)*Ycsm2MPi)>(0z-F-&8 zY_b{MAIw8A98{4{J6AbBC!OP*$`7X2ngb%%9r&6{>KDn`wgB2yyH z%d}=a+>mum!cl){@yxbLFZ7OR5RnH7CmkH8vd1CV;kgmUIm1}3zccp|p}l!pn4;Pg z59@WYV6#{wWh)gqFwUc%NqJtBl!ib;6$b!;Y&%S4uY7f%w%0A&#fgcT)h>#v-F_-^ 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ⅇ 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 diff --git a/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java b/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java index 98afeb7..1aa31a8 100644 --- a/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java +++ b/src/main/java/org/gnuhpc/bigdata/controller/KafkaController.java @@ -50,13 +50,16 @@ import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; -/** Created by gnuhpc on 2017/7/16. */ +/** + * Created by gnuhpc on 2017/7/16. + */ @Log4j @RequestMapping("/kafka") @RestController public class KafkaController { - @Autowired private KafkaAdminService kafkaAdminService; + @Autowired + private KafkaAdminService kafkaAdminService; // @Autowired private KafkaProducerService kafkaProducerService; @@ -91,15 +94,16 @@ public Map> describeLogDirs( @RequestParam(required = false) List brokerList, @RequestParam(required = false) List logDirList, @RequestBody(required = false) Map> topicPartitionMap) { - return kafkaAdminService.describeLogDirsByBrokerAndTopic(brokerList, logDirList, topicPartitionMap); + return kafkaAdminService + .describeLogDirsByBrokerAndTopic(brokerList, logDirList, topicPartitionMap); } - - @GetMapping(value = "/brokers/replicalogdirs") - @ApiOperation(value = "Describe replica log dirs.") - public Map describeReplicaLogDirs( - @RequestParam List replicas) { - return kafkaAdminService.describeReplicaLogDirs(replicas); + @GetMapping(value = "/brokers/replicalogdir/{brokerId}/{topic}/{partition}") + @ApiOperation(value = "Describe replica log dir.") + public ReplicaLogDirInfo describeReplicaLogDirs(@PathVariable int brokerId, + @PathVariable String topic, @PathVariable int partition) { + TopicPartitionReplica replica = new TopicPartitionReplica(topic, partition, brokerId); + return kafkaAdminService.describeReplicaLogDir(replica); } @GetMapping(value = "/brokers/{brokerId}/conf") @@ -179,7 +183,8 @@ public List getMessage( @RequestParam(required = false, defaultValue = "StringDeserializer") String keyDecoder, @RequestParam(required = false, defaultValue = "StringDeserializer") String valueDecoder, @RequestParam(required = false) String avroSchema, - @RequestParam(required = false, defaultValue = "30000") long fetchTimeoutMs) throws ApiException { + @RequestParam(required = false, defaultValue = "30000") long fetchTimeoutMs) + throws ApiException { return kafkaAdminService.getRecordsByOffset(topic, partition, offset, maxRecords, keyDecoder, valueDecoder, avroSchema, fetchTimeoutMs); } @@ -241,7 +246,8 @@ public Map addPartition(@RequestBody List @PostMapping(value = "/partitions/reassign/generate") @ApiOperation(value = "Generate plan for the partition reassignment") - public List generateReassignPartitions(@RequestBody ReassignWrapper reassignWrapper) { + public List generateReassignPartitions( + @RequestBody ReassignWrapper reassignWrapper) { return kafkaAdminService.generateReassignPartition(reassignWrapper); } @@ -260,9 +266,9 @@ public ReassignStatus executeReassignPartitions( @ApiOperation(value = "Check the partition reassignment process") @ApiResponses( value = { - @ApiResponse(code = 1, message = "Reassignment Completed"), - @ApiResponse(code = 0, message = "Reassignment In Progress"), - @ApiResponse(code = -1, message = "Reassignment Failed") + @ApiResponse(code = 1, message = "Reassignment Completed"), + @ApiResponse(code = 0, message = "Reassignment In Progress"), + @ApiResponse(code = -1, message = "Reassignment Failed") }) public ReassignStatus checkReassignPartitions(@RequestBody ReassignModel reassign) { return kafkaAdminService.checkReassignStatus(reassign); @@ -314,7 +320,7 @@ public ConsumerGroupMeta getConsumerGroupMeta(@PathVariable String consumerGroup public List getConsumerGroupsMeta() { Set consumerGroupList = kafkaAdminService.listAllNewConsumerGroups(); List consumerGroupMetaList = new ArrayList<>(); - for (String consumerGroup:consumerGroupList) { + for (String consumerGroup : consumerGroupList) { if (kafkaAdminService.isNewConsumerGroup(consumerGroup)) { consumerGroupMetaList.add(kafkaAdminService.getConsumerGroupMeta(consumerGroup)); } else { @@ -369,10 +375,10 @@ public GeneralResponse resetOffset( @PathVariable int partition, @PathVariable String consumergroup, @PathVariable - @ApiParam( - value = - "[earliest/latest/{long}/yyyy-MM-dd HH:mm:ss.SSS] can be supported. " - + "The date type is only valid for new consumer group.") + @ApiParam( + value = + "[earliest/latest/{long}/yyyy-MM-dd HH:mm:ss.SSS] can be supported. " + + "The date type is only valid for new consumer group.") String offset, @PathVariable ConsumerType type) { return kafkaAdminService.resetOffset(topic, partition, consumergroup, type, offset); diff --git a/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java b/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java index cc71317..67fc8e7 100644 --- a/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java +++ b/src/main/java/org/gnuhpc/bigdata/service/KafkaAdminService.java @@ -536,6 +536,13 @@ public Map> describeLogDirsByBrokerAndTopic( return logDirInfosByBroker; } + public ReplicaLogDirInfo describeReplicaLogDir(TopicPartitionReplica topicPartitionReplica) { + Map replicaLogDirInfoMap = + describeReplicaLogDirs(Collections.singletonList(topicPartitionReplica)); + + return replicaLogDirInfoMap.get(topicPartitionReplica); + } + public Map describeReplicaLogDirs( List replicas) { org.apache.kafka.clients.admin.AdminClient kafkaAdminClient = createKafkaAdminClient(); From a33b312e3ca4f5264cb573b2f73ae1be6589f8ce Mon Sep 17 00:00:00 2001 From: wenqiao <315524513@qq.com> Date: Sun, 5 May 2019 17:43:20 +0800 Subject: [PATCH 24/24] Modify docs. --- docs/definitions.adoc | 27 +- docs/index.html | 488 ++++++++++-------- docs/index.pdf | Bin 1312630 -> 1333703 bytes docs/paths.adoc | 91 ++-- src/docs/swagger/swagger.json | 2 +- .../service/KafkaAdminServiceTest.java | 24 +- 6 files changed, 364 insertions(+), 268 deletions(-) diff --git a/docs/definitions.adoc b/docs/definitions.adoc index 940cb86..c08acf2 100644 --- a/docs/definitions.adoc +++ b/docs/definitions.adoc @@ -419,10 +419,10 @@ __optional__|< string > array [options="header", cols=".^3,.^4"] |=== |Name|Schema -|**decoder** + -__optional__|string |**key** + -__optional__|object +__optional__|string +|**keyDecoder** + +__optional__|string |**offset** + __optional__|integer(int64) |**timestamp** + @@ -431,6 +431,8 @@ __optional__|integer(int64) __optional__|string |**value** + __optional__|string +|**valueDecoder** + +__optional__|string |=== @@ -476,6 +478,8 @@ __optional__|integer(int64) __optional__|number(double) |**numPartition** + __optional__|integer(int32) +|**replicationFactor** + +__optional__|integer(int32) |**topic** + __optional__|string |=== @@ -551,23 +555,6 @@ __optional__|< <<_node,Node>> > array |=== -[[_topicpartitionreplica]] -=== TopicPartitionReplica - -[options="header", cols=".^3,.^4"] -|=== -|Name|Schema -|**brokerId** + -__optional__|integer(int32) -|**hash** + -__optional__|integer(int32) -|**partition** + -__optional__|integer(int32) -|**topic** + -__optional__|string -|=== - - [[_topicpartitionreplicaassignment]] === TopicPartitionReplicaAssignment diff --git a/docs/index.html b/docs/index.html index d26165d..5256782 100644 --- a/docs/index.html +++ b/docs/index.html @@ -450,19 +450,19 @@

    Kafka REST API SwaggerUI

  91. 4.3. User-controller @@ -540,12 +541,11 @@

    Kafka REST API SwaggerUI

  92. 5.31. TopicMeta
  93. 5.32. TopicPartition
  94. 5.33. TopicPartitionInfo
  95. -
  96. 5.34. TopicPartitionReplica
  97. -
  98. 5.35. TopicPartitionReplicaAssignment
  99. -
  100. 5.36. User
  101. -
  102. 5.37. ZkServerClient
  103. -
  104. 5.38. ZkServerEnvironment
  105. -
  106. 5.39. ZkServerStat
  107. +
  108. 5.34. TopicPartitionReplicaAssignment
  109. +
  110. 5.35. User
  111. +
  112. 5.36. ZkServerClient
  113. +
  114. 5.37. ZkServerEnvironment
  115. +
  116. 5.38. ZkServerStat
  117. @@ -1095,10 +1095,10 @@
    Produces
    -

    4.2.3. Describe log dirs by broker list and topic list

    +

    4.2.3. Describe log dirs by broker list and topic list

    -
    GET /kafka/brokers/logdirs/detail
    +
    POST /kafka/brokers/logdirs/detail
    @@ -1134,11 +1134,11 @@
    Parameters

    < string > array(multi)

    - - + - - + +

    Query

    topicList
    +

    Body

    topicPartitionMap
    optional

    topicList

    < string > array(multi)

    topicPartitionMap

    < string, < integer(int32) > array > map

    @@ -1165,6 +1165,11 @@
    Responses

    < string, < string, LogDirInfo > map > map

    +

    201

    +

    Created

    +

    No Content

    + +

    401

    Unauthorized

    No Content

    @@ -1204,10 +1209,10 @@
    Produces
    -

    4.2.4. Describe replica log dirs.

    +

    4.2.4. Describe replica log dir.

    -
    GET /kafka/brokers/replicalogdirs
    +
    GET /kafka/brokers/replicalogdir/{brokerId}/{topic}/{partition}
    @@ -1229,10 +1234,24 @@
    Parameters -

    Query

    -

    replicas
    +

    Path

    +

    brokerId
    +required

    +

    brokerId

    +

    integer(int32)

    + + +

    Path

    +

    partition
    +required

    +

    partition

    +

    integer(int32)

    + + +

    Path

    +

    topic
    required

    -

    replicas

    +

    topic

    string

    @@ -1257,7 +1276,7 @@
    Responses

    200

    OK

    -

    < string, ReplicaLogDirInfo > map

    +

    ReplicaLogDirInfo

    401

    @@ -1765,7 +1784,7 @@
    Produces
    -

    4.2.10. Get the message from the offset of the partition in the topic, decoder is not supported yet

    +

    4.2.10. Get the message from the offset of the partition in the topic

    GET /kafka/consumer/{topic}/{partition}/{offset}
    @@ -1775,10 +1794,11 @@

    4

    Parameters
    ----+++++ @@ -1786,6 +1806,7 @@
    Parameters<
    + @@ -1795,6 +1816,7 @@
    Parameters< required

    + @@ -1802,6 +1824,7 @@
    Parameters< required

    + @@ -1809,13 +1832,47 @@
    Parameters< required

    + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + +
    Name Description SchemaDefault

    offset

    integer(int64)

    Path

    partition

    integer(int32)

    Path

    topic

    string

    Query

    avroSchema
    +optional

    avroSchema

    string

    Query

    fetchTimeoutMs
    +optional

    fetchTimeoutMs

    integer(int64)

    "30000"

    Query

    keyDecoder
    +optional

    keyDecoder

    string

    "StringDeserializer"

    Query

    decoder
    +

    maxRecords
    optional

    decoder

    maxRecords

    integer(int32)

    Query

    valueDecoder
    +optional

    valueDecoder

    string

    "StringDeserializer"

    @@ -1839,7 +1896,7 @@
    Responses

    200

    OK

    -

    Record

    +

    < Record > array

    401

    @@ -2322,41 +2379,13 @@
    Produces
    -

    4.2.15. Get the meta data of the specified new consumer groups, including state, coordinator, assignmentStrategy, members

    +

    4.2.15. Get all the meta data of new consumer groups, including state, coordinator, assignmentStrategy, members

    GET /kafka/consumergroups/meta
    -
    Parameters
    - ------ - - - - - - - - - - - - - - - - -
    TypeNameDescriptionSchema

    Query

    consumerGroupList
    -required

    consumerGroupList

    < string > array(multi)

    -
    -
    Responses
    @@ -2424,7 +2453,7 @@

    -
    Parameters
    +

    Parameters
    @@ -2519,7 +2548,7 @@

    -
    Parameters
    +

    Parameters
    @@ -2621,7 +2650,7 @@

    -
    Parameters
    +

    Parameters
    @@ -2723,7 +2752,7 @@

    -
    Parameters
    +

    Parameters
    @@ -2966,7 +2995,7 @@

    -
    Parameters
    +
    Parameters

    @@ -3066,7 +3095,7 @@

    -
    Parameters
    +

    Parameters
    @@ -3181,7 +3210,7 @@

    -
    Parameters
    +

    Parameters
    @@ -3302,7 +3331,7 @@

    -
    Parameters
    +

    Parameters
    @@ -3395,10 +3424,10 @@
    Produces
    -

    4.2.26. List topics

    +

    4.2.26. Stop the partition reassignment process

    -
    GET /kafka/topics
    +
    PUT /kafka/partitions/reassign/stop
    @@ -3420,7 +3449,12 @@
    Responses
    - + + + + + + @@ -3462,14 +3496,81 @@
    Produces
    -

    4.2.27. Delete a topic list (you should enable topic deletion

    +

    4.2.27. List topics

    +
    +
    +
    GET /kafka/topics
    +
    +
    +
    +
    Responses
    +

    200

    OK

    < string > array

    GeneralResponse

    201

    Created

    No Content

    401

    +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    HTTP CodeDescriptionSchema

    200

    OK

    < string > array

    401

    Unauthorized

    No Content

    403

    Forbidden

    No Content

    404

    Not Found

    No Content

    +
    +
    +
    Consumes
    +
    +
      +
    • +

      application/json

      +
    • +
    +
    +
    +
    +
    Produces
    +
    +
      +
    • +

      /

      +
    • +
    +
    +
    +
    +
    +

    4.2.28. Delete a topic list (you should enable topic deletion

    DELETE /kafka/topics
    -
    Parameters
    +
    Parameters
    @@ -3497,7 +3598,7 @@
    Parameters<
    -
    Responses
    +
    Responses
    @@ -3536,7 +3637,7 @@
    Responses
    -
    Consumes
    +
    Consumes
    • @@ -3546,7 +3647,7 @@
      Consumes
    -
    Produces
    +
    Produces
    • @@ -3557,14 +3658,14 @@
      Produces
    -

    4.2.28. Create topics

    +

    4.2.29. Create topics

    POST /kafka/topics/create
    -
    Parameters
    +
    Parameters
    @@ -3592,7 +3693,7 @@
    Parameters<
    -
    Responses
    +
    Responses
    @@ -3631,7 +3732,7 @@
    Responses
    -
    Consumes
    +
    Consumes
    • @@ -3641,7 +3742,7 @@
      Consumes
    -
    Produces
    +
    Produces
    • @@ -3652,14 +3753,14 @@
      Produces
    -

    4.2.29. Describe a topic by fetching the metadata and config

    +

    4.2.30. Describe a topic by fetching the metadata and config

    GET /kafka/topics/{topic}
    -
    Parameters
    +
    Parameters
    @@ -3687,7 +3788,7 @@
    Parameters<
    -
    Responses
    +
    Responses
    @@ -3726,7 +3827,7 @@
    Responses
    -
    Consumes
    +
    Consumes
    • @@ -3736,7 +3837,7 @@
      Consumes
    -
    Produces
    +
    Produces
    • @@ -3747,14 +3848,14 @@
      Produces
    -

    4.2.30. Get topic configs

    +

    4.2.31. Get topic configs

    GET /kafka/topics/{topic}/conf
    -
    Parameters
    +
    Parameters
    @@ -3782,7 +3883,7 @@
    Parameters<
    -
    Responses
    +
    Responses
    @@ -3821,7 +3922,7 @@
    Responses
    -
    Consumes
    +
    Consumes
    • @@ -3831,7 +3932,7 @@
      Consumes
    -
    Produces
    +
    Produces
    • @@ -3842,14 +3943,14 @@
      Produces
    -

    4.2.31. Update topic configs

    +

    4.2.32. Update topic configs

    PUT /kafka/topics/{topic}/conf
    -
    Parameters
    +
    Parameters
    @@ -3884,7 +3985,7 @@
    Parameters<
    -
    Responses
    +
    Responses
    @@ -3928,7 +4029,7 @@
    Responses
    -
    Consumes
    +
    Consumes
    • @@ -3938,7 +4039,7 @@
      Consumes
    -
    Produces
    +
    Produces
    • @@ -3949,14 +4050,14 @@
      Produces
    -

    4.2.32. Get topic config by key

    +

    4.2.33. Get topic config by key

    GET /kafka/topics/{topic}/conf/{key}
    -
    Parameters
    +
    Parameters
    @@ -3991,7 +4092,7 @@
    Parameters<
    -
    Responses
    +
    Responses
    @@ -4030,7 +4131,7 @@
    Responses
    -
    Consumes
    +
    Consumes
    • @@ -4040,7 +4141,7 @@
      Consumes
    -
    Produces
    +
    Produces
    • @@ -4051,14 +4152,14 @@
      Produces
    -

    4.2.33. Update a topic config by key

    +

    4.2.34. Update a topic config by key

    PUT /kafka/topics/{topic}/conf/{key}={value}
    -
    Parameters
    +
    Parameters
    @@ -4100,7 +4201,7 @@
    Parameters<
    -
    Responses
    +
    Responses
    @@ -4144,7 +4245,7 @@
    Responses
    -
    Consumes
    +
    Consumes
    • @@ -4154,7 +4255,7 @@
      Consumes
    -
    Produces
    +
    Produces
    • @@ -4165,14 +4266,14 @@
      Produces
    -

    4.2.34. Get topic dyn configs

    +

    4.2.35. Get topic dyn configs

    GET /kafka/topics/{topic}/dynconf
    -
    Parameters
    +
    Parameters
    @@ -4200,7 +4301,7 @@
    Parameters<
    -
    Responses
    +
    Responses
    @@ -4239,7 +4340,7 @@
    Responses
    -
    Consumes
    +
    Consumes
    • @@ -4249,7 +4350,7 @@
      Consumes
    -
    Produces
    +
    Produces
    • @@ -4260,14 +4361,14 @@
      Produces
    -

    4.2.35. Tell if a topic exists

    +

    4.2.36. Tell if a topic exists

    GET /kafka/topics/{topic}/exist
    -
    Parameters
    +
    Parameters
    @@ -4295,7 +4396,7 @@
    Parameters<
    -
    Responses
    +
    Responses
    @@ -4334,7 +4435,7 @@
    Responses
    -
    Consumes
    +
    Consumes
    • @@ -4344,7 +4445,7 @@
      Consumes
    -
    Produces
    +
    Produces
    • @@ -4355,14 +4456,14 @@
      Produces
    -

    4.2.36. List topics Brief

    +

    4.2.37. List topics Brief

    GET /kafka/topicsbrief
    -
    Responses
    +
    Responses
    @@ -4401,7 +4502,7 @@
    Responses
    -
    Consumes
    +
    Consumes
    • @@ -4411,7 +4512,7 @@
      Consumes
    -
    Produces
    +
    Produces
    • @@ -4435,7 +4536,7 @@

      4.3.1

    -
    Parameters
    +
    Parameters
    @@ -4463,7 +4564,7 @@
    Parameters<
    -
    Responses
    +
    Responses
    @@ -4507,7 +4608,7 @@
    Responses
    -
    Consumes
    +
    Consumes
    • @@ -4517,7 +4618,7 @@
      Consumes
    -
    Produces
    +
    Produces
    • @@ -4535,7 +4636,7 @@

      4.3.2

    -
    Responses
    +
    Responses
    @@ -4574,7 +4675,7 @@
    Responses
    -
    Consumes
    +
    Consumes
    • @@ -4584,7 +4685,7 @@
      Consumes
    -
    Produces
    +
    Produces
    • @@ -4602,7 +4703,7 @@

      4

    -
    Parameters
    +
    Parameters
    @@ -4630,7 +4731,7 @@
    Parameters<
    -
    Responses
    +
    Responses
    @@ -4674,7 +4775,7 @@
    Responses
    -
    Consumes
    +
    Consumes
    • @@ -4684,7 +4785,7 @@
      Consumes
    -
    Produces
    +
    Produces
    • @@ -4702,7 +4803,7 @@

      4

    -
    Parameters
    +
    Parameters
    @@ -4730,7 +4831,7 @@
    Parameters<
    -
    Responses
    +
    Responses
    @@ -4769,7 +4870,7 @@
    Responses
    -
    Consumes
    +
    Consumes
    • @@ -4779,7 +4880,7 @@
      Consumes
    -
    Produces
    +
    Produces
    • @@ -4803,7 +4904,7 @@

    -
    Responses
    +
    Responses
    @@ -4842,7 +4943,7 @@
    Responses
    -
    Consumes
    +
    Consumes
    • @@ -4852,7 +4953,7 @@
      Consumes
    -
    Produces
    +
    Produces
    • @@ -4870,7 +4971,7 @@

      4.4.2. Ge

    -
    Responses
    +
    Responses
    @@ -4909,7 +5010,7 @@
    Responses
    -
    Consumes
    +
    Consumes
    • @@ -4919,7 +5020,7 @@
      Consumes
    -
    Produces
    +
    Produces
    • @@ -4937,7 +5038,7 @@

      4.4.3. Get data

    -
    Parameters
    +
    Parameters
    @@ -4965,7 +5066,7 @@
    Parameters<
    -
    Responses
    +
    Responses
    @@ -5004,7 +5105,7 @@
    Responses
    -
    Consumes
    +
    Consumes
    • @@ -5014,7 +5115,7 @@
      Consumes
    -
    Produces
    +
    Produces
    • @@ -5032,7 +5133,7 @@

      4.4.4. List a zoo

    -
    Parameters
    +
    Parameters
    @@ -5060,7 +5161,7 @@
    Parameters<
    -
    Responses
    +
    Responses
    @@ -5099,7 +5200,7 @@
    Responses
    -
    Consumes
    +
    Consumes
    • @@ -5109,7 +5210,7 @@
      Consumes
    -
    Produces
    +
    Produces
    • @@ -5127,7 +5228,7 @@

      4.4.5.

    -
    Responses
    +
    Responses
    @@ -5166,7 +5267,7 @@
    Responses
    -
    Consumes
    +
    Consumes
    • @@ -5176,7 +5277,7 @@
      Consumes
    -
    Produces
    +
    Produces
    • @@ -6113,14 +6214,14 @@

      5.26. Record

    - - - + + + + +

    decoder
    +

    key
    optional

    string

    key
    +

    keyDecoder
    optional

    object

    string

    offset
    @@ -6142,6 +6243,11 @@

    5.26. Record

    optional

    string

    valueDecoder
    +optional

    string

    @@ -6239,6 +6345,11 @@

    5.29. TopicBrief<

    integer(int32)

    +

    replicationFactor
    +optional

    +

    integer(int32)

    + +

    topic
    optional

    string

    @@ -6400,44 +6511,7 @@

    5

    -

    5.34. TopicPartitionReplica

    - ---- - - - - - - - - - - - - - - - - - - - - - - - - -
    NameSchema

    brokerId
    -optional

    integer(int32)

    hash
    -optional

    integer(int32)

    partition
    -optional

    integer(int32)

    topic
    -optional

    string

    -
    -
    -

    5.35. TopicPartitionReplicaAssignment

    +

    5.34. TopicPartitionReplicaAssignment

    @@ -6474,7 +6548,7 @@

    -

    5.36. User

    +

    5.35. User

    @@ -6506,7 +6580,7 @@

    5.36. User

    -

    5.37. ZkServerClient

    +

    5.36. ZkServerClient

    @@ -6553,7 +6627,7 @@

    5.37. ZkS

    -

    5.38. ZkServerEnvironment

    +

    5.37. ZkServerEnvironment

    @@ -6575,7 +6649,7 @@

    -

    5.39. ZkServerStat

    +

    5.38. ZkServerStat

    diff --git a/docs/index.pdf b/docs/index.pdf index 8c4ad6bc594256900799bbecf64acd32af09436a..9cea301dcb90d04beed4ee0ca14dffac30cff2ff 100644 GIT binary patch delta 165916 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 F{~vSyMfLyy delta 152080 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 Q7eEEH@vHUv;d#6I1?!?-;Q#;t diff --git a/docs/paths.adoc b/docs/paths.adoc index 36d8c56..8da815d 100644 --- a/docs/paths.adoc +++ b/docs/paths.adoc @@ -200,10 +200,10 @@ __optional__|brokerList|< integer(int32) > array(multi) * `*/*` -[[_describelogdirsusingget]] +[[_describelogdirsusingpost]] ==== Describe log dirs by broker list and topic list .... -GET /kafka/brokers/logdirs/detail +POST /kafka/brokers/logdirs/detail .... @@ -216,8 +216,8 @@ GET /kafka/brokers/logdirs/detail __optional__|brokerList|< integer(int32) > array(multi) |**Query**|**logDirList** + __optional__|logDirList|< string > array(multi) -|**Query**|**topicList** + -__optional__|topicList|< string > array(multi) +|**Body**|**topicPartitionMap** + +__optional__|topicPartitionMap|< string, < integer(int32) > array > map |=== @@ -227,6 +227,7 @@ __optional__|topicList|< string > array(multi) |=== |HTTP Code|Description|Schema |**200**|OK|< string, < string, <<_logdirinfo,LogDirInfo>> > map > map +|**201**|Created|No Content |**401**|Unauthorized|No Content |**403**|Forbidden|No Content |**404**|Not Found|No Content @@ -244,9 +245,9 @@ __optional__|topicList|< string > array(multi) [[_describereplicalogdirsusingget]] -==== Describe replica log dirs. +==== Describe replica log dir. .... -GET /kafka/brokers/replicalogdirs +GET /kafka/brokers/replicalogdir/{brokerId}/{topic}/{partition} .... @@ -255,8 +256,12 @@ GET /kafka/brokers/replicalogdirs [options="header", cols=".^2,.^3,.^9,.^4"] |=== |Type|Name|Description|Schema -|**Query**|**replicas** + -__required__|replicas|string +|**Path**|**brokerId** + +__required__|brokerId|integer(int32) +|**Path**|**partition** + +__required__|partition|integer(int32) +|**Path**|**topic** + +__required__|topic|string |=== @@ -265,7 +270,7 @@ __required__|replicas|string [options="header", cols=".^2,.^14,.^4"] |=== |HTTP Code|Description|Schema -|**200**|OK|< string, <<_replicalogdirinfo,ReplicaLogDirInfo>> > map +|**200**|OK|<<_replicalogdirinfo,ReplicaLogDirInfo>> |**401**|Unauthorized|No Content |**403**|Forbidden|No Content |**404**|Not Found|No Content @@ -473,7 +478,7 @@ GET /kafka/cluster [[_getmessageusingget]] -==== Get the message from the offset of the partition in the topic, decoder is not supported yet +==== Get the message from the offset of the partition in the topic .... GET /kafka/consumer/{topic}/{partition}/{offset} .... @@ -481,17 +486,25 @@ GET /kafka/consumer/{topic}/{partition}/{offset} ===== Parameters -[options="header", cols=".^2,.^3,.^9,.^4"] +[options="header", cols=".^2,.^3,.^9,.^4,.^2"] |=== -|Type|Name|Description|Schema +|Type|Name|Description|Schema|Default |**Path**|**offset** + -__required__|offset|integer(int64) +__required__|offset|integer(int64)| |**Path**|**partition** + -__required__|partition|integer(int32) +__required__|partition|integer(int32)| |**Path**|**topic** + -__required__|topic|string -|**Query**|**decoder** + -__optional__|decoder|string +__required__|topic|string| +|**Query**|**avroSchema** + +__optional__|avroSchema|string| +|**Query**|**fetchTimeoutMs** + +__optional__|fetchTimeoutMs|integer(int64)|`"30000"` +|**Query**|**keyDecoder** + +__optional__|keyDecoder|string|`"StringDeserializer"` +|**Query**|**maxRecords** + +__optional__|maxRecords|integer(int32)| +|**Query**|**valueDecoder** + +__optional__|valueDecoder|string|`"StringDeserializer"` |=== @@ -500,7 +513,7 @@ __optional__|decoder|string [options="header", cols=".^2,.^14,.^4"] |=== |HTTP Code|Description|Schema -|**200**|OK|<<_record,Record>> +|**200**|OK|< <<_record,Record>> > array |**401**|Unauthorized|No Content |**403**|Forbidden|No Content |**404**|Not Found|No Content @@ -691,22 +704,12 @@ __optional__|type|enum (NEW, OLD) [[_getconsumergroupsmetausingget]] -==== Get the meta data of the specified new consumer groups, including state, coordinator, assignmentStrategy, members +==== Get all the meta data of new consumer groups, including state, coordinator, assignmentStrategy, members .... GET /kafka/consumergroups/meta .... -===== Parameters - -[options="header", cols=".^2,.^3,.^9,.^4"] -|=== -|Type|Name|Description|Schema -|**Query**|**consumerGroupList** + -__required__|consumerGroupList|< string > array(multi) -|=== - - ===== Responses [options="header", cols=".^2,.^14,.^4"] @@ -1120,6 +1123,36 @@ __required__|reassignWrapper|<<_reassignwrapper,ReassignWrapper>> * `*/*` +[[_stopreassignpartitionsusingput]] +==== Stop the partition reassignment process +.... +PUT /kafka/partitions/reassign/stop +.... + + +===== Responses + +[options="header", cols=".^2,.^14,.^4"] +|=== +|HTTP Code|Description|Schema +|**200**|OK|<<_generalresponse,GeneralResponse>> +|**201**|Created|No Content +|**401**|Unauthorized|No Content +|**403**|Forbidden|No Content +|**404**|Not Found|No Content +|=== + + +===== Consumes + +* `application/json` + + +===== Produces + +* `*/*` + + [[_listtopicsusingget]] ==== List topics .... diff --git a/src/docs/swagger/swagger.json b/src/docs/swagger/swagger.json index 7494ad5..bf7e941 100644 --- a/src/docs/swagger/swagger.json +++ b/src/docs/swagger/swagger.json @@ -1 +1 @@ -{"swagger":"2.0","info":{"description":"Kafka REST API SwaggerUI","version":"0.1.0","title":"Kafka REST API SwaggerUI","contact":{"name":"gnuhpc","url":"https://github.com/gnuhpc","email":"gnuhpc@gmail.com"}},"host":"localhost:8080","basePath":"/","tags":[{"name":"collector-controller","description":"Rest API for Collecting JMX Metric Data"},{"name":"user-controller","description":"Security User Management Controller."},{"name":"zookeeper-controller","description":"Zookeeper Controller"},{"name":"kafka-controller","description":"Kafka Controller"}],"paths":{"/jmx/v1":{"get":{"tags":["collector-controller"],"summary":"Fetch all JMX metric data","operationId":"collectJMXMetricUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"jmxurl","in":"query","description":"Parameter jmxurl should be a comma-separated list of {IP:Port} or set to 'default'","required":false,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/JMXMetricDataV1"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/jmx/v2":{"post":{"tags":["collector-controller"],"summary":"Fetch JMX metric data with query filter. You can get the query filter template through the API /jmx/v2/filters.","operationId":"collectJMXMetricUsingPOST","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"jmxurl","in":"query","description":"Parameter jmxurl should be a comma-separated list of {IP:Port} or set to 'default'","required":false,"type":"string"},{"in":"body","name":"jmxQuery","description":"jmxQuery","required":true,"schema":{"$ref":"#/definitions/JMXQuery"}}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/JMXMetricData"}}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/jmx/v2/filters":{"get":{"tags":["collector-controller"],"summary":"List the query filter templates with the filterKey. If filterKey is set to empty, it will return all the templates.","operationId":"listJMXFilterTemplateUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"filterKey","in":"query","description":"filterKey","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"object"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/brokers":{"get":{"tags":["kafka-controller"],"summary":"List brokers in this cluster","operationId":"listBrokersUsingGET","consumes":["application/json"],"produces":["*/*"],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/BrokerInfo"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/brokers/logdirs":{"get":{"tags":["kafka-controller"],"summary":"List log dirs by broker list","operationId":"listLogDirsUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"brokerList","in":"query","description":"brokerList","required":false,"type":"array","items":{"type":"integer","format":"int32"},"collectionFormat":"multi"}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/brokers/logdirs/detail":{"get":{"tags":["kafka-controller"],"summary":"Describe log dirs by broker list and topic list","operationId":"describeLogDirsUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"brokerList","in":"query","description":"brokerList","required":false,"type":"array","items":{"type":"integer","format":"int32"},"collectionFormat":"multi"},{"name":"logDirList","in":"query","description":"logDirList","required":false,"type":"array","items":{"type":"string"},"collectionFormat":"multi"},{"name":"topicList","in":"query","description":"topicList","required":false,"type":"array","items":{"type":"string"},"collectionFormat":"multi"}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"object","additionalProperties":{"$ref":"#/definitions/LogDirInfo"}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/brokers/replicalogdirs":{"get":{"tags":["kafka-controller"],"summary":"Describe replica log dirs.","operationId":"describeReplicaLogDirsUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"replicas","in":"query","description":"replicas","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"$ref":"#/definitions/ReplicaLogDirInfo"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/brokers/{brokerId}/conf":{"get":{"tags":["kafka-controller"],"summary":"Get broker configs, including dynamic configs","operationId":"getBrokerConfigUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"brokerId","in":"path","description":"brokerId","required":true,"type":"integer","format":"int32"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/CustomConfigEntry"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/brokers/{brokerId}/dynconf":{"get":{"tags":["kafka-controller"],"summary":"Get broker dynamic configs","operationId":"getBrokerDynConfigUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"brokerId","in":"path","description":"brokerId","required":true,"type":"integer","format":"int32"}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"object"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}},"put":{"tags":["kafka-controller"],"summary":"Update broker configs","operationId":"updateBrokerDynConfigUsingPUT","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"brokerId","in":"path","description":"brokerId","required":true,"type":"integer","format":"int32"},{"in":"body","name":"props","description":"props","required":true,"schema":{"type":"object","additionalProperties":{"type":"object"}}}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"object"}}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}},"delete":{"tags":["kafka-controller"],"summary":"Remove broker dynamic configs","operationId":"removeBrokerDynConfigUsingDELETE","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"brokerId","in":"path","description":"brokerId","required":true,"type":"integer","format":"int32"},{"name":"configKeysToBeRemoved","in":"query","description":"configKeysToBeRemoved","required":true,"type":"array","items":{"type":"string"},"collectionFormat":"multi"}],"responses":{"200":{"description":"OK"},"204":{"description":"No Content"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"}}}},"/kafka/cluster":{"get":{"tags":["kafka-controller"],"summary":"Describe cluster, nodes, controller info.","operationId":"describeClusterUsingGET","consumes":["application/json"],"produces":["*/*"],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/ClusterInfo"}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/consumer/{topic}/{partition}/{offset}":{"get":{"tags":["kafka-controller"],"summary":"Get the message from the offset of the partition in the topic, decoder is not supported yet","operationId":"getMessageUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"topic","in":"path","description":"topic","required":true,"type":"string"},{"name":"partition","in":"path","description":"partition","required":true,"type":"integer","format":"int32"},{"name":"offset","in":"path","description":"offset","required":true,"type":"integer","format":"int64"},{"name":"decoder","in":"query","description":"decoder","required":false,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/Record"}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/consumergroup/{consumergroup}/{type}":{"delete":{"tags":["kafka-controller"],"summary":"Delete Consumer Group","operationId":"deleteOldConsumerGroupUsingDELETE","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"consumergroup","in":"path","description":"consumergroup","required":true,"type":"string"},{"name":"type","in":"path","description":"type","required":true,"type":"string","enum":["NEW","OLD"]}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/GeneralResponse"}},"204":{"description":"No Content"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"}}}},"/kafka/consumergroup/{consumergroup}/{type}/topic/{topic}/lastcommittime":{"get":{"tags":["kafka-controller"],"summary":"getLastCommitTimestamp","operationId":"getLastCommitTimestampUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"consumergroup","in":"path","description":"consumergroup","required":true,"type":"string"},{"name":"topic","in":"path","description":"topic","required":true,"type":"string"},{"name":"type","in":"path","description":"type","required":true,"type":"string","enum":["NEW","OLD"]}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"object","additionalProperties":{"type":"integer","format":"int64"}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/consumergroup/{consumergroup}/{type}/topic/{topic}/{partition}/{offset}":{"put":{"tags":["kafka-controller"],"summary":"Reset consumer group offset, earliest/latest can be used. Support reset by time for new consumer group, pass a parameter that satisfies yyyy-MM-dd HH:mm:ss.SSS to offset.","operationId":"resetOffsetUsingPUT","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"topic","in":"path","description":"topic","required":true,"type":"string"},{"name":"partition","in":"path","description":"partition","required":true,"type":"integer","format":"int32"},{"name":"consumergroup","in":"path","description":"consumergroup","required":true,"type":"string"},{"name":"offset","in":"path","description":"[earliest/latest/{long}/yyyy-MM-dd HH:mm:ss.SSS] can be supported. The date type is only valid for new consumer group.","required":false,"type":"string"},{"name":"type","in":"path","description":"type","required":true,"type":"string","enum":["NEW","OLD"]}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/GeneralResponse"}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/consumergroups":{"get":{"tags":["kafka-controller"],"summary":"List all consumer groups from zk and kafka","operationId":"listAllConsumerGroupsUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"type","in":"query","description":"type","required":false,"type":"string","enum":["NEW","OLD"]},{"name":"topic","in":"query","description":"topic","required":false,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/consumergroups/meta":{"get":{"tags":["kafka-controller"],"summary":"Get the meta data of the specified new consumer groups, including state, coordinator, assignmentStrategy, members","operationId":"getConsumerGroupsMetaUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"consumerGroupList","in":"query","description":"consumerGroupList","required":true,"type":"array","items":{"type":"string"},"collectionFormat":"multi"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/ConsumerGroupMeta"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/consumergroups/{consumerGroup}/meta":{"get":{"tags":["kafka-controller"],"summary":"Get the meta data of the specify new consumer group, including state, coordinator, assignmentStrategy, members","operationId":"getConsumerGroupMetaUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"consumerGroup","in":"path","description":"consumerGroup","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/ConsumerGroupMeta"}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/consumergroups/{consumerGroup}/{type}":{"get":{"tags":["kafka-controller"],"summary":"Describe consumer group, showing lag and offset, may be slow if multi topics are listened","operationId":"describeConsumerGroupUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"consumerGroup","in":"path","description":"consumerGroup","required":true,"type":"string"},{"name":"type","in":"path","description":"type","required":true,"type":"string","enum":["NEW","OLD"]}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"array","items":{"$ref":"#/definitions/ConsumerGroupDesc"}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/consumergroups/{consumerGroup}/{type}/topic":{"get":{"tags":["kafka-controller"],"summary":"Get the topics involved of the specify consumer group","operationId":"listTopicByConsumerGroupUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"consumerGroup","in":"path","description":"consumerGroup","required":true,"type":"string"},{"name":"type","in":"path","description":"type","required":true,"type":"string","enum":["NEW","OLD"]}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"type":"string"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/consumergroups/{type}/topic/{topic}":{"get":{"tags":["kafka-controller"],"summary":"Describe consumer groups by topic, showing lag and offset","operationId":"describeConsumerGroupByTopicUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"consumerGroup","in":"query","description":"consumerGroup","required":false,"type":"string"},{"name":"type","in":"path","description":"type","required":true,"type":"string","enum":["NEW","OLD"]},{"name":"topic","in":"path","description":"topic","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/ConsumerGroupDesc"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/controller":{"get":{"tags":["kafka-controller"],"summary":"Get controller in this cluster","operationId":"getControllerIdUsingGET","consumes":["application/json"],"produces":["*/*"],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/Node"}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/health":{"get":{"tags":["kafka-controller"],"summary":"Check the cluster health.","operationId":"healthCheckUsingGET","consumes":["application/json"],"produces":["*/*"],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/HealthCheckResult"}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/partitions/add":{"post":{"tags":["kafka-controller"],"summary":"Add partitions to the topics","operationId":"addPartitionUsingPOST","consumes":["application/json"],"produces":["*/*"],"parameters":[{"in":"body","name":"addPartitions","description":"addPartitions","required":true,"schema":{"type":"array","items":{"$ref":"#/definitions/AddPartition"}}}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"$ref":"#/definitions/GeneralResponse"}}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/partitions/reassign/check":{"put":{"tags":["kafka-controller"],"summary":"Check the partition reassignment process","operationId":"checkReassignPartitionsUsingPUT","consumes":["application/json"],"produces":["*/*"],"parameters":[{"in":"body","name":"reassign","description":"reassign","required":true,"schema":{"$ref":"#/definitions/ReassignModel"}}],"responses":{"-1":{"description":"Reassignment Failed"},"0":{"description":"Reassignment In Progress"},"1":{"description":"Reassignment Completed"},"200":{"description":"OK","schema":{"$ref":"#/definitions/ReassignStatus"}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/partitions/reassign/execute":{"put":{"tags":["kafka-controller"],"summary":"Execute the partition reassignment","operationId":"executeReassignPartitionsUsingPUT","consumes":["application/json"],"produces":["*/*"],"parameters":[{"in":"body","name":"reassign","description":"reassign","required":true,"schema":{"$ref":"#/definitions/ReassignModel"}},{"name":"interBrokerThrottle","in":"query","description":"interBrokerThrottle","required":false,"type":"integer","format":"int64"},{"name":"replicaAlterLogDirsThrottle","in":"query","description":"replicaAlterLogDirsThrottle","required":false,"type":"integer","format":"int64"},{"name":"timeoutMs","in":"query","description":"timeoutMs","required":false,"type":"integer","format":"int64"}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/ReassignStatus"}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/partitions/reassign/generate":{"post":{"tags":["kafka-controller"],"summary":"Generate plan for the partition reassignment","operationId":"generateReassignPartitionsUsingPOST","consumes":["application/json"],"produces":["*/*"],"parameters":[{"in":"body","name":"reassignWrapper","description":"reassignWrapper","required":true,"schema":{"$ref":"#/definitions/ReassignWrapper"}}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/ReassignModel"}}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/topics":{"get":{"tags":["kafka-controller"],"summary":"List topics","operationId":"listTopicsUsingGET","consumes":["application/json"],"produces":["*/*"],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"type":"string"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}},"delete":{"tags":["kafka-controller"],"summary":"Delete a topic list (you should enable topic deletion","operationId":"deleteTopicListUsingDELETE","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"topicList","in":"query","description":"topicList","required":true,"type":"array","items":{"type":"string"},"collectionFormat":"multi"}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"$ref":"#/definitions/GeneralResponse"}}},"204":{"description":"No Content"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"}}}},"/kafka/topics/create":{"post":{"tags":["kafka-controller"],"summary":"Create topics","operationId":"createTopicUsingPOST","consumes":["application/json"],"produces":["*/*"],"parameters":[{"in":"body","name":"topicList","description":"topicList","required":true,"schema":{"type":"array","items":{"$ref":"#/definitions/TopicDetail"}}}],"responses":{"201":{"description":"Created","schema":{"type":"object","additionalProperties":{"$ref":"#/definitions/GeneralResponse"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/topics/{topic}":{"get":{"tags":["kafka-controller"],"summary":"Describe a topic by fetching the metadata and config","operationId":"describeTopicUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"topic","in":"path","description":"topic","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/TopicMeta"}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/topics/{topic}/conf":{"get":{"tags":["kafka-controller"],"summary":"Get topic configs","operationId":"getTopicConfigUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"topic","in":"path","description":"topic","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/CustomConfigEntry"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}},"put":{"tags":["kafka-controller"],"summary":"Update topic configs","operationId":"updateTopicConfigUsingPUT","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"topic","in":"path","description":"topic","required":true,"type":"string"},{"in":"body","name":"props","description":"props","required":true,"schema":{"type":"object","additionalProperties":{"type":"object"}}}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/CustomConfigEntry"}}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/topics/{topic}/conf/{key}":{"get":{"tags":["kafka-controller"],"summary":"Get topic config by key","operationId":"getTopicConfigByKeyUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"topic","in":"path","description":"topic","required":true,"type":"string"},{"name":"key","in":"path","description":"key","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"object"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/topics/{topic}/conf/{key}={value}":{"put":{"tags":["kafka-controller"],"summary":"Update a topic config by key","operationId":"updateTopicConfigByKeyUsingPUT","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"topic","in":"path","description":"topic","required":true,"type":"string"},{"name":"key","in":"path","description":"key","required":true,"type":"string"},{"name":"value","in":"path","description":"value","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/CustomConfigEntry"}}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/topics/{topic}/dynconf":{"get":{"tags":["kafka-controller"],"summary":"Get topic dyn configs","operationId":"getTopicDynConfigUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"topic","in":"path","description":"topic","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"object"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/topics/{topic}/exist":{"get":{"tags":["kafka-controller"],"summary":"Tell if a topic exists","operationId":"existTopicUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"topic","in":"path","description":"topic","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"boolean"}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/topicsbrief":{"get":{"tags":["kafka-controller"],"summary":"List topics Brief","operationId":"listTopicBriefUsingGET","consumes":["application/json"],"produces":["*/*"],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/TopicBrief"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/users":{"get":{"tags":["user-controller"],"summary":"Get user list.","operationId":"listUserUsingGET","consumes":["application/json"],"produces":["*/*"],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"type":"string"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}},"post":{"tags":["user-controller"],"summary":"Add user.","operationId":"addUserUsingPOST","consumes":["application/json"],"produces":["*/*"],"parameters":[{"in":"body","name":"user","description":"user","required":true,"schema":{"$ref":"#/definitions/User"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/GeneralResponse"}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}},"put":{"tags":["user-controller"],"summary":"Modify user information.","operationId":"modifyUserUsingPUT","consumes":["application/json"],"produces":["*/*"],"parameters":[{"in":"body","name":"user","description":"user","required":true,"schema":{"$ref":"#/definitions/User"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/GeneralResponse"}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/users/{username}":{"delete":{"tags":["user-controller"],"summary":"Delete user.","operationId":"delUserUsingDELETE","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"username","in":"path","description":"username","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/GeneralResponse"}},"204":{"description":"No Content"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"}}}},"/zk/connstate":{"get":{"tags":["zookeeper-controller"],"summary":"Get the connection state of zookeeper","operationId":"zkConnStateUsingGET","consumes":["application/json"],"produces":["*/*"],"responses":{"200":{"description":"OK","schema":{"type":"string"}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/zk/env":{"get":{"tags":["zookeeper-controller"],"summary":"Get the environment information of zookeeper","operationId":"getEnvUsingGET","consumes":["application/json"],"produces":["*/*"],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"$ref":"#/definitions/ZkServerEnvironment"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/zk/get/path":{"get":{"tags":["zookeeper-controller"],"summary":"Get data of a zookeeper path","operationId":"getUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"path","in":"query","description":"path","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"string"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/zk/ls/path":{"get":{"tags":["zookeeper-controller"],"summary":"List a zookeeper path","operationId":"lsUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"path","in":"query","description":"path","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"type":"string"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/zk/stat":{"get":{"tags":["zookeeper-controller"],"summary":"Get the service state of zookeeper","operationId":"getStatUsingGET","consumes":["application/json"],"produces":["*/*"],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"$ref":"#/definitions/ZkServerStat"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}}},"definitions":{"AddPartition":{"type":"object","properties":{"topic":{"type":"string"},"numPartitionsAdded":{"type":"integer","format":"int32"},"replicaAssignment":{"type":"array","items":{"type":"array","items":{"type":"integer","format":"int32"}}}}},"BrokerInfo":{"type":"object","properties":{"endpoints":{"type":"array","items":{"type":"string"}},"host":{"type":"string"},"id":{"type":"integer","format":"int32"},"jmxPort":{"type":"integer","format":"int32"},"port":{"type":"integer","format":"int32"},"rack":{"type":"string"},"securityProtocol":{"type":"object"},"startTime":{"type":"string","format":"date-time"},"version":{"type":"integer","format":"int32"}}},"ClusterInfo":{"type":"object","properties":{"clusterId":{"type":"string"},"controller":{"$ref":"#/definitions/Node"},"nodes":{"type":"array","items":{"$ref":"#/definitions/Node"}}}},"ConsumerGroupDesc":{"type":"object","properties":{"assignmentStrategy":{"type":"string"},"clientId":{"type":"string"},"consumerId":{"type":"string"},"coordinator":{"$ref":"#/definitions/Node"},"currentOffset":{"type":"integer","format":"int64"},"groupName":{"type":"string"},"host":{"type":"string"},"lag":{"type":"integer","format":"int64"},"logEndOffset":{"type":"integer","format":"int64"},"partitionId":{"type":"integer","format":"int32"},"state":{"type":"string","enum":["Unknown","PreparingRebalance","CompletingRebalance","Stable","Dead","Empty"]},"topic":{"type":"string"},"type":{"type":"string","enum":["NEW","OLD"]}}},"ConsumerGroupMeta":{"type":"object","properties":{"assignmentStrategy":{"type":"string"},"coordinator":{"$ref":"#/definitions/Node"},"groupId":{"type":"string"},"members":{"type":"array","items":{"$ref":"#/definitions/MemberDescription"}},"state":{"type":"string","enum":["Unknown","PreparingRebalance","CompletingRebalance","Stable","Dead","Empty"]}}},"CustomConfigEntry":{"type":"object","properties":{"isReadOnly":{"type":"boolean"},"isSensitive":{"type":"boolean"},"name":{"type":"string"},"readOnly":{"type":"boolean"},"sensitive":{"type":"boolean"},"source":{"type":"string","enum":["DYNAMIC_TOPIC_CONFIG","DYNAMIC_BROKER_CONFIG","DYNAMIC_DEFAULT_BROKER_CONFIG","STATIC_BROKER_CONFIG","DEFAULT_CONFIG","UNKNOWN"]},"value":{"type":"string"}}},"CustomTopicPartitionInfo":{"type":"object","properties":{"endOffset":{"type":"integer","format":"int64"},"in_sync":{"type":"boolean"},"messageAvailable":{"type":"integer","format":"int64"},"startOffset":{"type":"integer","format":"int64"},"topicPartitionInfo":{"$ref":"#/definitions/TopicPartitionInfo"}}},"GeneralResponse":{"type":"object","properties":{"data":{"type":"object"},"msg":{"type":"string"},"state":{"type":"string","enum":["success","failure"]}}},"HashMap«string,object»":{"type":"object","additionalProperties":{"type":"object"}},"HealthCheckResult":{"type":"object","properties":{"msg":{"type":"string"},"status":{"type":"string"},"timestamp":{"type":"string","example":"yyyy-MM-dd HH:mm:ss"}}},"HostAndPort":{"type":"object","properties":{"hasBracketlessColons":{"type":"boolean"},"host":{"type":"string"},"hostText":{"type":"string"},"port":{"type":"integer","format":"int32"}}},"JMXConfiguration":{"type":"object","properties":{"include":{"$ref":"#/definitions/JMXFilter"},"exclude":{"$ref":"#/definitions/JMXFilter"}}},"JMXFilter":{"type":"object","properties":{"attribute":{"type":"object"},"beanNames":{"type":"array","items":{"type":"string"}},"beanRegexes":{"type":"array","items":{"$ref":"#/definitions/Pattern"}},"domain":{"type":"string"},"domainRegex":{"$ref":"#/definitions/Pattern"},"emptyBeanName":{"type":"boolean"},"filter":{"type":"object","additionalProperties":{"type":"object"}}}},"JMXMetricData":{"type":"object","properties":{"collected":{"type":"boolean"},"host":{"type":"string"},"metrics":{"type":"array","items":{"$ref":"#/definitions/HashMap«string,object»"}},"msg":{"type":"string"},"timestamp":{"type":"string","example":"yyyy-MM-dd HH:mm:ss"}}},"JMXMetricDataV1":{"type":"object","properties":{"collected":{"type":"boolean"},"host":{"type":"string"},"mbeanInfo":{"type":"object"},"msg":{"type":"string"},"timestamp":{"type":"string","example":"yyyy-MM-dd HH:mm:ss"}}},"JMXQuery":{"type":"object","properties":{"filters":{"type":"array","items":{"$ref":"#/definitions/JMXConfiguration"}}}},"LogDirInfo":{"type":"object","properties":{"error":{"type":"string","enum":["UNKNOWN_SERVER_ERROR","NONE","OFFSET_OUT_OF_RANGE","CORRUPT_MESSAGE","UNKNOWN_TOPIC_OR_PARTITION","INVALID_FETCH_SIZE","LEADER_NOT_AVAILABLE","NOT_LEADER_FOR_PARTITION","REQUEST_TIMED_OUT","BROKER_NOT_AVAILABLE","REPLICA_NOT_AVAILABLE","MESSAGE_TOO_LARGE","STALE_CONTROLLER_EPOCH","OFFSET_METADATA_TOO_LARGE","NETWORK_EXCEPTION","COORDINATOR_LOAD_IN_PROGRESS","COORDINATOR_NOT_AVAILABLE","NOT_COORDINATOR","INVALID_TOPIC_EXCEPTION","RECORD_LIST_TOO_LARGE","NOT_ENOUGH_REPLICAS","NOT_ENOUGH_REPLICAS_AFTER_APPEND","INVALID_REQUIRED_ACKS","ILLEGAL_GENERATION","INCONSISTENT_GROUP_PROTOCOL","INVALID_GROUP_ID","UNKNOWN_MEMBER_ID","INVALID_SESSION_TIMEOUT","REBALANCE_IN_PROGRESS","INVALID_COMMIT_OFFSET_SIZE","TOPIC_AUTHORIZATION_FAILED","GROUP_AUTHORIZATION_FAILED","CLUSTER_AUTHORIZATION_FAILED","INVALID_TIMESTAMP","UNSUPPORTED_SASL_MECHANISM","ILLEGAL_SASL_STATE","UNSUPPORTED_VERSION","TOPIC_ALREADY_EXISTS","INVALID_PARTITIONS","INVALID_REPLICATION_FACTOR","INVALID_REPLICA_ASSIGNMENT","INVALID_CONFIG","NOT_CONTROLLER","INVALID_REQUEST","UNSUPPORTED_FOR_MESSAGE_FORMAT","POLICY_VIOLATION","OUT_OF_ORDER_SEQUENCE_NUMBER","DUPLICATE_SEQUENCE_NUMBER","INVALID_PRODUCER_EPOCH","INVALID_TXN_STATE","INVALID_PRODUCER_ID_MAPPING","INVALID_TRANSACTION_TIMEOUT","CONCURRENT_TRANSACTIONS","TRANSACTION_COORDINATOR_FENCED","TRANSACTIONAL_ID_AUTHORIZATION_FAILED","SECURITY_DISABLED","OPERATION_NOT_ATTEMPTED","KAFKA_STORAGE_ERROR","LOG_DIR_NOT_FOUND","SASL_AUTHENTICATION_FAILED","UNKNOWN_PRODUCER_ID","REASSIGNMENT_IN_PROGRESS","DELEGATION_TOKEN_AUTH_DISABLED","DELEGATION_TOKEN_NOT_FOUND","DELEGATION_TOKEN_OWNER_MISMATCH","DELEGATION_TOKEN_REQUEST_NOT_ALLOWED","DELEGATION_TOKEN_AUTHORIZATION_FAILED","DELEGATION_TOKEN_EXPIRED","INVALID_PRINCIPAL_TYPE","NON_EMPTY_GROUP","GROUP_ID_NOT_FOUND","FETCH_SESSION_ID_NOT_FOUND","INVALID_FETCH_SESSION_EPOCH"]},"replicaInfos":{"type":"object","additionalProperties":{"$ref":"#/definitions/ReplicaInfo"}}}},"Map«int,long»":{"type":"object","additionalProperties":{"type":"integer","format":"int64"}},"Map«string,LogDirInfo»":{"type":"object","additionalProperties":{"$ref":"#/definitions/LogDirInfo"}},"MemberDescription":{"type":"object","properties":{"assignment":{"type":"array","items":{"$ref":"#/definitions/TopicPartition"}},"clientId":{"type":"string"},"host":{"type":"string"},"memberId":{"type":"string"}}},"Node":{"type":"object","properties":{"empty":{"type":"boolean"},"hash":{"type":"integer","format":"int32"},"host":{"type":"string"},"id":{"type":"integer","format":"int32"},"idString":{"type":"string"},"port":{"type":"integer","format":"int32"},"rack":{"type":"string"}}},"Pattern":{"type":"object","properties":{"cursor":{"type":"integer","format":"int32"},"flags":{"type":"integer","format":"int32"},"pattern":{"type":"string"}}},"ReassignModel":{"type":"object","properties":{"partitions":{"type":"array","items":{"$ref":"#/definitions/TopicPartitionReplicaAssignment"}},"version":{"type":"integer","format":"int32"}}},"ReassignStatus":{"type":"object","properties":{"msg":{"type":"string"},"partitionsReassignStatus":{"type":"object","additionalProperties":{"type":"integer","format":"int32"}},"removeThrottle":{"type":"boolean"},"replicasReassignStatus":{"type":"object","additionalProperties":{"type":"integer","format":"int32"}}}},"ReassignWrapper":{"type":"object","properties":{"brokers":{"type":"array","items":{"type":"integer","format":"int32"}},"topics":{"type":"array","items":{"type":"string"}}}},"Record":{"type":"object","properties":{"decoder":{"type":"string"},"key":{"type":"object"},"offset":{"type":"integer","format":"int64"},"timestamp":{"type":"integer","format":"int64"},"topic":{"type":"string"},"value":{"type":"string"}}},"ReplicaInfo":{"type":"object","properties":{"isFuture":{"type":"boolean"},"offsetLag":{"type":"integer","format":"int64"},"size":{"type":"integer","format":"int64"}}},"ReplicaLogDirInfo":{"type":"object","properties":{"currentReplicaLogDir":{"type":"string"},"currentReplicaOffsetLag":{"type":"integer","format":"int64"},"futureReplicaLogDir":{"type":"string"},"futureReplicaOffsetLag":{"type":"integer","format":"int64"}}},"TopicBrief":{"type":"object","properties":{"isrRate":{"type":"number","format":"double"},"numPartition":{"type":"integer","format":"int32"},"topic":{"type":"string"}}},"TopicDetail":{"type":"object","properties":{"partitions":{"type":"integer","format":"int32"},"factor":{"type":"integer","format":"int32"},"name":{"type":"string"},"prop":{"type":"object","additionalProperties":{"type":"object"}},"replicasAssignments":{"type":"object","additionalProperties":{"type":"array","items":{"type":"integer","format":"int32"}}}}},"TopicMeta":{"type":"object","properties":{"internal":{"type":"boolean"},"partitionCount":{"type":"integer","format":"int32"},"replicationFactor":{"type":"integer","format":"int32"},"topicName":{"type":"string"},"topicPartitionInfos":{"type":"array","items":{"$ref":"#/definitions/CustomTopicPartitionInfo"}}}},"TopicPartition":{"type":"object","properties":{"hash":{"type":"integer","format":"int32"},"partition":{"type":"integer","format":"int32"},"topic":{"type":"string"}}},"TopicPartitionInfo":{"type":"object","properties":{"isr":{"type":"array","items":{"$ref":"#/definitions/Node"}},"leader":{"$ref":"#/definitions/Node"},"partition":{"type":"integer","format":"int32"},"replicas":{"type":"array","items":{"$ref":"#/definitions/Node"}}}},"TopicPartitionReplica":{"type":"object","properties":{"brokerId":{"type":"integer","format":"int32"},"hash":{"type":"integer","format":"int32"},"partition":{"type":"integer","format":"int32"},"topic":{"type":"string"}}},"TopicPartitionReplicaAssignment":{"type":"object","properties":{"topic":{"type":"string"},"partition":{"type":"integer","format":"int32"},"replicas":{"type":"array","items":{"type":"integer","format":"int32"}},"log_dirs":{"type":"array","items":{"type":"string"}}}},"User":{"type":"object","properties":{"username":{"type":"string"},"password":{"type":"string"},"role":{"type":"string"}}},"ZkServerClient":{"type":"object","properties":{"host":{"type":"string"},"ops":{"type":"integer","format":"int32"},"port":{"type":"integer","format":"int32"},"queued":{"type":"integer","format":"int32"},"received":{"type":"integer","format":"int32"},"sent":{"type":"integer","format":"int32"}}},"ZkServerEnvironment":{"type":"object","properties":{"attributes":{"type":"object","additionalProperties":{"type":"string"}}}},"ZkServerStat":{"type":"object","properties":{"avgLatency":{"type":"integer","format":"int32"},"buildDate":{"type":"string"},"clients":{"type":"array","items":{"$ref":"#/definitions/ZkServerClient"}},"connections":{"type":"integer","format":"int32"},"maxLatency":{"type":"integer","format":"int32"},"minLatency":{"type":"integer","format":"int32"},"mode":{"type":"string","enum":["Leader","Follower","Observer","Standalone","Down","Unknow"]},"msg":{"type":"string"},"nodes":{"type":"integer","format":"int32"},"outstanding":{"type":"integer","format":"int32"},"received":{"type":"integer","format":"int32"},"sent":{"type":"integer","format":"int32"},"version":{"type":"string"},"zxId":{"type":"string"}}}}} +{"swagger":"2.0","info":{"description":"Kafka REST API SwaggerUI","version":"0.1.0","title":"Kafka REST API SwaggerUI","contact":{"name":"gnuhpc","url":"https://github.com/gnuhpc","email":"gnuhpc@gmail.com"}},"host":"localhost:8080","basePath":"/","tags":[{"name":"collector-controller","description":"Rest API for Collecting JMX Metric Data"},{"name":"user-controller","description":"Security User Management Controller."},{"name":"zookeeper-controller","description":"Zookeeper Controller"},{"name":"kafka-controller","description":"Kafka Controller"}],"paths":{"/jmx/v1":{"get":{"tags":["collector-controller"],"summary":"Fetch all JMX metric data","operationId":"collectJMXMetricUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"jmxurl","in":"query","description":"Parameter jmxurl should be a comma-separated list of {IP:Port} or set to 'default'","required":false,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/JMXMetricDataV1"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/jmx/v2":{"post":{"tags":["collector-controller"],"summary":"Fetch JMX metric data with query filter. You can get the query filter template through the API /jmx/v2/filters.","operationId":"collectJMXMetricUsingPOST","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"jmxurl","in":"query","description":"Parameter jmxurl should be a comma-separated list of {IP:Port} or set to 'default'","required":false,"type":"string"},{"in":"body","name":"jmxQuery","description":"jmxQuery","required":true,"schema":{"$ref":"#/definitions/JMXQuery"}}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/JMXMetricData"}}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/jmx/v2/filters":{"get":{"tags":["collector-controller"],"summary":"List the query filter templates with the filterKey. If filterKey is set to empty, it will return all the templates.","operationId":"listJMXFilterTemplateUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"filterKey","in":"query","description":"filterKey","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"object"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/brokers":{"get":{"tags":["kafka-controller"],"summary":"List brokers in this cluster","operationId":"listBrokersUsingGET","consumes":["application/json"],"produces":["*/*"],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/BrokerInfo"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/brokers/logdirs":{"get":{"tags":["kafka-controller"],"summary":"List log dirs by broker list","operationId":"listLogDirsUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"brokerList","in":"query","description":"brokerList","required":false,"type":"array","items":{"type":"integer","format":"int32"},"collectionFormat":"multi"}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/brokers/logdirs/detail":{"post":{"tags":["kafka-controller"],"summary":"Describe log dirs by broker list and topic list","operationId":"describeLogDirsUsingPOST","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"brokerList","in":"query","description":"brokerList","required":false,"type":"array","items":{"type":"integer","format":"int32"},"collectionFormat":"multi"},{"name":"logDirList","in":"query","description":"logDirList","required":false,"type":"array","items":{"type":"string"},"collectionFormat":"multi"},{"in":"body","name":"topicPartitionMap","description":"topicPartitionMap","required":false,"schema":{"type":"object","additionalProperties":{"type":"array","items":{"type":"integer","format":"int32"}}}}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"object","additionalProperties":{"$ref":"#/definitions/LogDirInfo"}}}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/brokers/replicalogdir/{brokerId}/{topic}/{partition}":{"get":{"tags":["kafka-controller"],"summary":"Describe replica log dir.","operationId":"describeReplicaLogDirsUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"brokerId","in":"path","description":"brokerId","required":true,"type":"integer","format":"int32"},{"name":"topic","in":"path","description":"topic","required":true,"type":"string"},{"name":"partition","in":"path","description":"partition","required":true,"type":"integer","format":"int32"}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/ReplicaLogDirInfo"}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/brokers/{brokerId}/conf":{"get":{"tags":["kafka-controller"],"summary":"Get broker configs, including dynamic configs","operationId":"getBrokerConfigUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"brokerId","in":"path","description":"brokerId","required":true,"type":"integer","format":"int32"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/CustomConfigEntry"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/brokers/{brokerId}/dynconf":{"get":{"tags":["kafka-controller"],"summary":"Get broker dynamic configs","operationId":"getBrokerDynConfigUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"brokerId","in":"path","description":"brokerId","required":true,"type":"integer","format":"int32"}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"object"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}},"put":{"tags":["kafka-controller"],"summary":"Update broker configs","operationId":"updateBrokerDynConfigUsingPUT","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"brokerId","in":"path","description":"brokerId","required":true,"type":"integer","format":"int32"},{"in":"body","name":"props","description":"props","required":true,"schema":{"type":"object","additionalProperties":{"type":"object"}}}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"object"}}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}},"delete":{"tags":["kafka-controller"],"summary":"Remove broker dynamic configs","operationId":"removeBrokerDynConfigUsingDELETE","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"brokerId","in":"path","description":"brokerId","required":true,"type":"integer","format":"int32"},{"name":"configKeysToBeRemoved","in":"query","description":"configKeysToBeRemoved","required":true,"type":"array","items":{"type":"string"},"collectionFormat":"multi"}],"responses":{"200":{"description":"OK"},"204":{"description":"No Content"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"}}}},"/kafka/cluster":{"get":{"tags":["kafka-controller"],"summary":"Describe cluster, nodes, controller info.","operationId":"describeClusterUsingGET","consumes":["application/json"],"produces":["*/*"],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/ClusterInfo"}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/consumer/{topic}/{partition}/{offset}":{"get":{"tags":["kafka-controller"],"summary":"Get the message from the offset of the partition in the topic","operationId":"getMessageUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"topic","in":"path","description":"topic","required":true,"type":"string"},{"name":"partition","in":"path","description":"partition","required":true,"type":"integer","format":"int32"},{"name":"offset","in":"path","description":"offset","required":true,"type":"integer","format":"int64"},{"name":"maxRecords","in":"query","description":"maxRecords","required":false,"type":"integer","format":"int32"},{"name":"keyDecoder","in":"query","description":"keyDecoder","required":false,"type":"string","default":"StringDeserializer"},{"name":"valueDecoder","in":"query","description":"valueDecoder","required":false,"type":"string","default":"StringDeserializer"},{"name":"avroSchema","in":"query","description":"avroSchema","required":false,"type":"string"},{"name":"fetchTimeoutMs","in":"query","description":"fetchTimeoutMs","required":false,"type":"integer","default":30000,"format":"int64"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/Record"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/consumergroup/{consumergroup}/{type}":{"delete":{"tags":["kafka-controller"],"summary":"Delete Consumer Group","operationId":"deleteOldConsumerGroupUsingDELETE","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"consumergroup","in":"path","description":"consumergroup","required":true,"type":"string"},{"name":"type","in":"path","description":"type","required":true,"type":"string","enum":["NEW","OLD"]}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/GeneralResponse"}},"204":{"description":"No Content"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"}}}},"/kafka/consumergroup/{consumergroup}/{type}/topic/{topic}/lastcommittime":{"get":{"tags":["kafka-controller"],"summary":"getLastCommitTimestamp","operationId":"getLastCommitTimestampUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"consumergroup","in":"path","description":"consumergroup","required":true,"type":"string"},{"name":"topic","in":"path","description":"topic","required":true,"type":"string"},{"name":"type","in":"path","description":"type","required":true,"type":"string","enum":["NEW","OLD"]}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"object","additionalProperties":{"type":"integer","format":"int64"}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/consumergroup/{consumergroup}/{type}/topic/{topic}/{partition}/{offset}":{"put":{"tags":["kafka-controller"],"summary":"Reset consumer group offset, earliest/latest can be used. Support reset by time for new consumer group, pass a parameter that satisfies yyyy-MM-dd HH:mm:ss.SSS to offset.","operationId":"resetOffsetUsingPUT","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"topic","in":"path","description":"topic","required":true,"type":"string"},{"name":"partition","in":"path","description":"partition","required":true,"type":"integer","format":"int32"},{"name":"consumergroup","in":"path","description":"consumergroup","required":true,"type":"string"},{"name":"offset","in":"path","description":"[earliest/latest/{long}/yyyy-MM-dd HH:mm:ss.SSS] can be supported. The date type is only valid for new consumer group.","required":false,"type":"string"},{"name":"type","in":"path","description":"type","required":true,"type":"string","enum":["NEW","OLD"]}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/GeneralResponse"}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/consumergroups":{"get":{"tags":["kafka-controller"],"summary":"List all consumer groups from zk and kafka","operationId":"listAllConsumerGroupsUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"type","in":"query","description":"type","required":false,"type":"string","enum":["NEW","OLD"]},{"name":"topic","in":"query","description":"topic","required":false,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/consumergroups/meta":{"get":{"tags":["kafka-controller"],"summary":"Get all the meta data of new consumer groups, including state, coordinator, assignmentStrategy, members","operationId":"getConsumerGroupsMetaUsingGET","consumes":["application/json"],"produces":["*/*"],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/ConsumerGroupMeta"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/consumergroups/{consumerGroup}/meta":{"get":{"tags":["kafka-controller"],"summary":"Get the meta data of the specify new consumer group, including state, coordinator, assignmentStrategy, members","operationId":"getConsumerGroupMetaUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"consumerGroup","in":"path","description":"consumerGroup","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/ConsumerGroupMeta"}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/consumergroups/{consumerGroup}/{type}":{"get":{"tags":["kafka-controller"],"summary":"Describe consumer group, showing lag and offset, may be slow if multi topics are listened","operationId":"describeConsumerGroupUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"consumerGroup","in":"path","description":"consumerGroup","required":true,"type":"string"},{"name":"type","in":"path","description":"type","required":true,"type":"string","enum":["NEW","OLD"]}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"array","items":{"$ref":"#/definitions/ConsumerGroupDesc"}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/consumergroups/{consumerGroup}/{type}/topic":{"get":{"tags":["kafka-controller"],"summary":"Get the topics involved of the specify consumer group","operationId":"listTopicByConsumerGroupUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"consumerGroup","in":"path","description":"consumerGroup","required":true,"type":"string"},{"name":"type","in":"path","description":"type","required":true,"type":"string","enum":["NEW","OLD"]}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"type":"string"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/consumergroups/{type}/topic/{topic}":{"get":{"tags":["kafka-controller"],"summary":"Describe consumer groups by topic, showing lag and offset","operationId":"describeConsumerGroupByTopicUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"consumerGroup","in":"query","description":"consumerGroup","required":false,"type":"string"},{"name":"type","in":"path","description":"type","required":true,"type":"string","enum":["NEW","OLD"]},{"name":"topic","in":"path","description":"topic","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/ConsumerGroupDesc"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/controller":{"get":{"tags":["kafka-controller"],"summary":"Get controller in this cluster","operationId":"getControllerIdUsingGET","consumes":["application/json"],"produces":["*/*"],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/Node"}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/health":{"get":{"tags":["kafka-controller"],"summary":"Check the cluster health.","operationId":"healthCheckUsingGET","consumes":["application/json"],"produces":["*/*"],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/HealthCheckResult"}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/partitions/add":{"post":{"tags":["kafka-controller"],"summary":"Add partitions to the topics","operationId":"addPartitionUsingPOST","consumes":["application/json"],"produces":["*/*"],"parameters":[{"in":"body","name":"addPartitions","description":"addPartitions","required":true,"schema":{"type":"array","items":{"$ref":"#/definitions/AddPartition"}}}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"$ref":"#/definitions/GeneralResponse"}}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/partitions/reassign/check":{"put":{"tags":["kafka-controller"],"summary":"Check the partition reassignment process","operationId":"checkReassignPartitionsUsingPUT","consumes":["application/json"],"produces":["*/*"],"parameters":[{"in":"body","name":"reassign","description":"reassign","required":true,"schema":{"$ref":"#/definitions/ReassignModel"}}],"responses":{"-1":{"description":"Reassignment Failed"},"0":{"description":"Reassignment In Progress"},"1":{"description":"Reassignment Completed"},"200":{"description":"OK","schema":{"$ref":"#/definitions/ReassignStatus"}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/partitions/reassign/execute":{"put":{"tags":["kafka-controller"],"summary":"Execute the partition reassignment","operationId":"executeReassignPartitionsUsingPUT","consumes":["application/json"],"produces":["*/*"],"parameters":[{"in":"body","name":"reassign","description":"reassign","required":true,"schema":{"$ref":"#/definitions/ReassignModel"}},{"name":"interBrokerThrottle","in":"query","description":"interBrokerThrottle","required":false,"type":"integer","format":"int64"},{"name":"replicaAlterLogDirsThrottle","in":"query","description":"replicaAlterLogDirsThrottle","required":false,"type":"integer","format":"int64"},{"name":"timeoutMs","in":"query","description":"timeoutMs","required":false,"type":"integer","format":"int64"}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/ReassignStatus"}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/partitions/reassign/generate":{"post":{"tags":["kafka-controller"],"summary":"Generate plan for the partition reassignment","operationId":"generateReassignPartitionsUsingPOST","consumes":["application/json"],"produces":["*/*"],"parameters":[{"in":"body","name":"reassignWrapper","description":"reassignWrapper","required":true,"schema":{"$ref":"#/definitions/ReassignWrapper"}}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/ReassignModel"}}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/partitions/reassign/stop":{"put":{"tags":["kafka-controller"],"summary":"Stop the partition reassignment process","operationId":"stopReassignPartitionsUsingPUT","consumes":["application/json"],"produces":["*/*"],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/GeneralResponse"}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/topics":{"get":{"tags":["kafka-controller"],"summary":"List topics","operationId":"listTopicsUsingGET","consumes":["application/json"],"produces":["*/*"],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"type":"string"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}},"delete":{"tags":["kafka-controller"],"summary":"Delete a topic list (you should enable topic deletion","operationId":"deleteTopicListUsingDELETE","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"topicList","in":"query","description":"topicList","required":true,"type":"array","items":{"type":"string"},"collectionFormat":"multi"}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"$ref":"#/definitions/GeneralResponse"}}},"204":{"description":"No Content"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"}}}},"/kafka/topics/create":{"post":{"tags":["kafka-controller"],"summary":"Create topics","operationId":"createTopicUsingPOST","consumes":["application/json"],"produces":["*/*"],"parameters":[{"in":"body","name":"topicList","description":"topicList","required":true,"schema":{"type":"array","items":{"$ref":"#/definitions/TopicDetail"}}}],"responses":{"201":{"description":"Created","schema":{"type":"object","additionalProperties":{"$ref":"#/definitions/GeneralResponse"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/topics/{topic}":{"get":{"tags":["kafka-controller"],"summary":"Describe a topic by fetching the metadata and config","operationId":"describeTopicUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"topic","in":"path","description":"topic","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/TopicMeta"}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/topics/{topic}/conf":{"get":{"tags":["kafka-controller"],"summary":"Get topic configs","operationId":"getTopicConfigUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"topic","in":"path","description":"topic","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/CustomConfigEntry"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}},"put":{"tags":["kafka-controller"],"summary":"Update topic configs","operationId":"updateTopicConfigUsingPUT","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"topic","in":"path","description":"topic","required":true,"type":"string"},{"in":"body","name":"props","description":"props","required":true,"schema":{"type":"object","additionalProperties":{"type":"object"}}}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/CustomConfigEntry"}}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/topics/{topic}/conf/{key}":{"get":{"tags":["kafka-controller"],"summary":"Get topic config by key","operationId":"getTopicConfigByKeyUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"topic","in":"path","description":"topic","required":true,"type":"string"},{"name":"key","in":"path","description":"key","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"object"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/topics/{topic}/conf/{key}={value}":{"put":{"tags":["kafka-controller"],"summary":"Update a topic config by key","operationId":"updateTopicConfigByKeyUsingPUT","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"topic","in":"path","description":"topic","required":true,"type":"string"},{"name":"key","in":"path","description":"key","required":true,"type":"string"},{"name":"value","in":"path","description":"value","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/CustomConfigEntry"}}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/topics/{topic}/dynconf":{"get":{"tags":["kafka-controller"],"summary":"Get topic dyn configs","operationId":"getTopicDynConfigUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"topic","in":"path","description":"topic","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"object"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/topics/{topic}/exist":{"get":{"tags":["kafka-controller"],"summary":"Tell if a topic exists","operationId":"existTopicUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"topic","in":"path","description":"topic","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"boolean"}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/kafka/topicsbrief":{"get":{"tags":["kafka-controller"],"summary":"List topics Brief","operationId":"listTopicBriefUsingGET","consumes":["application/json"],"produces":["*/*"],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/TopicBrief"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/users":{"get":{"tags":["user-controller"],"summary":"Get user list.","operationId":"listUserUsingGET","consumes":["application/json"],"produces":["*/*"],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"type":"string"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}},"post":{"tags":["user-controller"],"summary":"Add user.","operationId":"addUserUsingPOST","consumes":["application/json"],"produces":["*/*"],"parameters":[{"in":"body","name":"user","description":"user","required":true,"schema":{"$ref":"#/definitions/User"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/GeneralResponse"}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}},"put":{"tags":["user-controller"],"summary":"Modify user information.","operationId":"modifyUserUsingPUT","consumes":["application/json"],"produces":["*/*"],"parameters":[{"in":"body","name":"user","description":"user","required":true,"schema":{"$ref":"#/definitions/User"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/GeneralResponse"}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/users/{username}":{"delete":{"tags":["user-controller"],"summary":"Delete user.","operationId":"delUserUsingDELETE","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"username","in":"path","description":"username","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/GeneralResponse"}},"204":{"description":"No Content"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"}}}},"/zk/connstate":{"get":{"tags":["zookeeper-controller"],"summary":"Get the connection state of zookeeper","operationId":"zkConnStateUsingGET","consumes":["application/json"],"produces":["*/*"],"responses":{"200":{"description":"OK","schema":{"type":"string"}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/zk/env":{"get":{"tags":["zookeeper-controller"],"summary":"Get the environment information of zookeeper","operationId":"getEnvUsingGET","consumes":["application/json"],"produces":["*/*"],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"$ref":"#/definitions/ZkServerEnvironment"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/zk/get/path":{"get":{"tags":["zookeeper-controller"],"summary":"Get data of a zookeeper path","operationId":"getUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"path","in":"query","description":"path","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"string"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/zk/ls/path":{"get":{"tags":["zookeeper-controller"],"summary":"List a zookeeper path","operationId":"lsUsingGET","consumes":["application/json"],"produces":["*/*"],"parameters":[{"name":"path","in":"query","description":"path","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"type":"string"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},"/zk/stat":{"get":{"tags":["zookeeper-controller"],"summary":"Get the service state of zookeeper","operationId":"getStatUsingGET","consumes":["application/json"],"produces":["*/*"],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"$ref":"#/definitions/ZkServerStat"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}}},"definitions":{"AddPartition":{"type":"object","properties":{"topic":{"type":"string"},"numPartitionsAdded":{"type":"integer","format":"int32"},"replicaAssignment":{"type":"array","items":{"type":"array","items":{"type":"integer","format":"int32"}}}}},"BrokerInfo":{"type":"object","properties":{"endpoints":{"type":"array","items":{"type":"string"}},"host":{"type":"string"},"id":{"type":"integer","format":"int32"},"jmxPort":{"type":"integer","format":"int32"},"port":{"type":"integer","format":"int32"},"rack":{"type":"string"},"securityProtocol":{"type":"object"},"startTime":{"type":"string","format":"date-time"},"version":{"type":"integer","format":"int32"}}},"ClusterInfo":{"type":"object","properties":{"clusterId":{"type":"string"},"controller":{"$ref":"#/definitions/Node"},"nodes":{"type":"array","items":{"$ref":"#/definitions/Node"}}}},"ConsumerGroupDesc":{"type":"object","properties":{"assignmentStrategy":{"type":"string"},"clientId":{"type":"string"},"consumerId":{"type":"string"},"coordinator":{"$ref":"#/definitions/Node"},"currentOffset":{"type":"integer","format":"int64"},"groupName":{"type":"string"},"host":{"type":"string"},"lag":{"type":"integer","format":"int64"},"logEndOffset":{"type":"integer","format":"int64"},"partitionId":{"type":"integer","format":"int32"},"state":{"type":"string","enum":["Unknown","PreparingRebalance","CompletingRebalance","Stable","Dead","Empty"]},"topic":{"type":"string"},"type":{"type":"string","enum":["NEW","OLD"]}}},"ConsumerGroupMeta":{"type":"object","properties":{"assignmentStrategy":{"type":"string"},"coordinator":{"$ref":"#/definitions/Node"},"groupId":{"type":"string"},"members":{"type":"array","items":{"$ref":"#/definitions/MemberDescription"}},"state":{"type":"string","enum":["Unknown","PreparingRebalance","CompletingRebalance","Stable","Dead","Empty"]}}},"CustomConfigEntry":{"type":"object","properties":{"isReadOnly":{"type":"boolean"},"isSensitive":{"type":"boolean"},"name":{"type":"string"},"readOnly":{"type":"boolean"},"sensitive":{"type":"boolean"},"source":{"type":"string","enum":["DYNAMIC_TOPIC_CONFIG","DYNAMIC_BROKER_CONFIG","DYNAMIC_DEFAULT_BROKER_CONFIG","STATIC_BROKER_CONFIG","DEFAULT_CONFIG","UNKNOWN"]},"value":{"type":"string"}}},"CustomTopicPartitionInfo":{"type":"object","properties":{"endOffset":{"type":"integer","format":"int64"},"in_sync":{"type":"boolean"},"messageAvailable":{"type":"integer","format":"int64"},"startOffset":{"type":"integer","format":"int64"},"topicPartitionInfo":{"$ref":"#/definitions/TopicPartitionInfo"}}},"GeneralResponse":{"type":"object","properties":{"data":{"type":"object"},"msg":{"type":"string"},"state":{"type":"string","enum":["success","failure"]}}},"HashMap«string,object»":{"type":"object","additionalProperties":{"type":"object"}},"HealthCheckResult":{"type":"object","properties":{"msg":{"type":"string"},"status":{"type":"string"},"timestamp":{"type":"string","example":"yyyy-MM-dd HH:mm:ss"}}},"HostAndPort":{"type":"object","properties":{"hasBracketlessColons":{"type":"boolean"},"host":{"type":"string"},"hostText":{"type":"string"},"port":{"type":"integer","format":"int32"}}},"JMXConfiguration":{"type":"object","properties":{"include":{"$ref":"#/definitions/JMXFilter"},"exclude":{"$ref":"#/definitions/JMXFilter"}}},"JMXFilter":{"type":"object","properties":{"attribute":{"type":"object"},"beanNames":{"type":"array","items":{"type":"string"}},"beanRegexes":{"type":"array","items":{"$ref":"#/definitions/Pattern"}},"domain":{"type":"string"},"domainRegex":{"$ref":"#/definitions/Pattern"},"emptyBeanName":{"type":"boolean"},"filter":{"type":"object","additionalProperties":{"type":"object"}}}},"JMXMetricData":{"type":"object","properties":{"collected":{"type":"boolean"},"host":{"type":"string"},"metrics":{"type":"array","items":{"$ref":"#/definitions/HashMap«string,object»"}},"msg":{"type":"string"},"timestamp":{"type":"string","example":"yyyy-MM-dd HH:mm:ss"}}},"JMXMetricDataV1":{"type":"object","properties":{"collected":{"type":"boolean"},"host":{"type":"string"},"mbeanInfo":{"type":"object"},"msg":{"type":"string"},"timestamp":{"type":"string","example":"yyyy-MM-dd HH:mm:ss"}}},"JMXQuery":{"type":"object","properties":{"filters":{"type":"array","items":{"$ref":"#/definitions/JMXConfiguration"}}}},"LogDirInfo":{"type":"object","properties":{"error":{"type":"string","enum":["UNKNOWN_SERVER_ERROR","NONE","OFFSET_OUT_OF_RANGE","CORRUPT_MESSAGE","UNKNOWN_TOPIC_OR_PARTITION","INVALID_FETCH_SIZE","LEADER_NOT_AVAILABLE","NOT_LEADER_FOR_PARTITION","REQUEST_TIMED_OUT","BROKER_NOT_AVAILABLE","REPLICA_NOT_AVAILABLE","MESSAGE_TOO_LARGE","STALE_CONTROLLER_EPOCH","OFFSET_METADATA_TOO_LARGE","NETWORK_EXCEPTION","COORDINATOR_LOAD_IN_PROGRESS","COORDINATOR_NOT_AVAILABLE","NOT_COORDINATOR","INVALID_TOPIC_EXCEPTION","RECORD_LIST_TOO_LARGE","NOT_ENOUGH_REPLICAS","NOT_ENOUGH_REPLICAS_AFTER_APPEND","INVALID_REQUIRED_ACKS","ILLEGAL_GENERATION","INCONSISTENT_GROUP_PROTOCOL","INVALID_GROUP_ID","UNKNOWN_MEMBER_ID","INVALID_SESSION_TIMEOUT","REBALANCE_IN_PROGRESS","INVALID_COMMIT_OFFSET_SIZE","TOPIC_AUTHORIZATION_FAILED","GROUP_AUTHORIZATION_FAILED","CLUSTER_AUTHORIZATION_FAILED","INVALID_TIMESTAMP","UNSUPPORTED_SASL_MECHANISM","ILLEGAL_SASL_STATE","UNSUPPORTED_VERSION","TOPIC_ALREADY_EXISTS","INVALID_PARTITIONS","INVALID_REPLICATION_FACTOR","INVALID_REPLICA_ASSIGNMENT","INVALID_CONFIG","NOT_CONTROLLER","INVALID_REQUEST","UNSUPPORTED_FOR_MESSAGE_FORMAT","POLICY_VIOLATION","OUT_OF_ORDER_SEQUENCE_NUMBER","DUPLICATE_SEQUENCE_NUMBER","INVALID_PRODUCER_EPOCH","INVALID_TXN_STATE","INVALID_PRODUCER_ID_MAPPING","INVALID_TRANSACTION_TIMEOUT","CONCURRENT_TRANSACTIONS","TRANSACTION_COORDINATOR_FENCED","TRANSACTIONAL_ID_AUTHORIZATION_FAILED","SECURITY_DISABLED","OPERATION_NOT_ATTEMPTED","KAFKA_STORAGE_ERROR","LOG_DIR_NOT_FOUND","SASL_AUTHENTICATION_FAILED","UNKNOWN_PRODUCER_ID","REASSIGNMENT_IN_PROGRESS","DELEGATION_TOKEN_AUTH_DISABLED","DELEGATION_TOKEN_NOT_FOUND","DELEGATION_TOKEN_OWNER_MISMATCH","DELEGATION_TOKEN_REQUEST_NOT_ALLOWED","DELEGATION_TOKEN_AUTHORIZATION_FAILED","DELEGATION_TOKEN_EXPIRED","INVALID_PRINCIPAL_TYPE","NON_EMPTY_GROUP","GROUP_ID_NOT_FOUND","FETCH_SESSION_ID_NOT_FOUND","INVALID_FETCH_SESSION_EPOCH"]},"replicaInfos":{"type":"object","additionalProperties":{"$ref":"#/definitions/ReplicaInfo"}}}},"Map«int,long»":{"type":"object","additionalProperties":{"type":"integer","format":"int64"}},"Map«string,LogDirInfo»":{"type":"object","additionalProperties":{"$ref":"#/definitions/LogDirInfo"}},"MemberDescription":{"type":"object","properties":{"assignment":{"type":"array","items":{"$ref":"#/definitions/TopicPartition"}},"clientId":{"type":"string"},"host":{"type":"string"},"memberId":{"type":"string"}}},"Node":{"type":"object","properties":{"empty":{"type":"boolean"},"hash":{"type":"integer","format":"int32"},"host":{"type":"string"},"id":{"type":"integer","format":"int32"},"idString":{"type":"string"},"port":{"type":"integer","format":"int32"},"rack":{"type":"string"}}},"Pattern":{"type":"object","properties":{"cursor":{"type":"integer","format":"int32"},"flags":{"type":"integer","format":"int32"},"pattern":{"type":"string"}}},"ReassignModel":{"type":"object","properties":{"partitions":{"type":"array","items":{"$ref":"#/definitions/TopicPartitionReplicaAssignment"}},"version":{"type":"integer","format":"int32"}}},"ReassignStatus":{"type":"object","properties":{"msg":{"type":"string"},"partitionsReassignStatus":{"type":"object","additionalProperties":{"type":"integer","format":"int32"}},"removeThrottle":{"type":"boolean"},"replicasReassignStatus":{"type":"object","additionalProperties":{"type":"integer","format":"int32"}}}},"ReassignWrapper":{"type":"object","properties":{"brokers":{"type":"array","items":{"type":"integer","format":"int32"}},"topics":{"type":"array","items":{"type":"string"}}}},"Record":{"type":"object","properties":{"key":{"type":"string"},"keyDecoder":{"type":"string"},"offset":{"type":"integer","format":"int64"},"timestamp":{"type":"integer","format":"int64"},"topic":{"type":"string"},"value":{"type":"string"},"valueDecoder":{"type":"string"}}},"ReplicaInfo":{"type":"object","properties":{"isFuture":{"type":"boolean"},"offsetLag":{"type":"integer","format":"int64"},"size":{"type":"integer","format":"int64"}}},"ReplicaLogDirInfo":{"type":"object","properties":{"currentReplicaLogDir":{"type":"string"},"currentReplicaOffsetLag":{"type":"integer","format":"int64"},"futureReplicaLogDir":{"type":"string"},"futureReplicaOffsetLag":{"type":"integer","format":"int64"}}},"TopicBrief":{"type":"object","properties":{"isrRate":{"type":"number","format":"double"},"numPartition":{"type":"integer","format":"int32"},"replicationFactor":{"type":"integer","format":"int32"},"topic":{"type":"string"}}},"TopicDetail":{"type":"object","properties":{"partitions":{"type":"integer","format":"int32"},"factor":{"type":"integer","format":"int32"},"name":{"type":"string"},"prop":{"type":"object","additionalProperties":{"type":"object"}},"replicasAssignments":{"type":"object","additionalProperties":{"type":"array","items":{"type":"integer","format":"int32"}}}}},"TopicMeta":{"type":"object","properties":{"internal":{"type":"boolean"},"partitionCount":{"type":"integer","format":"int32"},"replicationFactor":{"type":"integer","format":"int32"},"topicName":{"type":"string"},"topicPartitionInfos":{"type":"array","items":{"$ref":"#/definitions/CustomTopicPartitionInfo"}}}},"TopicPartition":{"type":"object","properties":{"hash":{"type":"integer","format":"int32"},"partition":{"type":"integer","format":"int32"},"topic":{"type":"string"}}},"TopicPartitionInfo":{"type":"object","properties":{"isr":{"type":"array","items":{"$ref":"#/definitions/Node"}},"leader":{"$ref":"#/definitions/Node"},"partition":{"type":"integer","format":"int32"},"replicas":{"type":"array","items":{"$ref":"#/definitions/Node"}}}},"TopicPartitionReplicaAssignment":{"type":"object","properties":{"topic":{"type":"string"},"partition":{"type":"integer","format":"int32"},"replicas":{"type":"array","items":{"type":"integer","format":"int32"}},"log_dirs":{"type":"array","items":{"type":"string"}}}},"User":{"type":"object","properties":{"username":{"type":"string"},"password":{"type":"string"},"role":{"type":"string"}}},"ZkServerClient":{"type":"object","properties":{"host":{"type":"string"},"ops":{"type":"integer","format":"int32"},"port":{"type":"integer","format":"int32"},"queued":{"type":"integer","format":"int32"},"received":{"type":"integer","format":"int32"},"sent":{"type":"integer","format":"int32"}}},"ZkServerEnvironment":{"type":"object","properties":{"attributes":{"type":"object","additionalProperties":{"type":"string"}}}},"ZkServerStat":{"type":"object","properties":{"avgLatency":{"type":"integer","format":"int32"},"buildDate":{"type":"string"},"clients":{"type":"array","items":{"$ref":"#/definitions/ZkServerClient"}},"connections":{"type":"integer","format":"int32"},"maxLatency":{"type":"integer","format":"int32"},"minLatency":{"type":"integer","format":"int32"},"mode":{"type":"string","enum":["Leader","Follower","Observer","Standalone","Down","Unknow"]},"msg":{"type":"string"},"nodes":{"type":"integer","format":"int32"},"outstanding":{"type":"integer","format":"int32"},"received":{"type":"integer","format":"int32"},"sent":{"type":"integer","format":"int32"},"version":{"type":"string"},"zxId":{"type":"string"}}}}} \ No newline at end of file diff --git a/src/test/java/org/gnuhpc/bigdata/service/KafkaAdminServiceTest.java b/src/test/java/org/gnuhpc/bigdata/service/KafkaAdminServiceTest.java index ead083f..b9e2f97 100644 --- a/src/test/java/org/gnuhpc/bigdata/service/KafkaAdminServiceTest.java +++ b/src/test/java/org/gnuhpc/bigdata/service/KafkaAdminServiceTest.java @@ -109,7 +109,7 @@ public class KafkaAdminServiceTest { private static final List TEST_KAFKA_BOOTSTRAP_SERVERS_ID = Arrays.asList(10, 11, 12); private static final int KAFKA_NODES_COUNT = TEST_KAFKA_BOOTSTRAP_SERVERS_ID.size(); private static final String TEST_ZK = "127.0.0.1:2181"; - private static final int TEST_CONTROLLER_ID = 10; + private static final int TEST_CONTROLLER_ID = 11; // private static final List TEST_KAFKA_LOG_DIRS = // Arrays.asList( // "/home/xiangli/bigdata/kafka_2.11-1.1.1/kafka-logs,/home/xiangli/bigdata/kafka_2.11-1.1.1/kafka111_2-logs,/home/xiangli/bigdata/kafka_2.11-1.1.1/kafka111_3-logs", @@ -1524,16 +1524,18 @@ public void testExecuteReassignReplicasInnerBroker() throws InterruptedException Map replicasReassignStatus = reassignResult.getReplicasReassignStatus(); - int replicaReassignState = replicasReassignStatus.get(topicPartitionReplica); - assertTrue( - replicaReassignState == ReassignmentState.ReassignmentInProgress.code() - || replicaReassignState == ReassignmentState.ReassignmentCompleted.code()); - if (partitionReassignState == ReassignmentState.ReassignmentCompleted.code() - && replicaReassignState == ReassignmentState.ReassignmentCompleted.code()) { - // If all completed, removeThrottle will be true - assertTrue(reassignResult.isRemoveThrottle()); - } else { - assertFalse(reassignResult.isRemoveThrottle()); + if (replicasReassignStatus != null && !replicasReassignStatus.isEmpty()) { + int replicaReassignState = replicasReassignStatus.get(topicPartitionReplica); + assertTrue( + replicaReassignState == ReassignmentState.ReassignmentInProgress.code() + || replicaReassignState == ReassignmentState.ReassignmentCompleted.code()); + if (partitionReassignState == ReassignmentState.ReassignmentCompleted.code() + && replicaReassignState == ReassignmentState.ReassignmentCompleted.code()) { + // If all completed, removeThrottle will be true + assertTrue(reassignResult.isRemoveThrottle()); + } else { + assertFalse(reassignResult.isRemoveThrottle()); + } } // Wait reassign to complete