Kafka简介与使用场景

Kafka简介与使用场景

为什么要引入消息队列

假设我们现在有一个在线购物网站,用户在网站上下单购买商品,网站需要向订单系统发送订单信息,订单系统再根据订单信息进行处理和发货,如果我们不引入任何中间件,网站和订单系统之间的交互可能会出现以下问题:

  1. 高并发流量导致宕机:当用户下单并提交订单后,网站需要立即向订单系统发送订单信息,如果此时用户量很大,会导致高并发流量,容易使网站宕机或响应时间过长,影响用户体验和业务运营。
  2. 系统耦合度高:如果网站直接调用订单系统的 API 来处理订单信息,会导致网站和订单系统之间的耦合度高,使得系统难以扩展和维护。
  3. 单点故障:如果网站和订单系统之间采用同步的方式进行交互,会使得网站和订单系统之间出现单点故障,当其中一个系统出现问题时,整个系统都将受到影响。
  4. 数据不一致:如果网站和订单系统之间采用异步的方式进行交互,但是没有消息队列的支持,可能会导致数据不一致的问题。例如,当网站发送订单信息到订单系统后,由于网络问题或者订单系统出现问题,订单系统无法处理订单信息,会导致订单信息丢失或者延迟,从而使得网站和订单系统之间的数据不一致。

非消息队列

为了解决以上问题,我们可以考虑将用户下单的订单信息写入消息队列中,订单系统异步地从队列中读取订单信息,并进行处理和发货。由于订单系统是异步处理订单信息,因此可以避免网站因为高并发流量而宕机,同时也降低了网站和订单系统之间的耦合度,提高了系统的可扩展性和可维护性。

此外,如果有多个订单系统需要处理订单信息,也可以通过消息队列来实现数据分发和共享,将订单信息写入消息队列中,各个订单系统可以异步地从队列中读取订单信息进行处理,提高了订单信息的利用率和共享效率。

消息队列

通过以上实例,可以看出消息队列在电商购物场景中的作用和必要性,包括解耦、流量削峰、异步处理、数据分发和共享等。同时,消息队列也可以通过水平扩展和容错机制来提高系统的可伸缩性和可靠性,是实现大规模高并发系统的重要技术手段之一。

Kafka是什么

Kafka是一个由Scala和Java编写的企业级的消息发布和订阅系统,最早是由Linkedin公司开发,最终开源到Apache软件基金会的项目。Kafka是一个分布式的,支持分区的,多副本的和多订阅者的高吞吐量的消息队列系统,被广泛应用在应用解耦、异步处理、限流削峰和消息驱动等场景。

Kafka的架构和基本概念

