0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

微服务循环依赖调用引发的血案

jf_ro2CN3Fa 来源:芋道源码 2023-01-16 10:28 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

  • 问题表现
  • 初步分析
  • 探寻原因
  • 验证
    • Eureka 服务器
    • 服务 Foo
    • 服务 Boo
    • Jmeter
    • jstack
  • 总结

问题表现

最近的迭代转测后遇到了一个比较有意思的问题。在测试环境整体运行还算平稳,但是过一段时间之后,就开始有接口超时了,日志中出现非常多的 “java.net.SocketTimeoutException: Read timed out”。试了几次重启大法,每次都是只能坚持一会之后,再次出现 SocketTimeoutException。

注意 :在测试环境于遇到问题重启服务,并不是一个好的实践,因为重启可能会让不容易出现的问题现场被破坏。如果问题在测试环境不能再重新,却在发版后出现在生产环境的话,那不仅会造成生产运维事件,还要在巨大的压力下去解决问题。

基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/ruoyi-vue-pro
  • 视频教程:https://doc.iocoder.cn/video/

初步分析

顺着测试汇报的出现问题的场景,跟踪调用链上相关服务的日志,发现出现了微服务之间循依赖调用。大致情况可以抽象如下所示(图中所有调用都是 http 协议):

ef8cf1aa-953f-11ed-bfe3-dac502259ad0.png
  • Client 调用服务 Foo.hello()
  • Foo.hello() 逻辑中会调用服务 Boo.boo()
  • Boo.boo() 又调用回服务 Foo 的另外一个方法 another()

当然真实的场景要比较这个复杂,调用链更长,不过最终形成了环形依赖调用。至于这个环形依赖为什么回导致超时,当时想了多种可能,比如数据库慢查询、数据库锁、分布式锁等等。但是整个调用链上都是查询请求,而且查询相关的数据量也非常小,不会有锁存在。发生问题的时候也没有与查询数据相关的数据库写请求。

鉴于这个环形依赖调用确实是这个迭代版本中引入的变更,以及虽然没有理清其中的因果关系原理,但是这个环性依赖调用还是很可疑的,而且是不必要的环形调用。就抱着将环形依赖调用去掉试试看的态度,做了修复。修复完后,SocketTimeoutException 不再出现了。问题解决了。

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/yudao-cloud
  • 视频教程:https://doc.iocoder.cn/video/

探寻原因

问题虽然不再出现,但是凭运气解决的问题,通常有可能不是真的的解决。只有弄清楚背后的原理,我们才能真正的确认问题是不是这个原因导致的,这样的修复是不是真的把问题解决了。

通过假设环形调用就是导致调用超时的直接原因。我们看看能不能推出因果关系。通过把Foo 服务容器画的更详细一点,如下图:

efa2417c-953f-11ed-bfe3-dac502259ad0.png

通过这个图示,我们可以发现,如果容器中接收请求的线程池如果都在等待服务Boo.boo() 的响应,而 Boo 又需要调用回服务 Foo.another()。这个时候,如果所有的线程都处于这样的状态,我们就会发现服务 Foo 容器中以及没有线程来处理 Boo 的请求了。某种程度上来说就是死锁了。到这里,我们就可以很确定了,这个环形依赖调用就是导致出现调用超时的罪魁祸首。当 client 发起的请求速度大于这个环形调用链的处理速度的时候,慢慢的就会导致服务 Foo 的所有线程都进入这种死锁状态。

验证

这里只列出关键的代码,具体的代码可以参考 gitee 工程:https://gitee.com/donghbcn/CircularDependency

Eureka 服务器

建个简单工程将Eureka server启动起来。

服务 Foo

创建 SpringBoot 工程实现 Foo 服务。Foo 通过 FeignClient 调用 Boo 服务。设置缺省的容器 Tomcat 的最大线程数为 16,Tomcat 默认配置最大线程数 200,对于验证这个场景有点了大了,要看到效果需要等的时间有点长。

application.properties

spring.application.name=demo-foo
server.port=8000
eureka.client.serviceUrl.defaultZone=http://localhost:8080/eureka
server.tomcat.threads.max=16
packagecom.cd.demofoo;

importorg.springframework.beans.factory.annotation.Autowired;
importorg.springframework.web.bind.annotation.RequestMapping;
importorg.springframework.web.bind.annotation.RestController;

