【JAVA】Kafka

1.基本架构

在这里插入图片描述

⑴Producer生产者
⑵Kafka Cluster
  • Broker kafka实例,每个服务器上有一个或多个kafka的实例
  • Topic 消息的主题,每个broker上都可以创建多个topic
  • Partition Topic的分区,每个topic可以有多个分区,分区的作用是做负载,提高kafka的吞吐量。同一个topic在不同的分区的数据是不重复的(重点),同一分区内的数据是按发送顺序有序的,partition的表现形式就是一个一个的文件夹,为什么要做分区:
    • 方便扩展
    • 提高并发
  • Replication:每一个分区都有多个副本,副本的作用是做备胎。当主分区(Leader)故障的时候会选择一个备胎(Follower)上位,成为Leader。在kafka中默认副本的最大数量是10个,且副本的数量不能大于Broker的数量,follower和leader绝对是在不同的机器,同一机器对同一个分区也只可能存放一个副本(包括自己)
⑶Consumer
  • Consumer 消费者
  • Consumer Group
⑷Zookeeper

保存集群的的元信息,来保证系统的可用性

2.发送数据

⑴基本流程

Producer在写入数据的时候永远的找leader,不会直接将数据写入follower在这里插入图片描述
如果某个topic有多个partition,producer又怎么知道该将数据发往哪个partition呢?kafka中有几个原则:

  • partition在写入的时候可以指定需要写入的partition,如果有指定,则写入对应的partition
  • 如果没有指定partition,但是设置了数据的key,则会根据key的值hash出一个partition
  • 如果既没指定partition,又没有设置key,则会轮询选出一个partition
    如果往不存在的topic写数据,kafka会自动创建topic,分区和副本的数量根据默认配置都是1
⑵发送端消息确认机制(从发送者到kafka)

在生产者向队列写入数据的时候可以设置参数来确定是否确认kafka接收到数据,这个参数可设置的值为0、1、all:

  • 0代表producer往集群发送数据不需要等到集群的返回,不确保消息发送成功。安全性最低但是效率最高
  • 代表producer往集群发送数据只要leader应答就可以发送下一条,只确保leader发送成功
  • all代表producer往集群发送数据需要所有的follower都完成从leader的同步才会发送下一条,确保leader发送成功和所有的副本都完成备份。安全性最高,但是效率最低

3.持久化数据

每个partition的文件夹下面会有多组segment文件,每组segment文件又包含.index文件、.log文件、.timeindex文件(早期版本中没有)三个文件, log文件就实际是存储message的地方,而index和timeindex文件为索引文件,用于检索消息。无论消息是否被消费,kafka都会保存所有的消息。那对于旧数据有什么删除策略呢:

  • 基于时间,默认配置是168小时(7天)
  • 基于大小,默认配置是1073741824

kafka读取特定消息的时间复杂度是O(1),所以这里删除过期的文件并不会提高kafka的性能!

4.Message结构

  • offset:offset是一个占8byte的有序id号,它可以唯一确定每条消息在parition内的位置!
  • 消息大小:消息大小占用4byte,用于描述消息的大小。
    消息体:消息体存放的是实际的消息数据(被压缩过),占用的空间根据具体的消息而不一样。

5.消费数据

在这里插入图片描述
消息存储在log文件后,消费者就可以进行消费了,消费者找leader去拉取消息。
多个消费者可以组成一个消费者组(consumer group),每个消费者组都有一个组id,同一组的消费者可以消费同一topic下不同分区的数据,但是不能消费同一个分区的数据。

  • 组内的消费者小于partition数量,会出现某个消费者消费多个partition数据
  • 消费者组的消费者多于partition的数量,多出来的消费者不消费任何partition的数据
  • 在实际的应用中,建议消费者组的consumer的数量与partition的数量一致

6.为什么Kafka速度这么快

