假如我本地kafka开启3个broker,3个分区后,生产者发送数据到broker,二个broker的follower都备份数据成功了,有一个broker挂掉了,这一个副本出现问题的时候,不能正常的返回ack,zk选举出新的leader后,生产者会重试发送消息,这时候同样的消息,发了两次,所以需要实现幂等性,
生产者要使用幂等性很简单,只需要增加以下配置即可:
enable.idempotence=true
其他的,kafka已经帮我们实现了,
Kafka通过为每条消息增加一个Sequence Numbler,通过Sequence Numbler来区分每条消息。每条消息对应一个分区,不同的分区产生的消息不可能重复。所有Sequence Numbler对应每个分区
Broker端在缓存中保存了这seq number,对于接收的每条消息,如果其序号比Broker缓存中序号大于1则接受它,否则将其丢弃。这样就可以实现了消息重复提交了。但是,只能保证单个Producer对于同一个<Topic, Partition>的Exactly Once语义。不能保证同一个Producer一个topic不同的partion幂等。
class Program
{
static TimeSpan DefaultTimeout = TimeSpan.FromSeconds(3);
static void Main(string[] args)
{
// 实现多个分区下的数据不丢失,而且实现幂等性
string brokerList = "192.168.1.1:9090,192.168.1.2:9091,192.168.1.3:9092";
// 不同的topic 的testtransactionalId 就不同
string topicName = "RemoveBill";
string transactionalId = "TransactionalId123";
var config = new ProducerConfig
{
BootstrapServers = brokerList,
//幂等性
EnableIdempotence = true,
Acks = Acks.All,
//MessageSendMaxRetries = 3,
TransactionalId = transactionalId,
// 客户端的唯一的标识,分区id , 消息的唯一标识
};
using (var producer = new ProducerBuilder<string, string>(config)
.Build())
{
try
{
producer.InitTransactions(DefaultTimeout);
producer.BeginTransaction();
for (int i = 100; i < 110; i++)
{
var content = i.ToString();
producer.Produce(
topicName, new Message<string, string> { Key = content, Value = content });
}
Console.WriteLine("测试123");
Console.ReadLine();
//var content1 = "107"; // 这些数据都是可以使用的
//producer.Produce(
// topicName, new Message<string, string> { Key = content1, Value = content1 });
// 数据写进去,,,没有确认,,,脏读 确认的状态
// 分布式事务--中间状态// kafka // pid ,事务id,msgid 存储下来
// 如果代码执行到这边
// 之前写进去的时候还会在吗-----时间到了就会清除
/// 如果 生产者写数据的时候,能把数据持久化一下-- 记录当前信息
/// 后台有一个线程,,补偿重试,发送之前没有发出去的数据
/// // 先记录
/// 后台有一个线程-- 再次发送-- 3.X
/// 分布式cap 本地路由表--
producer.CommitTransaction(DefaultTimeout);
}
catch (Exception ex)
{
//回滚
producer.AbortTransaction(DefaultTimeout);
Console.WriteLine(ex.Message);
}
Console.WriteLine($"end: {transactionalId}");
}
}
}