面试官:你说你做过微服务,那这些你讲得清吗?
刚开始做 Go 微服务那会儿,我以为面试就两类题:写算法、背八股。 下面这篇面经不讲经历,只给你面试题 + 示例回答(并附带常见追问)。 如果你能把这些回答“讲顺”,大概率能过掉后端/微服务方向的核心拷问。 面试官: 很多人的“标准答案”是: 但面试官真正想听的是:你是否理解它的边界和传播链路。 示例回答(面试版): 常见追问(别踩坑): 面试官:你们为什么用 Elasticsearch?它为什么快? 如果你只说“全文检索、分词”,基本等于没答。 示例回答(面试版): ES 快主要来自 Lucene 的索引结构与查询执行方式: 常见追问(面试官爱追): 面试官:你们服务/模块之间怎么通信? 示例回答(面试版): 常见追问: 面试官:你们为什么是 RabbitMQ,不是 Kafka? 示例回答(面试版)(按你项目取舍讲): 我们更看重低延迟、灵活路由、可靠投递、易运维: 常见追问: 这题建议你直接给“对比维度”,面试官听得最舒服。 示例回答(面试版): 补一句更像“项目经验”的话: 面试官:B+ 树你怎么理解?“本质”是什么? 示例回答(面试版): 它把数据(或主键)集中在叶子节点,内部节点只存索引键;叶子节点通过链表相连,因此: 常见追问: 面试官:Go 的 示例回答(面试版): 关闭语义: 常见追问(高频坑): 面试官:讲讲 GMP,如果 goroutine 阻塞了会怎样? 示例回答(面试版): 阻塞分两类(这是加分点): 常见追问: 面试官:有了 G 和 M,为什么还要 P? 示例回答(面试版): 面试官:协程泄漏你遇到过吗?一般怎么发生? 示例回答(面试版): 面试官喜欢听到的“工程化收尾”: 面试官:Docker 和 K8s 你怎么理解?区别是什么? 示例回答(面试版): 常用对象我能讲清楚: 一句话总结(很好用): 当这些题连着问的时候,面试官通常不是要你背概念,而是看你有没有三种能力: 写在最后: 最近私信问我面试题的小伙伴实在太多了,一个个回有点回不过来。 我大家公认最容易挂的 AI/Go/Java 面试坑点 整理成了一份 PDF 文档。里面不光有题,还有解题思路和避坑指南。 想要的同学,直接加我微信wangzhongyang1993,或者关注并私信我 【面试】,我统一发给大家。一场面试 11 连问,背八股很容易当场露馅
后来才发现,很多面试官真正喜欢的,是这种“连环追问”:你会用就行?那你说说为什么这么用;你说它快?那你说说快在哪;你说系统稳定?那你说说极端情况下会发生什么。
🪤 1)
context 怎么用的?context 你项目里怎么用?context 就是用来传参的,或者加个超时。context 主要解决 3 件事:取消(cancel)、超时/截止时间(deadline)、跨边界的请求级元数据(value)。ctx 作为函数的第一个参数,沿调用链往下传到所有可能阻塞的地方:DB/Redis/HTTP/gRPC/MQ publish 等。WithTimeout/WithCancel。WithValue 只放请求范围、必须跨 API 边界的元数据(比如 traceId、userId),不把业务参数塞进去,更不会把 ctx 存到 struct 里长期持有。func (s *Service) CreateOrder(ctx context.Context, req *CreateOrderReq) error {
ctx, cancel := context.WithTimeout(ctx, 2*time.Second)
defer cancel()
// 1) 传给 DB / RPC / Redis
if err := s.repo.InsertOrder(ctx, req); err != nil {
return err
}
// 2) 自己的 goroutine 也要“跟着 ctx 退出”
go func() {
select {
case <-ctx.Done():
return
case <-time.After(200 * time.Millisecond):
_ = s.metrics.Report(req.OrderID) // 示例:真正实现里也应传 ctx
}
}()
return nil
}context 里为什么不建议传业务参数?”——因为可读性差、无类型约束、容易滥用;更重要的是会让 ctx 的语义从“控制与元信息”变成“万能背包”。⚡ 2)为什么选 ES?ES 为什么快?
LIKE '%xx%' 或冗余字段很难做,且代价很高。🔌 3)模块之间的通信怎么做的?
同步(gRPC)适合:
+ 强依赖、必须拿到结果(比如查库存、查配置)
异步(MQ)适合:
+ 最终一致、允许延迟(比如通知、异步落库、索引更新)🐇 4)为什么选 RabbitMQ?
🆚 5)RabbitMQ 和 Kafka 的主要区别?
维度 RabbitMQ Kafka 模型 队列/交换机路由 分区日志(log) 消费 以投递/确认为核心 以 offset/重放为核心 历史消息 通常消费即删除(按队列语义) 按保留策略长期保存,可重放 吞吐 中高(偏低延迟业务) 很高(大吞吐流式) 顺序性 单队列可保证 分区内有序 典型场景 业务事件、任务队列、复杂路由 埋点日志、流式计算、CDC、事件总线 我们用 RabbitMQ 做“业务事件通知”,用 Kafka(如果有)做“日志/埋点/CDC 流”,这样职责更清晰。
🌳 6)B+ 树的本质是什么?
📮 7)
channel 了解多少?channel 你了解多少?有缓冲/无缓冲差别?chan 是 Go 的并发通信原语,本质是“同步/队列化的通信通道”,用来在 goroutine 之间安全传递数据。ok=false),但再发送会 panic;select {
case v := <-ch:
_ = v
case <-ctx.Done():
return ctx.Err()
}nil channel:收发都会永久阻塞,常用于在 select 里动态开关分支。context、超时、或明确关闭通道;不要让 for { <-ch } 在没有退出条件的情况下跑。⚙️ 8)GMP 模型:如果阻塞了会怎么样?
🧩 9)为什么要有 P?
GOMAXPROCS 控制并行度:最多同时有多少个 P 在跑,也就最多同时跑多少个 goroutine(严格说是同时执行的 G 的数量上限)。🕳️ 10)什么情况下会协程泄漏?
for range ch 等不到 close。context.Background() 或者忘了 cancel()。time.NewTicker 没有 Stop(),goroutine 一直被唤醒做无意义工作。errgroup 统一回收。g, ctx := errgroup.WithContext(ctx)
g.Go(func() error { return s.callA(ctx) })
g.Go(func() error { return s.callB(ctx) })
return g.Wait()我会给所有后台 goroutine 一个明确的退出条件(ctx / close channel / done),并且在压测和线上用 pprof 看 goroutine 数是否稳定。
🐳 11)讲一下 Docker 和 K8s
Deployment:无状态应用的发布与滚动升级Service:稳定访问入口(负载均衡/服务发现)Ingress:HTTP 路由入口(配合 Ingress Controller)ConfigMap/Secret:配置与密钥HPA:按指标自动扩缩容StatefulSet:有状态服务(按需)Docker 更像“把程序装进标准集装箱”,K8s 更像“港口调度系统”,负责把集装箱放到合适的船上、坏了自动换、忙了自动加船。
✅ 总结:面试官其实在考什么?
你不需要把每个点都讲到论文级别,但你需要在关键处“说出底层原因”,并且能落到工程实践的兜底方案上。
END