SpringBoot整合Spring Kafka

一. 版本

  • Zookeeper: zookeeper-3.4.14
  • Kafk-server: kafka_2.12-2.2.1
  • Spring Boot: 2.1.6.RELEASE
  • Spring Kafka: 2.2.7.RELEASE

二. 项目创建

  1. ZookeeperKafka-Server对应版本下载并启动

  2. 使用Idea创建SpringBoot项目时, 直接引入SpringKafka. Lombok, Web三个插件

    若是手动引入SpringKafka, Maven代码如下:

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

三. 类介绍

在开始撸代码之前我们要大致的先了解各个类有什么含义

3.1 生产者
  • ProducerConfig: 该类提供了生产者对象有哪些配置, 以及配置的详细描述

  • KafkaProducer: Kafka生产者对象

  • DefaultKafkaProducerFatory: 系统默认的创建KafkaProducer的工厂类, 其中采用单例模式的方式创建KafkaProducer对象, 方法如下:

    @Override
    	public Producer<K, V> createProducer() {
    		// ...
    		if (this.producer == null) {
    			synchronized (this) {
    				if (this.producer == null) {
              // 采用单例模式创建KafkaProducer对象
    					this.producer = new CloseSafeProducer<K, V>(createKafkaProducer());
    				}
    			}
    		}
    		return this.producer;
    	}
    
  • KafkaTemplate: 该类实现了KafkaOperations 接口, 主要就是用于定义生产者发送消息的方式

    public interface KafkaOperations<K, V> {
    
      // 消息只包含 消息实体
    	ListenableFuture<SendResult<K, V>> sendDefault(V data);
      // 消息有 键/值 组成
    	ListenableFuture<SendResult<K, V>> sendDefault(K key, V data);
      // 该消息指定了 键/值 并制定了消息所在的分区
    	ListenableFuture<SendResult<K, V>> sendDefault(Integer partition, K key, V data);
      // 该消息指定了 键/值 并制定了消息所在的分区并且指定了消息的时间戳
    	ListenableFuture<SendResult<K, V>> sendDefault(Integer partition, Long timestamp, K key, V data);
    
    }
    
    
  • ProducerRecord: 具体发送的消息对象

  • ListenableFuture: 是一个Future对象, 发送消息之后用来获取服务器返回信息

  • SendResult: 发送消息的之后服务器具体的返回的封装对象

3.2 消费者
  • ConsumerConfig: 该类提供了消费者对象有哪些属性配置, 以及介绍各个配置的含义
  • KafkaConsumer: 消费者对象
  • DefaultKafkaConsumerFactory: 具体用来创建KafkaConsumer对象的对象,
  • MessageListener : 该接口继承GenericMessageListener, 专门用来给用户重写onmessage()方法来处理收到的消息
  • KafkaMessageListenerContainer: 消费者对象的依赖类, 该类专门用来启动和关闭该消费者服务器
  • ContainerProperties: 用来定义KafkaMessageListenerContainer的属性, 比如MessageListener和当前消费者消费的topic主题等
  • ComsumerRecord : 具体消费的对象
3.3 特殊类
  • KafkaProperties 该类是Kafka在SpringBoot中的配置类, 我们可以在里面看到默认的配置信息,也可以在配置文件中修改配置信息
  • KafkaListener 该类是一个注解类, 其作用相当于创建一个KafkaMessageListenerContainer来监听当前的主题中的消息, 其注解的方法实体部分就是设置的MessageListener, 其实就是处理该主题中的消息的方法.

四. 实现

在这里我们模拟通过请求传输主题和消息, 让生产者发送该主题和消息到服务器上, 最后通过监听该主题的消费者打印消息的具体内容.

4.1 配置
# kafka的配置信息具体参照 KafkaProperties 类
spring.kafka.consumer.group-id=test
# 消费者获取到的偏移量错误时从分区的起始位置读取, 注意要是用了该配置的话, 那么在启动项目的时候消费者会消费主题对应分区中的消息, 出现重复消费消息的现象. 具体配置大家可以参考配置说明
spring.kafka.consumer.auto-offset-reset=earliest
4.2 控制器层
package com.pyi.kafka.demo.controller;

import com.pyi.kafka.demo.service.impl.HelloServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

/**
 * Slf4j 是Lombok插件的打印日志注解
 * @author pyi
 * @date 2019-06-26
 */
@RestController
@RequestMapping("hello")
@Slf4j
public class HelloController {

    @Resource
    private HelloServiceImpl helloService;

    /**
     * 这里我们监听请求携带主题和信息, 然后想服务器发送消息供消费者消费
     * @param topic 主题
     * @param message 信息
     * @return String
     */
    @GetMapping("hello/{topic}/{message}")
    public String hello(@PathVariable String topic, @PathVariable String message) {
        log.info("Topic = {}, Message = {}", topic, message);
        helloService.hello(topic, message);
        return "success";
    }

}
4.3 业务逻辑层
package com.pyi.kafka.demo.service.impl;

import com.pyi.kafka.demo.service.HelloService;
import org.apache.kafka.clients.producer.RecordMetadata;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.kafka.support.SendResult;
import org.springframework.stereotype.Service;
import org.springframework.util.concurrent.ListenableFuture;

import javax.annotation.Resource;

/**
 * @author pyi
 * @date 2019-06-26
 */
@Service
public class HelloServiceImpl implements HelloService {

    @Resource
    private KafkaTemplate<Integer, String> kafkaTemplate;

    @Override
    public void hello(String topic, String message) {
        // 这里如果我们忽略消息是否发送成功(生产者 acks = 0)的时候就不用处理返回值, 增强系统的吞吐量, 但是消息可能会丢失
        handlerSendRecord(kafkaTemplate.send(topic, message));
    }

    private void handlerSendRecord(ListenableFuture<SendResult<Integer, String>> resultListenableFuture) {
        try {
            //这里我们可以获取到生产者消息是否提交成功
            SendResult<Integer, String> integerStringSendResult = resultListenableFuture.get();
            RecordMetadata recordMetadata = integerStringSendResult.getRecordMetadata();
            // 打印消息被存储到哪个分区, 当前偏移量是多少
            System.out.println("partition = " + recordMetadata.partition());
            System.out.println("offset = " + recordMetadata.offset());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

4.4 消费者监听器
package com.pyi.kafka.demo.listener;

import lombok.extern.slf4j.Slf4j;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.stereotype.Component;

/**
 * 定义专门处理消息方法
 * @author pyi
 * @date 2019-06-26
 */
@Slf4j
@Component
public class Listener {

    /**
     *   KafkaListener注解 相当于 `KafkaMessageListenerContainer` 消费者的监听器, 方法实体部分相当于具体处理的消息内容
     * @param consumerRecord
     */
    @KafkaListener(id = "container_1", topics = {"topic1", "topic2"})
    public void test1(ConsumerRecord<?, ?> consumerRecord) {
        System.out.println(consumerRecord.toString());
    }

}

五. 测试

2019-06-26 18:37:32.612  INFO 7384 --- [nio-8080-exec-3] c.p.k.demo.controller.HelloController    : Topic = topic1, Message = demoData
partition = 0
offset = 16
ConsumerRecord(topic = topic1, partition = 0, offset = 16, CreateTime = 1561545452614, serialized key size = -1, serialized value size = 8, headers = RecordHeaders(headers = [], isReadOnly = false), key = null, value = demoData)

六. 项目地址

码云地址

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值