@RestController
publicclassFooController{
@Autowired
BooFeignClientbooFeignClient;
@RequestMapping("/hello")
publicStringhello(){
longstart=System.currentTimeMillis();
System.out.println("["+Thread.currentThread()+
"]foo:hellocalled,callboo:boonow");
booFeignClient.boo();
System.out.println("["+Thread.currentThread()+
"]foo:hellocalled,callboo:boo,totalcost:"+
(System.currentTimeMillis()-start));
return"helloworld";
}

@RequestMapping("/another")
publicStringanother(){
longstart=System.currentTimeMillis();
try{
//通过slepp模拟一个耗时调用
Thread.sleep(100);
}catch(InterruptedExceptione){
e.printStackTrace();
}
System.out.println("foo:anothercalled,totalcost:"+(System.currentTimeMillis()-start));
return"another";
}
}

服务 Boo

创建 SpringBoot 工程实现 Boo 服务。Boo 通过 FeignClient 调用 Foo 服务。

packagecom.cd.demoboo;

importorg.springframework.beans.factory.annotation.Autowired;
importorg.springframework.web.bind.annotation.RequestMapping;
importorg.springframework.web.bind.annotation.RestController;

@RestController
publicclassBooController{

@Autowired
FooFeignClientfooFeignClient;

@RequestMapping("/boo")
publicStringboo(){
longstart=System.currentTimeMillis();

fooFeignClient.another();
System.out.println("boo:boocalled,callfoo:another,totalcost:"+
(System.currentTimeMillis()-start));
return"boo";
}
}

Jmeter

采用 Jmeter 来模拟并发 Client 调用。配置了30 个 线程,无限循环。

efca1c60-953f-11ed-bfe3-dac502259ad0.png

很快服务 Foo 日志就卡死了。过一会 Boo 的日志开始出现 SocketTimeoutException,如下图:

efd8eed4-953f-11ed-bfe3-dac502259ad0.png

jstack

通过 jstack 我们可以看到 Foo 进程的所有线程都卡在 hello() 调用上了。

efe89a28-953f-11ed-bfe3-dac502259ad0.png

总结

微服务之间的环形依赖类似于类之间的循环依赖,当依赖关系形成了环,会造成比较严重的问题:

  • 微服务直接不能形成环形调用,否则非常容易出现死锁状态
  • 微服务之间的耦合性非常强,这严重违反了微服务的初衷;这种情况往往是服务之间的调用没有约束导致的,为了方便取到或更新数据,服务之间可以随意的调用,以”微服务“为设计目标的系统会逐渐演变成一个分布式大单体


审核编辑 :李倩



声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 线程
    +关注

    关注

    0

    文章

    511

    浏览量

    20878
  • 微服务
    +关注

    关注

    0

    文章

    150

    浏览量

    8146

原文标题:微服务循环依赖调用引发的血案

文章出处:【微信号:芋道源码,微信公众号:芋道源码】欢迎添加关注!文章转载请注明出处。

