记一次微服务无响应问题分析

67 阅读3分钟

某天下午,运营同学突然反馈,app点击各个按钮都无反应,当时的第一反应是可能是个别用户,app死机之类的问题,但是运营同学说不是,好多人在反馈同样的问题,我就赶紧拿自己手机里的app试了一下,确实某些页面点了以后无反应。然后赶紧登录k8s管理台,看一下微服务是不是down掉了,看了一下所有的微服务都是正常的没有容器down掉。再去看服务的日志,看看有没有内存溢出的情况,看了所有的微服务日志,没有内存溢出的异常打印。当初在想奇怪了,容器还在怎么不能提供服务了呢。 此时在想是不是线程堵塞了,用arthas排查了一遍确实是线程阻塞了。这时候想起来原来压测的时候,也遇到过这个问题。当时的问题是springboot里面的web服务器线程数配的太少,因为微服务循环依赖,导致了微服务死锁了。用一张图来展示:

image.png

图中,base是一个基础服务,给各个服务提供基础的一些功能,大部分的其他微服务都需要依赖base服务,由于历史原因,base服务中的某些特殊的接口需要依赖edu服务,这样服务和服务直接就形成了一个环,当此时流量突然增加的时候,就有可能造成base和edu服务之间的死锁,原因分两种,1、服务相互依赖,2、微服务的线程数不够。 我们当时用的是undertow作为web容器,undertow如果没有配置的话,默认的线程是计算规则如下:

I/O线程数
  • 默认值Math.max(Runtime.getRuntime().availableProcessors(), 2)
  • 计算逻辑:CPU核心数和2之间取较大值
  • 作用:处理网络I/O操作,负责接收请求和发送响应
工作线程数(Worker线程)
  • 默认值I/O线程数 * 8
  • 计算逻辑:I/O线程数乘以8
  • 作用:处理具体的业务逻辑和请求处理
举例说明

假设你的服务器有4个CPU核心:

  • I/O线程数 = max(4, 2) = 4
  • 工作线程数 = 4 * 8 = 32

一般情况我们的docker都是配置的2ge或者4gcpu,工作线程数也就是在16或者32,当瞬时流量上来的时候,有可能会导致微服务之间的死锁,这个时候最简单的解决办法就是加大undertow的线程数:

server:
  port: 0
  shutdown: graceful
  undertow:
    threads:
      worker: 256

再一个就是在微服务设计的时候,尽量避免微服务相互调用。
避免微服务相互调用的核心策略:

  1. 正确的领域建模 - DDD方法论
  2. 事件驱动架构 - 异步解耦
  3. 数据冗余策略 - 减少跨服务查询
  4. 单向依赖原则 - 清晰的依赖方向
  5. 聚合器模式 - 专门的组合服务
  6. 消息队列 - 异步通信中间件