KAFKA事务和偏移量提交

KAFKA事务和偏移量提交

生产者是线程安全的,跨线程共享单个生成器实例通常比拥有多个实例更快。
生成器包括一个缓冲空间池,其中包含尚未传输到服务器的记录,以及一个后台I/O线程,后者负责将这些记录转换为请求并将它们传输到集群。使用后未能关闭生成器将
泄漏这些资源。
send()方法是异步的。 调用时,它会将记录添加到待处理记录发送的缓冲区中并立即返回。 这允许生产者将各个记录一起批处理以提高效率。
生产者为每个分区维护未发送记录的缓冲区。这些缓冲区的大小由batch.size配置指定。使这个更大可以导致更多的批处理,但需要更多的内存(因为通常每个活动分区
都有一个这样的缓冲区)。
默认情况下,即使缓冲区中有额外的未使用空间,也可以立即发送缓冲区。但是,如果要减少请求数量,可以将linger.ms设置为大于0的值。这将指示生产者在发送请求
之前等待该毫秒数,希望更多记录到达以填满同一批次。这类似于TCP中的Nagle算法。例如,在上面的代码片段中,由于我们将逗留时间设置为1毫秒,因此可能会在单个
请求中发送所有100条记录。但是,如果我们没有填满缓冲区,此设置会为我们的请求增加1毫秒的延迟,等待更多记录到达。请注意,即使在linger.ms = 0的情况下,
及时到达的记录通常也会一起批处理,因此在重负载情况下,无论延迟配置如何,都会发生批处理;但是,将此设置为大于0的值可以在不受最大负载影响的情况下会以少量
延迟为代价,导致更少、更高效的请求。
buffer.memory控制生产者可用于缓冲的总内存量。如果记录的发送速度快于传输到服务器的速度,则此缓冲区空间将耗尽。当缓冲区空间耗尽时,其他发送调用将被阻止。
阻塞时间的阈值由max.block.ms确定,之后它会抛出TimeoutException。
key.serializer和value.serializer指示如何将用户提供的键和值对象及其ProducerRecord转换为字节。您可以将包含的ByteArraySerializer或StringSerializer用于
简单的字符串或字节类型。

从Kafka 0.11开始,KafkaProducer支持另外两种模式:幂等生成器和事务生成器。幂等生成器将Kafka的传递语义从至少一次增强到
恰好一次。特别是生产者重试将不再引入重复。事务生成器允许应用程序以原子方式向多个分区(和主题!)发送消息。
enable.idempotence=true设置幂等生成器(需要注意的是生产者只能保证在单次会话中发送的消息具有幂等性,也就是单个分区,),幂等生成器不必
设置重试次数,因为默认为Intger.value,而且acks为all,如果一个send返回一个错误,即使是无限次重试(如果消息在发送之前在缓
冲区中过期),那么关闭生成器并检查最后生成的消息的内容,以确保它没有重复

Kafka是如何具体实现幂等的呢?
Kafka为此引入了producer id(以下简称PID)和序列号(sequence number)这两个概念。每个新的生产者实例在初始化的时候都会被分配一个PID,这个PID对用户而言是
完全透明的。对于每个PID,消息发送到的每一个分区都有对应的序列号,这些序列号从0开始单调递增。生产者每发送一条消息就会将对应的序列号的值加1。
broker端会在内存中为每一对维护一个序列号。对于收到的每一条消息,只有当它的序列号的值(SN_new)比broker端中维护的对应的序列号的值(SN_old)大1
(即SN_new = SN_old + 1)时,broker才会接收它。
如果SN_new< SN_old + 1,那么说明消息被重复写入,broker可以直接将其丢弃。如果SN_new> SN_old + 1,那么说明中间有数据尚未写入,出现了乱序,暗示可能有消息
丢失,这个异常是一个严重的异常。
引入序列号来实现幂等也只是针对每一对而言的,也就是说,Kafka的幂等只能保证单个生产者会话(session)中单分区的幂等。幂等性不能跨多个分区运作,而事务可以
弥补这个缺陷。

事务可以保证对多个分区写入操作的原子性。操作的原子性是指多个操作要么全部成功,要么全部失败,不存在部分成功、部分失败的可能。
要使用事务和相关api,你必须设置transactional.id,设置后将自动启用幂等性,并且事务中的主题复制因子应该设置为3,最小应该为2,此外消费者应该被配置为只读取
提交的消息
transactional.id的目的用于跨单个生产者实例的多个会话启用事务恢复,对于分区应用程序中运行的每个生成器实例,它应该是唯一的
所有新的事务API都是阻塞的,并且会在失败时抛出异常。
transactionalId与PID一一对应,两者之间所不同的是transactionalId由用户显式设置,而PID是由Kafka内部分配的。
另外,为了保证新的生产者启动后具有相同transactionalId的旧生产者能够立即失效,每个生产者通过transactionalId获取PID的同时,还会获取一个单调递增的producer
epoch。如果使用同一个transactionalId开启两个生产者,那么前一个开启的生产者会报错。
从生产者的角度分析,通过事务,Kafka可以保证跨生产者会话的消息幂等发送,以及跨生产者会话的事务恢复。
前者表示具有相同transactionalId的新生产者实例被创建且工作的时候,旧的且拥有相同transactionalId的生产者实例将不再工作。
后者指当某个生产者实例宕机后,新的生产者实例可以保证任何未完成的旧事务要么被提交(Commit),要么被中止(Abort),如此可以使新的生产者实例从一个正常的状
态开始工作。
KafkaProducer提供了5个与事务相关的方法,详细如下:
void initTransactions();
void beginTransaction() throws ProducerFencedException;
void sendOffsetsToTransaction(Map<TopicPartition, OffsetAndMetadata> offsets,
String consumerGroupId)
throws ProducerFencedException;
void commitTransaction() throws ProducerFencedException;
void abortTransaction() throws ProducerFencedException;

import kafka.common.AuthorizationException;
import kafka.common.KafkaException;
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.kafka.common.errors.OutOfOrderSequenceException;
import org.apache.kafka.common.errors.ProducerFencedException;
import org.apache.kafka.common.serialization.StringSerializer;

import java.util.Properties;


public class Application {
   
    public static void main(String[]args){
   
        Properties props = new Properties();
        props.put("bootstrap.servers", "localhost:9092");
        //设置事务id
        props.put("transactional.id", "my-transactional-id");
        KafkaProducer producer = new KafkaProducer(props, new StringSerializer(), new StringSerializer());
        //
        producer.initTransactions();
        try {
   
            //开启事务
            producer.beginTransaction();
            for (int i = 0; i < 100; i++)
                producer.send(new ProducerRecord("my-topic", Integer.toString(i)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值