Kafka核心API——AdminClient API

五類Kafka客戶端作用和區別

上文中介紹瞭如何搭建一個Kafka服務,那麼在開發中我們要如何去訪問、集成Kafka呢?這就需要使用到本文將要介紹的Kafka客戶端API。下圖是官方文檔中的一個圖,形象表示了能與Kafka集成的客戶端類型:
Kafka核心API——AdminClient API

這些客戶端通過API與Kafka進行集成,Kafka的五類客戶端API類型如下:

  • AdminClient API:允許管理和檢測Topic、broker以及其他Kafka實例,與Kafka自帶的腳本命令作用類似
  • Producer API:發佈消息到1個或多個Topic,也就是生產者或者說發佈方需要用到的API
  • Consumer API:訂閱1個或多個Topic,並處理產生的消息,也就是消費者或者說訂閱方需要用到的API
  • Stream API:高效地將輸入流轉換到輸出流,通常應用在一些流處理場景
  • Connector API:從一些源系統或應用程序拉取數據到Kafka,如上圖中的DB

創建工程

在接下來的篇章中將會演示AdminClient API的具體使用,其餘的API則會在後續的文章中進行介紹。首先,我們在IDEA中創建一個Spring Boot工程,該工程的pom.xml文件內容如下:

<?xml version="1.0" encoding="UTF-8"?>
<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.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.0.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.zj.study</groupId>
    <artifactId>kafka-study</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>kafka-study</name>
    <description>Kafka study project for Spring Boot</description>

    <properties>
        <java.version>11</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- Kafka 客戶端依賴 -->
        <dependency>
            <groupId>org.apache.kafka</groupId>
            <artifactId>kafka-clients</artifactId>
            <version>2.5.0</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

AdminClient客戶端的建立

常用的AdminClient API對象如下:
Kafka核心API——AdminClient API

顯然,操作AdminClient API的前提是需要創建一個AdminClient實例。代碼示例:

/**
 * 配置並創建AdminClient
 */
public static AdminClient adminClient() {
    Properties properties = new Properties();
    // 配置Kafka服務的訪問地址及端口號
    properties.setProperty(AdminClientConfig.
            BOOTSTRAP_SERVERS_CONFIG, "127.0.0.1:9092");

    // 創建AdminClient實例
    return AdminClient.create(properties);
}

創建了AdminClient的實例對象後,我們就可以通過它提供的方法操作Kafka,常用的方法如下:

方法名稱 作用
createTopics 創建一個或多個Topic
listTopics 查詢Topic列表
deleteTopics 刪除一個或多個Topic
describeTopics 查詢Topic的描述信息
describeConfigs 查詢Topic、Broker等的所有配置項信息
alterConfigs 用於修改Topic、Broker等的配置項信息(該方法在新版本中被標記爲已過期)
incrementalAlterConfigs 同樣也是用於修改Topic、Broker等的配置項信息,但功能更多、更靈活,用於代替alterConfigs
createPartitions 用於調整Topic的Partition數量,只能增加不能減少或刪除,也就是說新設置的Partition數量必須大於等於之前的Partition數量

Tips:

  • describeTopicsdescribeConfigs的意義主要是在監控上,很多用於監控Kafka的組件都會使用到這兩個API,因爲通過這兩個API可以獲取到Topic自身和周邊的詳細信息

創建Topic

使用createTopics方法可以創建Topic,傳入的參數也與kafka-topics.sh命令腳本的參數一樣。代碼示例:

/**
 * 創建topic
 */
public static void createTopic() throws ExecutionException, InterruptedException {
    AdminClient adminClient = adminClient();
    // topic的名稱
    String name = "MyTopic3";
    // partition數量
    int numPartitions = 1;
    // 副本數量
    short replicationFactor = 1;
    NewTopic topic = new NewTopic(name, numPartitions, replicationFactor);
    CreateTopicsResult result = adminClient.createTopics(List.of(topic));
    // 避免客戶端連接太快斷開而導致Topic沒有創建成功
    Thread.sleep(500);
    // 獲取topic設置的partition數量
    System.out.println(result.numPartitions(name).get());
}

