3、发送消息到Kafka
- 最简单的方式
- 1、生产者的send()方法将ProducerRecord对象作为参数,所以药创建一个ProducerRecord对象。
- 2、我们使用生产者的send()方法发送ProducerRecord对象。
- 从生产者的架构图里面可以看到,消息先试被放进缓冲区,然后使用单独的线程发送到服务端。
- send()方法会返回一个包含RecordMetadata的Future对象,不过因为我们会忽略返回值,所以无法知道消息是否发送成功
- 如果不关心发送结果,那么可以使用这种发送方式。
- 3、其他异常
- 我们可以忽略发送消息时可能发生的错误或在服务器端可能发生的错误,但是在发送消息之前,生产者我还是有可能发生其他异常。
- 这些异常有可能是SerializationException(序列化失败)、BufferExhaustedException或TimeoutException(说明缓冲区已经满了)又或者是InterruptException(说明线程被中断)
- 同步消息发送
- 1、Producer.send()
- 方法返回一个Future对象,然后调用Future对象的get()方法等待Kafka响应。
- 如果服务器返回做粗,get()方法会跑出异常,
- 如果没有发生错误,我们会得到一个RecordMetaData对象,可以用它获取消息的偏移量。
- 2、如果在发送数据之前回或者在发送过程中发生任何需错误,记录下来
- 可重试错误:连接错误:可以通过再次链接来解决。“无主”错误:可以通过重新为分区选举首领来解决。如果多次重试任然无法解决,应用程序会收到一个重试异常。
- 无法通过重试解决:消息太大:对于这类错误,KafkaProducer不会进行任何重试,直接抛出异常。
- 异步发送消息
- 如果kafka集群之间一个来回需要10ms,如果在发送完每个消息来回需要10ms.如果在发送完每个消息之后都等待回应,那么发送一百个消息就需要1秒。但如果只发送消息不等待响应,那么发送100个消息。所需要的时间会少很多
- 大多数的情况下我们并不需要等待响应。
- 1、为了使用回调,需要一个实现了org.apache.kafka.clients.producer.Callback接口的类,这个类只有一个onCompletion方法。
- 2、如果kafka返回一个错误,onCompletion方法会抛出一个非空异常。这里我们只是简单的打印出来,但是生产环境应该有更好的处理方式。
- 3、记录与之前的一样
- 4、在发送消息时穿进去一个回调对象。
4、生产者配置
- acks:指定了必须有多少个分区副本接收到消息,生产者才会认为是消息写入成功了。这个参数对于消息丢失有重要的影响。
- acks=0:生产者在成功写入消息之前不会等待任何来自服务器的响应。肯能会导致数据丢失,但是吞吐量高
- acks=1:只要首节点收到消息,生产者就会收到一个来自服务器的成功响应。如果一个没有收到消息的节点成为新首领会导致消息丢失。这时候的吞吐量取决是同步发送还是异步发送分。
- ack=all:只有当所有参与复制的节点全部收到消息时,生产者才会收到一个来自服务器的响应。这种模式是安全的名单时延迟高。
- buffer.memory 该参数用来设置生产者内存缓冲区的大小,生产者用它缓存要发送到服务器的消息
- 如果应用程序发送消息的速度超过发送到服务器的速度,会导致生产者空间不足
- 这个时候send()方法调用要么被阻塞,要么抛异常,取决于如何设置block.on.buffer.full参数。
- compression.type 它指定了消息被发送给broker之前使用哪一种算法进行压缩,默认不压缩,该参数可以设置为snappy, gzip或lz4。
- snappy压缩算法是谷歌发明的,它占用较少的CPU,提供较好的性能和相当可观的压缩比,如果比较关注性能和网络宽带,可以使用这种算法。
- gzip一般使用较多的CPU,但是会提供更高的压缩比,所以如果网络宽带有限,可以使用这种算法。
- 使用压缩可以降低网络传输开销和存储开销,而这往往是kafka发送消息的瓶颈所在。
- retires:生产者收到的服务器返回的错误可能是临时的(比如分区找不到首领),在这种情况下,这个参数决定了重发的次数。
- 默认情况下,生产者每次重试之间会等待100ms。可以通过retry.backoff.ms参数来改变时间间隔。
- 一般情况下,因为生产者会自动进行重试,所以就没必要在代码逻辑处理那些可重试错误,你只需要处理那些不可重试的错误,或者重试次数超出上限的情况。
- batch.size 当多个消息需要被发送到同一个分区的时候,生产者会把他们放在同一个批次里。该参数指定了一个批次可以用的内存大小,按照字节计算(而不是消息个数)
- 当批次被填满,批次里面的所有消息会被发送出去。
- 生产者不一定会等到批次被占满才发送数据。
- 我们一般把这个之设置的很大,也不会造成延迟,只是会多占用一些内存。
- 如果设置的太小,生产者需要更频繁的发送消息,会增加一些额外一些开销。
- linger.ms 该参数指定了生产者在发送批次之前等待更多消息加入批次的时间。
- kafkaProducer会在批次填满或者linger.ms达到上限时把批次发出去。
- 把linger.ms设置成比0大的数,让生产者在发送之前等待一会儿,使更多的消息加入这个批次,虽然这样会增加延迟,但是也会提高吞吐量(因为一次性发送更多的消息,每个消息的开销就变小了)
- client.id 消息标识,该参数可以是任何的字符串,服务器用它来识别消息来源,还可以用在日志和配额指标里面。
- max.in.flight.requests.per.connection: 指定了生产者在收到服务器响应之前可以发送多个消息。
- 值越大,占用的内存越大
- 值越大,吞吐量越高
- 设置为1,保证消息顺序发送,即使发生了重试。
- timeout.ms、request.timeout.ms、metadata.fetch.timeout.ms
- timeout.ms 指定了broker等待同步副本返回消息确认的时间,与acks的配置相匹配
- 如果指定时间没收到同步副本确认,broker返回一个错误
- request.timeout.ms 生产者在发送数据的时候等待服务器返回响应的时间。
- 如果等待超时,要么重发数据,要么返回一个错误
- metadata.fetch.timeout.ms 生产者在获取元数据(比如目标分区的首领是谁)是等待服务器返回响应的时间。
- 如果等待超时,要么重发数据,要么返回一个错误
- max.block.ms 指定了在调用send()方法或者使用partitionFor()方法获取元数据时生产者阻塞时间。
- 当生产者的发送缓冲区已满,或没有可用元数据,这些方法就会阻塞
- 在阻塞时间达到配置的值时,生产者就会跑出异常
- max.request.size 用于控制生产者发送的请求大小。可以指发送消息的单个大小,也可以指单个请求里所有消息的大小。
- receive.buffer.bytes、send.buffer.bytes 分别指定了Tcp socket接收和发送数据的缓冲区大小。
- -1,就是使用系统的默认值
- 如果生产者或消费者与broker处于不同的数据中心,那么可以适当增大这个值。