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

    文章

    507

    浏览量

    19769
  • 微服务
    +关注

    关注

    0

    文章

    143

    浏览量

    7451

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

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

收藏 人收藏

    评论

    相关推荐

    NVIDIA发布全新NIM AI Guardrail微服务

    NVIDIA近期推出了一项旨在保障代理式AI应用安全性的重要技术——NIM AI Guardrail微服务。这一全新微服务是NVIDIA NeMo Guardrails软件工具系列的重要组成部分
    的头像 发表于 01-18 11:48 470次阅读

    NVIDIA 发布保障代理式 AI 应用安全的 NIM 微服务

    NVIDIA NeMo Guardrails 包含全新 NVIDIA NIM 微服务,能够为各行业构建 AI 的企业提高 AI 的准确性、安全性和可控性。   AI 智能体有望成为能够完成各种任务
    发表于 01-17 16:29 87次阅读

    微服务容器化部署好处多吗?

    微服务容器化部署好处有很多,包括环境一致性、资源高效利用、快速部署与启动、隔离性与安全性、版本控制与回滚以及持续集成与持续部署。这些优势助力应用可靠稳定运行,提升开发运维效率,是现代软件架构的优质选择。UU云小编认为微服务容器化部署好处主要体现在以下几个方面:
    的头像 发表于 01-17 10:22 98次阅读

    容器化能替代微服务吗?两者有何区别

    和可维护性。而容器化技术则是一种轻量级的虚拟化技术,它将应用程序及其依赖项打包到一个独立的容器中,使其能够在不同的环境中一致地运行。虽然容器化技术为微服务提供了一个理想的运行环境,但微服务架构本身所强调的
    的头像 发表于 01-13 10:40 121次阅读

    宝藏级微服务架构工具合集

    宝藏级热门微服务架构工具包含Spring Boot、Eclipse Vert.X、Kubernetes、Tyk、RabbitMQ、Apache Kafka等。其中,Spring Boot简化了微服务
    的头像 发表于 12-21 16:33 280次阅读

    NVIDIA NIM微服务登陆亚马逊云科技

    经过优化的 NIM 微服务现可在 Amazon Bedrock Marketplace、SageMaker JumpStart 和 AWS Marketplace 上获取,用于各种 NVIDIA 和生态系统模型。
    的头像 发表于 12-06 13:33 327次阅读

    SSR与微服务架构的结合应用

    随着互联网技术的快速发展,前端技术栈不断更新迭代,后端架构也经历了从单体应用到微服务的变革。在这个过程中,服务端渲染(SSR)作为一种提升页面加载速度和SEO性能的技术,与微服务架构的结合应用,为
    的头像 发表于 11-18 11:34 420次阅读

    微服务架构与容器云的关系与区别

    微服务架构与容器云密切相关又有所区别。微服务将大型应用拆分为小型、独立的服务,而容器云基于容器技术,为微服务提供构建、发布和运行的平台。区别在于,
    的头像 发表于 10-21 17:28 296次阅读

    入门级攻略:如何容器化部署微服务

    第一步理解容器化基础,第二步创建Dockerfile,第三步构建推送镜像,第四步部署微服务,第五步管理微服务、第六步优化更新。容器化部署微服务是现代软件开发中的一种高效方法,可提供良好的可移植性、可扩展性和管理性。容器化部署
    的头像 发表于 10-09 10:08 221次阅读

    Proxyless的多活流量和微服务治理

    1. 引言 1.1 项目的背景及意义 在当今的微服务架构中,应用程序通常被拆分成多个独立的服务,这些服务通过网络进行通信。这种架构的优势在于可以提高系统的可扩展性和灵活性,但也带来了新的挑战,比如
    的头像 发表于 08-28 16:54 1661次阅读
    Proxyless的多活流量和<b class='flag-5'>微服务</b>治理

    NVIDIA NIM微服务带来巨大优势

    服务通过热门 AI 模型为数百万开发者带来高达 5 倍的 token 效率提升,使他们能够立即访问在 NVIDIA DGX Cloud 上运行的 NIM 微服务
    的头像 发表于 08-23 15:20 618次阅读

    采用OpenUSD和NVIDIA NIM微服务创建精准品牌视觉

    全球领先的创意和制作服务机构率先采用 OpenUSD 和 NVIDIA NIM 微服务来创建精准的品牌视觉。
    的头像 发表于 08-01 14:33 497次阅读

    全新 NVIDIA NeMo Retriever微服务大幅提升LLM的准确性和吞吐量

    企业能够通过提供检索增强生成功能的生产就绪型 NVIDIA NIM 推理微服务,充分挖掘业务数据的价值。这些微服务现已集成到 Cohesity、DataStax、NetApp 和 Snowflake 平台中。
    的头像 发表于 07-26 11:13 947次阅读
    全新 NVIDIA NeMo Retriever<b class='flag-5'>微服务</b>大幅提升LLM的准确性和吞吐量

    英伟达推出全新NVIDIA AI Foundry服务和NVIDIA NIM推理微服务

    NVIDIA 宣布推出全新 NVIDIA AI Foundry 服务和 NVIDIA NIM 推理微服务,与同样刚推出的 Llama 3.1 系列开源模型一起,为全球企业的生成式 AI 提供强力支持。
    的头像 发表于 07-25 09:48 806次阅读

    【算能RADXA微服务器试用体验】Radxa Fogwise 1684X Mini 规格

    通过网络可以了解到,算能RADXA微服务器的具体规格: 处理器:BM1684X 算力:高达32Tops INT8峰值算力 内存:16GB LPDDR4X 内存 存储:64GB eMMC 编程框架
    发表于 02-28 11:21