查看Topic列表

listTopics方法用於查詢Topic列表,通過傳入ListTopicsOptions參數可以設置一些可選項。代碼示例:

/**
 * 查詢Topic列表
 */
public static void topicLists() throws ExecutionException, InterruptedException {
    AdminClient adminClient = adminClient();
    ListTopicsResult result1 = adminClient.listTopics();
    // 打印Topic的名稱
    System.out.println(result1.names().get());
    // 打印Topic的信息
    System.out.println(result1.listings().get());

    ListTopicsOptions options = new ListTopicsOptions();
    // 是否列出內部使用的Topic
    options.listInternal(true);
    ListTopicsResult result2 = adminClient.listTopics(options);
    System.out.println(result2.names().get());
}

關於listInternal選項:

listInternal選項在Kafka 0.x的版本里是沒有的,因爲在0.x版本中Kafka是將consumer的offset信息存儲在Zookeeper裏,但由於Zookeeper同步consumer的offset信息比較慢,於是在1.x後就遷移到Kafka的Topic中進行存儲了,這也是爲了提高吞吐量和性能


刪除Topic

deleteTopics方法可以刪除一個或多個Topic,代碼示例:

/**
 * 刪除Topic
 */
public static void delTopics() throws ExecutionException, InterruptedException {
    AdminClient adminClient = adminClient();
    DeleteTopicsResult result = adminClient.deleteTopics(List.of("MyTopic1"));
    System.out.println(result.all().get());
}

Topic的描述信息查看

一個Topic會有自身的描述信息,例如:partition的數量,副本集的數量,是否爲internal等等。AdminClient中提供了describeTopics方法來查詢這些描述信息。代碼示例:

/**
 * 查詢Topic的描述信息
 */
public static void describeTopics() throws ExecutionException, InterruptedException {
    AdminClient adminClient = adminClient();
    DescribeTopicsResult result = adminClient.describeTopics(List.of("MyTopic"));
    Map<String, TopicDescription> descriptionMap = result.all().get();
    descriptionMap.forEach((key, value) ->
            System.out.println("name: " + key + ", desc: " + value));
}

輸出的內容如下:

name: MyTopic, desc: (name=MyTopic, internal=false, partitions=(partition=0, leader=127.0.0.1:9092 (id: 0 rack: null), replicas=127.0.0.1:9092 (id: 0 rack: null), isr=127.0.0.1:9092 (id: 0 rack: null)), authorizedOperations=null)

Topic配置信息查看

除了Kafka自身的配置項外,其內部的Topic也會有非常多的配置項,我們可以通過describeConfigs方法來獲取某個Topic中的配置項信息。代碼示例:

/**
 * 查詢Topic的配置信息
 */
public static void describeConfig() throws ExecutionException, InterruptedException {
    AdminClient adminClient = adminClient();
    ConfigResource configResource = new ConfigResource(
            ConfigResource.Type.TOPIC, "MyTopic"
    );
    DescribeConfigsResult result = adminClient.describeConfigs(List.of(configResource));
    Map<ConfigResource, Config> map = result.all().get();
    map.forEach((key, value) ->
            System.out.println("name: " + key.name() + ", desc: " + value));
}

輸出的內容如下,會輸出所有的配置信息,內容比較多:

