86 lines
6.9 KiB
Markdown
86 lines
6.9 KiB
Markdown
---
|
||
id: backend.grpc-microservices.interview
|
||
title: gRPC 与微服务面试题
|
||
type: interview
|
||
domain: backend
|
||
stack: [grpc-microservices]
|
||
tags: [gRPC, 微服务, 服务治理, 分布式事务, 面试]
|
||
level: advanced
|
||
related: [backend.grpc-microservices.grpc-proto, backend.grpc-microservices.governance]
|
||
week: [14, 16]
|
||
---
|
||
|
||
# gRPC 与微服务面试题
|
||
|
||
## 基础
|
||
|
||
### 【基础】gRPC 和 REST 有什么区别?
|
||
|
||
**参考答案**:gRPC 基于 HTTP/2,用 Protobuf 二进制序列化,有 IDL 和代码生成,强类型契约,支持流式调用;REST 通常基于 HTTP/1.1 + JSON,可读性好、浏览器原生支持、生态通用。内部服务之间、追求性能和契约时用 gRPC;对外开放、前端直接调用用 REST。
|
||
|
||
**追问**:浏览器能直接调用 gRPC 吗?→ 不能直接调用(浏览器无法完全控制 HTTP/2 帧),需要 gRPC-Web + 代理,或者网关把 gRPC 转成 HTTP/JSON(如 grpc-gateway)。
|
||
|
||
### 【基础】gRPC 有哪几种调用方式?
|
||
|
||
**参考答案**:一元调用(一请求一响应)、服务端流(一请求多响应,如推送进度)、客户端流(多请求一响应,如分片上传)、双向流(如聊天)。
|
||
|
||
**追问**:流式调用适合什么场景?→ 服务端流适合推送进度、大模型逐字输出;客户端流适合上传大量数据;双向流适合实时交互。流式调用的负载均衡和重试比一元调用复杂。
|
||
|
||
### 【基础】Protobuf 字段编号有什么规则?
|
||
|
||
**参考答案**:编号决定序列化后的字段标识,发布后不能修改、不能复用;删除字段用 `reserved` 保留编号和名字;新增字段使用新编号,对老客户端兼容(老客户端会忽略未知字段)。1~15 编码只占一个字节,留给高频字段。
|
||
|
||
**追问**:删除一个字段要怎么做?→ 不要复用它的编号和名称,用 `reserved` 声明保留,防止以后被误用导致新旧版本解析错乱。
|
||
|
||
### 【基础】微服务相比单体有什么优缺点?
|
||
|
||
**参考答案**:优点是独立部署、独立扩容、技术栈可以不同、故障隔离、团队可以独立开发。缺点是网络调用带来的延迟和失败、分布式事务和数据一致性、运维和监控复杂度、调试和排查困难。团队小、业务早期通常模块化单体更合适。
|
||
|
||
**追问**:团队只有几个人,适合微服务吗?→ 一般不适合。先做模块化单体(模块边界清晰、独立的数据访问层),等团队和业务规模需要独立部署和扩展时再拆。
|
||
|
||
## 进阶
|
||
|
||
### 【进阶】什么是服务雪崩?怎么防止?
|
||
|
||
**参考答案**:一个下游服务变慢,调用方的线程 / 连接被占住,进而调用方也变慢,最终一层层传导导致整个系统不可用。防止:所有调用设超时;熔断故障的下游;限流保护自己;降级非核心功能;隔离资源(不同下游用不同的连接池 / 并发限制);重试要有限次数并退避。
|
||
|
||
**追问**:限流和熔断的区别?→ 限流保护自己,限制进入的请求量;熔断保护调用方,下游故障时快速失败,不再持续调用。
|
||
|
||
### 【进阶】讲一下熔断器的状态机。
|
||
|
||
**参考答案**:关闭状态正常放行并统计失败;失败达到阈值(次数或比例)后打开,直接返回失败;冷却时间后进入半开,放少量请求试探;试探成功则关闭,失败则重新打开。只把下游故障(超时、不可用)计为失败,业务错误不算。
|
||
|
||
**追问**:半开状态放行多少请求?→ 少量探测请求(一个或几个),成功率达标才关闭熔断器,否则重新打开;具体阈值根据业务配置。
|
||
|
||
### 【进阶】重试要注意什么?
|
||
|
||
**参考答案**:只重试可重试的错误(如 `Unavailable`)和幂等操作;指数退避加随机抖动;限制次数,并受整体截止时间约束;只在一层做重试,避免逐层放大;熔断打开时不重试;非幂等操作需要幂等键。
|
||
|
||
**追问**:什么样的请求可以重试?→ 幂等的请求,以及明确可重试的错误(超时、连接失败、`UNAVAILABLE`);非幂等请求要配合幂等键才能重试。
|
||
|
||
### 【进阶】分布式事务有哪些方案?怎么选?
|
||
|
||
**参考答案**:2PC 强一致但性能差、协调者单点,很少用于互联网业务;TCC 通过 Try/Confirm/Cancel 预留资源,一致性较好但业务侵入大;Saga 把长事务拆成本地事务 + 补偿,适合流程长的业务;可靠消息(发件箱 + MQ + 幂等)实现最终一致,最常用。一般优先“可靠消息 + 对账”,对资金等强一致场景再考虑 TCC。
|
||
|
||
**追问**:Saga 的补偿失败怎么办?→ 补偿操作本身要幂等、可重试;多次失败后进入人工处理队列并告警,所有步骤记录状态便于对账。
|
||
|
||
## 场景题
|
||
|
||
### 【场景题】订单服务调用库存服务,库存服务偶尔超时,应该怎么处理?
|
||
|
||
**参考答案**:设置合理的超时(小于订单接口自身的超时);扣库存接口要求幂等(带订单号作为幂等键),这样超时后可以安全重试一两次;超时结果不确定时,不能直接认为失败——通过查询接口或后续对账确认;持续超时时熔断,下单接口返回“系统繁忙”;排查库存服务本身的慢查询或资源问题。
|
||
|
||
**追问**:超时时间怎么设置?→ 根据下游的 P99 延迟设定,并且上游总超时要大于下游超时之和(或通过 deadline 传递剩余时间),避免上游已经放弃,下游还在执行。
|
||
|
||
### 【场景题】团队想把单体系统改成微服务,你怎么推进?
|
||
|
||
**参考答案**:先明确要解决的问题,能用模块化、读写分离、缓存解决的不急于拆;选边界清晰、需求明确的模块先拆;用绞杀者模式:先在单体内抽接口,再实现远程服务,用开关灰度切流,随时可回退;数据分库通过双写或 CDC 迁移并校验;同时建设超时熔断、链路追踪、集中日志等基础设施。详见[单体拆分场景](scenarios/monolith-split.md)。
|
||
|
||
**追问**:拆分时数据怎么迁移?→ 先拆代码不拆库,再拆库:新服务通过接口访问数据;数据迁移用双写或 CDC 同步,校验一致后切换读,再切换写。
|
||
|
||
### 【场景题】一个请求经过 5 个服务,用户反馈偶尔很慢,怎么定位?
|
||
|
||
**参考答案**:靠链路追踪:入口生成 trace ID,各服务通过 OpenTelemetry 记录 span 并传递上下文,在追踪系统里按耗时筛选慢请求,看是哪一个 span 慢;再结合该服务的日志(带 trace ID)和指标(CPU、GC、连接池、数据库慢查询)找原因。没有追踪系统时,至少要在日志里统一打印请求 ID。
|
||
|
||
**追问**:没有链路追踪系统怎么办?→ 至少在入口生成请求 ID,所有服务透传并写进日志,用日志平台按请求 ID 搜索,并在每个服务记录调用耗时。
|