收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    【快速温变循环】快速温变循环试验箱的“循环”之道:宏展科技如何定义“一个循环

    在军工、航天、汽车电子等高端制造领域,快速温变循环试验是验证产品可靠性的核心手段。然而,“一个循环”到底如何定义?是简单的升降温,还是对速率、驻留时间、温变曲线精度、循环重复性的严苛约束?广东宏展
    的头像 发表于 04-16 09:38 361次阅读
    【快速温变<b class='flag-5'>循环</b>】快速温变<b class='flag-5'>循环</b>试验箱的“<b class='flag-5'>循环</b>”之道:宏展科技如何定义“一个<b class='flag-5'>循环</b>”

    Springboot+SpringData+SpringCloud微服务架构课程

      后端进阶必学:SpringCloud 微服务高可用落地实战 在互联网技术飞速迭代的今天,单体应用架构已逐渐难以承载亿级流量的重担。对于渴望突破瓶颈、迈向架构师行列的后端开发者而言,掌握微服务架构
    的头像 发表于 03-19 16:08 482次阅读

    微视图灵高精度人体关键点算法成功“落户”算能AI微服务器,公检法、校园与养老场景全面赋能

    在人工智能技术加速落地民生关键领域的今天,微视图灵AI团队研发的新一代高精度人体关键点检测算法,已成功完成在国产信创核心硬件——算能(SOPHGO)AI 微服务器上的深度适配与规模化部署。 此次适配
    的头像 发表于 03-17 10:59 529次阅读
    微视图灵高精度人体关键点算法成功“落户”算能AI<b class='flag-5'>微服务</b>器,公检法、校园与养老场景全面赋能

    光伏四可装置软件系统架构:微服务化设计与容器化部署方案

    ,某一模块升级需整体停机,无法适配光伏场景对实时性与连续性的要求;物理机部署模式则导致环境一致性差,跨场景迁移成本高。为此,基于微服务化设计与容器化部署的软件架构应运而生,通过“功能解耦、弹性部署、高效
    的头像 发表于 03-03 15:47 574次阅读

    基于OpenTelemetry的全链路追踪微服务可观测性实践

    微服务拆分到第三年,我们的服务数量从最初的5个膨胀到了47个。一个用户下单请求要经过API Gateway -> 用户服务 -> 商品服务 -> 库存
    的头像 发表于 02-26 15:43 615次阅读

    Istio服务网格的核心原理与部署实战

    微服务拆分之后,服务调用关系变得复杂。一个请求从网关进来,经过认证服务、用户服务、订单服务、库
    的头像 发表于 02-26 09:49 349次阅读

    选错PCB基材引发血案

    PCB电路板主要由 覆铜箔层压板 (Copper Clad Laminates,CCL)、 半固化片 (PP片)、 铜箔 (Copper Foil)、 阻焊层 (又称阻焊膜)(Solder Mask)、 表面处理 、 字符 组成。 PCB电路板的主要材料是覆铜板, 而覆铜板(敷铜板)是由铜箔(成本占比30%-40%)、玻璃布(基板,成本占比20%-25%)、树脂(粘合剂,成本占比25%-30%)和填料构成的。基板是由高分子合成树脂和增强材料组成的绝缘层板;在基板的表面覆盖着一层导电率较高、焊接性良好的纯铜箔,常用厚
    的头像 发表于 01-29 09:06 1031次阅读
    选错PCB基材<b class='flag-5'>引发</b>的<b class='flag-5'>血案</b>

    控制流和函数调用的精细调整

    特性,避免不必要的计算。 函数调用涉及开销,因为它需要保存当前执行环境并跳转到新的执行环境。减少函数调用,尤其是在频繁执行的循环中,可以显著提高性能。 对于简单且频繁调用的函数,使用内
    发表于 11-14 06:32

    华纳云VPS容器服务网格流量管理:实现微服务高效路由

    在云计算和微服务架构日益普及的今天,华纳云香港VPS凭借其优越的地缘优势和网络自由,成为众多企业部署容器化应用的热门选择。复杂的微服务架构带来了流量管理的巨大挑战。本文将深入探讨如何利用容器服务
    的头像 发表于 10-16 17:09 696次阅读

    基于RFID与微服务架构的智能仓库管理系统:实现仓储数据的全链路精准采集与管控

    针对传统仓储管理中普遍存在的账实不符、流程效率低下及信息孤岛等问题,本文介绍一套基于RFID射频识别技术与微服务软件架构的智能仓库管理系统。系统通过“一物一码”的电子身份标识,实现了对物资从入库
    的头像 发表于 10-13 11:18 977次阅读
    基于RFID与<b class='flag-5'>微服务</b>架构的智能仓库管理系统:实现仓储数据的全链路精准采集与管控

    人工智能行业如何使用for循环语句进行循环

    人工智能行业可以使用以下是关于for循环在不同编程语言中的基本用法说明: Python中的for循环: 主要用于遍历序列(列表、元组、字符串等) 典型结构:for item in sequence
    的头像 发表于 09-10 12:55 718次阅读

    如何基于Nginx构建微服务网关

    今天,我将分享我们团队如何基于Nginx构建了一个日均处理10亿+请求的微服务网关,以及踩过的那些坑。这套方案已经稳定运行2年+,经历过多次大促考验。
    的头像 发表于 09-02 16:29 1001次阅读

    Jtti海外VPS微服务架构下的日志采集与分析优化方案

    随着跨境业务和分布式应用的普及,越来越多的企业在海外VPS上构建微服务架构,以提升系统扩展性和灵活性。然而,微服务化带来了一个新的挑战:日志数据分散在多个服务和节点中,若缺乏统一采集与分析机制,将
    的头像 发表于 08-27 17:13 722次阅读

    电商API的微服务架构优化策略

    ​ 随着电子商务的快速发展,API(应用程序编程接口)已成为电商平台的核心组件,负责连接用户、商家和后台系统。微服务架构通过将应用拆分为独立、可扩展的服务单元,显著提升了系统的灵活性和可维护性。然而
    的头像 发表于 07-23 14:30 749次阅读
    电商API的<b class='flag-5'>微服务</b>架构优化策略

    蔡司“微服务”——全能在线售后管家,24小时守护您的设备!

    还在为设备故障烦恼? 急需技术支援却找不到人? 想快速获取用户手册或软件升级? 现在 只需微信扫一扫设备上的蓝色标签二维码 蔡司“微服务”一键触达! 9大功能板块 全方位解决您的售后需求 服务更高
    发表于 07-10 16:44 1719次阅读
    蔡司“<b class='flag-5'>微服务</b>”——全能在线售后管家,24小时守护您的设备!