Skip to content

【面试题】1. Spring Cloud Netflix 全套和 Spring Cloud Alibaba 全套在实际项目中怎么选?各自的优劣势是什么? ​

【标准答案】Netflix套件的优势是生态久、文档多,但大部分组件已停更(Eureka、Hystrix、Zuul都进维护模式了)。Alibaba套件优势是组件全是国产、社区活跃、经历过双11验证,Nacos一个组件干注册+配置两件事,Sentinel比Hystrix更灵活。实际选型做到"Nacos + Sentinel + Gateway"三板斧就能覆盖大部分场景。如果你团队对Netflix熟、存量系统大,可以逐步迁移,不用一刀切。核心看三点:社区活跃度、你团队的熟悉成本、生产环境的验证案例。现在新项目基本无脑选Alibaba了。

【面试题】2. K8s 的 Service + Ingress 能不能替代注册中心(Nacos/Eureka)?什么场景下必须保留注册中心? ​

【标准答案】K8s的Service本质是四层负载均衡,基于iptables/IPVS做Pod间转发,适合简单的无状态服务直连场景。但一旦你上了微服务框架(Dubbo/Spring Cloud),注册中心就不能省,原因三个:一、注册中心携带服务元数据(版本、分组、权重),K8s Service没有;二、注册中心支持细粒度的流量治理(灰度路由、同机房优先),K8s Service做不到;三、注册中心是服务框架抽象层,如果你要混合部署K8s+虚拟机甚至多云,注册中心是统一的服务发现层。结论:纯K8s+简单HTTP服务可以不要注册中心,微服务框架技术栈下注册中心是必须的。

【面试题】3. 配置中心的长轮询(Long Polling)和长连接推送(gRPC Stream)两种配置下发机制,原理上有什么区别?哪种更快? ​

【标准答案】长轮询是客户端发起HTTP请求,服务端hold住连接不立即返回,等有变更或超时(比如30秒)才响应,客户端收到后马上再发一个请求。本质是"伪实时",但在HTTP协议下最省事,Nacos 1.x客户端用的就是这个。长连接推送是客户端通过gRPC双向流维持一条TCP连接,服务端有变更直接push过来,不需要客户端不断轮询。Nacos 2.x升级后就用gRPC替代长轮询了,延迟更低、节省连接数。理论上gRPC推送更快,但长轮询在大部分场景下延迟也就1-3秒,够用了。如果配置变更不要求秒级生效,长轮询的方案更简单可靠。

【面试题】4. 分布式系统中为什么不能依赖物理时钟做全局排序?NTP同步的误差会导致什么问题?逻辑时钟和向量时钟各自解决了什么? ​

【标准答案】分布式系统最要命的就是"时间不可靠"。每台机器的晶振频率不同,经过NTP同步后仍有毫秒级的偏差,如果你用物理时间戳做"A事件先于B事件"的判断,时钟偏差会导致因果倒置。逻辑时钟(Lamport Clock)不关心物理时间,只关心事件发生的"happens-before"关系:每个节点维护一个计数器,发消息时带上自己的计数器值,接收方取max(自己的值,收到的值)+1。但逻辑时钟只能确定偏序,A→B和C→D之间没有因果关系时,无法比较。向量时钟给每个节点独立维护一个计数器数组,能更精确判断因果和并发关系。面试时说到Lamport Clock和Vector Clock的原理就够用了。

【面试题】5. RocketMQ 的事务消息和传统 2PC 有什么本质区别?半消息(Half Message)机制是怎么实现的? ​

【标准答案】传统2PC是同步协议,协调者等所有参与者投票,任何一个参与者挂了就全体阻塞,所以生产上几乎没人用。RocketMQ事务消息是异步模型,核心是"半消息"机制:生产者先发一条"半消息"到MQ,这条消息对消费者不可见;然后执行本地事务;如果本地事务成功,发Commit让半消息对消费者可见;如果失败,发Rollback把半消息删掉。关键兜底是"事务状态回查"——如果MQ没收到Commit/Rollback(比如生产者挂了),MQ会定时回调生产者接口查询事务状态,反复查直到拿到明确结果。整个机制保证了"本地事务提交→消息可见"的最终一致性,不会像2PC那样锁资源。

【面试题】6. 自适应负载均衡里 P2C(Power of Two Choices)算法是怎么工作的?为什么它比纯随机和加权轮询更适合高并发场景? ​