⑴写入数据
  • 顺序写入
    硬盘最讨厌随机I/O,最喜欢顺序I/O。为了提高读写硬盘的速度,Kafka就是使用顺序I/O
  • Memory Mapped Files
    内存映射文件,通过mmap,进程像读写硬盘一样读写内存(当然是虚拟机内存),也不必关心内存的大小有虚拟内存为我们兜底。使用这种方式可以获取很大的I/O提升,省去了用户空间到内核空间复制的开销,但是也不太可靠,写到mmap中的数据并没有被真正的写到硬盘,操作系统会在程序主动调用flush的时候才把数据真正的写到硬盘。Kafka提供了一个参数producer.type来控制是不是主动flush
⑵读取数据
  • 基于sendfile实现Zero Copy
  • 批量压缩 在很多情况下,系统的瓶颈不是CPU或磁盘,而是网络IO
    • Kafka使用了批量压缩,即将多个消息一起压缩而不是单个消息压缩
    • Kafka支持多种压缩协议,包括Gzip和Snappy压缩协议

7.kafka中的zookeeper

在这里插入图片描述
kafka集群的 broker,和 Consumer 都需要连接 Zookeeper,Producer 直接连接 Broker。

⑴Broker注册

Broker在启动时会到Zookeeper上进行注册,即到/brokers/ids下创建属于自己的节点(临时节点)/brokers/ids/[0…N],然后每个Broker就会将自己的IP地址和端口信息记录到该节点中去,Broker宕机,则节点也会被自动删除。

⑵Topic注册

同一个Topic的消息会被分成多个分区并将其分布在多个Broker上,这些分区信息及与Broker的对应关系也都是由Zookeeper在维护,由专门的节点来记录,如:/brokers/topics
Kafka中每个Topic都会以/brokers/topics/[topic]的形式被记录,如/brokers/topics/login,Broker服务器启动后,会到对应Topic节点上注册自己的Broker ID并写入针对该Topic的分区总数,如/brokers/topics/login/3->2,这个节点表示Broker ID为3的一个Broker服务器,对于"login"这个Topic的消息,提供了2个分区进行消息存储,同样,这个分区节点也是临时节点。

⑶生产者负载均衡

由于同一个Topic消息会被分区并将其分布在多个Broker上,因此,生产者需要将消息合理地发送到这些分布式的Broker上,那么如何实现生产者的负载均衡,Kafka支持传统的四层负载均衡,也支持Zookeeper方式实现负载均衡。

  • 四层负载均衡,根据生产者的IP地址和端口来为其确定一个相关联的Broker。这不是严格意义的负载均衡
  • 使用Zookeeper进行负载均衡,由于每个Broker启动时,都会完成Broker注册过程,生产者会通过该节点的变化来动态地感知到Broker服务器列表的变更,这样就可以实现动态的负载均衡机制。
⑷消费者负载均衡

Kafka中的消费者同样需要进行负载均衡来实现多个消费者合理地从对应的Broker服务器上接收消息,每个消费者分组包含若干消费者,每条消息都只会发送给分组中的一个消费者,不同的消费者分组消费自己特定的Topic下面的消息,互不干扰。

⑸分区 与 消费者 的关系

在Kafka中,规定了每个消息分区 只能被同组的一个消费者进行消费,因此,需要在 Zookeeper 上记录 消息分区 与 Consumer 之间的关系,每个消费者一旦确定了对一个消息分区的消费权力,需要将其Consumer ID 写入到 Zookeeper 对应消息分区的临时节点上,例如:
/consumers/[group_id]/owners/[topic]/[broker_id-partition_id]
其中,[broker_id-partition_id]就是一个 消息分区 的标识,节点内容就是该 消息分区 上 消费者的Consumer ID。

⑹消息 消费进度Offset 记录

需要定时地将分区消息的消费进度Offset记录到Zookeeper上,以便在该消费者进行重启或者其他消费者重新接管该消息分区的消息消费后,能够从之前的进度开始继续进行消息消费。Offset在Zookeeper中由一个专门节点进行记录,其节点路径为:
/consumers/[group_id]/offsets/[topic]/[broker_id-partition_id]
节点内容就是Offset的值。

