Kafka核心原理(五):Broker与副本机制
2020-05-21·7 分钟阅读
Kafka核心原理(五):Broker与副本机制
前言
Broker 是 Kafka 集群的核心服务节点,负责消息存储、副本管理、请求处理等关键职责。本章将深入剖析 Broker 的架构设计、副本同步机制与高可用保障。
Broker 架构
整体架构图
┌─────────────────────────────────────────────────────────────────────────┐
│ Kafka Broker 架构 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ SocketServer │ │
│ │ ┌────────────────────────────────────────────────────────────┐ │ │
│ │ │ Network Layer │ │ │
│ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │
│ │ │ │ Acceptor │ │Processor │ │Processor │ ... │ │ │
│ │ │ │ Thread │ │ #1 │ │ #N │ │ │ │
│ │ │ └──────────┘ └──────────┘ └──────────┘ │ │ │
│ │ └────────────────────────────────────────────────────────────┘ │ │
│ └──────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ RequestChannel │ │
│ │ ┌────────────────────┐ ┌────────────────────┐ │ │
│ │ │ Request Queue │ │ Response Queue │ │ │
│ │ │ ┌───┐ ┌───┐ ... │ │ ┌───┐ ┌───┐ ... │ │ │
│ │ │ │ R │ │ R │ │ │ │ R │ │ R │ │ │ │
│ │ │ └───┘ └───┘ │ │ └───┘ └───┘ │ │ │
│ │ └────────────────────┘ └────────────────────┘ │ │
│ └──────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ KafkaRequestHandlerPool │ │
│ │ ┌──────────────────────────────────────────────────────────┐ │ │
│ │ │ Request Handler Threads │ │ │
│ │ │ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ │ │ │
│ │ │ │Handler1│ │Handler2│ │Handler3│ │HandlerN│ │ │ │
│ │ │ └────────┘ └────────┘ └────────┘ └────────┘ │ │ │
│ │ └──────────────────────────────────────────────────────────┘ │ │
│ └──────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌─────────────────────────┼─────────────────────────┐ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ LogManager│ │ ReplicaManager│ │ Controller │ │
│ │ │ │ │ │ │ │
│ │ ┌─────────┐ │ │ ┌─────────┐ │ │ ┌─────────┐ │ │
│ │ │ Log │ │ │ │ Replica │ │ │ │ ZK/KRaft│ │ │
│ │ │ Manager │ │ │ │ Manager │ │ │ │ Client │ │ │
│ │ └─────────┘ │ │ └─────────┘ │ │ └─────────┘ │ │
│ │ ┌─────────┐ │ │ ┌─────────┐ │ │ ┌─────────┐ │ │
│ │ │ Log │ │ │ │ ISR │ │ │ │ State │ │ │
│ │ │ Cleaner │ │ │ │ Manager │ │ │ │ Manager │ │ │
│ │ └─────────┘ │ │ └─────────┘ │ │ └─────────┘ │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
核心组件说明
Broker 核心组件:
├── SocketServer
│ ├── Acceptor:接收新连接
│ ├── Processor:处理网络IO(NIO Selector)
│ └── 连接管理与请求分发
│
├── RequestChannel
│ ├── Request Queue:请求队列
│ ├── Response Queue:响应队列
│ └── 请求响应传递通道
│
├── KafkaRequestHandlerPool
│ ├── 请求处理线程池
│ └── 从RequestQueue取请求处理
│
├── LogManager
│ ├── 日志段管理
│ ├── 日志清理与压缩
│ └── 文件系统操作
│
├── ReplicaManager
│ ├── 副本生命周期管理
│ ├── Leader/Follower 角色管理
│ └── 消息同步与拉取
│
└── Controller
├── 集群元数据管理
├── 分区状态管理
└── 副本选举与重分配
请求处理流程
┌─────────────────────────────────────────────────────────────────────────┐
│ Broker 请求处理流程 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ Client │
│ │ │
│ │ 1. 发送请求 │
│ ▼ │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ SocketServer │ │
│ │ │ │
│ │ Acceptor │ │
│ │ │ │ │
│ │ │ 2. 接收连接 │ │
│ │ ▼ │ │
│ │ Processor (NIO Selector) │ │
│ │ │ │ │
│ │ │ 3. 读取请求,封装成 Request对象 │ │
│ │ │ │ │
│ └─────┼───────────────────────────────────────────────────────────┘ │
│ │ │
│ │ 4. 放入 Request Queue │
│ ▼ │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ RequestChannel │ │
│ │ │ │
│ │ ┌─────────────────────────────────────────────────────────┐ │ │
│ │ │ Request Queue │ │ │
│ │ │ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ │ │ │
│ │ │ │ Req │ │ Req │ │ Req │ │ Req │ │ Req │ ... │ │ │
│ │ │ └─────┘ └─────┘ └─────┘ └─────┘ └─────┘ │ │ │
│ │ └─────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ │ │
│ │ 5. Handler 线程取出请求 │
│ ▼ │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ KafkaRequestHandlerPool │ │
│ │ │ │
│ │ ┌──────────┐ │ │
│ │ │ Handler │ 6. 根据 API Key 分发到对应 API Handler │ │
│ │ │ Thread │ │ │
│ │ └────┬─────┘ │ │
│ │ │ │ │
│ │ │ 7. 业务处理 │ │
│ │ │ ├── LogManager: 写入日志 │ │
│ │ │ ├── ReplicaManager: 副本同步 │ │
│ │ │ └── Controller: 元数据更新 │ │
│ │ │ │ │
│ │ │ 8. 生成响应,放入 Response Queue │ │
│ │ │ │ │
│ └───────┼────────────────────────────────────────────────────────┘ │
│ │ │
│ │ 9. Processor 发送响应 │
│ ▼ │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ SocketServer │ │
│ │ Processor 发送响应 │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ │ │
│ │ 10. 返回给 Client │
│ ▼ │
│ Client │
│ │
└─────────────────────────────────────────────────────────────────────────┘
副本机制
副本角色
┌─────────────────────────────────────────────────────────────────────────┐
│ 副本角色说明 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ Topic: orders, Partition: 0, Replication Factor: 3 │
│ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ Broker 1 Broker 2 Broker 3 │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │ Leader │◄───同步────│Follower │◄──同步───│Follower │ │ │
│ │ │ (AR) │ │ (AR) │ │ (AR) │ │ │
│ │ │ (ISR) │ │ (ISR) │ │ (ISR) │ │ │
│ │ └─────────┘ └─────────┘ └─────────┘ │ │
│ │ ▲ │ │
│ │ │ │ │
│ │ 生产者写入 消费者读取 │ │
│ │ │ │ │ │
│ │ └──────────────────────────────┘ │ │
│ │ │ │
│ └──────────────────────────────────────────────────────────────────┘ │
│ │
│ 角色说明: │
│ ├── Leader:处理所有读写请求,维护 ISR 列表 │
│ ├── Follower:被动复制 Leader 日志,不处理客户端请求 │
│ └── AR (Assigned Replicas):所有分配的副本集合 │
│ │
│ ISR (In-Sync Replicas):与 Leader 保持同步的副本集合 │
│ ├── 只有 ISR 中的副本才能被选举为 Leader │
│ ├── ISR 由 Leader 维护和更新 │
│ └── ISR 变化会通知 Controller │
│ │
└─────────────────────────────────────────────────────────────────────────┘
副本同步机制
┌─────────────────────────────────────────────────────────────────────────┐
│ 副本同步流程 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ Follower 拉取 Leader 数据(Pull 模式) │
│ │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ Leader (Broker 1) Follower (Broker 2) │ │
│ │ │ │ │ │
│ │ │◄───FetchRequest(fetchOffset)── │ │
│ │ │ │ │ │
│ │ │ 1. 检查 fetchOffset │ │ │
│ │ │ 2. 读取日志数据 │ │ │
│ │ │ 3. 更新 Follower 的 │ │ │
│ │ │ LEO/Lag │ │ │
│ │ │ │ │ │
│ │ │────FetchResponse───────────►│ │ │
│ │ │ (日志数据 + HW + LEO) │ │ │
│ │ │ │ │ │
│ │ │ │ 4. 追加日志 │ │
│ │ │ │ 5. 更新 LEO │ │
│ │ │ │ 6. 更新 HW │ │
│ │ │ │ │ │
│ │ │◄───FetchRequest(新LEO)────── │ │
│ │ │ │ │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ │
│ 关键概念: │
│ ├── LEO (Log End Offset):日志末端位移,下一条待写入消息的位移 │
│ ├── HW (High Watermark):高水位,ISR 中所有副本已同步的消息位移 │
│ │ └── 消费者只能消费到 HW 之前的消息 │
│ └── Log Start Offset:日志起始位移,最早可用消息的位移 │
│ │
│ HW 更新规则: │
│ ├── Leader:ISR 中所有副本 LEO 的最小值 │
│ └── Follower:min(LeaderHW, 自身LEO) │
│ │
└─────────────────────────────────────────────────────────────────────────┘
ISR 管理
┌─────────────────────────────────────────────────────────────────────────┐
│ ISR 管理机制 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ISR 扩容(添加副本到 ISR) │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ 条件:Follower LEO >= Leader HW - replica.lag.time.max.ms │ │
│ │ │ │
│ │ 流程: │ │
│ │ 1. Follower 追上 Leader 数据 │ │
│ │ 2. Leader 更新 ISR 列表,添加该副本 │ │
│ │ 3. Leader 更新 ZooKeeper/KRaft 中的 ISR 元数据 │ │
│ │ 4. Controller 收到通知,更新集群元数据 │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ │
│ ISR 收缩(从 ISR 移除副本) │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ 条件: │ │
│ │ ├── replica.lag.time.max.ms 内未发送 FetchRequest │ │
│ │ └── 或 LEO 落后 Leader 超过阈值 │ │
│ │ │ │
│ │ 流程: │ │
│ │ 1. Leader 检测到 Follower 失效或落后 │ │
│ │ 2. Leader 从 ISR 中移除该副本 │ │
│ │ 3. Leader 更新 HW(ISR 变小,HW 可能变大) │ │
│ │ 4. Leader 更新 ZooKeeper/KRaft │ │
│ │ 5. Controller 处理 ISR 变化 │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ │
│ 关键配置: │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ replica.lag.time.max.ms=30000 # ISR 同步超时 30秒 │ │
│ │ min.insync.replicas=1 # 最小 ISR 副本数 │ │
│ │ unclean.leader.election.enable=false # 禁止不完全选举 │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
Leader 选举
Controller 选举
┌─────────────────────────────────────────────────────────────────────────┐
│ Controller 选举机制 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ZooKeeper 模式(Kafka 3.x 之前): │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ Broker 1 Broker 2 Broker 3 │ │
│ │ │ │ │ │ │
│ │ │ │ │ │ │
│ │ └───────────────┼───────────────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌────────────────┐ │ │
│ │ │ ZooKeeper │ │ │
│ │ │ /controller │ ← 临时节点,抢注 │ │
│ │ └────────────────┘ │ │
│ │ │ │
│ │ 选举流程: │ │
│ │ 1. 所有 Broker 尝试创建 /controller 临时节点 │ │
│ │ 2. 第一个成功创建的 Broker 成为 Controller │ │
│ │ 3. 其他 Broker 监听该节点变化 │ │
│ │ 4. 节点删除时触发重新选举 │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ │
│ KRaft 模式(Kafka 3.x): │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ Controller Nodes (Quorum) │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │Controller│ │Controller│ │Controller│ │ │
│ │ │ Node 1 │ │ Node 2 │ │ Node 3 │ │ │
│ │ │(Leader) │ │(Follower)│ │(Follower)│ │ │
│ │ └─────────┘ └─────────┘ └─────────┘ │ │
│ │ │ │ │ │ │
│ │ └────────────┼────────────┘ │ │
│ │ │ │ │
│ │ Raft 共识协议 │ │
│ │ │ │
│ │ 特点: │ │
│ │ ├── 内置 Raft 共识,无需 ZooKeeper │ │
│ │ ├── Controller 集群使用 Raft 选举 Leader │ │
│ │ ├── 元数据存储在 __cluster_metadata 主题 │ │
│ │ └── 更简单的部署架构 │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
分区 Leader 选举
┌─────────────────────────────────────────────────────────────────────────┐
│ 分区 Leader 选举流程 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 场景:Leader 所在 Broker 故障 │
│ │
│ Leader (Broker 1) Follower (Broker 2) Follower (Broker 3) │
│ │ │ │ │
│ │ X 故障 │ │ │
│ ▼ │ │ │
│ ┌──────┐ │ │ │
│ │ 失效 │ │ │ │
│ └──────┘ │ │ │
│ │ │ │
│ │ │ │
│ └──────────┬─────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────┐ │
│ │ Controller │ │
│ │ (Broker 2) │ │
│ └────────┬─────────┘ │
│ │ │
│ │ 1. 监听到 Broker 1 失效 │
│ │ │
│ │ 2. 从 ISR 中选择新 Leader │
│ │ (Broker 2 或 Broker 3) │
│ │ │
│ │ 3. 更新 ZooKeeper/KRaft │
│ │ - leader = Broker 2 │
│ │ - ISR = [Broker 2, Broker 3] │
│ │ │
│ │ 4. 发送 LeaderAndIsrRequest │
│ │ 给所有副本 │
│ │ │
│ ▼ │
│ ┌──────────────────┐ │
│ │ 新 Leader │ │
│ │ (Broker 2) │ │
│ │ │ │
│ │ - 截断日志到 HW │ │
│ │ - 开始接收请求 │ │
│ └──────────────────┘ │
│ │
│ 选举策略: │
│ ├── 优先从 ISR 中选择 │
│ ├── ISR 为空且允许不完全选举:从 AR 中选择 │
│ └── ISR 为空且禁止不完全选举:分区不可用 │
│ │
│ Preferred Leader: │
│ ├── 副本分配时的第一个副本 │
│ ├── 目标:均衡 Leader 分布 │
│ └── auto.leader.rebalance.enable=true 可自动恢复 │
│ │
└─────────────────────────────────────────────────────────────────────────┘
Leader Epoch
┌─────────────────────────────────────────────────────────────────────────┐
│ Leader Epoch 机制 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 作用:解决 HW 截断导致的数据不一致问题 │
│ │
│ 问题场景: │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 1. Leader A (epoch=0) 写入消息 m1 (offset=0) │ │
│ │ 2. Follower B 复制 m1 │ │
│ │ 3. Leader A 宕机,B 被选为新 Leader (epoch=1) │ │
│ │ 4. B 写入消息 m2 (offset=0) ← 覆盖了 m1? │ │
│ │ 5. A 恢复,作为 Follower 加入 │ │
│ │ 问题:A 的 m1 和 B 的 m2 不一致 │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ │
│ Leader Epoch 解决方案: │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 每个分区维护 Leader Epoch 信息: │ │
│ │ ├── leader-epoch-checkpoint 文件 │ │
│ │ └── 记录: (epoch, startOffset) │ │
│ │ │ │
│ │ 示例: │ │
│ │ Epoch 0: startOffset=0 │ │
│ │ Epoch 1: startOffset=1 │ │
│ │ Epoch 2: startOffset=5 │ │
│ │ │ │
│ │ Follower 截断流程: │ │
│ │ 1. Follower A 恢复,发送 LeaderEpochRequest │ │
│ │ 2. 新 Leader B 返回最新的 epoch 和 endOffset │ │
│ │ 3. A 比较,发现 epoch 不一致 │ │
│ │ 4. A 根据自己的 epoch 信息截断到正确的 offset │ │
│ │ 5. A 开始从正确位置同步 │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
Controller 详解
Controller 职责
Controller 核心职责:
├── 集群元数据管理
│ ├── 所有 Topic 的分区分配信息
│ ├── 所有分区的 Leader 和 ISR 信息
│ └── 所有 Broker 的状态信息
│
├── 分区状态管理
│ ├── 创建/删除 Topic
│ ├── 分区扩容
│ └── 副本重分配
│
├── Leader 选举
│ ├── 分区 Leader 选举
│ ├── Preferred Leader 平衡
│ └── 选举策略执行
│
├── Broker 上下线管理
│ ├── 新 Broker 注册
│ ├── Broker 故障处理
│ └── 分区迁移
│
└── 配置管理
├── Topic 级别配置
└── 动态配置更新
Controller 故障转移
┌─────────────────────────────────────────────────────────────────────────┐
│ Controller 故障转移 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 场景:当前 Controller 故障 │
│ │
│ ZooKeeper 模式: │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 1. ZooKeeper 检测到 /controller 节点删除 │ │
│ │ 2. 所有 Broker 收到 Watch 通知 │ │
│ │ 3. 所有 Broker 尝试创建 /controller 节点 │ │
│ │ 4. 第一个成功的 Broker 成为新 Controller │ │
│ │ 5. 新 Controller 从 ZooKeeper 加载完整元数据 │ │
│ │ 6. 新 Controller 初始化,处理未完成操作 │ │
│ │ │ │
│ │ 故障转移时间:秒级 │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ │
│ KRaft 模式: │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 1. Controller Quorum 检测到 Leader 失效 │ │
│ │ 2. Raft 协议触发新的 Leader 选举 │ │
│ │ 3. 新 Leader 从 __cluster_metadata 恢复元数据 │ │
│ │ 4. 新 Leader 开始处理请求 │ │
│ │ │ │
│ │ 故障转移时间:更短,无 ZK 依赖 │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
高可用配置
关键配置参数
# 副本配置
default.replication.factor=3 # 默认副本数
min.insync.replicas=2 # 最小同步副本数
# ISR 配置
replica.lag.time.max.ms=30000 # ISR 同步超时
replica.socket.timeout.ms=30000 # 副本 socket 超时
replica.fetch.timeout.ms=30000 # 副本拉取超时
replica.fetch.max.bytes=1048576 # 单次拉取最大字节
replica.fetch.wait.max.ms=500 # 拉取等待时间
# Leader 选举
auto.leader.rebalance.enable=true # 自动 Leader 平衡
leader.imbalance.per.broker.percentage=10 # 不平衡阈值
leader.imbalance.check.interval.seconds=300 # 检查间隔
unclean.leader.election.enable=false # 禁止不完全选举
# Controller
controller.socket.timeout.ms=30000 # Controller socket 超时
controller.request.timeout.ms=30000 # Controller 请求超时
# 日志配置
log.flush.interval.messages=10000 # 刷盘间隔(消息数)
log.flush.interval.ms=1000 # 刷盘间隔(时间)
高可用最佳实践
┌─────────────────────────────────────────────────────────────────────────┐
│ 高可用最佳实践 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 1. 副本数配置 │
│ ├── 生产环境:至少 3 副本 │
│ ├── 跨机架分布:避免单机架故障 │
│ └── min.insync.replicas=2 │
│ │
│ 2. Leader 分布优化 │
│ ├── 启用自动 Leader 平衡 │
│ ├── 监控 Leader 分布均匀性 │
│ └── 手动触发平衡:kafka-leader-election.sh │
│ │
│ 3. ISR 监控 │
│ ├── 监控 ISR 收缩告警 │
│ ├── 监控 Under Replicated Partitions │
│ └── 及时处理落后副本 │
│ │
│ 4. 故障恢复 │
│ ├── 准备 Controller 故障转移方案 │
│ ├── 配置合理的超时时间 │
│ └── 禁止不完全选举(生产环境) │
│ │
│ 5. 监控指标 │
│ ├── OfflinePartitionsCount(离线分区数) │
│ ├── UnderReplicatedPartitions(副本不足分区) │
│ ├── ActiveControllerCount(活跃 Controller 数) │
│ └── ISRShrinks/ISRExpands(ISR 变化) │
│ │
└─────────────────────────────────────────────────────────────────────────┘
小结
本章我们学习了:
- Broker 架构:SocketServer、RequestChannel、HandlerPool、LogManager、ReplicaManager
- 副本机制:Leader/Follower 角色、副本同步流程
- ISR 管理:ISR 扩容/收缩机制、关键配置
- Leader 选举:Controller 选举、分区 Leader 选举、Leader Epoch
- 高可用配置:关键参数、最佳实践
参考资料
- Kafka Broker Configuration
- Kafka Replication
- KIP-500: Replace ZooKeeper with a Self-Managed Metadata Quorum
下一章预告
在下一章《消息可靠性保证》中,我们将深入探讨:
- 消息不丢失的最佳实践
- Exactly Once 语义实现
- 消息顺序性保证
- 生产者与消费者可靠性配置
Kafka 核心原理系列持续更新中,欢迎关注!