当前位置: 移动技术网 > 网络运营>服务器>Linux > Kafka源码解析之Kafka高性能的秘密概述(一)

Kafka源码解析之Kafka高性能的秘密概述(一)

2020年07月23日  | 移动技术网网络运营  | 我要评论

1. Kafka是什么

1.1 为什么会有消息系统

image-20200721213709607

如上图所示,我们有一个华为手机的生产线,我们手机的主板使用的是机械手臂生产的然后通过人工搬运的方式送到组装车间,进行手机的组装,最后去往我们的质检车间,这个时候我们应该可以发现一个问题,人工搬运过去有点慢啊,我们应该引入传送带,传过去,这样就省掉了人工搬运的过程

image-20200721204150955

这样我们就引入了传送带提高了效率,运行了几天我们又发现了一个问题,机械手臂生产的很快,组装比较慢,质检比较快,这样就到了生产效率不一致的问题

这个时候我们应该怎么解决这个问题呢,我们是不是在每个车间引入一个仓库啊,生产出来的中间东西都放到我们每个车间的仓库里面,这样就可以根据我们的生产速率自己去拿半成品来生产了

image-20200721204527403

那么这个时候我们还有问题吗,其实还是存在的,器械手臂生产的很快,组装车间也行,质检比较慢 毕竟是人工啊,这个时候我们应该怎么做呢,我们多开几个质检车间不就好了吗

image-20200721205050537

那么这个仓库其实就是我们的消息系统,俗称的消息中间件,他起到了一个缓冲的作用,可以做到削峰填谷

1.2 常用的消息中间件

  • RabbbitMQ
  • ActiveMQ
  • Kafka

1.3 Kakfa概述

image-20200721205334829

Kafka是由Apache软件基金会开发的一个开源流处理平台,由ScalaJava编写。Kafka是一种高吞吐量的分布式发布订阅消息系统,它可以处理消费者在网站中的所有动作流数据。 这种动作(网页浏览,搜索和其他用户的行动)是在现代网络上的许多社会功能的一个关键因素。 这些数据通常是由于吞吐量的要求而通过处理日志和日志聚合来解决。 对于像Hadoop一样的日志数据和离线分析系统,但又要求实时处理的限制,这是一个可行的解决方案。Kafka的目的是通过Hadoop的并行加载机制来统一线上和离线的消息处理,也是为了通过集群来提供实时的消息。

kafka的架构师jay kreps对于kafka的名称由来是这样讲的,由于jay kreps非常喜欢franz kafka,并且觉得kafka这个名字很酷,因此取了个和消息传递系统完全不相干的名称kafka,该名字并没有特别的含义。

kafka的诞生,是为了解决linkedin的数据管道问题,起初linkedin采用了ActiveMQ来进行数据交换,大约是在2010年前后,那时的ActiveMQ还远远无法满足linkedin对数据传递系统的要求,经常由于各种缺陷而导致消息阻塞或者服务无法正常访问,为了能够解决这个问题,linkedin决定研发自己的消息传递系统,当时linkedin的首席架构师jay kreps便开始组织团队进行消息传递系统的研发;

Kafka是一个开源的高吞吐量分布式发布订阅消息系统

让我们来瞻仰一下Big Old

image-20200721210930324

2. Kafka核心概念

image-20200721210338254

我们的主题就相当于oracle中的,分区就当于Oracle中的,消息就相当于我们的数据

3. Kafka基础架构

image-20200721212117601

3.1 我们看到了架构图,那么有一些问题? 谁是Controller? 高可用怎么保证的?

  1. 我们应该知道zookeeper一个节点只能存在一个,那么我们每启动一台Broker就会去zookeeper创建一个目录,谁能创建成功这个Node谁就是Controller,别人启动的时候发现已经有大哥了,那么我就只能当小弟了,小弟不愿意当啊,期待着卧薪尝胆,等老大死了我就有希望了
  2. 假如Controller死掉了,然后其他的Follower一直监听着呢,发现老大死了,我要做老大,就都去Zookeeper都去创建Node谁创建成功谁就是Controller

3.2 Controller比较Follower有什么不同呢?

Controller是老大,那么老大是那么好当的吗? Controller创建成功节点以后,就会一直监听着zookeeper中的元数据,假如元数据有变化,就会把这些元数据发送给其他的follower,每个follower中都有一份元数据

区别就是谁主动去监听元数据

3.3 那么我们知道了这些,那么既然有分区的概念,那么Kafka是如何保证数据安全的呢?

image-20200721213622348

我们知道kafka中有主题的概念,主题下有分区,那么单分区的话,一台节点宕机了怎么办,假设第二台Broker宕机了,那么我们的数据就丢了啊,还如何保证数据安全呢

这个时候kafka就引入了一个分区副本的概念和hdfs的block是一样的,不是很了解hdfs的可以看我的hdfs源码解析,分区副本的意思也就是如下图

image-20200721214116811

Kafka的副本概念和HDFS的副本概念还是有些不同的,例如hdfs可以读取所有的block包含副本,但是kafka不是,对外工作的只有分区leader

Kafka中的副本有一个leader的概念,leader是对外进行服务的分区,follower只是从leader进行拉取数据,保证机器宕机后,数据可用,分区存在哪个机器上是有负载均衡的概念,假如两个分区的leader在一个机器,那么压力不是很大吗,所以会有负载均衡的概念

