kafka学习(五)
kafka可靠的数据传递
kafka可靠性保证
ACID 是关系型数据库保证数据的规范,指的是原子性,一致性,隔离性和持久性,这是数据库给出的可靠性保证。
kafka给出的保证是什么?
1.kafka可以保证分区消息的顺序。
2.只有当消息被写入分区的所有同步副本时,它才认为是已提交的。
3.只要一个副本是活跃的,那么已经提交的消息就不会丢失。
4.消费者只能读取已经提交的消息。
复制
kafka的复制机制和分区的多副本架构师kafka可靠性保证的核心。
kafka复制机制
kafka的主题被分为多个分区,分区是基本的数据块。分区存储在单个磁盘上,kafka可以保证分区里的事件是有序的,分区可以在线(可用),也可以离线(不可用).每个分区有多个副本,只有一个首领副本,所有的事件都会发给首领副本,其他副本只需要同步首领副本,并及时复制最新的事件,当首领副本不可用的时候,其中一个同步副本将成为新首领。
分区首领是同步副本,而对于跟随者来说,它需要满足以下条件才能被认为是同步的。
1.与zookeeper之间有一个活跃的绘画,也就是说,它在过去的6s(可配置)内向zookeeper发送过心跳。
2.在过去10s内(可配置)从首领哪里获取过消息。
3.在过去的10s内从首领哪里获取过最新的消息。光从首领哪里获取消息是不够的,它还必须是几乎零延迟。
kafka可靠性保证之broker配置
broker有3个配置参数会影响kafka消息存储的可靠性。
复制系数
主题级别的配置参数是replication.factor 而在broker级别则可以通过default.replication,factor来配置自动创建的主题。如果复制系数是n 那么在n-1个broker失效的情况下,仍然能够从主题读取数据或向主题写入数据。
不完全的首领选举
unclean.leader.election 只能在broker级别进行配置,它的默认值是true。当分区首领不可用时,一个同步副本才会被选举为新首领。如果在选举过程中没有丢失数据,也就是说提交数据同时存在于所以的同步副本上,那么这个选举就是完全的。
但假设在首领不可用时其他副本都是不同步的,这种情况会在两种场景里出现。
1.分区有3个副本,其中两个副本跟随者副本不可用。
2.分区有3个副本,因为网络问题导致两个跟随者复制消息滞后,所以尽管他们还在复制消息,但已经不同步了。首领副本一直继续接受消息,但是这个时候首领副本不可用。另外两个就再也无法变成同步了。
对于这两种场景,我们要作出一个两难的选择
1.如果不同步的副本不能被提升为新首领,那么分区在旧首领(最后一个同步副本)恢复之前是不可用的。
2.如果不同步的副本可以被提升为新首领,那么在这个副本变为不同步之后写入旧首领的消息会全部丢失,导致数据不一致。
所以一般我们把unclean.election.enable 设为false 从而降低了可用性,从而保证了数据的一致性,如果要求不高,可以设置为false,提高效率。
最少同步副本
在主题级别和broker级别上,这个参数都叫min.insync.replicas
在可靠的系统里使用生产者
根据可靠性需求配置恰当的acks
在参数配置和代码正确处处理错误。
发送确认把acks设置为all是保证数据提交和一致性的保证。
配置生产者重试的参数,一般环境临时问题都会重试发送消息。这个重试的参数是保证数据发送成功的必要条件。
额外的错误处理
使用生产者内置的重试机制可以不造成消息丢失的情况下处理了很多错误,不过对于开发人员来说。仍然需要处理其他类型的错误,包括:
1.不可重试错误,列如消息大小错误,认证错误
2.消息发送前的序列化错误
3.生产者达到重试次数上限时或者消息占用的内存达到上限时发送的错误。
在可靠的系统里使用消费者
消息者的可靠性配置
1.group.id 确保设置唯一的group.id
2.auto.offset.reset(earliest/latest 默认latest 会减少重复消息的)
3.enable.auto.commit (自动提交偏移量)
显示提交偏移量
如果选择了自动提交偏移量,就不需要关心显示提交的问题。
1.总是处理完事件后再提交偏移量。
2.提交频度是性能和重复消息数量之间的权衡
3.确保对提交的偏移量心里有数
4.再均衡,提交的时候应注意再均衡问题。
5.消费者可能需要重试。
列如:轮询拿到数据之后插入数据库的时候,数据库不可能用的时候我们需要重试 如果你此时提交的偏移量是30 那么30之内的消息将会被确认消费。但是其实只有29失败了,这条信息一般我们都会用单独的日志存储,手工来处理,但是kafka提供了两种模式来解决这个问题
第一种模式,在遇到可重试错误的时候,提交最后一个偏移量,然后在消息存入缓冲区,下一次轮询就不会把消息覆盖掉,调用consumer.pause()来保证其他的轮询不会返回消息。然后在保持轮询的过程中尝试重新处理,如果尝试成功,或者次数达到上限并决定放弃,那么把错误记录下俩并丢弃消息,然后调用resume()方法让消费者继续从轮询里获取新数据
第二种模式。在遇到可重试错误时,把错误写入一个独立的主题,然后继续。
消费者可能需要维护状态
一般会在数据库存储一张表来维护消息的主题,消费者 偏移量等等。
长时间处理
如果在轮询的时候需要与外部系统交互时间过久的情况,那么我们要保持客户端与broker的连接,因为心跳包是在轮询中完成,但是这时我们可能长时间阻塞在与某个外部系统交互中,这个时候这种情况一般用一个独立的线程去完成。要保持轮询发送心跳包,这样broker才不会认为应用程序已经死掉了。
仅一次传递
kafka还不能完全支持仅一次 完成生产 消费,这其中会出现消息丢失或者消息重复 丢失可以记录 但是重复消费是无法避免的,因为我们考虑到broken可能不可用集群发送了再均衡之后,这时候选择的策略选择为最后一次提交偏移量的位置,但是如果是首领副本不可用的时候,这个时候跟随者却没有同步到,那么有一部分的消息将已经被消费了,这个时候选举跟随者当选首领副本,但是这个跟随者的偏移量并没有同步,那只能按照跟随者的偏移量开始,那么差的这部分消息就会被重复消费,一般做法是消费时候采用一个支持唯一键的系统 如关系型数据库db的主键。那么即使是重复消息也不会有太大的问题。
验证系统可靠性
通过测试和应用程序的监控来保证系统的可靠性
配置验证,需要做一些测试
1.首领选举,停掉首领会发生什么?生产者和消费者需要多久恢复正常需要多少时间?
2.控制器选举,重启控制器后系统需要多少时间来恢复状态
3.依次重启会不会不丢失任何数据吗
4.不完全首领选举测试
应用程序验证
1.客户端从服务器断开连接
2.首领选举
3.依次重启broker
4.依次重启消费者
5.依次重启生产者。
每次学习都是在走人生路