什么是MQ
消息队列(Message Queue)是一种进程间通信或同一进程的不同线程间的通信方式,主要解决应用耦合、异步消息、流量削峰等问题。实现高性能、高可用、可伸缩和最终一致性架构。是大型分布式系统不可缺少的中间件。消息发布者只管把消息发布到 MQ 中而不用管谁来取,消息使用者只管从 MQ 中取消息而不管是谁发布的。这样发布者和使用者都不用知道对方的存在。
图片
MQ使用场景
异步处理
系统耗时比较久的操作,异步处理后,主流程主响应时间变短,其他的都通过异步的方式进行处理
应用解耦
多个强依赖的系统,一旦涉及到添加和删除和修改便会使得系统难以维护,此时可以通过MQ来交互,进行解耦。
流量削峰
流量高峰时期对服务器压力大。大部分时间流量都不太高,扩充太多的机器利用率太低,这个时候可以通过mq来进行削峰
基本概念
发布订阅的对象是主题(Topic)
向主题发布消息的客户端应用程序称为生产者(Producer)
订阅这些主题消息的客户端应用程序就被称为消费者(Consumer)
每个消费者在消费消息的过程中必然需要有个字段记录它当前消费到了分区的哪个位置上,这个字段就是消费者位移(Consumer Offset),表示消费者消费进度,每个消费者都有自己的消费者位移。
消费者组:Consumer Group。多个消费者实例共同组成的一个组,同时消费多个分区以实现高吞吐。
重平衡:Rebalance。消费者组内某个消费者实例挂掉后,其他消费者实例自动重新分配订阅主题分区的过程。Rebalance 是 Kafka 消费者端实现高可用的重要手段。
一个 Kafka 集群由多个 Broker 组成,Broker 负责接收和处理客户端发送过来的请求,以及对消息进行持久化。一台服务器一般只部署一个broker,所以可以把broker看做一个服务器节点
相同的数据拷贝在 Kafka 中被称为副本(Replica),领导者副本(Leader Replica-对外提供服务)和追随者副本(Follower Replica-保持与领导者的同步)。
Kafka 中的分区机制指的是将每个主题划分成多个分区(Partition),每个分区是一组有序的消息日志。
Kafka 使用顺序写消息日志(Log)来保存数据,一个日志就是磁盘上一个只能追加写(Append-only)消息的物理文件。
第一层是主题层,每个主题可以配置 M 个分区,而每个分区又可以配置 N 个副本。
第二层是分区层,每个分区的 N 个副本中只能有一个充当领导者角色,对外提供服务;其他 N-1 个副本是追随者副本,只是提供数据冗余之用。
第三层是消息层,分区中包含若干条消息,每条消息的位移从 0 开始,依次递增。
最后,客户端程序只能与分区的领导者副本进行交互。
Replica(副本)
副本机制的好处
提供数据冗余。即使系统部分组件失效,系统依然能够继续运转,因而增加了整体可用性以及数据持久性。
提供高伸缩性。支持横向扩展,能够通过增加机器的方式来提升读性能,进而提高读操作吞吐量。
改善数据局部性。
kafka 的副本(Replica),本质就是一个只能追加写消息的提交日志。在 Kafka 中,追随者副本是不对外提供服务的。所以Kafka 只能享受到副本机制带来的第 1 个好处。
第一,在 Kafka 中,副本分成两类:领导者副本(Leader Replica)和追随者副本(Follower Replica)。每个分区在创建时都要选举一个副本,称为领导者副本,其余的副本自动称为追随者副本。
第二,Kafka 的副本机制比其他分布式系统要更严格一些。在 Kafka 中,追随者副本是不对外提供服务的。这它唯一的任务就是从领导者副本异步拉取消息,并写入到自己的提交日志中,从而实现与领导者副本的同步。所以 Kafka 只能享受到副本机制带来的第 1 个好处。
第三,当领导者副本挂掉了,或者说领导者副本所在的 Broker 宕机时,Kafka 依托于 ZooKeeper 提供的监控功能能够实时感知到,并立即开启新一轮的领导者选举,从追随者副本中选一个作为新的领导者。老 Leader 副本重启回来后,只能作为追随者副本加入到集群中。
1.方便实现“Read-your-writes”。
所谓 Read-your-writes,顾名思义就是,当你使用生产者 API 向 Kafka 成功写入消息后,马上使用消费者 API 去读取刚才生产的消息。
2.方便实现单调读(Monotonic Reads)。
什么是单调读呢?就是对于一个消费者用户而言,在多次消费消息时,它不会看到某条消息一会儿存在一会儿不存在。
In-sync Replicas(ISR)
Leader 副本天然就在 ISR 中。也就是说,ISR 不只是追随者副本集合,它必然包括 Leader 副本。甚至在某些情况下,ISR 只有 Leader 这一个副本。
能够进入到 ISR 的追随者副本要满足一定的条件,这个标准就是 Broker 端参数 replica.lag.time.max.ms 参数值。这个参数的含义是 Follower 副本能够落后 Leader 副本的最长时间间隔,当前默认值是 10 秒。
Unclean 领导者选举(Unclean Leader Election)Kafka 把所有不在 ISR 中的存活副本都称为非同步副本。Broker 端参数 unclean.leader.election.enable 控制是否允许 Unclean 领导者选举。开启 Unclean 领导者选举可能会造成数据丢失,但好处是,它使得分区 Leader 副本一直存在,不至于停止对外提供服务,因此提升了高可用性。反之,禁止 Unclean 领导者选举的好处在于维护了数据的一致性,避免了消息丢失,但牺牲了高可用性。如果你听说过 CAP 理论的话,你一定知道,一个分布式系统通常只能同时满足一致性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance)中的两个。显然,在这个问题上,Kafka 赋予你选择 C 或 A 的权利。
分区策略
分区是实现负载均衡以及高吞吐量的关键,故在生产者这一端就要仔细盘算合适的分区策略
常使用的分区策略有两种:
1 轮询策略有非常优秀的负载均衡表现,它总是能保证消息最大限度地被平均分配到所有分区上,故默认情况下它是最合理的分区策略,也是我们最常用的分区策略之一。
2 还可以按照消息键值分区,除此之外可以自己定义分区规则,自定义分区规则在编写生产者程序时实现,以编写一个具体的类实现org.apache.kafka.clients.producer.Partitioner接口。这个接口也很简单,只定义了两个方法:partition()和close(),通常只需要实现最重要的 partition 方法
kafka同一个topic是无法保证数据的顺序性的,但是同一个partition中的数据是有顺序的。如果要保证业务的消息有序,需要把改业务的消息通过(按消息键值)到同一partition。
消息
关于消息压缩
Producer 端压缩、Broker 端保持、Consumer 端解压缩。
在相同条件下,不论是否启用压缩,V2版本的消息格式都比 V1 版本节省磁盘空间。当启用压缩时,这种节省空间的效果更加明显。
消息格式不同会导致Kafka 丧失了引以为豪的 Zero Copy 特性。
Kafka 正式支持 Zstandard 算法(简写为 zstd)。它是 Facebook 开源的一个压缩算法,能够提供超高的压缩比(compression ratio),工程方面一般考虑这种压缩算法。
关于消息丢失
一句话概括,Kafka 只对“已提交”的消息(committed message)做有限度的(指kafka集群存活,消息没有过期等)持久化保证。
怎么保证消息不丢失
Producer 永远要使用带有回调通知的发送 API,也就是说不要使用 producer.send(msg),而要使用 producer.send(msg, callback)。不要小瞧这里的 callback(回调),它能准确地告诉你消息是否真的提交成功了。一旦出现消息提交失败的情况,你就可以有针对性地进行处理。
应对Kafka 中 Consumer 端的消息丢失:维持先消费消息(阅读),再更新位移(书签)的顺序即可。这样就能最大限度地保证消息不丢失。
当然,这种处理方式可能带来的问题是消息的重复处理,类似于同一页书被读了很多遍,但这不属于消息丢失的情形。
如果是多线程异步处理消费消息,Consumer 程序不要开启自动提交位移,而是要应用程序手动提交位移。
消息丢失的最佳实践
不要使用 producer.send(msg),而要使用 producer.send(msg, callback)。记住,一定要使用带有回调通知的 send 方法。
设置 acks = all。acks 是 Producer 的一个参数,代表了你对“已提交”消息的定义。如果设置成 all,则表明所有副本 Broker 都要接收到消息,该消息才算是“已提交”。这是最高等级的“已提交”定义。
设置 retries 为一个较大的值。这里的 retries 同样是 Producer 的参数,对应前面提到的 Producer 自动重试。当出现网络的瞬时抖动时,消息发送可能会失败,此时配置了 retries > 0 的 Producer 能够自动重试消息发送,避免消息丢失。
设置 unclean.leader.election.enable = false。这是 Broker 端的参数,它控制的是哪些 Broker 有资格竞选分区的 Leader。如果一个 Broker 落后原先的 Leader 太多,那么它一旦成为新的 Leader,必然会造成消息的丢失。故一般都要将该参数设置成 false,即不允许这种情况的发生。
设置 replication.factor >= 3。这也是 Broker 端的参数。其实这里想表述的是,最好将消息多保存几份,毕竟目前防止消息丢失的主要机制就是冗余。
设置 min.insync.replicas > 1。这依然是 Broker 端参数,控制的是消息至少要被写入到多少个副本才算是“已提交”。设置成大于 1 可以提升消息持久性。在实际环境中千万不要使用默认值 1。
确保 replication.factor > min.insync.replicas。如果两者相等,那么只要有一个副本挂机,整个分区就无法正常工作了。我们不仅要改善消息的持久性,防止数据丢失,还要在不降低可用性的基础上完成。推荐设置成 replication.factor = min.insync.replicas + 1。
确保消息消费完成再提交。Consumer 端有个参数 enable.auto.commit,最好把它设置成 false,并采用手动提交位移的方式。就像前面说的,这对于单 Consumer 多线程处理的场景而言是至关重要的。
Kafka 还有一种特别隐秘的消息丢失场景:增加主题分区。当增加主题分区后,在某段“不凑巧”的时间间隔后,Producer 先于 Consumer 感知到新增加的分区,而 Consumer 设置的是“从最新位移处”开始读取消息,因此在 Consumer 感知到新分区前,Producer 发送的这些消息就全部“丢失”了,或者说 Consumer 无法读取到这些消息。严格来说这是 Kafka 设计上的一个小缺陷,你有什么解决的办法吗?------程序停止再增加分区
kafka拦截器相当于Python的装饰器,可以灵活的在消费者或者生产者实现,在生产者时可以选择在消息在消息发送之前被调用或者在消息成功提交或发送失败之后被调用。在生产者时可以选择在消息返回给 Consumer 程序之前调用或者Consumer 在提交位移之后调用该方法。
保证消息的exactly once
kafka怎么保证精确一次(exactly once)(消息不会丢失,也不会被重复发送)发送消息。
幂等性 Producer 和事务型 Producer 都是 Kafka 社区力图为 Kafka 实现精确一次处理语义所提供的工具,只是它们的作用范围是不同的。
幂等性 Producer 只能保证单分区、单会话上的消息幂等性。方法:设置props.put(“enable.idempotence”, ture)。
而事务能够保证跨分区、跨会话间的幂等性。从交付语义上来看,自然是事务型 Producer 能做的更多。使用方法为开启 enable.idempotence = true同时如下图提交消息。
producer.initTransactions();
try {
producer.beginTransaction();
producer.send(record1);
producer.send(record2);
producer.commitTransaction();
} catch (KafkaException e) {
producer.abortTransaction();
}
控制器组件(Controller)
控制器组件(Controller),是 Apache Kafka 的核心组件。它的主要作用是在 Apache ZooKeeper 的帮助下管理和协调整个 Kafka 集群。kafka控制器是重度依赖 ZooKeeper 的。每个正常运转的 Kafka 集群,在任意时刻都有且只有一个控制器。 Kafka 供了一个重要的 JMX 指标 activeController 做控制器数量监控。如果发现该值大于1,要赶快处理,这通常都预示着集群出现了如“脑裂”这样的严重问题。
Apache ZooKeeper 是一个提供高可靠性的分布式协调服务框架。它使用的数据模型类似于文件系统的树形结构,根目录也是以“/”开始。该结构上的每个节点被称为 znode,用来保存一些元数据协调信息。znode 可分为持久性 znode 和临时 znode。持久性 znode 不会因为 ZooKeeper 集群重启而消失,而临时 znode 则与创建该 znode 的 ZooKeeper 会话绑定,一旦会话结束,该节点会被自动删除。ZooKeeper 赋予客户端监控 znode 变更的能力,即所谓的 Watch 通知功能。一旦 znode 节点被创建、删除,子节点数量发生变化,抑或是 znode 所存的数据本身变更,ZooKeeper 会通过节点变更监听器 (ChangeHandler) 的方式显式通知客户端。依托于这些功能,ZooKeeper 常被用来实现集群成员管理、分布式锁、领导者选举等功能。
kafka第一个成功创建 /controller 节点的 Broker 会被指定为控制器。
kafka控制器功能:
1.主题管理(创建、删除、增加分区)
2.分区重分配
3.Preferred 领导者选举
4.集群成员管理(新增 Broker、Broker 主动关闭、Broker 宕机)
5.数据服务
控制器故障转移(Failover)指的是,当运行中的控制器突然宕机或意外终止时,Kafka 能够快速地感知到,并立即启用备用控制器来代替之前失败的控制器。控所以当制器组件出现问题时,比如主题无法删除了,或者重分区 hang 住了,可以不用重启 Kafka Broker 或控制器。可以直接去 ZooKeeper 中手动删除 /controller 节点。具体命令是 rmr /controller。这样做的好处是,既可以引发控制器的重选举,又可以避免重启 Broker 导致的消息处理中断。
控制器把多线程的方案改成了单线程加事件队列的方案,规避了多线程同步开销。
Consumer Group
模型
Consumer Group
同一Topic的一条消息只能被同一个Consumer Group内的一个Consumer消费,但多个Consumer Group可同时消费这一消息。
Consumer Group 下可以有一个或多个 Consumer 实例。这里的实例可以是一个单独的进程,也可以是同一进程下的线程。在实际场景中,使用进程更为常见一些。
Group ID 是一个字符串,在一个 Kafka 集群中,它标识唯一的一个 Consumer Group。
Kafka 仅仅使用 Consumer Group 这一种机制,却同时实现了传统消息引擎系统的两大模型:如果所有实例都属于同一个 Group,那么它实现的就是消息队列模型;如果所有实例分别属于不同的 Group,那么它实现的就是发布 / 订阅模型。
Consumer 实例的数量应该等于该 Group 订阅主题的分区总数。
老版本的 Consumer Group 把位移保存在 ZooKeeper 中,新版本的 Consumer Group 将位移保存在 Broker 端的内部主题中。
Rebalance
Rebalance 本质上是一种协议,规定了一个 Consumer Group 下的所有 Consumer 如何达成一致,来分配订阅 Topic 的每个分区。
Rebalance 出现的情况
1.组成员数发生变更。
2.订阅主题数发生变更。
3.订阅主题的分区数发生变更
Rebalance 缺点
1.在 Rebalance 过程中,所有 Consumer 实例都会停止消费,等待 Rebalance 完成。
2.Rebalance 目前由所有 Consumer 实例共同参与,全部重新分配所有分区。
3.Rebalance很慢(有个国外用户的 Group 内有几百个 Consumer 实例,成功 Rebalance 一次要几个小时!)
要避免因为各种参数或逻辑不合理而导致的组成员意外离组或退出的情形导致Rebalance
与之相关的主要参数有:
session.timeout.ms:Consumer 存活性的时间间隔,建议设置为 6s
heartbeat.interval.ms 控制发送心跳请求频率的参数,建议设置为 2s
要保证 Consumer 实例在被判定为“dead”之前,能够发送至少 3 轮的心跳请求,即 session.timeout.ms >= 3 * heartbeat.interval.ms
max.poll.interval.ms Consumer 最大消息消费时间,要设置的时间需要大于一次消息(可能有多条)消费逻辑的最大时长
GC (垃圾回收), GC 设置不合理会导致程序频发 Full GC 而引发的非预期 Rebalance
控制器与重平衡
Katka为消费者组定义的5种状态: Empty,Dead,PreparingRebalance, CompletingRebalance,Stable。
消费者端的重平衡的2个步骤:加入组和等待领导者消费者分配方案。这2个步骤分别对应JoinGroup请求和syncGroup请求。
Controller处理重平衡的4个场景:新成员入组;组成员主动离组;组成员崩溃离组;重平衡时协调者对组内成员提交位移的处理。
提交位移(Committing Offsets)
Consumer 需要向 Kafka 汇报自己的位移数据,这个汇报过程被称为提交位移(Committing Offsets)。Consumer 需要为分配给它的每个分区提交各自的位移数据。提交位移主要是为了表征 Consumer 的消费进度,这样当 Consumer 发生故障重启之后,就能够从 Kafka 中读取之前提交的位移值,然后从相应的位移处继续消费,从而避免整个消费过程重来一遍。
Kafka Consumer 的位移提交,是实现 Consumer 端语义保障的重要手段。位移提交分为自动提交( enable.auto.commit)和手动提交,而手动提交又分为同步提交(KafkaConsumer#commitSync())和异步提交(KafkaConsumer#commitAsync())。在实际使用过程中,推荐你使用手动提交机制,因为它更加可控,也更加灵活。另外,建议你同时采用同步提交和异步提交两种方式,这样既不影响 TPS,又支持自动重试,改善 Consumer 应用的高可用性。更精细化的位移提交(分片消费提交, commitSync(Map
高水位和Leader Epoch
位移值等于高水位的消息也属于未提交消息。也就是说,高水位上的消息是不能被消费者消费的。同一个副本对象,其高水位值不会大于 LEO 值。分区的高水位就是其 Leader 副本的高水位。高水位可以帮助kafka完成副本同步,标识消息可见性。
Leader Epoch,规避因高水位更新错配导致的各种不一致问题,大致可以认为是 Leader 版本。它由两部分数据组成。
Epoch。一个单调增加的版本号。每当副本领导权发生变更时,都会增加该版本号。小版本号的 Leader 被认为是过期 Leader,不能再行使 Leader 权力。
起始位移(Start Offset)。Leader 副本在该 Epoch 值上写入的首条消息的位移。
CommitFailedException异常怎么处理?
场景:处理一次消费时,循环调用KafkaConsumer.poll 方法超时(超过设置的max.poll.interval.ms)
缩短单条消息处理的时间。比如,之前下游系统消费一条消息的时间是 100 毫秒,优化之后成功地下降到 50 毫秒,那么此时 Consumer 端的 TPS 就提升了一倍。
增加 Consumer 端允许下游系统消费一批消息的最大时长。这取决于 Consumer 端参数 max.poll.interval.ms 的值。在最新版的 Kafka 中,该参数的默认值是 5 分钟。如果你的消费逻辑不能简化,那么提高该参数值是一个不错的办法。值得一提的是,Kafka 0.10.1.0 之前的版本是没有这个参数的,因此如果你依然在使用 0.10.1.0 之前的客户端 API,那么你需要增加 session.timeout.ms 参数的值。不幸的是,session.timeout.ms 参数还有其他的含义,因此增加该参数的值可能会有其他方面的“不良影响”,这也是社区在 0.10.1.0 版本引入 max.poll.interval.ms 参数,将这部分含义从 session.timeout.ms 中剥离出来的原因之一。
减少下游系统一次性消费的消息总数。这取决于 Consumer 端参数 max.poll.records 的值。当前该参数的默认值是 500 条,表明调用一次 KafkaConsumer.poll 方法,最多返回 500 条消息。可以说,该参数规定了单次 poll 方法能够返回的消息总数的上限。如果前两种方法对你都不适用的话,降低此参数值是避免 CommitFailedException 异常最简单的手段。
下游系统使用多线程来加速消费。这应该算是“最高级”同时也是最难实现的解决办法了。让下游系统手动创建多个消费线程处理 poll 方法返回的一批消息。
冷门场景:应用中同时出现了设置相同 group.id 值的消费者组程序和独立消费者(消费组中只有一个消费者)程序,那么当独立消费者程序手动提交位移时,Kafka 就会立即抛出 CommitFailedException 异常。
消费者监控
消费者 Lag 或 Consumer Lag,就是滞后程度,就是指消费者当前落后于生产者的程度。
在实际业务场景中必须时刻关注消费者的消费进度。由于消费者的速度无法匹及生产者的速度,极有可能导致它消费的数据已经不在操作系统的页缓存中了,那么这些数据就会失去享有 Zero Copy 技术的资格。那些 Lag 原本就很大的消费者会越来越慢,Lag 也会越来越大。
监控消费进度有 3 种方法。
使用 Kafka 自带的命令行工具 kafka-consumer-groups 脚本。
kafka-consumer-groups 脚本是 Kafka 为我们提供的最直接的监控消费者消费进度的工具。
$ bin/kafka-consumer-groups.sh --bootstrap-server <Kafka broker 连接信息 > --describe --group <group 名称 >
Kafka 连接信息就是 < 主机名:端口 > 对,而 group 名称就是你的消费者程序中设置的 group.id 值
eg : $ bin/kafka-consumer-groups.sh --bootstrap-server localhost:9092 --describe --group group_test
使用 Kafka Java Consumer API 编程。
推荐使用 Kafka 自带的 JMX 监控指标。
Kafka 消费者提供了一个名为 kafka.consumer:type=consumer-fetch-manager-metrics,client-id=“{client-id}”的 JMX 指标,里面有很多属性。和我们今天所讲内容相关的有两组属性:records-lag-max 和 records-lead-min,它们分别表示此消费者在测试窗口时间内曾经达到的最大的 Lag 值和最小的 Lead 值。
Lag 值的含义我们已经反复讲过了,我就不再重复了。这里的 Lead 值是指消费者最新消费消息的位移与分区当前第一条消息位移的差值。很显然,Lag 和 Lead 是一体的两个方面:Lag 越大的话,Lead 就越小,反之也是同理。一旦监测到 Lead 越来越小,甚至是快接近于 0 了,你就一定要小心了,这可能预示着消费者端要丢消息了。在实际生产环境中,一定要同时监控 Lag 值和 Lead 值!!!。分区级别的 JMX 指标中多了 records-lag-avg 和 records-lead-avg 两个属性,可以计算平均的 Lag 值和 Lead 值。在实际场景中,我们会更多地使用这两个 JMX 指标。
TCP连接管理
Producer 端
对最新版本的 Kafka(2.1.0)而言,Java Producer 端管理 TCP 连接的方式是:
KafkaProducer 实例创建时启动 Sender 线程,从而创建与 bootstrap.servers 中所有 Broker 的 TCP 连接。
KafkaProducer 实例首次更新元数据信息之后,还会再次创建与集群中所有 Broker 的 TCP 连接。
如果 Producer 端发送消息到某台 Broker 时发现没有与该 Broker 的 TCP 连接,那么也会立即创建连接。
如果设置 Producer 端 connections.max.idle.ms 参数大于 0,则步骤 1 中创建的 TCP 连接会被自动关闭;如果设置该参数 =-1,那么步骤 1 中创建的 TCP 连接将无法被关闭,从而成为“僵尸”连接。
Consume端
Kafka 的 Java Consumer如何 管理 TCP 连接
TCP 连接是在调用 KafkaConsumer.poll 方法时被创建的。再细粒度地说,在 poll 方法内部有 3 个时机可以创建 TCP 连接。
1.发起 FindCoordinator 请求时。
费者端有个组件叫协调者(Coordinator),它驻留在 Broker 端的内存中,负责消费者组的组成员管理和各个消费者的位移提交管理。当消费者程序首次启动调用 poll 方法时,它需要向 Kafka 集群发送一个名为 FindCoordinator 的请求,希望 Kafka 集群告诉它哪个 Broker 是管理它的协调者。消费者程序会向集群中当前负载最小的那台 Broker 发送请求。
2.连接协调者时。
Broker 处理完上一步发送的 FindCoordinator 请求之后,会返还对应的响应结果(Response),显式地告诉消费者哪个 Broker 是真正的协调者,因此在这一步,消费者知晓了真正的协调者后,会创建连向该 Broker 的 Socket 连接。只有成功连入协调者,协调者才能开启正常的组协调操作,比如加入组、等待组分配方案、心跳请求处理、位移获取、位移提交等。
3.消费数据时。
消费者会为每个要消费的分区创建与该分区领导者副本所在 Broker 连接的 TCP。
消费者程序会创建 3 类 TCP 连接:
确定协调者和获取集群元数据。
连接协调者,令其执行组成员管理操作。
执行实际的消息获取。
消费者关闭 Socket 也分为主动关闭和 Kafka 自动关闭。
主动关闭是指你显式地调用消费者 API 的方法去关闭消费者,具体方式就是手动调用 KafkaConsumer.close() 方法,或者是执行 Kill 命令,不论是 Kill -2 还是 Kill -9;
而 Kafka 自动关闭是由消费者端参数 connection.max.idle.ms控制的,该参数现在的默认值是 9 分钟,即如果某个 Socket 连接上连续 9 分钟都没有任何请求“过境”的话,那么消费者会强行“杀掉”这个 Socket 连接。
当第三类 TCP 连接成功创建后,消费者程序就会废弃第一类 TCP 连接,之后在定期请求元数据时,它会改为使用第三类 TCP 连接。也就是说,最终你会发现,第一类 TCP 连接会在后台被默默地关闭掉。对一个运行了一段时间的消费者程序来说,只会有后面两类 TCP 连接存在。