IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 大数据 -> KAFKA私房笔记 by 葵鱼 -> 正文阅读

[大数据]KAFKA私房笔记 by 葵鱼

kafka是个什么

一个消息中间件。

为什么要消息中间件? 传统的分布式系统 service1->service2 这种方式会产生比较大的耦合行,如果service2挂掉了 service1没法调用或无法从service2获取到对应的数据,就会产生长时间等待之类的问题,影响性能。

所以产生了消息中间件,将模式变为 service1->MQ->service2的模式,从而通过mq做到解耦,削锋,异步等功能

AKF:其实AKF本来是一个微服务划分的原则。AKF?立方体也叫做scala?cube,它在《The?Art?of?Scalability》一书中被首次提出,旨在提供一个系统化的扩展思路。AKF?把系统扩展分为以下三个维度:

  • X?轴:直接水平复制应用进程来扩展系统。
  • Y?轴:将功能拆分出来扩展系统。
  • Z?轴:基于用户信息扩展系统。

1、topic——>将消息按照业务划分,从而保证在某一类业务系统升级的时候,不会影响到其他业务的消息,从而保证了隔离性。与此同时,划分开的业务数据可以放入某些特定节点,每个节点只关注自己的业务类别,增加了资源使用的效率。

2、partition——>分区,增加了可用性(随机,hash或特定映射去对信息拆分)但需要保证消息消费的一致性(规划数据路由,将相关数据聚合放入相同partition,从而保证相关数据的顺序,无关数据可以分区存放,增加无关数据并行度。)partition利用了高性能i/o,在磁盘进行了处理。

3、kafka为了避免mysql这种读写分离产生的一致性问题,采用了最小粒度的划分,只能在主partition进行W/R,可以对每个partition进行若干备份。

4、offset:通过偏移量来保证消息是按顺序消费的(避免重复消费,丢失等问题),同时支持了不同的组维护不同的offset

topic是逻辑分区,每个topic下分为不同的partition物理分区。

每个partition运行在一个jvm中,jvm中启动了若干个broker维护各自的partiton(每个broker维护一个partition,一般情况下每个机器就其一个broker,运行在一个jvm中)

zookeper与kafka的关系:由于kafka的设计倾向,必然会倾向于复杂的多机集群,而zookeeper作分布式协调(zookeeper的作用是分布式协调而非存储,如果想做缓存,用redis或其他缓存不好么)帮助kafka进行分布式协调。

由于broker是主从进行协调运行的,所以在集群多个broker存在的时候,选择一个broker作为controller(主)就会使用zookeeper进行抢锁,抢到锁后,这个broker变为controller,controller会创建META-DATA(元数据),通过集群内的分布式通讯,维护META-DATA维护。

kafka存在一套admin-api(管理员api)去进行controller选举等操作,一般情况下这些api都是直接作用在controller上的,但是由于选举模式(如果controller挂了,会选个新的)controller并不固定,所以admin-api会先访问zookeeper获取谁才是controller,然后再去对操作对应的broker进行。

老版本的kafka,我们的生产信息的项目producer会链接zookeeper获取所有broker的地址,但是新版本会在配置里配置一部分broker地址,prodecer从地址中的broker,获取全部broker的地址。其原因是,如果我们的producer变多了,大家都去zookeeper获取地址,会对zookeeper产生巨大的压力,所以将部分原数据的维护拿到broker中间进行操作,避免zookeeper成为集群负担。

分区和分组:分区是针对数据的,而分组是针对分区的。

例子:如果我们有多套系统要消费partition,一套系统是入库的,另一套系统是放入ES分析的,就需要对partition进行分组。每个分组维护不同的offset(组和组隔离,一个分区的数据可以发给不同组的consumer,但是禁止多个consumer消费同一个组的数据)。

offset: 我们在调用接口的时候,其实不需要传任何参数也能按顺序消费消息,其实是kafka自己维护了一份offset在内存中。但是在内存中肯定会产生持久化的问题。

老版本kafka:维护在zookeeper中,但zookeeper作为分布式协调工具,不应被业务相关占用带宽从而影响选举等分布式协调工作。

新版本:维护了一份业务无关的topic,默认是50个partition,将offset持久化到磁盘上,自己进行维护。(老版本使用zookeeper进行维护,所以在使用老版本的时候都会通过第三方进行存储,redis、mysql等,而且面试挺火热的,但是目前kafka的新版本已经自己维护了,并不用非要存在redis中。)

我们在使用的时候会产生丢失,重复这种问题,其实归根结底是维护offset的节奏和频率(到底什么时候维护offset)

