在上两节中,我们分别在本地和Docker中安装启动了Kafka,并演示了Kafka消息的发送和接收过程。本节我们将我们基于Spring Boot和Kafka API来实现Kafka消息的生产与消费代码示例。
通过本文,你可以学到:
本文完整代码见My Github Link .
首先需要说明的是,本文采用的是Kafka提供的消息发送和消费原生API。如果使用Spring-kafka提供的kafkaTemplate,使用方法也是一样的。
在Kafka消息发送消息之前,需要在本地先启动ZK、Kafka,并启动一个消费者用于测试。以本人为例,我在Docker端启动一个Kafka容器后,分别启动kafka server和一个消费者,如下:
(1)启动Kafka
(2)创建新的Topic用于测试
(3)运行一个消费者,用于测试消息的消费
没有报错表示环境搭建正常。
Kafka消息发送的过程很简单,指定发送的Broker IP以及producer的一些参数后,就可以发送消息了。消息发送有同步和异步两种实现,下面我们来看下两种如何实现。
(1)Produce参数配置
application.yml
### Kafka
spring:
kafka:
bootstrap-servers: 192.168.1.199:9092
#生产者的配置,大部分我们可以使用默认的,这里列出几个比较重要的属性
producer:
batch-size: 128
retries: 3
buffer-memory: 40960
acks: all
properties:
linger.ms: 10
关于以上参数含义,将在下述进行讲解。
注意:spring.kafka.bootstrap-servers后面的值需要换成自己本机启动的server IP.
(2)Producer初始化
import lombok.extern.slf4j.Slf4j;
import org.apache.kafka.clients.producer.*;
import org.apache.kafka.common.serialization.StringSerializer;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.Properties;
@Slf4j(topic = "kafka")
@Component
public class KafkaSender implements InitializingBean {
/**
* Kafka server服务器地址
*/
@Value("${kafka.producer.servers}")
private String kafkaServers;
/**
* Producer发送消息失败的时候重试次数
*/
@Value("${kafka.producer.retries}")
private int retries;
/**
* batch.size是producer批量发送的基本单位,
* 默认是16384Bytes,即16kB
*/
@Value("${kafka.producer.batch.size}")
private int batchSize;
/**
* lingger.ms是sender线程在检查batch是否ready时候,
* 判断有没有过期的参数,默认大小是0ms
*/
@Value("${kafka.producer.linger}")
private int linger;
/**
* producer可以用来缓存数据的内存大小
*/
@Value("${kafka.producer.buffer.memory}")
private int bufferMemory;
/**
* 多少ISR副本写入成功才算消息发送成功, all代表全部
*/
@Value("${kafka.producer.acks}")
private String acks;
private KafkaProducer<String, String> producer;
/**
* 初始化
*/
@Override
public void afterPropertiesSet() throws Exception {
Properties props = new Properties();
props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaServers);
props.put(ProducerConfig.RETRIES_CONFIG, retries);
props.put(ProducerConfig.BATCH_SIZE_CONFIG, batchSize);
props.put(ProducerConfig.LINGER_MS_CONFIG, linger);
props.put(ProducerConfig.BUFFER_MEMORY_CONFIG, bufferMemory);
props.put(ProducerConfig.ACKS_CONFIG, acks);
props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
producer = new KafkaProducer<>(props);
}
}
(3)消息发送
生产者在启动的时候,首先与我们指定的Broker建立一个TCP连接,之后向Kafka Broker发送消息。
目前,新版本的Kafka Producer的消息发送都是异步的,这代表你调用Kafka Producer的消息发送API会立即返回,但是不表示消息一定发送成功。
Kafka Producer消息发送API一般有两种:
// 无回调
producer.send(msg);
// 有回调
producer.send(msg, callback)
这两个有啥区别呢?
producer.send(msg)
是无回调的,即调用者调用该API后会立即返回,无论是否发送成功。假如发送过后因为网络抖动导致消息没有发送至Kafka Broker端,调用者是无法感知该消息是否发送成功的,所以可能导致消息丢失;
producer.send(msg, callback)
:带回调的消息发送,即调用者调用该API后,消息如果成功发送至Broker端会在回调中告知消息成功发送。所以我们可以在回调里做一些处理,如果回调内容为空或者返回异常,那么就代表此条消息没有发送成功,我们可以通过重试来重新发送。该API可以避免Producer端消息丢失(当然,还需要Broker端的一些参数配置)。不过,由于带有回调,所以性能要比不带回调的API性能低一点。
下面想敲个黑板,简单说下生产环境如何正确实现Kafka producer消息发送。
(1)在生产环境中,我们优先推荐使用带有回调的API:producer.send(msg, callback)
。
(2)设置 acks = all,acks 是 Producer 的一个参数,如果设置为all代表所有副本Broker接收并写入消息,这条消息才算是发送成功。如果不设置为all,假设第一台接收到消息并返回Producer消息成功后立马宕机,那么这条消息可能就算是丢失。