前言
一般情况下,我们都习惯使用Kafka中bin目录下的脚本工具来管理查看Kafka,但是有些时候需要将某些管理查看的功能集成到系统(比如Kafka Manager)中,那么就需要调用一些API来直接操作Kafka了。在Kafka0.11.0.0版本之前,可以通过kafka-core包(Kafka的服务端代码,采用Scala编写)下的AdminClient和AdminUtils来实现部分的集群管理操作,比如笔者之前在Kafka解析之topic创建(1)和Kafka解析之topic创建(2)两篇文章中所讲解的Topic的创建就用到了AdminUtils类。而在Kafka0.11.0.0版本之后,又多了一个AdminClient,这个是在kafka-client包下的,这是一个抽象类,具体的实现是org.apache.kafka.clients.admin.KafkaAdminClient,这个就是本文所要陈述的重点了。
功能与原理介绍
在Kafka官网中这么描述AdminClient:The AdminClient API supports managing and inspecting topics, brokers, acls, and other Kafka objects. 具体的KafkaAdminClient包含了一下几种功能(以Kafka1.0.0版本为准):
创建Topic
createTopics(Collection newTopics)
删除Topic
deleteTopics(Collection topics)
罗列所有Topic
listTopics()
查询Topic
describeTopics(Collection topicNames)
查询集群信息
describeCluster()
查询ACL信息
describeAcls(AclBindingFilter filter)
创建ACL信息
createAcls(Collection acls)
删除ACL信息
deleteAcls(Collection filters)
查询配置信息
describeConfigs(Collection resources)
修改配置信息
alterConfigs(Map<ConfigResource, Config> configs)
修改副本的日志目录
alterReplicaLogDirs(Map<TopicPartitionReplica, String> replicaAssignment)
查询节点的日志目录信息
describeLogDirs(Collection brokers)
查询副本的日志目录信息
describeReplicaLogDirs(Collection replicas)
增加分区
createPartitions(Map<String, NewPartitions> newPartitions)
其内部原理是使用Kafka自定义的一套二进制协议来实现,详细可以参见Kafka协议。主要实现步骤:
1、客户端根据方法的调用创建相应的协议请求,比如创建Topic的createTopics方法,其内部就是发送CreateTopicRequest请求。
2、客户端发送请求至Kafka Broker。
3、Kafka Broker处理相应的请求并回执,比如与CreateTopicRequest对应的是CreateTopicResponse。
4、客户端接收相应的回执并进行解析处理。
和协议有关的请求和回执的类基本都在org.apache.kafka.common.requests包中,AbstractRequest和AbstractResponse是这些请求和回执类的两个基本父类。
示例代码
package com.donwait;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import org.apache.kafka.clients.admin.AdminClient;
import org.apache.kafka.clients.admin.CreateTopicsResult;
import org.apache.kafka.clients.admin.DeleteTopicsResult;
import org.apache.kafka.clients.admin.ListTopicsResult;
import org.apache.kafka.clients.admin.NewTopic;
import org.apache.kafka.clients.producer.ProducerConfig;
/**
* kafka管理接口
* @author Administrator
*/
public class KafkaAdmin {
private AdminClient adminClient = null;
public KafkaAdmin(String brokerList){
Properties props = new Properties();
// kafka服务器地址: IP:PORT形式,多个以逗号隔开
props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, brokerList);
// 新建管理客户端
adminClient = AdminClient.create(props);
}
/**
* 新建主题
* @param topic
* @return
*/
public boolean createTopic(String topic, int partitionNum, short replicationFactor){
ArrayList<NewTopic> topics = new ArrayList<NewTopic>();
NewTopic newTopic = new NewTopic(topic, partitionNum, replicationFactor);
topics.add(newTopic);
CreateTopicsResult result = adminClient.createTopics(topics);
boolean success = false;
try {
result.all().get();
success = true;
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
return success;
}
/**
* 删除主题
* @param topic
* @return
*/
public boolean deleteTopic(String topic){
DeleteTopicsResult result = adminClient.deleteTopics(Arrays.asList(topic));
boolean success = false;
try {
result.all().get();
success = true;
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
return success;
}
public List<String> findAllTopic(){
List<String> lst = new ArrayList<String>();
ListTopicsResult result = adminClient.listTopics();
try {
Set<String> s = result.names().get();
for(String str: s){
lst.add(str);
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
return lst;
}
}
系统配置
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.easystudy</groupId>
<artifactId>kafkaClient</artifactId>
<version>0.0.1-SNAPSHOT</version>
<!-- 项目属性:子模块不能引用父项目的properties变量 -->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<!-- 日志包 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.5</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.5</version>
</dependency>
<!-- 会自动下载scala-library与zookeeper依赖库 -->
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka_2.11</artifactId>
<version>2.2.0</version>
</dependency>
</dependencies>
<build>
<sourceDirectory>src/main/java</sourceDirectory>
<testSourceDirectory>src/main/test</testSourceDirectory>
<plugins>
<!-- maven-compiler-plugin,他不能将项目依赖的所有库一同打包进jar里面 -->
<!-- 需要将所有的依赖jar包一同打包到jar中,能让jar独立运行,那么必须添加插件maven-assembly-plugin -->
<!-- 将pom中所有的依赖全部打包进一个jar包中:采用export成runnable jar包的方式是行不通的 -->
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>com.dondown.WordCountApp</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<!-- 如下两行是控制生的jar包的名称,默认会增加jar-with-dependencies后缀 -->
<finalName>kafkaClient</finalName>
<appendAssemblyId>false</appendAssemblyId>
</configuration>
<!-- 关联到package打包步骤 -->
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>