Kafka架构

  1. Topic:消息的类别,主要用于对消息进行逻辑上的区分,每条发送到Kafka集群的消息都需要有一个指定的Topic,消费者根据Topic对指定的消息进行消费,类似于关系数据库的表,需要获取什么topic的信息就监听对应的topic即可

    Topic

  2. Partition:消息的分区,Partition是一个物理上的概念,相当于一个文件夹,Kafka会为每个topic的每个分区创建一个文件夹,一个Topic的消息会存储在一个或者多个Partition中,Topic 是一个逻辑概念,而 Partition 是分布式存储单元。

    Partition

  3. Producer:Producer是Kafka中的消息生产者,主要用于生产带有特定Topic的消息,生产者生产的消息通过Topic进行归类,保存在Kafka 集群的Broker上,具体的是保存在指定的partition 的目录下,以Segment的方式(.log文件和.index文件)进行存储

  4. Consumer:Consumer是Kafka中的消费者,主要用于消费指定Topic的消息,Consumer是通过主动拉取的方式从Kafka集群中消费消息,消费者一定属于某一个特定的消费组。

  5. Message:Message是实际发送和订阅的信息是实际载体,Producer发送到Kafka集群中的每条消息,都被Kafka包装成了一个Message对象,之后再存储在磁盘中,而不是直接存储的。

  6. Consumer Group:每个Consumer属于一个特定的Consumer Group,新建Consumer的时候需要指定对应的Consumer Group ID

    我们在消费数据时会在代码里面指定一个group.id,这个 id 代表的是消费组的名字,而且这个 group.id就算不设置,系统也会默认设置:

    1
    conf.setProperty("group.id","tellYourDream")

    我们所熟知的一些消息系统一般来说会这样设计,就是只要有一个消费者去消费了消息系统里面的数据,那么其余所有的消费者都不能再去消费这个数据。可是 Kafka 并不是这样,比如现在 ConsumerA 去消费了一个 TopicA 里面的数据:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    consumerA:
    group.id = a
    consumerB:
    group.id = a

    consumerC:
    group.id = b
    consumerD:
    group.id = b

    再让 ConsumerB 也去消费 TopicA 的数据,它是消费不到了,但是我们在 ConsumerC 中重新指定一个另外的 group.id,ConsumerC 是可以消费到 TopicA 的数据的,而 ConsumerD 也是消费不到的,所以在 Kafka 中,不同组可有唯一的一个消费者去消费同一主题的数据。

    消费者组就是让多个消费者并行消费信息而存在的,而且它们不会消费到同一个消息。

  7. Broker:Kafka集群中的服务实例,也称之为节点,每个Kafka集群包含一个或者多个Broker(一个Broker就是一个服务器或节点)

  8. Segment:一个partition当中存在多个segment文件段(分段存储),每个Segment分为两部分,.log文件和 .index 文件,其中 .index 文件是索引文件,主要用于快速查询.log 文件当中数据的偏移量位置

    1. .log文件:存放Message的数据文件,在Kafka中把数据文件就叫做日志文件。一个分区下面默认有n多个.log文件(分段存储)。一个.log文件大默认1G,消息会不断追加在.log文件中,当.log文件的大小超过1G的时候,会自动新建一个新的.log文件
    2. .index文件:存放.log文件的索引数据,每个.index文件有一个对应同名的.log文件。

Kafka高性能的实现

零拷贝

传统的拷贝过程

  1. 操作系统将数据从磁盘文件中读取到内核空间的页面缓存;
  2. 应用程序将数据从内核空间读入用户空间缓冲区;
  3. 应用程序将读到数据写回内核空间并放入socket缓冲区;
  4. 操作系统将数据从socket缓冲区复制到网卡接口,此时数据才能通过网络发送。

这个过程涉及到 4 次上下文切换以及 4 次数据的复制,并且有两次复制操作是由 CPU 完成。但是这个过程中,数据完全没有进行变化,仅仅是从磁盘复制到网卡缓冲区。

在这种情况下,如果能够减少用户空间与内核空间之间的切换,即去掉2和3流程,比传统性能高。这样子首先数据被从磁盘读取到 Read Buffer 中,然后再发送到 Socket Buffer,最后才发送到网卡。虽然减少了用户空间和内核空间之间的数据交换,但依然存在多次数据复制。

可以看出性能都消耗在彼此之间的数据复制过程中,那么进一步减少数据的复制过程,或者干脆没有数据复制这一过程,性能会明显增强。这就是DMA技术了。

传统拷贝过程)

DMA技术

DMA(Direct Memory Access,直接存储器访问) 是所有现代电脑的重要特色,它允许不同速度的硬件装置来沟通,而不需要依赖于CPU的大量中断负载。否则,CPU 需要从来源把每一片段的资料复制到暂存器,然后把它们再次写回到新的地方。在这个时间中,CPU 对于其他的工作来说就无法使用。

传统的内存访问,所有的请求都会发送到 CPU ,然后再由 CPU 来完成相关调度工作。当 DMA 技术的出现,数据文件在各个层之间的传输,则可以直接绕过CPU,使得外围设备可以通过DMA控制器直接访问内存。与此同时,CPU可以继续执行程序。

现在电脑中很多硬件都是支持 DMA 技术的,这里面其中就包括我们此处用到的网卡。

