【Java微服务组件】异步通信P2—Kafka与消息

news2025/5/23 2:23:08

欢迎来到啾啾的博客🐱。
记录学习点滴。分享工作思考和实用技巧,偶尔也分享一些杂谈💬。
欢迎评论交流,感谢您的阅读😄。

目录

  • 引言
  • Kafka与消息
    • 生产者发送消息到Kafka
      • 批处理发送设计
      • 消息的幂等信息
      • 确保消息送达
        • acks配置
        • send()方法返回
        • retries配置
    • 消息在Kafka的存储
      • Kafka Cluster\Broker\Topic\Partition
        • Cluster
        • Broker
        • Topic/Partition
        • 数据冗余与分布式协调带来可靠
        • 零拷贝和操作系统内存利用是高吞吐的核心
      • 消息怎么过期(移除)
        • 消息滞后风险
    • 消息者消费消息
      • 消费者群组
      • 偏移量
      • 消息积压怎么处理
  • Kafka为什么快

引言

在上一篇【Java微服务组件】异步通信P1—消息队列基本概念
中已经简单介绍了MQ。
目前主流的消息队列应该就是Kafka与RocketMQ了。在RocketMQ官网中有讲解其与Kafka的区别,如下:

消息产品客户端 SDK协议与规范顺序消息定时消息批量消息广播消息消息过滤服务端触发重投递消息存储消息回溯消息优先级高可用与故障转移消息轨迹配置方式管理与运维工具
KafkaJava, Scala 等拉模型 (Pull Model), 支持 TCP 协议保证分区内消息有序不支持支持, 通过异步生产者实现不支持支持, 可使用 Kafka Streams 进行消息过滤不支持高性能文件存储支持, 通过偏移量 (offset) 定位不支持支持, 依赖 ZooKeeper 服务不支持Kafka 使用键值对格式进行配置。这些值可以通过文件或编程方式提供。支持, 使用终端命令暴露核心指标
RocketMQJava, C++, Go拉模型 (Pull Model), 支持 TCP, JMS, OpenMessaging 协议保证严格顺序消息, 且能优雅地水平扩展支持支持, 通过同步模式避免消息丢失支持支持, 基于 SQL92 的属性过滤表达式支持高性能、低延迟文件存储支持, 通过时间戳和偏移量 (offset) 两种方式定位不支持支持, 主从模型, 无需额外组件支持开箱即用, 用户只需关注少量配置支持, 丰富的 Web 界面和终端命令暴露核心指标

但今天我们还是先深入了解Kafka的设计与功能。因为Kafka快。

资料引用自《Kafka权威指南》、https://developer.confluent.io/courses/architecture/broker/
在这里插入图片描述

Kafka与消息

生产者发送消息到Kafka

怎么可靠、高效地发送消息到Kafka呢?

批处理发送设计

如果每一条消息都单独穿行于网络中,那么就会导致大量的网络开销,把消息分成批次传输可以减少网络开销。
所以,Kafka Producer 的核心设计之一就是客户端批处理。生产者会将多条消息收集到一个批次 (Batch) 中,然后一次性将整个批次发送给对应的Broker。

为此,Kafka Producer 内部有一个缓冲区 (Accumulator / RecordAccumulator)。消息在缓冲区按照目标的Topic-Partition进行组织,满足以下任一条件后随批次发送给Broker:

  1. batch.size: 某个批次的消息累积大小达到了配置的 batch.size(默认16KB)。
  2. linger.ms: 距离该批次第一条消息进入缓冲区的时间超过了配置的 linger.ms(默认0ms,但实际中为了启用批处理,通常会设置一个大于0的值,比如5ms, 10ms)。即使批次未满 batch.size,到达 linger.ms 后也会发送,以避免消息在缓冲区停留过久导致延迟。
  3. producer.flush()被调用: 应用程序显式要求将所有缓冲区的消息立即发送。
  4. producer.close()被调用: 在关闭生产者之前,会确保所有缓冲区的消息都被发送出去。

![[异步通信P2—Kafka.png]]
批处理设计通过牺牲一点点单条消息的即时性,节省网络开销(TCP/IP开销、传输数据量)、磁盘开销,从整体提升消息吞吐量、网络效率、资源利用率。
对于大多数高吞吐量的消息系统场景来说,这样做是非常值得的。

消息的幂等信息