name: ConfigResource(type=TOPIC, name='MyTopic'), desc: Config(entries=[ConfigEntry(name=compression.type, value=producer, source=DEFAULT_CONFIG, isSensitive=false, isReadOnly=false, synonyms=[]), ConfigEntry(name=leader.replication.throttled.replicas, value=, source=DEFAULT_CONFIG, isSensitive=false, isReadOnly=false, synonyms=[]), ConfigEntry(name=message.downconversion.enable, value=true, source=DEFAULT_CONFIG, isSensitive=false, isReadOnly=false, synonyms=[]), ConfigEntry(name=min.insync.replicas, value=1, source=DEFAULT_CONFIG, isSensitive=false, isReadOnly=false, synonyms=[]), ConfigEntry(name=segment.jitter.ms, value=0, source=DEFAULT_CONFIG, isSensitive=false, isReadOnly=false, synonyms=[]), ConfigEntry(name=cleanup.policy, value=delete, source=DEFAULT_CONFIG, isSensitive=false, isReadOnly=false, synonyms=[]), ConfigEntry(name=flush.ms, value=9223372036854775807, source=DEFAULT_CONFIG, isSensitive=false, isReadOnly=false, synonyms=[]), ConfigEntry(name=follower.replication.throttled.replicas, value=, source=DEFAULT_CONFIG, isSensitive=false, isReadOnly=false, synonyms=[]), ConfigEntry(name=segment.bytes, value=1073741824, source=STATIC_BROKER_CONFIG, isSensitive=false, isReadOnly=false, synonyms=[]), ConfigEntry(name=retention.ms, value=604800000, source=DEFAULT_CONFIG, isSensitive=false, isReadOnly=false, synonyms=[]), ConfigEntry(name=flush.messages, value=9223372036854775807, source=DEFAULT_CONFIG, isSensitive=false, isReadOnly=false, synonyms=[]), ConfigEntry(name=message.format.version, value=2.5-IV0, source=DEFAULT_CONFIG, isSensitive=false, isReadOnly=false, synonyms=[]), ConfigEntry(name=max.compaction.lag.ms, value=9223372036854775807, source=DEFAULT_CONFIG, isSensitive=false, isReadOnly=false, synonyms=[]), ConfigEntry(name=file.delete.delay.ms, value=60000, source=DEFAULT_CONFIG, isSensitive=false, isReadOnly=false, synonyms=[]), ConfigEntry(name=max.message.bytes, value=1048588, source=DEFAULT_CONFIG, isSensitive=false, isReadOnly=false, synonyms=[]), ConfigEntry(name=min.compaction.lag.ms, value=0, source=DEFAULT_CONFIG, isSensitive=false, isReadOnly=false, synonyms=[]), ConfigEntry(name=message.timestamp.type, value=CreateTime, source=DEFAULT_CONFIG, isSensitive=false, isReadOnly=false, synonyms=[]), ConfigEntry(name=preallocate, value=false, source=DEFAULT_CONFIG, isSensitive=false, isReadOnly=false, synonyms=[]), ConfigEntry(name=min.cleanable.dirty.ratio, value=0.5, source=DEFAULT_CONFIG, isSensitive=false, isReadOnly=false, synonyms=[]), ConfigEntry(name=index.interval.bytes, value=4096, source=DEFAULT_CONFIG, isSensitive=false, isReadOnly=false, synonyms=[]), ConfigEntry(name=unclean.leader.election.enable, value=false, source=DEFAULT_CONFIG, isSensitive=false, isReadOnly=false, synonyms=[]), ConfigEntry(name=retention.bytes, value=-1, source=DEFAULT_CONFIG, isSensitive=false, isReadOnly=false, synonyms=[]), ConfigEntry(name=delete.retention.ms, value=86400000, source=DEFAULT_CONFIG, isSensitive=false, isReadOnly=false, synonyms=[]), ConfigEntry(name=segment.ms, value=604800000, source=DEFAULT_CONFIG, isSensitive=false, isReadOnly=false, synonyms=[]), ConfigEntry(name=message.timestamp.difference.max.ms, value=9223372036854775807, source=DEFAULT_CONFIG, isSensitive=false, isReadOnly=false, synonyms=[]), ConfigEntry(name=segment.index.bytes, value=10485760, source=DEFAULT_CONFIG, isSensitive=false, isReadOnly=false, synonyms=[])])