3.4 核心概念

  • Producer 发送消息
  • Consumer 订阅消息
  • Broker kafka的节点
  • Controller kafka服务器的主节点
  • Follower kafka服务的从节点
  • Topic 主题,类似数据库里面的表
  • Partition 分区,一个主题可以有多个分区,类似于数据库里面的分区
  • Replica 副本,一个,为了保证数据安全,每个Partiiton可以设置多个副本 (leader replica folllower replica)
  • Message 消息,也就是数据存储在分区里
  • Offset 消息存储进度/消费者的消费进度

我感觉Kafka是对等架构,因为每个broker的作用几乎是一样的,并且谁都可以做controller

思考 kafka是一个把性能用到极致的框架,是一个支持高并发,高吞吐,高可用的分布式消息系统,假如我们自己来设计,我们会怎么设计他的服务端,应该从哪些角度去思考呢?

4. Kafka的服务端设计

4.1 服务端请求应该如何处理

4.1.1 顺序处理请求

while (true){
    Request req = accept(connection);
    handle(req);
}

这样有什么不好的地方吗?

并发很低,会很慢,每个请求必须等待前一个请求处理完了以后才会得到处理,吞吐量太差!

4.1.2 异步处理

while (true){
    Request req = accept(connection);
    Thread thread = new Thread(()->{handle(req);});
    thread.start();
}

每个请求都会创建一个线程去处理,请求不会阻塞,但是每个请求都创建一个线程开销太大,会内存爆炸

4.2 NIO 处理

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TUpzJ4VQ-1595428376232)(https://gitee.com/wuqingzhi128/blogImg/raw/master//image-20200721220817141.png)]

  1. 生产者发送连接请求给我们的多路复用器,然后把数据再去多路复用器注册读操作
  2. 这个时候我们对应的是读操作,首先把这个写入我们的Message队列
  3. 然后我们的线程池不停地去轮询我们的队列进行处理,将我们的响应再给多路复用器

那么我们为什么要设计这个队列呢?

其实就起到了一个缓冲的概念,这个队列就是起到了缓冲的作用,缓解压力

这样真的好吗?

其实还有瓶颈,我们只有一个Selector这个时候并不好,压力太大了

4.2.1 引入Reactor设计模式

image-20200721222113261

这个时候引入了多个selcetor

  1. 首先Producer先连接
  2. Acceptor线程会把请求交给selectors进行请求
  3. 剩下的都一样了

4.2.2 kafka的三层网络架构

略…

哈哈哈…慢慢来

4.3 Kafka如何保证高性能

4.3.1 顺序写磁盘

Kakfa是将消息记录持久化到本地磁盘中的,一般人会认为磁盘读写性能差,对kafka性能如何保证提出质疑

实际上不管是内存还是磁盘,快或慢关键在于寻址的方式,磁盘分为顺序读写与随机读写,内存也一样分为顺序读写与随机读写

基于磁盘的随机读写确实很慢,但磁盘的顺序读写性能却很高,一般而言要高出磁盘随机读写三个数量级,一些情况下磁盘顺序读写性能甚至高于内存随机读写

image-20200722211505034

4.3.2 跳表设计

我们的kafka最终数据会存储在磁盘上,那么存储的方式也有一些好的方式,日志存储方式像下面这样

image-20200722211850087

其中index和log的文件名都是一样的,成对出现的,这个文件名是以log文件里的第一条消息的offset来进行命名的,如下第一个文件的文件名0000000000000103909,代表着这个文件里的第一个消息的offset就是103909,也就是说第二条消息的偏移量就是103910,log文件的大小为1G

这个时候我们假如需要寻找在某个偏移量之后消费怎么办,直接读取所有的log文件吗,那么是不是太慢了

image-20200722213053573

这个时候就引入了跳表的概念,可以根据我们的偏移量进行快速查询所在文件,那么这个时候结束了吗?并没有

4.3.3 稀疏索引机制

image-20200722215632032

假如我们假如要找到1000的偏移量,先去稀疏索引去找,这个文件是存储在内存里面的也有,然后就找到了

image-20200722215906904

4.3.4 零拷贝

image-20200722220348064

我们先看这个不是零拷贝的时候,我们发现在消费数据的时候有两次数据拷贝的现象,先从物理内存拷贝到jvm内存,然后再把数据拷贝到网卡缓冲区,再交给网卡消费数据,那么这两个拷贝是否真的有必要呢,假如我们要是直接读到数据就发给网卡不就好了吗,kafka调用了os的底层api实现了这个事情

image-20200722221101656

4.4 Kafka 服务端设计总结

  1. 高并发,高性能的网络架构
  2. 顺序读写
  3. 跳表设计
  4. 稀疏索引
  5. 零拷贝

5. Producer端设计

  1. 消息批处理,减少连接数
  2. 封装同一服务器请求,减少网络传输
  3. 生产者内存池 减少full gc

6. Consumer端设计

P2P模型:也成为点对点模型,指同一条消息只能被一个消费者消费,也就是说一个消息如果被这个消费者消费了,其他的消费者就都不能消费了,传统的消息系统就是这种模式

发布订阅模型:允许消息被多个Consumer消费,但是一个Consumer需要订阅主题的所有分区

image-20200722222744966

在0.8版本之前偏移量是存储在zookeeper

在0.8以后,改为kafka自己存储偏移量,有一个主题topic是conusmer offsets 默认的partitions是50 ,因为kafka自己就是高并发而且自己就是高频读写就改为了存储自己的topic并且zookeeper并发性能不够

本文地址:https://blog.csdn.net/weixin_43704599/article/details/107525150

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网