Q:怎么保证Kafka存储(收到)的消息是唯一的?

从 Kafka 3.0.0 版本开始,Kafka 生产者内置了幂等性支持。幂等可以防止因Broker未及时响应ack导致消息在Broker重复存储。
配置默认为true。

spring.kafka.producer.enable-idempotence=true

它通过为每个生产者分配一个唯一的生产者ID (PID) 和为发送到每个分区的每条消息分配一个序列号来实现。Broker 会跟踪这些 (PID, Partition, SequenceNumber) 组合,并丢弃重复的写入尝试。

需要注意的时,幂等启动时,除非用户显示配置了其他值,retries 会被设置为 Integer.MAX_VALUE,acks 会被设置为 all,max.in.flight.requests.per.connection 会被限制为 <= 5 (默认为5)。

确保消息送达

生产者发送消息的producer.send() 方法本身是异步的,它将消息放入生产者的发送缓冲区,并由一个后台线程负责批量发送(哪怕linger.ms配置为0)。
生产者有以下方式来确保消息送达。

acks配置

这个参数决定了生产者在认为一个请求完成之前需要等待多少个 Broker 副本的确认。它有三个主要的值:

  • acks=0
    完全没有送达保证。
  • acks=1
    Leader确认后则认为送达。
  • acks=all
    Leader Broker 在自己写入并且所有ISR都向它报告已写入后,才向生产者发送确认响应。

一般配置acks=all,虽然会略微增加延迟,但数据不丢失通常是更重要的考量。
acks一般还与min.insync.replicas一起使用,min.insync.replicas默认值通常是1,这意味着只要有一个副本(包括leader副本)已经接收并同步了消息,就可以认为该消息是成功写入的。
所以配置acks=all时,一般推荐min.insync.replicas=2,即有两个副本同步了消息。

send()方法返回

producer.send(record) 方法会返回一个 java.util.concurrent.Future<RecordMetadata> 对象。常用的KafkaTemplate.send()则返回一个ListenableFuture<SendResult<K, V>>,两者都可以用于等待返回结果(阻塞或非阻塞)。

// 阻塞等待

try {
    SendResult<String, String> result = kafkaTemplate.send("myTopic", "key", "value").get();
    RecordMetadata metadata = result.getRecordMetadata();
    log.info("Sent message to topic " + metadata.topic() +
                       " partition " + metadata.partition() +
                       " offset " + metadata.offset());
} catch (InterruptedException | ExecutionException e) {
    // 处理发送失败,e.getCause() 通常是 KafkaException
	log.error("Failed to send message: " + e.getMessage());
}

// 非阻塞
ListenableFuture<SendResult<String, String>> future = kafkaTemplate.send("myTopic", "key", "value");
future.addCallback(new ListenableFutureCallback<SendResult<String, String>>() {
    @Override
    public void onSuccess(SendResult<String, String> result) {
        RecordMetadata metadata = result.getRecordMetadata();
        log.info("Successfully sent message to topic " + metadata.topic() +
                           " partition " + metadata.partition() +
                           " offset " + metadata.offset());
    }

    @Override
    public void onFailure(Throwable ex) {
        // 处理发送失败
        log.error("Failed to send message: " + ex.getMessage());
    }
});
retries配置

Kafka 生产者客户端内置了重试机制,用于处理可恢复的错误(如网络抖动、Leader 切换等)。
spring.kafka.producer.retries一般设置3或者5。
另外还有控制重试等待时间的spring.kafka.producer.properties.retry.backoff.ms。
控制producer.send() 和 producer.flush() 调用完成的总时间的spring.kafka.producer.properties.delivery.timeout.ms。这个时间包括消息在缓冲区等待的时间+网络传输时间+Broker确认时间+所有重试时间。如果超过这个时间,send()返回的Future会报超时。
默认的两分钟通常是够的。

spring.kafka.producer.retries=3
spring.kafka.producer.properties.retry.backoff.ms=1000 # 例如设置为1秒
spring.kafka.producer.properties.delivery.timeout.ms=120000
# 如果开启幂等性 (通常推荐)
spring.kafka.producer.enable-idempotence=true
# 当 enable-idempotence=true 时,retries 默认为 Integer.MAX_VALUE, acks 默认为 all
# max.in.flight.requests.per.connection 默认为 5

消息在Kafka的存储

消息在Kafka是怎么存储的,怎么保证数据安全可靠?