⑺消费者注册

注册到消费者分组。每个消费者服务器启动时,都会到Zookeeper的指定节点下创建一个属于自己的消费者节点,例如/consumers/[group_id]/ids/[consumer_id],完成节点创建后,消费者就会将自己订阅的Topic信息写入该临时节点。
对 消费者分组 中的 消费者 的变化注册监听。每个 消费者 都需要关注所属 消费者分组 中其他消费者服务器的变化情况,即对/consumers/[group_id]/ids节点注册子节点变化的Watcher监听,一旦发现消费者新增或减少,就触发消费者的负载均衡。
对Broker服务器变化注册监听。消费者需要对/broker/ids/[0-N]中的节点进行监听,如果发现Broker服务器列表发生变化,那么就根据具体情况来决定是否需要进行消费者负载均衡。
进行消费者负载均衡。为了让同一个Topic下不同分区的消息尽量均衡地被多个 消费者 消费而进行 消费者 与 消息 分区分配的过程,通常,对于一个消费者分组,如果组内的消费者服务器发生变更或Broker服务器发生变更,会发出消费者负载均衡。

Java Kafka是一个用于构建实时数据流应用程序和数据管道的开源分布式流处理平台。它使用高吞吐量、低延迟的方式来处理大规模的数据流。Kafka可以被用于构建实时流数据管道,以及处理实时数据流应用程序的管理和处理。 举个例子,可以使用拦截器来实现按量付费的功能。可以编写一个实现了ProducerInterceptor接口的拦截器类,在发送消息的时候触发onSend方法,在这个方法中可以实现扣费的逻辑。然后通过配置,在Kafka生产者中使用这个拦截器类来实现按量付费的功能。具体的代码示例如下: ```java public class ChargingInterceptor implements ProducerInterceptor<String, String> { @Override public ProducerRecord<String, String> onSend(ProducerRecord<String, String> record) { System.out.println("1分钱1条消息,不管那么多反正先扣钱"); return record; } @Override public void onAcknowledgement(RecordMetadata metadata, Exception exception) { System.out.println("消息被服务端接收啦"); } @Override public void close() { System.out.println("生产者关闭了"); } @Override public void configure(Map<String, ?> configs) { System.out.println("configure..."); } } ``` 另外,在Kafka中,消息有一个key和一个value,可以通过指定发送的主题、消息的key和消息的value来发送消息。具体的示例代码如下: ```java Properties prop = new Properties(); prop.put("bootstrap.servers", "192.168.221.131:9092"); prop.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer"); prop.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer"); prop.put("group.id", "con-1"); prop.put("auto.offset.reset", "latest"); prop.put("auto.commit.intervals.ms","true"); prop.put("auto.commit.interval.ms","1000") KafkaConsumer<String, String> consumer = new KafkaConsumer<>(prop); ArrayList<String> topics = new ArrayList<>(); topics.add("hello"); consumer.subscribe(topics); while(true){ ConsumerRecords<String,String> poll = consumer.poll(Duration.ofSeconds(20)); for(ConsumerRecord<String,String> consumerRecord : poll){ System.out.println(consumerRecord); } } ``` 此外,Kafka引入了Consumer Offset的概念来标记消费者组在一个分区即将消费的下一条记录。Consumer Offset的信息会保存在一个特殊的topic中,叫做__consumer_offsets。这个topic默认会创建50个分区。这样,当一个消费者组在消费一半时重启了,就可以通过Consumer Offset来继续上一次的位置读取消息。 以上就是Java Kafka的一些基本原理和使用方法的介绍。希望对你有帮助。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [JAVA知识体系之分布式篇(四)——Kafka](https://blog.csdn.net/qsmiley10/article/details/115000474)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] - *2* [通过java方式使用Kafka](https://blog.csdn.net/libaowen609/article/details/126529414)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值