优化阅读格式
This commit is contained in:
@ -13,11 +13,11 @@
|
||||
|
||||
## 一、生产者发送消息的过程
|
||||
|
||||
首先介绍一下Kafka生产者发送消息的过程:
|
||||
首先介绍一下 Kafka 生产者发送消息的过程:
|
||||
|
||||
+ Kafka会将发送消息包装为ProducerRecord对象, ProducerRecord对象包含了目标主题和要发送的内容,同时还可以指定键和分区。在发送ProducerRecord对象前,生产者会先把键和值对象序列化成字节数组,这样它们才能够在网络上传输。
|
||||
+ 接下来,数据被传给分区器。如果之前已经在ProducerRecord对象里指定了分区,那么分区器就不会再做任何事情。如果没有指定分区 ,那么分区器会根据ProducerRecord对象的键来选择一个分区,紧接着,这条记录被添加到一个记录批次里,这个批次里的所有消息会被发送到相同的主题和分区上。有一个独立的线程负责把这些记录批次发送到相应的broker上。
|
||||
+ 服务器在收到这些消息时会返回一个响应。如果消息成功写入Kafka,就返回一个RecordMetaData对象,它包含了主题和分区信息,以及记录在分区里的偏移量。如果写入失败,则会返回一个错误。生产者在收到错误之后会尝试重新发送消息,如果达到指定的重试次数后还没有成功,则直接抛出异常,不再重试。
|
||||
+ Kafka 会将发送消息包装为 ProducerRecord 对象, ProducerRecord 对象包含了目标主题和要发送的内容,同时还可以指定键和分区。在发送 ProducerRecord 对象前,生产者会先把键和值对象序列化成字节数组,这样它们才能够在网络上传输。
|
||||
+ 接下来,数据被传给分区器。如果之前已经在 ProducerRecord 对象里指定了分区,那么分区器就不会再做任何事情。如果没有指定分区 ,那么分区器会根据 ProducerRecord 对象的键来选择一个分区,紧接着,这条记录被添加到一个记录批次里,这个批次里的所有消息会被发送到相同的主题和分区上。有一个独立的线程负责把这些记录批次发送到相应的 broker 上。
|
||||
+ 服务器在收到这些消息时会返回一个响应。如果消息成功写入 Kafka,就返回一个 RecordMetaData 对象,它包含了主题和分区信息,以及记录在分区里的偏移量。如果写入失败,则会返回一个错误。生产者在收到错误之后会尝试重新发送消息,如果达到指定的重试次数后还没有成功,则直接抛出异常,不再重试。
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/BigData-Notes/blob/master/pictures/kafka-send-messgaes.png"/> </div>
|
||||
|
||||
@ -25,7 +25,7 @@
|
||||
|
||||
### 2.1 项目依赖
|
||||
|
||||
本项目采用Maven构建,想要调用Kafka生产者API,需要导入`kafka-clients`依赖,如下:
|
||||
本项目采用 Maven 构建,想要调用 Kafka 生产者 API,需要导入 `kafka-clients` 依赖,如下:
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
@ -37,9 +37,9 @@
|
||||
|
||||
### 2.2 创建生产者
|
||||
|
||||
创建Kafka生产者时,以下三个属性是必须指定的:
|
||||
创建 Kafka 生产者时,以下三个属性是必须指定的:
|
||||
|
||||
+ **bootstrap.servers** :指定broker的地址清单,清单里不需要包含所有的broker地址,生产者会从给定的broker里查找broker的信息。不过建议至少要提供两个broker的信息作为容错;
|
||||
+ **bootstrap.servers** :指定 broker 的地址清单,清单里不需要包含所有的 broker 地址,生产者会从给定的 broker 里查找 broker 的信息。不过建议至少要提供两个 broker 的信息作为容错;
|
||||
+ **key.serializer** :指定键的序列化器;
|
||||
+ **value.serializer** :指定值的序列化器。
|
||||
|
||||
@ -71,13 +71,13 @@ public class SimpleProducer {
|
||||
}
|
||||
```
|
||||
|
||||
> 本篇文章的所有示例代码可以从Github上进行下载:[kafka-basis](https://github.com/heibaiying/BigData-Notes/tree/master/code/Kafka/kafka-basis)
|
||||
> 本篇文章的所有示例代码可以从 Github 上进行下载:[kafka-basis](https://github.com/heibaiying/BigData-Notes/tree/master/code/Kafka/kafka-basis)
|
||||
|
||||
### 2.3 测试
|
||||
|
||||
#### 1. 启动Kakfa
|
||||
|
||||
Kafka的运行依赖于zookeeper,需要预先启动,可以启动Kafka内置的zookeeper,也可以启动自己安装的:
|
||||
Kafka 的运行依赖于 zookeeper,需要预先启动,可以启动 Kafka 内置的 zookeeper,也可以启动自己安装的:
|
||||
|
||||
```shell
|
||||
# zookeeper启动命令
|
||||
@ -87,7 +87,7 @@ bin/zkServer.sh start
|
||||
bin/zookeeper-server-start.sh config/zookeeper.properties
|
||||
```
|
||||
|
||||
启动单节点kafka用于测试:
|
||||
启动单节点 kafka 用于测试:
|
||||
|
||||
```shell
|
||||
# bin/kafka-server-start.sh config/server.properties
|
||||
@ -116,7 +116,7 @@ bin/kafka-topics.sh --create \
|
||||
|
||||
#### 4. 运行项目
|
||||
|
||||
此时可以看到消费者控制台,输出如下,这里`kafka-console-consumer`只会打印出值信息,不会打印出键信息。
|
||||
此时可以看到消费者控制台,输出如下,这里 `kafka-console-consumer` 只会打印出值信息,不会打印出键信息。
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/BigData-Notes/blob/master/pictures/kafka-simple-producer.png"/> </div>
|
||||
|
||||
@ -124,7 +124,7 @@ bin/kafka-topics.sh --create \
|
||||
|
||||
### 2.4 可能出现的问题
|
||||
|
||||
在这里可能出现的一个问题是:生产者程序在启动后,一直处于等待状态。这通常出现在你使用默认配置启动Kafka的情况下,此时需要对`server.properties`文件中的`listeners`配置进行更改:
|
||||
在这里可能出现的一个问题是:生产者程序在启动后,一直处于等待状态。这通常出现在你使用默认配置启动 Kafka 的情况下,此时需要对 `server.properties` 文件中的 `listeners` 配置进行更改:
|
||||
|
||||
```shell
|
||||
# hadoop001 为我启动kafka服务的主机名,你可以换成自己的主机名或者ip地址
|
||||
@ -135,11 +135,11 @@ listeners=PLAINTEXT://hadoop001:9092
|
||||
|
||||
## 二、发送消息
|
||||
|
||||
上面的示例程序调用了`send`方法发送消息后没有做任何操作,在这种情况下,我们没有办法知道消息发送的结果。想要知道消息发送的结果,可以使用同步发送或者异步发送来实现。
|
||||
上面的示例程序调用了 `send` 方法发送消息后没有做任何操作,在这种情况下,我们没有办法知道消息发送的结果。想要知道消息发送的结果,可以使用同步发送或者异步发送来实现。
|
||||
|
||||
### 2.1 同步发送
|
||||
|
||||
在调用`send`方法后可以接着调用`get()`方法,`send`方法的返回值是一个Future\<RecordMetadata>对象,RecordMetadata里面包含了发送消息的主题、分区、偏移量等信息。改写后的代码如下:
|
||||
在调用 `send` 方法后可以接着调用 `get()` 方法,`send` 方法的返回值是一个 Future\<RecordMetadata>对象,RecordMetadata 里面包含了发送消息的主题、分区、偏移量等信息。改写后的代码如下:
|
||||
|
||||
```scala
|
||||
for (int i = 0; i < 10; i++) {
|
||||
@ -155,7 +155,7 @@ for (int i = 0; i < 10; i++) {
|
||||
}
|
||||
```
|
||||
|
||||
此时得到的输出如下:偏移量和调用次数有关,所有记录都分配到了0分区,这是因为在创建`Hello-Kafka`主题时候,使用`--partitions`指定其分区数为1,即只有一个分区。
|
||||
此时得到的输出如下:偏移量和调用次数有关,所有记录都分配到了 0 分区,这是因为在创建 `Hello-Kafka` 主题时候,使用 `--partitions` 指定其分区数为 1,即只有一个分区。
|
||||
|
||||
```shell
|
||||
topic=Hello-Kafka, partition=0, offset=40
|
||||
@ -172,7 +172,7 @@ topic=Hello-Kafka, partition=0, offset=49
|
||||
|
||||
### 2.2 异步发送
|
||||
|
||||
通常我们并不关心发送成功的情况,更多关注的是失败的情况,因此Kafka提供了异步发送和回调函数。 代码如下:
|
||||
通常我们并不关心发送成功的情况,更多关注的是失败的情况,因此 Kafka 提供了异步发送和回调函数。 代码如下:
|
||||
|
||||
```scala
|
||||
for (int i = 0; i < 10; i++) {
|
||||
@ -196,10 +196,10 @@ for (int i = 0; i < 10; i++) {
|
||||
|
||||
## 三、自定义分区器
|
||||
|
||||
Kafka有着默认的分区机制:
|
||||
Kafka 有着默认的分区机制:
|
||||
|
||||
+ 如果键值为 null, 则使用轮询(Round Robin)算法将消息均衡地分布到各个分区上;
|
||||
+ 如果键值不为null,那么Kafka会使用内置的散列算法对键进行散列,然后分布到各个分区上。
|
||||
+ 如果键值为 null, 则使用轮询 (Round Robin) 算法将消息均衡地分布到各个分区上;
|
||||
+ 如果键值不为 null,那么 Kafka 会使用内置的散列算法对键进行散列,然后分布到各个分区上。
|
||||
|
||||
某些情况下,你可能有着自己的分区需求,这时候可以采用自定义分区器实现。这里给出一个自定义分区器的示例:
|
||||
|
||||
@ -222,7 +222,7 @@ public class CustomPartitioner implements Partitioner {
|
||||
@Override
|
||||
public int partition(String topic, Object key, byte[] keyBytes, Object value,
|
||||
byte[] valueBytes, Cluster cluster) {
|
||||
/*key值为分数,当分数大于分数线时候,分配到1分区,否则分配到0分区*/
|
||||
/*key 值为分数,当分数大于分数线时候,分配到 1 分区,否则分配到 0 分区*/
|
||||
return (Integer) key >= passLine ? 1 : 0;
|
||||
}
|
||||
|
||||
@ -278,7 +278,7 @@ public class ProducerWithPartitioner {
|
||||
--topic Kafka-Partitioner-Test
|
||||
```
|
||||
|
||||
此时输入如下,可以看到分数大于等于6分的都被分到1分区,而小于6分的都被分到了0分区。
|
||||
此时输入如下,可以看到分数大于等于 6 分的都被分到 1 分区,而小于 6 分的都被分到了 0 分区。
|
||||
|
||||
```shell
|
||||
score:6, partition=1,
|
||||
@ -299,7 +299,7 @@ score:5, partition=0,
|
||||
|
||||
## 四、生产者其他属性
|
||||
|
||||
上面生产者的创建都仅指定了服务地址,键序列化器、值序列化器,实际上Kafka的生产者还有很多可配置属性,如下:
|
||||
上面生产者的创建都仅指定了服务地址,键序列化器、值序列化器,实际上 Kafka 的生产者还有很多可配置属性,如下:
|
||||
|
||||
### 1. acks
|
||||
|
||||
@ -315,7 +315,7 @@ acks 参数指定了必须要有多少个分区副本收到消息,生产者才
|
||||
|
||||
### 3. compression.type
|
||||
|
||||
默认情况下,发送的消息不会被压缩。如果想要进行压缩,可以配置此参数,可选值有snappy,gzip,lz4。
|
||||
默认情况下,发送的消息不会被压缩。如果想要进行压缩,可以配置此参数,可选值有 snappy,gzip,lz4。
|
||||
|
||||
### 4. retries
|
||||
|
||||
@ -331,29 +331,29 @@ acks 参数指定了必须要有多少个分区副本收到消息,生产者才
|
||||
|
||||
### 7. clent.id
|
||||
|
||||
客户端id,服务器用来识别消息的来源。
|
||||
客户端 id,服务器用来识别消息的来源。
|
||||
|
||||
### 8. max.in.flight.requests.per.connection
|
||||
|
||||
指定了生产者在收到服务器响应之前可以发送多少个消息。它的值越高,就会占用越多的内存,不过也会提升吞吐量,把它设置为1可以保证消息是按照发送的顺序写入服务器,即使发生了重试。
|
||||
指定了生产者在收到服务器响应之前可以发送多少个消息。它的值越高,就会占用越多的内存,不过也会提升吞吐量,把它设置为 1 可以保证消息是按照发送的顺序写入服务器,即使发生了重试。
|
||||
|
||||
### 9. timeout.ms, request.timeout.ms & metadata.fetch.timeout.ms
|
||||
|
||||
- timeout.ms 指定了borker等待同步副本返回消息的确认时间;
|
||||
- timeout.ms 指定了 borker 等待同步副本返回消息的确认时间;
|
||||
- request.timeout.ms 指定了生产者在发送数据时等待服务器返回响应的时间;
|
||||
- metadata.fetch.timeout.ms 指定了生产者在获取元数据(比如分区首领是谁)时等待服务器返回响应的时间。
|
||||
|
||||
### 10. max.block.ms
|
||||
|
||||
指定了在调用`send()`方法或使用`partitionsFor()`方法获取元数据时生产者的阻塞时间。当生产者的发送缓冲区已满,或者没有可用的元数据时,这些方法会阻塞。在阻塞时间达到max.block.ms 时,生产者会抛出超时异常。
|
||||
指定了在调用 `send()` 方法或使用 `partitionsFor()` 方法获取元数据时生产者的阻塞时间。当生产者的发送缓冲区已满,或者没有可用的元数据时,这些方法会阻塞。在阻塞时间达到 max.block.ms 时,生产者会抛出超时异常。
|
||||
|
||||
### 11. max.request.size
|
||||
|
||||
该参数用于控制生产者发送的请求大小。它可以指发送的单个消息的最大值,也可以指单个请求里所有消息总的大小。例如,假设这个值为1000K ,那么可以发送的单个最大消息为1000K ,或者生产者可以在单个请求里发送一个批次,该批次包含了 1000 个消息,每个消息大小为1K。
|
||||
该参数用于控制生产者发送的请求大小。它可以指发送的单个消息的最大值,也可以指单个请求里所有消息总的大小。例如,假设这个值为 1000K ,那么可以发送的单个最大消息为 1000K ,或者生产者可以在单个请求里发送一个批次,该批次包含了 1000 个消息,每个消息大小为 1K。
|
||||
|
||||
### 12. receive.buffer.bytes & send.buffer.byte
|
||||
|
||||
这两个参数分别指定TCP socket 接收和发送数据包缓冲区的大小,-1代表使用操作系统的默认值。
|
||||
这两个参数分别指定 TCP socket 接收和发送数据包缓冲区的大小,-1 代表使用操作系统的默认值。
|
||||
|
||||
|
||||
|
||||
@ -361,4 +361,4 @@ acks 参数指定了必须要有多少个分区副本收到消息,生产者才
|
||||
|
||||
## 参考资料
|
||||
|
||||
1. Neha Narkhede, Gwen Shapira ,Todd Palino(著) , 薛命灯(译) . Kafka权威指南 . 人民邮电出版社 . 2017-12-26
|
||||
1. Neha Narkhede, Gwen Shapira ,Todd Palino(著) , 薛命灯 (译) . Kafka 权威指南 . 人民邮电出版社 . 2017-12-26
|
||||
|
Reference in New Issue
Block a user