零拷贝技术

有了 DMA 技术的,通过网卡直接去访问系统的内存,就可以实现现绝对的零拷贝了。这样就可以最大程度提高传输性能。通过“零拷贝”技术,我们可以去掉那些没必要的数据复制操作, 同时也会减少上下文切换次数。

DMA

顺序写

Kafka是将消息记录持久化到本地磁盘中的,有的人会认为磁盘读写性能差,可能会对Kafka性能如何保证提岀质疑。实际上不管是内存还是磁盘,快或慢关键在于寻址的方式,磁盘分为顺序读写与随机读写,内存也一样分为顺序读写与随机读写。基于磁盘的随机读写确实很慢,但磁盘的顺序读写性能却很高,一般而言要高岀磁盘随机读写三个数量级,一些情况下磁盘顺序读写性能甚至要高于内存随机读写。

磁盘的顺序读写是磁盘使用模式中最有规律的,并且操作系统也对这种模式做了大 量优化,Kafka就是使用了磁盘顺序读写来提升的性能。Kafka的message是不断 追加到本地磁盘文件末尾的,而不是随机的写入,这使得Kafka写入吞吐量得到了显著提升。

具体来说,kafka的日志文件都是一个 log entrie 序列,每个 log entrie 包含一个 4 字节整型数值(值为 N+5),1 个字节的 “magic value”,4 个字节的 CRC 校验码,其后跟 N 个字节的消息体。每条消息都有一个当前 Partition 下唯一的 64 字节的 offset,它指明了这条消息的起始位置。磁盘上存储的消息格式如下:

1
2
3
4
message length4 bytes (value: 1+4+n)
"magic" value1 byte
crc : 4 bytes
payload : n bytes

这个 log entries 并非由一个文件构成,而是分成多个 segment,每个 segment 以该 segment 第一条消息的 offset 命名并以“.kafka”为后缀。另外会有一个索引文件,它标明了每个 segment 下包含的 log entry 的 offset 范围,如下图所示:

log

因为每条消息都被 append 到该 Partition 中,属于顺序写磁盘,因此效率非常高。

append

应用场景

Kafka 作为一款高性能的分布式消息队列系统,广泛应用于各种大数据场景中。下面列举几个常见的应用场景:

  1. 日志收集:在分布式系统中,日志是非常重要的一部分,通过将日志写入 Kafka 中,可以方便地进行收集、存储、处理和分析。例如,通过将应用程序的日志写入 Kafka 中,可以实现集中式的日志收集和分析,帮助开发人员快速定位和解决问题。
  2. 流式处理:Kafka 可以实现流式数据的收集、存储和分析,支持实时的数据流处理和实时数据的转换。例如,将 IoT 设备的数据写入 Kafka 中,通过流处理工具(例如 Apache Flink)进行实时的数据分析和计算,可以帮助企业更好地了解用户需求和行为,优化产品和服务。
  3. 消息系统:Kafka 可以作为消息系统,实现异步通信和消息传递,将应用程序解耦并提高系统的可伸缩性。例如,在一个微服务架构中,可以使用 Kafka 作为消息中间件,将各个服务之间的消息进行异步通信和传递,实现服务之间的解耦和流量削峰。
  4. 测试数据生成:在进行系统测试和性能测试时,通常需要生成大量的测试数据。通过将测试数据写入 Kafka 中,可以方便地生成大规模的测试数据,提高测试效率和测试覆盖率。
  5. 实时监控和报警:通过将系统的监控数据写入 Kafka 中,可以方便地进行实时监控和报警,及时发现和解决系统问题。例如,在分布式系统中,通过将各个节点的监控数据写入 Kafka 中,可以实时监控系统的健康状态,及时发现故障并进行处理。

Kafka简介与使用场景
https://shouldbeldy.github.io/2023/03/31/Kafka简介与使用场景/
作者
Daoyu Lei
发布于
2023年3月31日
许可协议