面灵AI→

得物 Golang 工程师二面面经

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

《面试题目》

  1. 请做一下自我介绍,并介绍你在腾讯的项目经历。
  2. 时间轮算法的原理是什么?
  3. 限流算法有哪些?
  4. 单机单实例固定限流和多机总体限流分别适用什么场景?
  5. 手撕:用 Go 协程打印多轮 abc,轮数不固定、单轮字符数不固定。
  6. 思维题:36 匹马、6 个赛道、没有计时器,最少需要多少轮比赛才能找到跑得最快的前三匹马?

《参考解析》

时间轮:核心是「按桶分槽 + 只推进当前槽」。 时间轮把一个时间范围切成若干个槽(slot),每个槽挂一条任务链表,再配合一个指针按固定 tick 前进;添加任务时按「延时 / tick」算出到期槽位挂进去,指针走到该槽就把任务取出执行。它把定时任务的插入与删除降到 O(1),比堆(O(log n))更适合海量短定时任务,Netty 的 HashedWheelTimer、Kafka 的延时队列都是这个思路。代价是多了一层时间精度上的取整(精度由 tick 决定),以及长延时会绕多圈——常用「圈数(round)」标记或分层时间轮(时/分/秒多级,类似钟表)解决。对比实现时可以说清三者的取舍:优先队列/最小堆适合任务量不大且精度要求高、延时队列(Redis ZSet、RabbitMQ TTL+DLX)适合跨进程但精度与吞吐受限、时间轮适合单进程内高并发低延时场景。

限流算法的清单要能顺着说下去,并给出各自缺点。 常见有:固定窗口计数(最简单,但窗口边界处会出现两倍瞬时流量)、滑动窗口(把窗口切成小格分别计数,边界更平滑)、滑动日志(记录请求时间戳,最精确但内存开销大)、漏桶(以固定速率流出,强制平滑但无法应对突发)、令牌桶(按固定速率发令牌,桶容量决定突发上限,最实用)、并发数/连接数限流(保护下游线程池)、以及自适应限流(按响应时间或负载动态调阈值)。落地时通常组合使用:入口按 IP/用户维度做令牌桶,服务间按接口做并发数限制,全局限流则用 Redis + Lua 保证原子性,或用 Sentinel 的集群流控模式由 token server 统一分配配额。追问常落在「Redis 限流的原子性和时钟问题」「限流后返回什么(429 + Retry-After)」「和熔断、降级的边界」,这几个都要能接住。

单机固定限流与多机总体限流:先明确「限的是谁」。 单机单实例固定限流把阈值写在应用内(本地令牌桶、Guava RateLimiter、信号量),不依赖外部组件,延迟低、不会因为网络抖动误伤,适合保护实例自身资源(线程池、连接池、本地缓存刷新)或下游允许的并发上限;缺点是扩容后总阈值随实例数放大,实例间负载不均时会有的被打满、有的空闲。多机总体限流(分布式限流)把阈值放到共享存储或中心节点(Redis + Lua、Sentinel 集群模式、网关层统一限流),保证整个集群对外表现一致,适合有对外配额约束的场景(第三方接口调用总量、风控阈值、活动库存类写入)。实践中往往是分层:入口网关做总体限流,服务内部再做单机兜底,形成两级保护。回答时补一句取舍——分布式限流每次请求都要访问中心存储,延迟与可用性风险都要评估,本地预取配额(如每次取一批令牌)是常见折中。

手撕「Go 协程多轮打印 abc」:考的是协程编排而不是语法。 题意是轮数与每轮字符数都不固定,所以不能写死三个固定协程各打印一个字母。稳妥解法是用 channel 做令牌传递:起 N 个协程(N = 单轮字符数),每个协程持有一个接收通道和下一个协程的发送通道,收到令牌就打印自己对应的字符并把令牌传给下一个,形成闭环;外层控制轮数,每轮由主协程发起一次、收满 N 个字符后进入下一轮。另一种写法是用带缓冲 channel 或者 sync.WaitGroup + 每轮重新同步。答题时要主动说清的点:为什么不能用共享变量加锁(可以,但通道更符合 Go 的沟通顺序理念)、channel 关闭与防泄漏(谁负责 close、如何保证所有协程退出)、以及如果有慢消费者会不会阻塞。写完补一句「如果是要求严格按 abc 顺序输出到标准输出,需要额外同步输出」,能体现你对并发的严谨。

36 匹马取前三:答案是 8 轮,关键是先排除不可能的马。 做法分三步:先分成 6 组各跑一场(6 场),得到每组内部排名;第 7 场让 6 个组的第一名比一次,假设结果为 A1 > B1 > C1 > D1 > E1 > F1。此时可以确定 A1 是全场最快,同时排除大量候选:D、E、F 三组的所有马都慢于各自组的第一名,而它们的第一名又慢于 C1,所以这三组全部出局;B 组里 B3 及以后慢于 B1、B2,C 组里 C2 及以后慢于 C1,也都出局。剩下的候选只有 A2、A3、B1、B2、C1 五匹——它们争夺第二、第三名,而赛道有 6 条,第 8 场让这五匹同场跑,取前二即可。整题考的是「用已经得到的偏序关系剪枝」,作答时把排除过程讲清楚比直接报答案更重要;如果面试官追问「要找前五名」「每场只能跑 5 匹」这类变体,思路仍是先建立组内与组间偏序,再最小化需要比较的候选集合。

这场面试的准备重点:项目深挖 + 中间件原理 + 白板并发。 整场 55 分钟、以腾讯项目为主线,说明面试官会从简历项目里顺着问技术选型(为什么用这个方案、有没有量化的收益、压测数据如何),所以项目要准备到「每个技术点都能往下一层讲」。复习顺序建议先把限流、时间轮、延迟队列、定时任务这类后端高频中间件原理成组过一遍,再把 Go 并发原语(goroutine、channel、select、context、sync 包)练到能白板写代码,最后刷一类思维题(称重、取 Top K、赛道取前三),这类题看重的是推理过程与边界条件,不要只背答案。