方案 1、默认方案,异步的,5s之内拉去若干数据进行处理,5s时间到了后,持久化offset(会产生重复处理的问题,可以通过幂等解决)

2、同步的:在代码中加入事务和锁,同步完成业务操作和offset的持久化(性能较差)

3、错误方法:先更新offset,再处理业务,业务处理失败就会产生丢失。

坑:

1、多个producer同时向kafka传输的时候,你可能能保证你多个producer消息产生的顺序,但producer向kafka发送的网络延迟等导致kafka接收到的信息的顺序不一定与你发送的顺序相同。

解决方案:你在lock的时候,要将向kafka发送数据的这部分代码也lock上,从而实现 获取到锁的先发送,没获取到锁的后发送,避免这汇总问题。大部分人都不会注意着点,就产生了并发发送消息的问题。

2、尽量避免一个partition对应多个consumer,因为会产生 consumer1从partition读取了插入操作,consumer2从partition读取了删除操作,本来这两条操作在partition中是有顺序的,但是如果consumer2处理比consumer1快,就会产生1还没插入2就删除了,然后就报错了。(有一种不太好的解决方案是在加锁判断offset,但是这样会大量降低性能)

mac下启动和关闭kafka和zk

/usr/local/Cellar/kafka/2.8.0/bin/zookeeper-server-start /usr/local/etc/kafka/zookeeper.properties &

/usr/local/Cellar/kafka/2.8.0/bin/£

kafka在poll的时候,是会取多个partition的数据的,但由于每个分区的内部是有序的,所以在一个poll到的分区中,可以按照线性处理,也可以多线程按照分区处理。

在手动持久化offset的时候可选的粒度:

1、每条提交:性能低下可靠性高一点

2、按分区提交 :最常用(如果多线程,每个线程处理对应partition的数据,处理完提交的时候,是不会产生覆盖问题的,因为kafka维护offset的粒度是partition而不是topic。)

所以即便消费partition0的线程消费成功,partition1的线程消费失败,也不会产生两个分区之前offset的冲突。

注意:如果想多线程处理多分区,可以用一个语义:一个job 启动(比如一次起5个线程,这5个线程加在一起叫一个job), 一次job用多线程并行处理分区,且job应该是串行的。

说白了,就是我在起多线程处理的时候,比如起了5个线程,必须要等五个线程都运行完(成功或者失败都可以,但要运行完),再向下运行。

小技巧:如何获取一批数据某个分区的最后一条的offset? 由于kafka的分区内拿到的数据是有序的,所以我们用最后一条的offset就可以,这样就可以通过流处理等,对这些数据进行加工。

3、批次提交:可能会重复消费数据,但是效率比较高。

KAFKA的磁盘持久化机制:

kafka单机:kafka单机一般代表一个broker,broker是一个application,是一个用户程序。

作为中间件,kafka偏向于mq的,由于broker是运行在jvm中,所有的消息进入broker的时候都是进入jvm内存的。所以必然会牵扯到持久化的问题。

在一条数据进入broker经过处理后,会将消息传入kafka kernel(kafka内核)。

kafk kernel中存在所谓的page cache(缓存页),当broker将数据写入pagecache后,keneral会返回broker一个返回值,broker则认为写入磁盘成功。但是keneral不会立即像磁盘写入,因为如果如果每来一条数据就刷一次磁盘效率就非常低(可以调整粒度)。

kafka的数据必定会写入磁盘,而不像redis 可以关闭持久化。

由于顺序读写必定快于随机读写,所以kafka会将消息在磁盘上进行顺序读写(消息存在临近位置,类似拉链),kafka在存储消息的时候,还会存储一份索引文件,记录每段消息起始的偏移量,保证我们在查找消息的时候可以快速寻址,降低便利复杂度。

由于我们很可能会需要通过时间进行查询与遍历,所以kafka存储了一份timestamp索引,通过timestamp索引查询到对应消息或消息段的偏移量。

0拷贝机制:由于kafka是偏向mq设计的,所以数据producer怎么给我,我就应该怎么给consumer发回去,不需要broker做任何处理。

consumer在发起poll请求的时候,流程为先发给我kernel,kernel将请求交给broker进行处理。broker处理好offset之类的信息后,通过kernel找到对应数据的磁盘位置,然后通过kernel的系统调用(sendfile()获取I/O流),将数据直接发送给consumer,不需要再将数据copy进jvm(broker),提高了速度。

ack配置与集群复制:针对单个broker,用于处理发送时不一样的可靠性需求

在kafka producer发送的时候,可以通过acks配置来控制发送语义,调节发送可靠性。

