写在开头,我目前只是一个CRUD girl,所有技术文档只是基于自己的理解和网上的总结来输出,有不对的地方欢迎批评指正。
一、简介
kafka是一个分布式发布 - 订阅消息系统和一个强大的队列,最初由Linkedin公司开发,于2010年贡献给了Apache基金会并成为顶级开源项目。
kafka具有分布式、多副本、解耦、可扩展、异步通信、削峰等特点,生产者(Producer)生产指定主题(Topic)消息到kafka,kafka集群的每个实例称为(Broker),消费者(Consumer)通过kafka消费消息,为了方便水平扩展,一个topic由多个partition组成,遇到瓶颈时可以通过扩容partition来进行扩展,消息只在单个partition内保证顺序。不论是Broker还是Consumer都需要通过zookeeper集群保存元数据(meta)信息来保证可用性。
二、使用场景
1、异步处理
针对一个秒杀系统,假设需要风控、扣减库存、创建订单、短信通知几个步骤,其中创建订单和短信通知并不影响秒杀结果,所以在风控和扣减库存成功之后即可返回秒杀成功,这样可以提高秒杀请求响应速度。针对这个场景,消息队列被用于服务的异步处理,这样做的好处就是:可以更快的返回结果;减少等待,实现步骤之间的并发,提高系统性能。
2、流量控制
针对秒杀系统还有一个问题就是,如何避免过多的请求压垮我们的系统呢?可以使用消息队列隔离网关和后端服务,以达到保护和流量控制的目的。这样可以根据后端的处理能力自动调节流量,达到“削峰填谷”的作用。
3、服务解耦
针对一个电商系统,一个订单信息可能会被多个业务方所需要,随着业务发展,订单的下游系统可能不断增加,并且每个系统可能只需要订单数据的一个子集,假设通过接口对接,每次下游系统接口变更都需要人力去更改订单模块接口并且进行一次上线,所以可以通过消息队列来解决系统耦合过于紧密的问题。订单服务只需要在变更订单后发送一条消息,下游系统来订阅主题,这样每个下游系统都可以获取一份完整的订单信息。
三、架构
这是我在网上找到的一个比较清晰的图来解释kafka的一个架构情形。
1、 producer作为生产者客户端, 向kafka服务器broker发送消息;
2、 broker作为kafka的服务器,一个kafka集群包含多个服务器;
3、 consumer作为消费消息的客户端,从broker取消息消费;
4、 每个consumer都属于一个consumer group,并且每条消息只会被consumer group里的一个consumer消费,但是可以被多个consumer group消费,从而实现了kafka消息的广播(发送给所有consumer,每个consumer group只包含一个consumer)和单播(发送给一个consumer,所有consumer在同一个consumer group里);
5、 一个topic可以分为多个partition,消息只在一个partition内保证顺序,其中每个消息都有一个offset序号,一个partition只对应一个broker,但是一个broker可以管理多个partition;
6、 partition的副本replica用来保证kafka的高可用,其中replica包含两种角色leader和follower,producer和consumer只跟partition的leader进行交互,follower用来同步leader数据;
7、 controller是kafka服务器中的一台,主要是作为 master 来仲裁 partition 的 leader 的,并维护 partition 和 replicas 的状态机,以及相应的 zk 的 watcher 注册;
8、 zookeeper用作存储kafka的元数据信息,broker和consumer都会在zookeeper注册,controller也会在zookeeper中标注是哪台broker;
四、高可用—副本机制
在没有副本机制的情况下,一旦某个机器宕机或者停止工作,都会导致 kafka的可用性降低。而kafka针对高可用引入的机制就是副本机制。引入副本机制之后,一个partition会有多个副本,在副本中选举一个leader,producer和consumer只会跟leader进行交互,其他的副本作为follower从leader同步数据。
1、副本机制介绍
AR
在Kafka中维护了一个AR列表,包括所有的分区的副本。AR又分为ISR和OSR。
ISR
ISR中的副本都要同步leader中的数据,只有都同步完成了数据才认为是成功提交了,成功提交之后才能供外界访问。
OSR
OSR内的follower尽力的去同步leader,不管是否同步成功,都不影响数据的提交,可能数据版本会落后。
最开始所有的副本都在ISR中,在kafka工作的过程中,如果某个副本同步速度慢于replica.lag.time.max.ms指定的阈值,则被踢出ISR存入OSR,如果后续速度恢复可以回到ISR中。
LEO
LogEndOffset:分区的最新的数据的offset,当数据写入leader后,LEO就立即执行该最新数据。相当于最新数据标识位。
HW
HighWatermark:只有写入的数据被同步到所有的ISR中的副本后,数据才认为已提交,HW更新到该位置,HW之前的数据才可以被消费者访问,保证没有同步完成的数据不会被消费者访问到。相当于所有副本同步数据标识位。
在leader宕机后,只能从ISR列表中选取新的leader,无论ISR中哪个副本被选为新的leader,它都知道HW之前的数据,可以保证在切换了leader后,消费者可以继续看到HW之前已经提交的数据。所以LEO代表已经写入的最新数据位置,而HW表示已经同步完成的数据,只有HW之前的数据才能被外界访问。
2、failover
zookeeper的failover:kafka对zookeeper是强依赖的,如果zookeeper网络不通或者连接不上,kafka是完全处于不可用状态,直到可以连上zookeeper为止;
broker的failover:一个broker的failure只会影响那些leader副本在该broker的partition,需要重新做leader选举,如果无法选举一个新的leader,会导致当前partition不可用,如果只是follower副本failure,不会影响partition状态,只是副本减少一个,kafka是不会自动补齐失败的副本。如果副本全部宕机,a、等待ISR中任意一个副本恢复,并选举为leader(等待时间可能较长,可用性低);b、选举第一个恢复的副本为leader,不管是否在ISR中(可用性高,可能导致数据丢失),根据指定参数:unclean.leader.election.enable决定。
controller的failover:broker试图去在“/controller” 目录抢占创建 ephemeral node,创建和初始化 partition 和 replicas 的状态机,并对 partitions 和 brokers 的目录的变化设置 watcher,可见,单纯的Controller 发生 failover,是不会影响正常数据读写的,只是 partition 的 leader 无法被重新选举,如果此时有 partition 的 leader fail,会导致 partition offline;但是 Controller 的 dead,往往是伴随着 broker 的 dead,所以在 Controller 发生 failover 的过程中,往往会出现 partition offline, 导致数据暂时不可用。
五、kafka存储
1、文件存储
在kafka集群中,每个broker有多个topic,在每个topic中又有多个partition,每个partition为一个分区。kafka的分区有自己的命名的规则,它的命名规则为topic的名称+有序序号,这个序号从0开始依次增加。在每个partition中有可以分为多个segment file。当生产者往partition中存储数据时,内存中存不下了,就会往segment file里面存储。kafka默认每个segment file的大小是500M,在存储数据时,会先生成一个segment file,当这个segment file到500M之后,再生成第二个segment file 以此类推。每个segment file对应两个文件,分别是以.log结尾的数据文件和以.index结尾的索引文件。在服务器上,每个partition是一个文件夹,每个segment是一个文件。
每个segment file也有自己的命名规则,每个名字有20个字符,不够用0填充。每个名字从0开始命名,下一个segment file文件的名字就是,上一个segment file中最后一条消息的索引值。在.index文件中,存储的是key-value格式的,key代表在.log中按顺序开始第几条消息,元数据物理位置,value代表该消息的位置偏移,物理偏移地址。但是在.index中不是对每条消息都做记录,它是每隔一些消息记录一次,避免占用太多内存。
2、磁盘存储
kafka采用顺序写,规避了随机读写带来的时间消耗,但是频繁的I/O还是会造成磁盘的性能瓶颈,kafka的另一个性能策略就是零拷贝。
举例消费组获取消息,服务器先从硬盘读取数据到内存,然后把内存中的数据原封不动的通过 socket 发送给消费者,操作系统将数据从磁盘读入到内核空间的页缓存,应用程序将数据读取到用户空间缓存,再写回到内核空间的socket缓存中,再从socket缓冲区复制到网卡缓冲区,以便发送网络消息;而“零拷贝”技术可以去掉这些没必要的数据复制操作,系统发起sendfile()调用后,磁盘数据通过DMA方式读取到内核缓冲区,内核缓冲区中的数据通过DMA聚合网络缓冲区,然后一齐发送到网卡中。
以上是对kafka理论知识简单介绍,如有错误理解,欢迎批评指正,后续会继续更新关于kafka生产者和消费者的介绍,敬请关注~