Kafka Cluster\Broker\Topic\Partition

Cluster

一个 Kafka Cluster 由一个或多个 Broker 组成。这些 Broker 协同工作,提供消息的存储、读取和高可用性。
可以通过增加Broker来扩展集群的处理能力和存储容量。集群之间的数据共享(通信)早起是ZooKeeper,现在是KRaft控制器。
![[异步通信P2—Kafka-6.png]]

Broker

集群中的一个服务器实例就是一个Broker,每个Broker有唯一的数字ID。 Broker 负责接收来自生产者的消息,并将它们存储在磁盘上(以分区的形式)。
![[异步通信P2—Kafka-5.png]]

生产者请求到Broker后进入请求队列,然后由I/O线程验证并存储批次最后持久化到磁盘。
Kafka利用了操作系统本身的Page Cache,即,Kafka的读写操作基本上是基于系统内存的,读写性能得到了极大的提升。
同时,Broker使用了零拷贝技术,消费取消息时不走用户空间缓存区,数据从磁盘读取到内存空间page cache后,直接复制到socket缓冲区。避免了内核空间到用户空间的来回拷贝,也极大提升了性能。

集群中会有一个 Broker被选举为 Controller。Controller 负责管理集群的元数据,例如创建/删除 Topic、分区分配、Leader 选举等。
Broker的Leader-Follower只负责管理Kafka Cluster的集群元数据、监控Broker状态、执行管理操作、发起Leader选举。并不进行消息读写管理。

Topic/Partition

Kafka的消息通过Topic进行分类,生产者将消息发布到特定的 Topic,消费者订阅特定的 Topic 来消费消息。
Topic是一个逻辑概念,会通过分散到集群的多个Broker上。
Topic被分为若干个Partition,一个Partition就是一个提交日志。

  • Partition日志
    Partition日志并不是一个无限大的文件,而是一系列的日志短文件(LOg Segment Files)组成的。
    一般用文件中第一条消息的offset命名,如00000000000000000000.log,00000000000000170123.log 等。
    每个日志文件都有两个索引文件:偏移量索引与时间戳索引。
    • 偏移量索引 (.index): 存储相对偏移量(相对于该段的基准偏移量)到消息在 .log 文件中物理位置(字节偏移)的稀疏映射。这使得 Kafka 可以通过 Offset 快速定位到消息在日志段中的大致位置,然后顺序扫描一小段来找到确切的消息。
    • 时间戳索引 (.timeindex): 存储时间戳到消息相对偏移量的稀疏映射。这使得 Kafka 可以通过时间戳快速定位到某个时间点附近的消息。

消息会以追加的方式被写入Partition,然后按照先入先出的顺序读取。一个Partition只能有一个Leader位于某个Broker上,且所有读写都在Leader上进行,其他Follower用于保障数据安全。

具体关系如下:
![[异步通信P2—Kafka-7.png]]

一个 Topic 的不同分区(及其副本)会分布到集群中的不同 Broker 上。Kafka可以保证消息在当前Partition的顺序。

Kafka Producer可以指定消息发送到哪个Topic,也可以指定到哪个Partition。没有指定Partition时,Kafka Producer会使用分区器 Partitioner来决定消息应该到哪个partition,策略以前是轮询,2.4版本后是“粘性分区”(简单来说就是先选一个分区一直发,然后换一个一直发)。
决定消息发往哪个Partition后,生产者会讲消息发给Partition中的Leader。然后Follower从Partition拉去信息同步进自己的文件中。
消费者也是连接Leader进行消费。

通常会把一个Topic的数据看成一个流。

数据冗余与分布式协调带来可靠

Kafka通过acks和min.insync.replicas配置防止消息发送失败,然后通过将数据冗余(数据副本)存放到多个 Follower Partition以及分布式共识选举机制来保障数据的可靠。

零拷贝和操作系统内存利用是高吞吐的核心

Kafka的读写操作基本上是基于系统内存的,读写性能得到了极大的提升。同时使用零拷贝技术,提升了消费的速率。

消息怎么过期(移除)

Kafka的数据过期(移除)和消费者是否消费完消息没有直接关系。消息移除主要由Topic级别配置的数据保留策略(Retention Policy)决定。