1:当数据持久化完,认定为完成发送。(也会出现服务直接挂掉的情况,所以会出现水位的问题 water mark 我们只能消费到多个broker中同步较少,水位较低的消息,因为可能会出现同步延迟,leader上接受了大量消息但是未能及时同步给follower)

0:producer发出了,既完成发送(有可能丢数据,磁盘有可能还没持久化呢)

-1:不仅持久化完,还需要备机备份好,才算完成发送

为了解决分区可靠性问题,我们需要每个分区多个节点,但这会引入更多的问题。

当我们对一个partition做了使用了3个broker节点,1个leader,2个follower。

kafka是没有读写分离的,所以kafka producer只能向leader推送数据。所以我们需要保证leader和follower的一致性。

1、强一致性:当producer生产一条数据后,必须leader向两个follower同步这条数据后,才返回ack 表示生产完成,这样能保证consumer在集群中消费任何一个节点,都能消费到全部partition的消息。要求:这种情况要求所有节点全部存活,如果有一个节点挂掉,整个集群就会停止服务(因为某个节点挂掉后,就无法向他同步数据,挂掉的节点再启动后数据肯定不同步,无法满足consumer消费所有消息的要求)。

2、最终一致性(最常用):过半通过,如果我们定义了奇数个节点,例如三个,如果有一个节点挂掉,但是另外两个节点能快速完成同步,就返回ack。

2-2:ISR(in sync replicas):通过连通性/活跃性,决定要多少个节点同步完成后返回ack,而不是写死过半,较为灵活。

所以在ack为-1的时候,我们听过ISR数量可以隐性的保证多个broker(ISR集合内)的消息进度是一致的

备注:ISR(in sync replicas):通过连通性/活跃性

OSR(outof-sync replicas)超过多长时间没有心跳(默认是10s)

AR(assigned replicas):面向分区的副本集合,在创建topic的时候,给出了分区的副本数,则controller在创建的时候就已经分配处了broker和分区的对应关系,并得到了该分区的broker集合。

AR=ISR+OSR

e.g: 给partition分配了broker1和broker2,则AR集合就是broker 1,2 如果broker1掉线了,那么ISR集合就是broker2,OSR集合就是broker1

如何确定ISR和OSR:假设一个learder 和两个follower1 follower2,

当一条消息1传入leader的时候,leader需要在10S内向follower1,follower2进行同步,10s内同步完成的为ISR,未成功的被置为OSR,那么返回的ISR ACK为2,所以不一定要全部节点都同步完才算发送完成,只要ISR集合中满足一定数量就可以。

producer可能会向kafka生产500条数据,但是consumer可能只能消费300条,也可能要求consumer将全部500条都消费完。

如何理解上面这句话?

kafka在消费数据的时候,能消费多少取决于kafka集群,而不取决于producer生产了多少,producer和consumer没有必然的耦合性。

LEO:LogEndOffset ,producer向leader broker发送了多少条数据,

High Water Mark:最高水位,consumer能从集群中消费多少消息。

当broker的ACK是1的时候,producer生产数据只需要向leader发送,leader完成持久化就可以完成整个发送周期(producer获得ack)。

但可能会出现这些数据并未能向follower进行同步。这事prodecer可以继续向leader发送消息,当前leader接受了多少消息,叫做LEO。

但是Consumer能从leader poll到的数据仅仅是完成同步的那部分数据,HW,所以在ACK=1的时候,HW和LEO可能会不一样。

为什么要这么设计?为了保证消费的可靠性,避免消费了6但是没消费5,产生更大的问题,所以干脆丢掉没同步的部分。

吐槽:这么设计其实就是kafka作为中间件 它不背锅,我就这么设计了,把控制的逻辑扔给写业务的程序员了。你怎么控制就是你自己的问题了,反正我就这么设计的,慢慢5,6数据被同步后,理论上也是能消费到全部数据的(侥幸心理)。如果leader挂了,那么就会生成新的leader,再来一条数据肯定就写进去了,5,6两条没同步的数据就丢了,所以还是会出问题。

  大数据 最新文章
实现Kafka至少消费一次
亚马逊云科技:还在苦于ETL?Zero ETL的时代
初探MapReduce
【SpringBoot框架篇】32.基于注解+redis实现
Elasticsearch:如何减少 Elasticsearch 集
Go redis操作
Redis面试题
专题五 Redis高并发场景
基于GBase8s和Calcite的多数据源查询
Redis——底层数据结构原理
上一篇文章      下一篇文章      查看所有文章
加:2021-07-30 12:48:25  更:2021-07-30 12:49:32 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/22 23:35:34-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码