Kafka性能測試初探

相信大家對Kafka不會陌生,但首先還是要簡單介紹一下。

Kafka是一種高性能的分佈式消息系統,由LinkedIn公司開發,用於處理海量的實時數據流。它採用了發佈/訂閱模式,可以將數據流分發到多個消費者端,同時提供了高可靠性、高吞吐量和低延遲的特性。

Kafka的應用場景非常廣泛,例如日誌收集、事件流處理、實時監控等。在這些場景中,Kafka可以提供高可靠性和低延遲的數據傳輸,確保數據的穩定性和實時性。與此同時,Kafka還提供了豐富的API和管理工具,使得用戶可以方便地配置和管理Kafka集羣。

很多高性能方案都會用到Kafka,今天我來分享如何使用Kafka Client API進行Kafka生產者和消費者壓測。

依賴

我用了Gradle創建的項目,依賴配置如下:

compile group: 'org.apache.kafka', name: 'kafka-clients', version: '3.4.0'

kafka服務端

我本地用了Kafka最新版本:kafka_2.12-3.4.0,這個版本可以不依賴zookeeper,非常方便,用來本地功能驗證和測試我是十分推薦的。基本做到了開箱即用。

具體的流程可以自行搜索。

生產者壓測Demo

在創建生產者時,會有不少的參數需要配置,這裏建議使用默認的。或者使用待測試參數組合。下面是我自己的配置,常用的參數我都列了出來。具體參數含義,可以自行搜索,這方面資料還是很多的,下面直接進入壓測用例環節。

package com.funtest.kafka


import com.funtester.frame.SourceCode
import com.funtester.frame.execute.FunQpsConcurrent
import com.funtester.utils.StringUtil
import groovy.util.logging.Log4j2
import org.apache.kafka.clients.producer.KafkaProducer
import org.apache.kafka.clients.producer.ProducerConfig
import org.apache.kafka.clients.producer.ProducerRecord
import org.apache.kafka.common.record.CompressionType
import org.apache.kafka.common.serialization.ByteArraySerializer
import org.apache.kafka.common.serialization.StringSerializer

@Log4j2
class Produce extends SourceCode {

    static void main(String[] args) {
        Properties properties = new Properties();
        properties.setProperty(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
        properties.setProperty(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
        properties.setProperty(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, ByteArraySerializer.class.getName());
        properties.setProperty(ProducerConfig.RETRIES_CONFIG, "3");
        properties.setProperty(ProducerConfig.ACKS_CONFIG, "all"); //所有分區副本都收到確認信息,才能確認寫入
        properties.setProperty(ProducerConfig.BATCH_SIZE_CONFIG, "16384")
        properties.setProperty(ProducerConfig.BUFFER_MEMORY_CONFIG, "33554432")
        properties.setProperty(ProducerConfig.LINGER_MS_CONFIG, "10")
        properties.setProperty(ProducerConfig.COMPRESSION_TYPE_CONFIG, CompressionType.LZ4.name);
        KafkaProducer<String, String> producer = new KafkaProducer<>(properties);
        def topic = "testkafka"
        def test = {
            producer.send(new ProducerRecord<>(topic, StringUtil.getString(10)))
        }
        new FunQpsConcurrent(test,"Kafka測試").start()

        producer.close();
    }

}

這裏用到了動態QPS模型,最後的close()也可以不使用,畢竟main方法的代碼結束了就真的結束了。

消費者

呼應生產者,消費者也有一堆需要配置的參數。這裏先按下不表,有興趣的可以自行學習。

Kafka消費者有兩種訂閱消息的方式,分別是訂閱模式和分配模式。

訂閱模式是指消費者訂閱一個或多個主題,然後自動分配分區進行消費。這種模式下,Kafka會自動管理消費者與分區之間的關係,當有新的消費者加入或者退出消費組時,Kafka會自動重新分配分區,保證每個消費者都能夠獲取到消息。

而分配模式則是由消費者主動向Kafka請求分配指定的分區進行消費。這種模式下,消費者需要手動管理分區與消費者之間的關係,需要注意的是,當有新的消費者加入或者退出消費組時,需要手動重新分配分區。

訂閱模式相對於分配模式來說更加簡單易用,但是分配模式可以更加靈活地控制消費者與分區之間的關係。所以我選擇了訂閱模式。

package com.funtest.kafka

import com.funtester.frame.SourceCode
import com.funtester.frame.execute.FunQpsConcurrent
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

import java.time.Duration

class Cunsumer extends SourceCode {

    static void main(String[] args) {
        KafkaConsumer<String, String> consumer;
        Properties properties = new Properties();
        properties.setProperty(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
        properties.setProperty(ConsumerConfig.GROUP_ID_CONFIG, "FunTester32");
        properties.setProperty(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringDeserializer");
        properties.setProperty(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringDeserializer");
        properties.setProperty(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG,"1000");
        properties.setProperty(ConsumerConfig.MAX_POLL_RECORDS_CONFIG,"10");
        properties.setProperty(ConsumerConfig.SESSION_TIMEOUT_MS_CONFIG,"10000");
        properties.setProperty(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG
                , "earliest");
        consumer = new KafkaConsumer<>(properties);

        String topic = "testkafka";
//        TopicPartition topicPartition = new TopicPartition(topic, 0);
//        List<TopicPartition> topics = Arrays.asList(topicPartition);
//        consumer.assign(topics);
//        consumer.seekToEnd(topics);
//        long current = consumer.position(topicPartition);
//        consumer.seek(topicPartition, current - 10);//手動設置偏移量
        consumer.subscribe([topic])//訂閱模式,不能與assign混用
        while (true) {
            ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(1000));
            for (ConsumerRecord<String, String> record : records) {
                System.out.printf("offset = %d, key = %s, value = %s%n", record.offset(), record.key(), record.value());
            }
            sleep(1.0)
        }

        def test = {
            consumer.poll(Duration.ofMillis(1000));
        }
        new FunQpsConcurrent(test,"Kafka消費").start()
        consumer.close()

    }
}

由於本地機器原因,需要在服務器上啓動一個Kafka服務,用來測試不同參數組合情況下Kafka的性能表現。後續有機會再來分享。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章