Kafka保证消息不丢失

Kafka保证消息不丢失

kafka保证消息不丢失,需要从三个方面考虑:

  • 生产者端消息不丢失
  • kafka服务器broker本身消息不丢失
  • 消费者端消息不丢失

这篇文章对最新发送kafka数据丢失的情况做一个总结。

1.为什么要出这样一个文档

首先,部门很多人都kafka的使用不太懂,主要集中才kafka参数的设置使用,比如producer、server下的topic参数、consumer使用,往往配置的参数设置不合适出现各种问题;

其次,我们的消息(传感器数据)的特点是:消息体大小差别很大,小的几kb,大的消息体可能有好几十M。总体是要设置一个较大的单挑消息的上限值,30M左右;消息的字节数大,导致吞吐量也比较大,kafka的设置需要适应较高的吞吐量;
最后,最近测试人员提出的kafka消息的丢失等问题,始终存在,不断解决类似涌现的问题,在远程办公的条件下,解决此类问题可以说是效率低下。

2.想要集中解决的问题:

通过设置合适的kafka参数,保证使用kafka的过程的的稳定性。解决由于个体差异导致类似问题不停重复出现的问题;

3.详细问题解答

3.1客户端保证消息不丢失
消息的完整性和系统的吞吐量是互斥的,为了确保消息不丢失就必然会损失系统的吞吐量
producer:
1、ack设置-1
该参数默认值为1,只是保证topic中的patition的leader刷写磁盘成功,此时副本不一定同步成功,如果此时该分区的leader挂掉,会导致数据丢失;
保证数据不丢失,建议设置为”all”或者“-1”,此时会保证分区下的所有副本都同步成功。目前副本数是2(即一主leader一副本replic)

2、设置副本同步成功的最小同步个数为副本数-1
min.insync.replicas
同步副本数量,如果分区replica副本数为3,此处最好设置为2
3、加大重试次数 reties = 10
该参数可以保证,第一次发送数据失败的时候,是否需要重新发送,此处为重试10,如果重试10次仍然不能成功,则producer会抛出异常或者不报异常直接丢弃数据。(默认是不报出异常的直接丢弃数据,如果想要获得失败的丢弃的数据,就可以使用producer的callBack函数获取异常感知);
4、同步发送
默认kafka采用异步方式发送数据,来保证较大的吞吐量;同步方式,需要每条消息都确认接受并刷写磁盘成功才会返回ack确认,才会开始发送下一条,效率较低;
5、对於单条数据过大,要设置可接收的单条数据的大小
该设置是在kafkaManager的每个topic中设置max.message.bytes来确定每个消息大小的。默认大小为1m;
6、对于异步发送,通过回调函数来感知丢消息,使用KafkaProducer.send(record, callback)方法而不是send(record)方法
如果是采用异步的方式发送数据,最好是采用kafka回调callback的方式,捕获发送失败情况下的异常,提前获知哪些消费发送失败;

7、配置不允许非ISR(In-Sync Replicas,副本同步队列)集合中的副本当leader。所有的副本(replicas)统称为 Assigned Replicas,即 AR
unclean.leader.election
kafka 选择非同步副本作为首领副本的行为叫做,不完全首领选举。如何控制kafka在leader宕机时,同步副本不可用时,是否选择非同步
作为首领?通过kafka的另外一个参数来控制的 : unclean.leader.election. 如果是true 则会发生不完全首领选举。
副本数建议3个就可以,多的话需要更多的磁盘,unclean.leader.election 建议false.

8、客户端缓冲区满了也可能会丢消息;或者异步情况下消息在客户端缓冲区还未发送,客户端就宕机

具体来说,建议的客户端配置如下:

Properties props = new Properties();
props.put("bootstrap.servers", kafkaIP);
props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
props.put("value.serializer", "org.apache.kafka.common.serialization.ByteArraySerializer");

//表明kafka的数据来源,标记produceId
props.put("client.id","myname-tank");
props.put("acks", "all");
props.put("retries", "10");
//同一个分区中每一个批次的中的大小字节数,默认是16342个字节;此处改为每个批次2M(此处可以具体调整)
props.put("batch.size","2000000");
//此处改为过多久发送一批次数据,即使批次没有达到要求的批次大小,也会触发发送;此处改为1000ms
props.put("linger.ms","1000");

//限制单个消息请求的最大值
props.put("max.request.size", MAX_NUMBER);
//TCP发送缓冲区(SO_SNDBUF)的大小,若send.buffer.bytes设为-1,则使用操作系统的默认值
props.put("send.buffer.bytes", MAX_NUMBER);
props.put("buffer.memory", MAX_NUMBER);

从kafkaManager的topic中配置详细的参数如下:

4.问题思考

以上方法固然可以避免kafka发送数据丢失的情况(说到底只是解决问题的手段),那么当前数据丢失的根本原因是什么呢?(根本问题)【思维方法:我们可以采用常规问题定位+验证,然后排除错误猜想,抽丝剥茧,根据已有依据,缩小猜想范围,不断迭代从而最终定位问题】
目前的经常性发送数据丢失,正常情况下是broker宕机导致kafka 的topic下的patition的中leader宕机,从而出现数据丢失的情况。
但是目前kafka系统并没有出现明显的broker宕机的日志,排除broker宕机导致的leader丢失情况,那么我们继续推测的话,发现zookeeper作为分布式协调服务中心存在,kafka每个节点的心跳信息,kafka的元数据管理(kafka的topic信息保存,offset保存(当前已经改为kafka自己保存了)),leader节点选举等工作归其管辖。
通过日志发现,其日志中存在大量的leader节点选举相关的日志输出。(我们知道这样一个前提,那就是kafka的leader节点在选举期间是不能提供对外读写服务的)。这种不能提供对外服务的情况使得client端报出找不到leader的错误(消费者连接kafka也可能出现这个错误)。通过kafka服务器端报出大量的选举日志和lsr同步日志。两种日志结合起来看,节点一旦出现选举,来不及同步副本的leader会出现挂机(acks=1),导致leader分区上的消息丢失。
以上的分析结果最有可能的,是zookeeper获取不到分区leader的心跳信息,以为其挂掉导致重新选举,因此问题转向zookeeper与kafka的心跳联系的问题;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章