主要的数据保留策略如下:

  • 基于时间
    配置参数: retention.ms (毫秒) 或 retention.hours (小时) 或 retention.days (天,较老版本,retention.ms 更精确且优先)。
    时间从消息写入开始算。
  • 基于大小
    配置参数: retention.bytes (字节)。每个Partition日志文件的最大总大小。
    当一个 Partition 的日志文件总大小超过这个配置值时,Kafka 会从日志的最旧端开始删除消息段 (Log Segments),直到该 Partition 的大小回落到配置的限制以下。
  • 日志压缩
    配置参数: cleanup.policy=compact (默认是 delete)。
    对于启用了日志压缩的 Topic,Kafka 会保留每个消息 Key 的最新版本的值。旧版本的消息(具有相同 Key 的旧值)会被删除。这主要用于构建状态存储、变更数据捕获 (CDC) 等场景,而不是传统的队列消息传递。
消息滞后风险

因此,kafka存在消息滞后风险,如果长时间没有消费消息,消息可能丢失。
因此,监控消费情况、设计兜底很重要。

消息者消费消息

Q:消费者该怎么消费?
A:Kafka限定消费者实例必须属于一个消费者组(有group.id)。
通常同一个消费者组内的所有实例应该订阅相同的Topic,让每个消费者组专注于自己需要处理的Topic集合。

consumer.subscribe(Collection<String> topics)

不同的消费者组消费消息表现得像发布-订阅模式。每个消费者组会独立跟踪自己的消费进度。
而在同一个消费者组内部,一个 Topic 的一个 Partition 在同一时间只会被该组内的一个消费者实例消费。
为同组消费者实例分配不同Partition的机制是Rebalance。机制有负载均衡的效果,将工作分散到多个实例上,提高整体的吞吐量。
所以设计上推荐消费者指定到具体的Topic即可,然后利用Rebalance机制提高吞吐。

即,消费者逻辑上属于一个消费组然后消费指定的Topic,且同一个消费者组内不同消费者消费不同Partition。

Q:消费者怎么确定消费倒哪了?
A:每个消费者组都会追踪自己订阅的Topic的自己的消费记录——偏移量。
每个消息都有一个整数偏移量。
消费者组在消费完消息后,会发送一个Offset Commit请求到Broker。由Broker记录消费者组消费到的最新偏移量(以前是zk)。

Kafka消费者采用的是拉模型(Pull Model)。消费者根据自己的处理能力和节奏,向 Kafka Broker 发送 Fetch 请求来获取消息。
在拉取消息前,消费者需要向负责所属消费者的Broker(Group Coordinator)询问自己被分配了哪些Topic(尽管订阅指定了Topic,还是需要重新获取)和Partition,然后Broker返回之前消费到的最新偏移量。

消费者群组

![[异步通信P2—Kafka-8.png]]

Kafka限定消费者实例必须属于一个消费者组(有group.id)。一个消费者组内的所有实例应该订阅相同的Topic,让每个消费者组专注于自己需要处理的Topic集合。

偏移量

偏移量:偏移量是一种元数据,不断递增的整数值。由Broker(Leader)在消息被成功写入Partition时,分配并添加到消息的元数据中,仅在当前Partition唯一。

消息的元数据有:offset、timestamp、key、partition 、Headers、topic

消费者会按照消息写入Partition的顺序读取消息,并通过检查消息的偏移量来区分已经读取过的消息。
消费者会定期或在消费完成后向 Kafka(或外部存储)提交(commit)它所消费的偏移量。
当消费者重启或因故障切换时,可以从上次提交的偏移量之后开始继续消费。

消息积压怎么处理

首先,检查消费速率,看是否消费者代码异常。
代码是否有bug,性能需要优化?

然后,检查生产者消息,是否消息过大?是否需要优化传输效率?

再然后,检查积压情况,看是怎么积压的?
积压情况关键指标——Consumer Lag (消费滞后量):
表示某个 Partition 最新消息的 Offset 与消费者已提交 Offset 之间的差值。

如果是所有Topic都积压,则需要考虑扩容提Broker和Consumer。垂直扩容、水平扩容。
如果是单个Topic积压,则要从生产者做Topic、Partition的消费拆分。
单个Partition积压,则拆具体生产者消息。全部Partition积压,则拆Topic。

具体情况具体分析:
![[Kafka消息积压处理2.png]]

