不是,哥们?又死锁了?

微服务死锁问题复盘

问题描述

在微服务A和B之间发生了死锁情况:

  1. 服务A发起同步RPC调用请求服务B的GetUser方法
  2. 服务B在处理GetUser请求时,需要调用服务A获取出战信息
  3. 此时服务A正在阻塞等待服务B的响应,无法处理新的请求
  4. 结果导致两个服务互相等待,形成死锁

问题根源

  1. ​不当的循环依赖​​:服务A依赖服务B的数据,服务B又依赖服务A的数据
  2. ​同步阻塞式调用​​:使用同步RPC加剧了问题,使系统更容易陷入死锁
  3. ​方法复用不当​​:直接复用了GetUser这个”重量级”方法,而实际只需要部分数据

历史背景

  1. 最初服务B提供了GetUser方法,该方法会聚合多个服务的数据(包括公会信息、出战信息等)
  2. 后来服务A需要服务B的部分数据,直接复用了GetUser方法
  3. 随着业务发展,GetUser方法内部逻辑扩展,开始需要服务A的数据

解决方案

  1. ​拆分聚合方法​​:
    • GetUser拆分为基本数据获取和扩展数据获取
    • 为服务A创建专用的轻量级数据接口,只返回必要字段
  2. ​避免循环依赖​​:
    • 重新设计数据流,确保服务间调用是单向的
    • 如果必须双向依赖,考虑通过消息队列异步处理
  3. ​缓存策略​​:
    • 对于不常变的数据,服务B可以缓存服务A的相关数据
    • 设置合理的缓存过期时间
  4. ​架构调整​​:
    • 考虑将高频互相访问的数据合并到一个服务中
    • 或者引入第三个服务作为数据聚合层

经验教训

  1. ​谨慎复用方法​​:看似方便的复用可能隐藏着设计问题
  2. ​警惕循环调用​​:微服务间调用应保持清晰的层级关系
  3. ​接口设计原则​​:接口应该单一职责,避免”全能”方法
  4. ​同步调用风险​​:同步阻塞式调用在复杂依赖关系中容易导致问题

改进计划

  1.  拆分GetUser方法,创建专用接口
  2.  评估将部分数据缓存在服务B的可行性
  3.  审查其他服务间调用是否存在类似风险
  4.  建立微服务调用规范,禁止循环同步调用

滚动至顶部