面灵AI→

麒麟软件后端一面:TCP、Redis 原子性、延迟任务与 Kafka

轮次
一面
时间
2026-10
来源
牛客网

《面试题目》

  1. 简单自我介绍,重点讲实习经历与负责的模块,控制两分钟以内。
  2. TCP 和 UDP 有什么区别?TCP 传输的内容是什么?三次握手的过程是什么?TCP 如何保证可靠传输?
  3. HTTP 不同版本之间有什么区别?
  4. Redis 有哪些常见的数据类型?String 类型有哪些应用场景?
  5. Redis 中使用 SETNX 时,如何保证设置过期时间的原子性?
  6. Redis Pipeline 有什么优势?存在哪些问题?
  7. 你在项目中使用过什么设计模式?策略模式和工厂模式有什么区别?如果新增一种策略,需要修改哪些地方?
  8. 为什么不采用 JVM 自带的延迟队列?
  9. 延迟任务系统的冷热分层机制是怎么实现的?
  10. 延迟任务系统如何进行对账和补偿?
  11. 如果消费者执行任务失败,应该如何处理?
  12. 目前这套延迟任务系统能支撑多大的任务量?如果每天有百万级任务,你会如何扩展?
  13. 你的 Agent 项目中,Tool Calling 是如何使用的?生成结果的校验和修复流程是怎样的?
  14. Kafka 中 Topic、Partition、Offset 分别是什么?
  15. 为什么采用实时聚合加离线重算的双通道架构?消费者组是如何设计的?
  16. 如果同一条消息需要触发短信和积分两个业务,应该如何处理?
  17. Kafka 为什么具有较高的吞吐量?
  18. 你的意向工作地点是哪里?

《参考解析》

TCP 与可靠传输:TCP 是面向连接、字节流、可靠有序的传输层协议;UDP 无连接、面向报文、不保证顺序与到达,开销小、延迟低,适合实时音视频和 DNS 这类「丢一帧没关系」的场景。三次握手是 SYN → SYN+ACK → ACK,目的除了确认双方收发能力,更重要的是同步初始序列号,防止历史连接上的旧报文被误当成新连接的数据。可靠传输靠五件事:序号与确认(累计确认 + 选择确认 SACK)、超时重传与快速重传、校验和、流量控制(滑动窗口)、拥塞控制(慢启动、拥塞避免、快重传快恢复)。「TCP 传输的内容是什么」这题其实在问「字节流」——它不保留应用层的消息边界,所以需要应用层自己定长/分隔符/长度前缀来拆包。

SETNX 与过期时间的原子性:SETNX 加 EXPIRE 是两条命令,中间可能崩溃导致 key 永不过期形成死锁。解法是用 SET key value NX EX seconds(一条命令完成),或者用 Lua 脚本把两步包进一个原子执行。更进一步,解锁也要原子:只能删自己持有的锁,所以 value 要放唯一标识(uuid + 线程 id),解锁用 Lua 比较后再删,避免误删别人的锁。

Pipeline 的收益与风险:收益是把 N 次网络往返压成 1 次,吞吐提升主要来自减少 RTT,而不是 Redis 执行更快。风险有三点:一是不具备原子性,中间某条失败不会回滚;二是阻塞,一次塞太多命令会长时间占用连接、也会让服务端输出缓冲区膨胀;三是无法在命令间做依赖判断(要依赖就得用 Lua 或事务)。实践上按批大小(比如 500~1000 条)切分,并做好异常重试的幂等。

策略模式与工厂模式:策略模式解决「同一行为有多种可替换实现」,把 if/else 分支换成多态分发,关注点在运行时选择算法;工厂模式解决「对象怎么创建」,把构造细节收口,关注点在创建。两者经常一起用:工厂按类型返回对应的策略实例。新增一种策略时,理想情况下只新增一个类并注册(依赖注入/注册表),不改调用方——这也是面试官真正想听的「开闭原则怎么落地」。

为什么不用 JVM 自带的延迟队列:DelayQueue 是单机内存结构,进程重启就丢、不能跨节点共享、容量受堆内存限制、也没有持久化和重试语义。业务上的延迟任务要求「至少执行一次 + 可观测 + 可补偿」,这些都得靠持久化存储 + 调度器来实现。

冷热分层与对账补偿:冷热分层是按触发时间远近拆存储——近期要触发的任务放内存时间轮或 Redis ZSET(热),远期任务放数据库按时间分片(冷),由定时任务把到期的那一批搬到热层。好处是热层扫描量恒定,不随总任务量增长。对账是双向的:正向查「该执行但没执行」(业务表有记录、任务表没有对应状态),反向查「执行了但业务没落库」(任务成功、业务失败),差异进补偿队列,补偿要带最大重试次数与人工兜底入口。

百万级任务的扩展:单机时间轮撑不住时按时间分片 + 任务分片扩展:调度层按秒级槽位分片,执行层用一致性哈希把任务分配到多台机器,每个消费者只拉自己负责的分片;存储层按时间分区表 + 定期归档。要防的是惊群(同一时刻大量任务到期)——加随机抖动把峰值摊平。

Tool Calling 与结果校验:Tool Calling 的关键是 schema 要严——参数类型、必填项、枚举值都写清楚,让模型在生成阶段就被约束;服务端仍然要独立校验(模型可能绕过或幻觉出参数)。校验失败的处理是「带错误信息重试一次 → 还不行为降级路径」,而不是无限重试。修复流程里最重要的是不要把模型输出直接当可信输入:所有副作用操作要有幂等键和人工确认边界。

Kafka 的几个概念与吞吐来源:Topic 是逻辑分类,Partition 是并行与有序的最小单位(分区内有序、跨分区无序),Offset 是消费者在分区内的位置。吞吐高来自:顺序写磁盘(追加日志)、页缓存、零拷贝(sendfile)、批量压缩、分区并行。同一条消息要触发短信和积分,正确做法是每个业务一个消费者组,各自独立消费、独立位点,而不是在一个消费者里串行做两件事——否则一个业务失败会拖累另一个。

双通道架构:实时聚合负责低延迟的近似结果,离线重算负责修正(补迟到数据、修正错误)。两套结果要有明确的版本与生效规则:离线结果覆盖实时结果时,前端要说清口径。这么做是因为流式处理的「恰好一次」很难做到,与其追求完美,不如承认近似 + 定期校正。