Kafka为什么快

  • 发的快
    设计有批量发送+与服务端轻量高效的通信协议,整体吞吐快。

  • 存的快
    利用了系统内存page cache+磁盘顺序读写,Kafka存的快。

  • 消息取的快
    文件分区分段且有索引+零拷贝,给消费者取消息取的快。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2383548.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

R语言空间数据处理入门教程

我的课程《R语言空间数据处理入门教程》已重新恢复课程售卖&#xff0c;有需要的读者可以学习。 &#x1f447;点击下方链接&#xff08;文末“阅读原文”可直达&#xff09;&#xff0c;立即开启你的空间数据之旅&#xff1a; https://www.bilibili.com/cheese/play/ss13775…

使用zap,对web应用/API接口 做安全检测

https://www.zaproxy.org/getting-started/ 检测方法 docker pull ghcr.io/zaproxy/zaproxy:stable# 执行baseline测试 docker run -t ghcr.io/zaproxy/zaproxy:stable zap-baseline.py \ -t https://baseline.yeshen.org# 执行api测试 docker run -t ghcr.io/zaproxy/zaproxy…

UE5.6新版本—— 动画光照系统重点更新

UE5.6预览版已经可以下载&#xff0c;发布会在下个月的6.5号发布。 5.6界面UI设计 5.6 对引擎进行了大规模的重新设计&#xff0c;先看整体内容&#xff0c;主题UI设计 被调整了位置&#xff0c;左边大多数的选择&#xff0c;框选工具&#xff0c;吸附工具&#xff0c;挪到了左…

腾讯位置服务重构出行行业的技术底层逻辑

位置智能&#xff1a;重构出行行业的技术底层逻辑 在智慧城市建设与交通出行需求爆发的双重驱动下&#xff0c;位置服务正从工具层跃升为出行行业的核心基础设施。腾讯位置服务以“连接物理世界与数字空间”为核心理念&#xff0c;通过构建高精度定位、实时数据融合、智能决策…

如何用JAVA手写一个Tomcat

一、初步理解Tomcat Tomcat是什么&#xff1f; Tomcat 是一个开源的 轻量级 Java Web 应用服务器&#xff0c;核心功能是 运行 Servlet/JSP。 Tomcat的核心功能&#xff1f; Servlet 容器&#xff1a;负责加载、实例化、调用和销毁 Servlet。 HTTP 服务器&#xff1a;监听端口…

使用 Qt QGraphicsView/QGraphicsScene 绘制色轮

使用 Qt QGraphicsView/QGraphicsScene 绘制色轮 本文介绍如何在 Qt 中利用 QGraphicsView 和 QGraphicsScene 实现基础圆形绘制&#xff0c;以及进阶的色轮&#xff08;Color Wheel&#xff09;效果。 色轮是色彩选择器的常见控件&#xff0c;广泛应用于图形设计、绘画和 UI …

使用glsl 来做视频矫正

描述、优点 使用glsl来代替opencv的undistort 和 鱼眼矫正,并且最后使用opencv的LUT给glsl 来使用,来达到加速的目的,并且做到和opencv 一模一样的效果,达到实时视频的加速矫正。 优点: 没有cuda,也可以做到实时视频矫正,包含各类板子和amd的cpu,intel核显 矫正的基本作…

03-Web后端基础(Maven基础)

1. 初始Maven 1.1 介绍 Maven 是一款用于管理和构建Java项目的工具&#xff0c;是Apache旗下的一个开源项目 。 Apache 软件基金会&#xff0c;成立于1999年7月&#xff0c;是目前世界上最大的最受欢迎的开源软件基金会&#xff0c;也是一个专门为支持开源项目而生的非盈利性…

蓝桥杯19682 完全背包

问题描述 有 N 件物品和一个体积为 M 的背包。第 i 个物品的体积为 vi​&#xff0c;价值为 wi​。每件物品可以使用无限次。 请问可以通过什么样的方式选择物品&#xff0c;使得物品总体积不超过 M 的情况下总价值最大&#xff0c;输出这个最大价值即可。 输入格式 第一行…

DeepSeek源码解构:从MoE架构到MLA的工程化实现

文章目录 **一、代码结构全景&#xff1a;从模型定义到分布式训练****二、MoE架构&#xff1a;动态路由与稀疏激活的工程化实践****1. 专家路由机制&#xff08;带负载均衡&#xff09;****数学原理&#xff1a;负载均衡损失推导** **三、MLA注意力机制&#xff1a;低秩压缩与解…

