MQ探究-Kafka

多種MQ的探究-Kafka

  1. ActiveMQ原理探究與開發部署
  2. RocketMQ原理探究與開發部署

Kafka

原理

簡介
kafka是一個支持高吞吐量的分佈式消息服務。
特點:
kafka依賴於zookeeper進行節點註冊。
kafka集羣:
kafka集羣與zk類似,leader擁有最新最全的信息,然後複製信息到從節點上。
springboot2.0版本集成的kafka2.x,消息offsets放置在zk節點topics上。
優缺點
採用分區機制,吞吐量高,擁有ack容錯機制,支持集羣負載均衡,對日誌文件的記錄比較好。
但由於高效率的原因,kafka對死信隊列、延遲隊列等方面不如其他mq。
原理
kafka集羣節點註冊到zk上,服務加載初始化時加載kafka集羣信息,然後進行消息處理。
kafka與zk類似,存在leader與follower,並實時監測服務狀態。
模式:
kafka----broker1(topic1…topicn)–consumer

kafka----brokern(topic1…topicn)–consumer
詳細:
kafka的topic會依據配置分區數量進行分區(partition),消息producer後push進入broker的各個分區中有序排列(offset)。消息消費監聽到當前分區有自己的消息進來,然後進行處理,並記錄offset。

producer

消息推送首先獲取節點分片情況,然後進行序列化處理

clusterAndWaitTime = waitOnMetadata(record.topic(), record.partition(), maxBlockTimeMs);
...
serializedKey = keySerializer.serialize(record.topic(), record.headers(), record.key());
serializedValue = valueSerializer.serialize(record.topic(), record.headers(), record.value());

我們可以在application配置文件中配置序列化:

key-serializer: org.apache.kafka.common.serialization.StringSerializer
value-serializer: org.apache.kafka.common.serialization.StringSerializer

然後獲取傳入的分片id、時間戳等信息創建分片對象,然後創建producer進行提交:

 ProducerBatch batch = new ProducerBatch(tp, recordsBuilder, time.milliseconds());
 FutureRecordMetadata future = Utils.notNull(batch.tryAppend(timestamp, key, value, headers, callback, time.milliseconds()));
consumer

消息消費有兩種方式:1-繼承函數接口實現onMessage() 2-註冊監聽@KafkaListener,兩種都是通過監聽器來實現,consumer通過poll分片來獲取消費對象。消息監聽後會進行註冊:

public class KafkaListenerEndpointRegistrar implements BeanFactoryAware, InitializingBean {

	private final List<KafkaListenerEndpointDescriptor> endpointDescriptors = new ArrayList<>();

	private KafkaListenerEndpointRegistry endpointRegistry;
	...

通過bean工廠來管理監聽器,當監聽到消息時會即時進行處理消費。
與其他mq監聽並無二致。

安裝

環境:centos7

下載

1.從官方服務器上下載
2.直接在服務器上下載
例如:wget http://mirror.bit.edu.cn/apache/kafka/1.0.0/kafka_2.11-1.0.0.tgz
然後解壓 tar -zxvf xxx.gz

配置

kafka擁有四種配置:

server.properties

作爲服務的主配置,其中可配置信息如下:

  1. listeners=PLAINTEXT://192.168.99.128:9092 監聽器監聽的ip與端口,需要放開,否則無法識別連接
  2. num.partitions=1 kafka的分區,默認1個區,僅支持一個consumer,如果需要一個主題多消費者則需要放開分區,例如num.partitions=5
  3. log.dirs=/tmp/kafka-logs 日誌存放路徑
  4. zookeeper.connect=192.168.99.128:2181 kafka依賴的zookeeper的連接地址(一般使用自己的zk)
  5. 等等…
producer.properties

消息生產者的配置信息,主要的配置有bootstrap.servers,默認爲當前機器單節點

consumer.properties

消息消費者,與生產者一致,其中配置了consumer.group-id作爲分組id

zookeeper.properties

單機zk的配置,如果使用外置zk則不需要配置

啓動

執行:./bin/kafka-server-start.sh -daemon ./config/server.properties
jps查詢運行的應用:
在這裏插入圖片描述
停止:./bin/kafka-server-stop.sh

應用

採用springboot集成kafka模式進行開發

服務

1.kafka組件
將kafka服務作爲獨立組件jar來實現
在這裏插入圖片描述
引入依賴:

  <dependency>
      <groupId>org.springframework.kafka</groupId>
      <artifactId>spring-kafka</artifactId>
  </dependency>

配置producer

@Service("kafkaProducer")
public class KafkaProducer
{
    @Resource
    private KafkaTemplate kafkaTemplate;
    
    public Boolean sendMessage(String topic, Object message)
    {
        try
        {
            kafkaTemplate.send(topic, message);
        } catch (Exception e)
        {
            return Boolean.FALSE;
        }
        return Boolean.TRUE;
    }
}

2.業務
引入kafka組件包
主題:

    public static final String KAFKA_MY_TOPIC = "MY_KAFKA_TOPIC";
    public static final String KAFKA_MY_TOPIC_BAK = "MY_KAFKA_TOPIC_BAK";

服務啓動後zk中可見:
在這裏插入圖片描述
說明:__consumer_offsets偏移量爲消費者已消費的元數據,存儲在消費者分區上,是在config配置中的num.partitions決定的,默認50個,0~49

消息推送:

    @PostMapping("/sendBatchKafka")
    @ResponseBody
    public String sendBatchKafka(@RequestBody String request)
    {
        kafkaProducer.sendMessage(KafkaTopic.KAFKA_MY_TOPIC, request);
        Map<String, String> params = (Map<String, String>) JSONObject.parse(request);
        String context = params.get("context");
        params.put("context", context + "123");
        kafkaProducer.sendMessage(KafkaTopic.KAFKA_MY_TOPIC_BAK, JSONObject.toJSONString(params));
        return "success";
    }

消息消費:

    @KafkaListener(topics = {KafkaTopic.KAFKA_MY_TOPIC})
    public void onMessage(String message)
    {
        logger.info("KafkaConsumer主題消息:" + message);
    }
    @KafkaListener(topics = {KafkaTopic.KAFKA_MY_TOPIC, KafkaTopic.KAFKA_MY_TOPIC_BAK}, containerFactory = KafkaConfig.TOPIC_FACTORY)
    public void dealMessage(String message)
    {
        logger.info("KafkaBatchConsumer主題消息(多主題):" + message);
    }

調用推送接口,即可發送消息,多主題都可以接收。
單消費配置

spring:
  kafka:
    bootstrap-servers: 192.168.99.128:9092
    consumer:
      group-id: test-consumer-group
      enable-auto-commit: true
    listener:
      missing-topics-fatal: false
    producer:
      batch-size: 4096

說明:kafka默認單消費者,選取最新加載的消費者進行消費,如下:
在這裏插入圖片描述
此時查看zk,則可發現只有一個分區:
在這裏插入圖片描述
如果我們需要的就是一對一模式,那麼在code的時候注意規範,完全可當做點對點來實現。
多消費者模式
但如果我們需要一對多模式,那麼就需要進行調整:

spring:
  kafka:
    bootstrap-servers: 192.168.99.128:9092
    consumer:
      group-id: test-consumer-group
      enable-auto-commit: true
    listener:
      missing-topics-fatal: false
      concurrency: 5
      type: batch
    producer:
      batch-size: 4096

listener.type改爲batch批量模式
listener.concurrency線程數調整爲依據server.properoties中的num.partitions=5來配置。
missing-topics-fatal默認會校驗不存在的topic,此處false,否則需要在啓動時創建,不然報錯。
topic設置分區信息即可,默認1個分區(會依據規則找最新註冊消費者消費),分區數不能大於配置的分區數:

    @Bean(name = "MY_KAFKA_TOPIC")
    public NewTopic myTopic()
    {
        return new NewTopic(KAFKA_MY_TOPIC, 5, (short)2);
    }

可見按照配置設置了5個分區:
在這裏插入圖片描述
詳細說明:
此處採用的是依據消費者的數量進行消息分區推送,因爲分區數>=消費者數,那麼自定義一個全局線程安全的容器來存放消費者的數量,故推送方法做出變化:

protected static Map<String, Integer> consumerCache = new ConcurrentHashMap<String, Integer>();

Integer concurrency = consumerCache.get(topic);
for (int i=0; i<concurrency; i++)
{
    kafkaTemplate.send(topic, i, groupId , message);
}

說明:多消費者模式包含單消費,單主題默認分區爲1個id爲0。

測試:
在這裏插入圖片描述
這樣三個消費者就都能收到消息了。

此處推送爲一個topic對應多consumer模式,另一種是多topic對應多consumer模式,需要消息根據不同的topic進行推送,如果幾千條就需要更多的topic,有點得不償失。

mq-kafka-code源碼
微信公衆號:像是風
在這裏插入圖片描述

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