麒麟软件后端一面:TCP、Redis 原子性、延迟任务与 Kafka
- 轮次
- 一面
- 时间
- 2026-10
- 来源
- 牛客网
《面试题目》
- 简单自我介绍,重点讲实习经历与负责的模块,控制两分钟以内。
- TCP 和 UDP 有什么区别?TCP 传输的内容是什么?三次握手的过程是什么?TCP 如何保证可靠传输?
- HTTP 不同版本之间有什么区别?
- Redis 有哪些常见的数据类型?String 类型有哪些应用场景?
- Redis 中使用 SETNX 时,如何保证设置过期时间的原子性?
- Redis Pipeline 有什么优势?存在哪些问题?
- 你在项目中使用过什么设计模式?策略模式和工厂模式有什么区别?如果新增一种策略,需要修改哪些地方?
- 为什么不采用 JVM 自带的延迟队列?
- 延迟任务系统的冷热分层机制是怎么实现的?
- 延迟任务系统如何进行对账和补偿?
- 如果消费者执行任务失败,应该如何处理?
- 目前这套延迟任务系统能支撑多大的任务量?如果每天有百万级任务,你会如何扩展?
- 你的 Agent 项目中,Tool Calling 是如何使用的?生成结果的校验和修复流程是怎样的?
- Kafka 中 Topic、Partition、Offset 分别是什么?
- 为什么采用实时聚合加离线重算的双通道架构?消费者组是如何设计的?
- 如果同一条消息需要触发短信和积分两个业务,应该如何处理?
- Kafka 为什么具有较高的吞吐量?
- 你的意向工作地点是哪里?
《参考解析》
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)、批量压缩、分区并行。同一条消息要触发短信和积分,正确做法是每个业务一个消费者组,各自独立消费、独立位点,而不是在一个消费者里串行做两件事——否则一个业务失败会拖累另一个。
双通道架构:实时聚合负责低延迟的近似结果,离线重算负责修正(补迟到数据、修正错误)。两套结果要有明确的版本与生效规则:离线结果覆盖实时结果时,前端要说清口径。这么做是因为流式处理的「恰好一次」很难做到,与其追求完美,不如承认近似 + 定期校正。