【VLNs篇】02:NavGPT-在视觉与语言导航中使用大型语言模型进行显式推理

方面 (Aspect)内容总结 (Content Summary)论文标题NavGPT: 在视觉与语言导航中使用大型语言模型进行显式推理 (NavGPT: Explicit Reasoning in Vision-and-Language Navigation with Large Language Models)核心问题探究大型语言模型 (LLM) 在复杂具身场景&#xff08;特别是视…

(T_T),不小心删掉RabbitMQ配置文件数据库及如何恢复

一、不小心删除 今天是2025年5月15日&#xff0c;非常沉重的一天&#xff0c;就在今早8点左右的时候我打算继续做我的毕业设计&#xff0c;由于开机的过程十分缓慢&#xff08;之前没有&#xff09;&#xff0c;加上刚开机电脑有卡死的迹象&#xff0c;再加上昨天晚上关电脑前…

TDengine 安全部署配置建议

背景 TDengine 的分布式、多组件特性导致 TDengine 的安全配置是生产系统中比较关注的问题。本文档旨在对 TDengine 各组件及在不同部署方式下的安全问题进行说明&#xff0c;并提供部署和配置建议&#xff0c;为用户的数据安全提供支持。 安全配置涉及组件 TDengine 包含多…

蓝桥杯框架-LED蜂鸣器继电器

蓝桥杯框架-LED蜂鸣器继电器 一&#xff0c;新建工程文件二&#xff0c;配置keil三&#xff0c;完善框架 一&#xff0c;新建工程文件 在桌面上新建一个文件夹&#xff1a;用于存放所有工程文件 在文件夹中再建立一个文件夹DEMO_01&#xff1a;这是我们的第一个工程文件 在第…

uniapp-商城-64-后台 商品列表(商品修改---页面跳转,深浅copy应用,递归调用等)

完成了商品的添加和展示&#xff0c;下面的文字将继续进行商品页面的处理&#xff0c;主要为商品信息的修改的页面以及后天逻辑的处理。 本文主要介绍了商品信息修改页面的实现过程。首先&#xff0c;页面布局包括编辑和删除功能&#xff0c;未来还可添加上架和下架按钮。通过c…

Dify的大语言模型(LLM) AI 应用开发平台-本地部署

前言 今天闲着&#xff0c;捣鼓一下 Dify 这个开源平台&#xff0c;在 mac 系统上&#xff0c;本地部署并运行 Dify 平台&#xff0c;下面记录个人在本地部署Dify 的过程。 Dify是什么&#xff1f; Dify是一个开源的大语言模型&#xff08;LLM&#xff09;应用开发平台&#…

使用教程:8x16模拟开关阵列可级联XY脚双向导通自动化接线

以下通过点亮LED进行基本使用流程演示&#xff0c;实际可以连接复杂外设&#xff08;SPI、CAN、ADC等&#xff09; 单模块使用 RX、TX、5V和GND接到串口模块&#xff1b;X5接5V&#xff1b;Y2接LED;LED-接GND 串口模块插上电脑后&#xff0c;LED没有亮&#xff1b;因为此时模…

8 种快速易用的Python Matplotlib数据可视化方法

你是否曾经面对一堆复杂的数据&#xff0c;却不知道如何让它们变得直观易懂&#xff1f;别慌&#xff0c;Python 的 Matplotlib 库是你数据可视化的最佳伙伴&#xff01;它简单易用、功能强大&#xff0c;能将枯燥的数字变成引人入胜的图表。无论是学生、数据分析师还是程序员&…

C# 深入理解类(实例构造函数)

实例构造函数 实例构造函数是一个特殊的方法&#xff0c;它在创建类的每个新实例时执行。 构造函数用于初始化类实例的状态。如果希望能从类的外部创建类的实例&#xff0c;需要将构造函数声明为public。 图7-2阐述了构造函数的语法。除了下面这几点&#xff0c;构造函数看起…

RabbitMQ——消息确认

一、消息确认机制 生产者发送的消息&#xff0c;可能有以下两种情况&#xff1a; 1> 消息消费成功 2> 消息消费失败 为了保证消息可靠的到达消费者&#xff08;&#xff01;&#xff01;&#xff01;注意&#xff1a;消息确认机制和前面的工作模式中的publisher confi…