Topic配置信息修改

除了可以查看Topic的配置項信息外,AdminClient還提供了相關方法來修改Topic配置項的值。在早期版本中,使用alterConfigs方法來修改配置項。代碼示例:

/**
 * 修改Topic的配置信息
 */
public static void alterConfig() throws Exception {
    // 指定ConfigResource的類型及名稱
    ConfigResource configResource = new ConfigResource(
            ConfigResource.Type.TOPIC, "MyTopic"
    );
    // 配置項以ConfigEntry形式存在
    Config config = new Config(List.of(
            new ConfigEntry("preallocate", "true")
    ));

    AdminClient adminClient = adminClient();
    Map<ConfigResource, Config> configMaps = new HashMap<>();
    configMaps.put(configResource, config);
    AlterConfigsResult result = adminClient.alterConfigs(configMaps);
    System.out.println(result.all().get());
}

 public static void main(String[] args) throws Exception {
    alterConfig();
    describeConfig();
}

執行以上代碼,控制檯輸出如下,可以看到成功將preallocate配置項的值改爲了true
Kafka核心API——AdminClient API

在新版本中則是使用incrementalAlterConfigs方法來修改Topic的配置項,該方法使用起來相對於alterConfigs要略微複雜一些,但因此功能更多、更靈活。代碼示例:

/**
 * 修改Topic的配置信息
 */
public static void incrementalAlterConfig() throws Exception {
    // 指定ConfigResource的類型及名稱
    ConfigResource configResource = new ConfigResource(
            ConfigResource.Type.TOPIC, "MyTopic"
    );
    // 配置項同樣以ConfigEntry形式存在,只不過增加了操作類型
    // 以及能夠支持操作多個配置項,相對來說功能更多、更靈活
    Collection<AlterConfigOp> configs = List.of(
            new AlterConfigOp(
                    new ConfigEntry("preallocate", "false"),
                    AlterConfigOp.OpType.SET
            )
    );

    AdminClient adminClient = adminClient();
    Map<ConfigResource, Collection<AlterConfigOp>> configMaps = new HashMap<>();
    configMaps.put(configResource, configs);
    AlterConfigsResult result = adminClient.incrementalAlterConfigs(configMaps);
    System.out.println(result.all().get());
}

public static void main(String[] args) throws Exception {
    incrementalAlterConfig();
    describeConfig();
}   
  • Tips:在某些版本中,incrementalAlterConfigs方法可能會存在些問題,對單實例的Kafka支持得不是很好,會出現無法成功修改配置項的情況,此時就可以使用alterConfigs方法來代替,這也是爲什麼這裏要介紹兩種方法的使用方式

執行以上代碼,控制檯輸出如下,可以看到成功將preallocate配置項的值改爲了false
Kafka核心API——AdminClient API


調整Topic的Partition數量

在創建Topic時我們需要設定Partition的數量,但如果覺得初始設置的Partition數量太少了,那麼就可以使用createPartitions方法來調整Topic的Partition數量,但是需要注意在Kafka中Partition只能增加不能減少。代碼示例:

/**
 * 增加Partition數量,目前Kafka不支持刪除或減少Partition
 */
public static void incrPartitions() throws ExecutionException, InterruptedException {
    AdminClient adminClient = adminClient();
    Map<String, NewPartitions> newPartitions = new HashMap<>();
    // 將MyTopic的Partition數量調整爲2
    newPartitions.put("MyTopic", NewPartitions.increaseTo(2));
    CreatePartitionsResult result = adminClient.createPartitions(newPartitions);
    System.out.println(result.all().get());
}

public static void main(String[] args) throws Exception {
    incrPartitions();
    describeTopics();
}  

執行以上代碼,控制檯輸出如下,可以看到成功爲該Topic增加了一個Partition:
Kafka核心API——AdminClient API

  • Tips:Partition的索引從0開始,所以第一個partition=0,第二個partition=1
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章