【标准答案】P2C的核心思路很简单:每次请求时随机选两个节点,挑其中负载更低的那个发请求。就这么简单的策略,数学上被证明能极大减少负载方差。纯随机的问题是有概率把很多请求打到同一个节点上,导致"忙的忙死,闲的闲死";加权轮询对节点性能差异有改善,但无法应对动态负载变化。P2C的巧妙在于:随机选两个的操作复杂度是O(1),不需要维护全局负载计数器,天然去中心化;而且"选最低负载"这一步让请求天然倾向于空闲节点,实现了自适应。Go的gRPC客户端负载均衡就默认用了P2C,生产效果很好。选多个的话效果提升不大,选两个就是最佳性价比。

【面试题】7. 微服务架构中,业务降级策略体系怎么分层设计?页面降级、功能降级、读降级分别怎么落地? ​

【标准答案】降级策略分三层设计最清晰。第一层"页面降级"——在前端或网关层做,当核心服务不可用时返回静态兜底页面或缓存内容,比如商品详情页服务挂了就展示上一次的快照。第二层"功能降级"——在服务层做,把非核心功能关掉,比如支付时不再实时计算积分,改成异步补算或者直接跳过。第三层"读降级"——读请求走缓存,甚至过期缓存也直接返回,避免因缓存失效触发大量请求打到底层数据库。落地要点:所有降级开关必须集中管理(配置中心),支持按服务、按接口、按流量百分比精细控制;降级触发条件建议同时走熔断器的自动触发和人工手动触发两条路;恢复时必须灰度放量逐步解除降级,防止瞬时流量打挂刚恢复的服务。

【面试题】8. 网关层的分布式限流怎么做?Redis Lua 原子计数 + 滑动窗口的方案和本地令牌桶方案各自的适用场景和坑是什么? ​

【标准答案】先说本地令牌桶:每个网关实例自己维护令牌桶,不依赖外部存储,性能极高、没有网络开销。但缺点致命——多实例网关下,每个实例放行QPS=N,N个实例总放行就是N×配置值,分布式限流彻底失效。Redis Lua方案是:每个请求去Redis执行一段Lua脚本,原子性地检查当前窗口计数并决定放行与否,保证全局计数准确。缺点是每次请求都要调用Redis,如果Redis挂了得考虑降级策略(是全部放行还是全部拦截)。生产上的最佳实践是双层限流:先在本地用令牌桶做第一层粗粒度限流(把总QPS除以实例数),再走Redis做第二层精确限流。这样大部分正常流量在本地就处理了,Redis只处理需要精确控制的边界情况。

【面试题】9. RedLock 算法到底安不安全?Martin Kleppmann 为什么质疑它?Redisson 的 RedLock 实现有什么改进? ​

【标准答案】Martin的核心质疑是:RedLock假设时钟是准确且单调的,但在分布式系统中GC暂停、网络延迟、时钟跳变都会打破这个假设。他的经典反驳场景是:客户端A在5个Redis实例中拿下3个锁,然后发生长时间GC暂停,等GC结束后锁已经自动过期了,另一个客户端B在此期间拿到了锁,导致两个客户端同时认为自己持有锁。Redis作者Antirez的反驳是:设置合理的锁超时+GC暂停概率极低+可以用fencing token做版本校验。Redisson的实现做了增强:加锁操作并行发到所有节点、可配置quorum数量、加锁失败时自动释放已获取的锁。结论:如果对一致性的要求是"极高"(比如金融扣款),用ZooKeeper或etcd而不是Redis锁;如果是一般业务场景(防止重复提交、定时任务互斥),RedLock方案够用且性能好很多。

【面试题】10. 生产环境收到告警:"核心接口P99延迟从200ms飙升到3秒",你怎么在 Logging、Metrics、Tracing 三者之间串联,3分钟内定位根因? ​

【标准答案】第一分钟看Metrics:先拉Grafana大盘,确认是哪个服务的哪个接口出问题,同时看该服务的CPU、内存、GC频率、线程池队列长度有没有异常。如果CPU和线程池队列同时飙升,基本是下游依赖堵塞。第二分钟看Tracing:在SkyWalking/Jaeger中按该接口的TraceId找几个慢请求的调用链,看火焰图,定位到是调用链上哪个Span耗时最长。比如发现调用订单服务的Span占了2.8秒,而正常是50ms。第三分钟看Logging:在ELK里搜这个时间段的订单服务日志,过滤error和timeout关键字。通常能直接看到类似"Connection pool exhausted, wait timeout 3000ms"的日志,或者慢SQL日志。三支柱的串联逻辑是:Metrics告诉你"哪里出问题了",Tracing告诉你"问题在调用链的哪一环",Logging告诉你"这一环具体发生了什么"。三者缺一不可。