微服务之间的调用关系如何处理,才能防止循环依赖


在微服务架构中,循环依赖是常见的设计问题,可能导致系统部署失败、启动顺序冲突、故障排查困难等问题。处理循环依赖的核心原则是通过架构设计打破依赖闭环,以下是具体的解决方案:
1. 重新划分服务边界(根本解决)
循环依赖往往源于服务职责划分不合理,应从业务领域出发重新梳理边界:
按聚合根拆分:基于领域驱动设计(DDD),将紧密关联的业务能力聚合到同一服务,避免跨服务的强依赖。
提取共享能力:若两个服务都依赖某部分功能,可将这部分功能拆分为独立的新服务(如用户认证服务、配置服务),让原服务都依赖这个新服务,打破闭环。
示例:
订单服务(A)依赖库存服务(B),而库存服务(B)又依赖订单服务(A)查询订单状态 → 可将 “订单状态查询” 中与库存相关的部分拆分到新的 “订单快照服务”(C),让 A 和 B 都依赖 C,消除 A↔B 的循环。
2. 引入事件驱动架构(异步解耦)
用事件驱动替代同步调用,通过消息队列传递事件,切断直接依赖:
服务 A 完成操作后,发布事件到消息队列(如 Kafka、RabbitMQ)。
服务 B 订阅该事件,无需主动调用 A,而是 A 和 B 之间不再有直接依赖。
示例:
订单服务(A)创建订单后,发布 “订单创建事件” → 库存服务(B)订阅该事件并扣减库存,无需调用 A;B 扣减库存后发布 “库存变更事件” → A 订阅该事件更新订单状态。此时 A 和 B 通过事件间接交互,无直接依赖。
3. 使用中介者模式(引入中间层)
在循环依赖的服务之间增加一个中介服务(如 API 网关、聚合服务),统一处理交互逻辑:
原服务 A 和 B 不再直接调用,而是都调用中介服务。
中介服务协调 A 和 B 的交互,避免 A↔B 的直接依赖。
适用场景:
当服务间依赖关系复杂(如 A↔B、B↔C、C↔A 的三角依赖),中介服务可简化调用链路。
4. 避免双向同步调用
若必须同步通信,需确保调用方向单向化:
明确服务的 “上游” 和 “下游” 关系,只允许上游调用下游,禁止下游反向调用上游。
若下游需要上游数据,可通过缓存(如 Redis)或数据同步(如 CDC 工具)将上游数据同步到下游,避免反向调用。
示例:
用户服务(上游)调用订单服务(下游)查询订单 → 订单服务若需用户信息,可通过缓存获取(用户服务更新时同步数据到缓存),而非直接调用用户服务。
5. 依赖注入与接口抽象(代码层面规避)
在代码实现层面,通过接口抽象和依赖注入(DI)降低耦合:
定义独立的接口模块(如 API 包),服务 A 和 B 都依赖接口,而非具体实现。
避免在服务内部硬编码对其他服务的直接引用,通过配置或注册中心动态获取依赖。
注意:此方法仅缓解代码层面的耦合,无法解决架构层面的循环依赖,需配合架构设计使用。
6. 部署与启动策略(临时规避)
若循环依赖暂时无法重构,可通过部署策略临时规避:
允许部分失败启动:服务启动时不强制检查依赖服务是否可用,待依赖服务启动后再重试连接。
固定启动顺序:在部署脚本中定义严格的启动顺序(如先启动 “基础服务”,再启动 “依赖服务”)。
缺点:仅为权宜之计,无法从根本上解决问题,且会增加运维复杂度。
总结
处理循环依赖的优先级为:
重构服务边界(最彻底)→ 2. 事件驱动异步解耦(推荐)→ 3. 引入中介层 → 4. 单向同步 + 数据同步 → 5. 临时部署策略。
核心思想是通过 “职责清晰化、交互异步化、依赖单向化” 打破闭环,同时结合领域设计和架构评审,在设计阶段避免循环依赖的产生。

重新划分服务边界的核心是基于业务领域的 “高内聚、低耦合” 原则,将紧密相关的业务能力聚合到同一服务,将无关或弱相关的能力拆分到其他服务。以下结合通过具体案例说明实现方法和思路。

案例背景:循环依赖的初始状态

假设我们有两个服务存在循环依赖:

  • 订单服务(Order Service):负责订单创建、支付状态更新。
  • 库存服务(Inventory Service):负责库存扣减、库存查询。

问题

  • 订单创建时,订单服务需要调用库存服务扣减库存(Order → Inventory)。
  • 库存不足时,库存服务需要调用订单服务取消订单(Inventory → Order)。
    形成循环依赖:Order ↔ Inventory。

步骤 1:梳理业务流程与依赖点

先明确两个服务的核心操作和依赖关系:

  1. 订单服务核心操作:

    • 创建订单(需检查并扣减库存)
    • 更新订单状态(包括因库存不足被取消的状态)
  2. 库存服务核心操作:

    • 扣减库存(若库存不足,需通知订单取消)
    • 查询库存
  3. 依赖痛点:库存服务为了处理 “库存不足” 的情况,必须直接调用订单服务的 “取消订单” 接口,形成反向依赖。

步骤 2:基于领域边界重新划分职责

通过分析发现:“订单取消” 是订单领域的核心能力,不应由库存服务直接触发,而应通过事件通知的方式间接触发。因此可以:

  • 订单服务:保留 “创建订单”“取消订单”“更新状态” 等核心能力,负责订单全生命周期管理。
  • 库存服务:专注于库存管理,仅负责 “扣减库存”“查询库存”,不再直接调用订单服务,而是通过事件告知库存状态。

步骤 3:引入事件机制打破循环

在两个服务之间引入事件总线(如 Kafka),用异步事件替代直接调用:

  1. 订单服务创建订单时,同步调用库存服务扣减库存(同步调用)。
  2. 若库存不足,库存服务发布 “库存不足事件”(而非直接调用订单服务)。
  3. 订单服务订阅 “库存不足事件”,收到事件后自行执行 “取消订单” 操作。

此时依赖关系变为单向:
Order → Inventory(同步调用),Inventory → 事件总线 → Order(异步通知),循环依赖被打破。

步骤 4:进一步优化(可选)提取共享能力

如果多个服务都需要 “订单状态查询” 能力(如库存服务、物流服务、支付服务),可进一步拆分:

  • 新增订单查询服务(Order Query Service):提供订单状态查询的只读接口,基于订单服务的数据库副本或缓存提供数据。
  • 原订单服务仅保留写操作(创建、更新、取消),其他服务若需查询订单,均调用订单查询服务。

效果:减少对核心订单服务的直接依赖,避免因查询操作过多导致的性能问题。

最终服务边界与依赖关系

plaintext

┌─────────────────┐       ┌─────────────────┐
│   订单服务      │       │   库存服务      │
│ - 创建订单      │◄──────┤ - 扣减库存      │
│ - 取消订单      │       │ - 查询库存      │
│ - 更新状态      │──────►│                 │
└────────┬────────┘       └────────┬────────┘│                        │▼                        ▼
┌─────────────────┐       ┌─────────────────┐
│   事件总线      │◄──────┤  订单查询服务   │
│ - 库存不足事件  │       │ - 订单状态查询  │
│ - 订单创建事件  │──────►│                 │
└─────────────────┘       └─────────────────┘

核心原则总结

  1. 按业务领域聚合:将同一业务流程中强相关的操作(如订单的创建、取消、状态更新)放在同一服务。
  2. 避免跨领域强依赖:不同领域服务间通过 “同步调用 + 异步事件” 组合,同步用于必要的即时交互,异步用于状态通知。
  3. 拆分 “读” 与 “写”:核心服务专注于写操作,读操作可拆分到专门的查询服务,降低依赖复杂度。

通过这种方式,服务边界清晰,依赖关系单向可控,从根本上避免了循环依赖。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/93939.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/93939.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

粗粮厂的基于flink的汽车实时数仓解决方案

基于flink的实时数仓解决方案1 背景2 业务模型1 业务框架2 难点痛点3技术选型1 计算引擎2 中间存储3 查询引擎4 flink计算架构设计1 纯实时架构2 纯实时定期补充离线数据3 纯实时定期刷新过期binlog4 lamdba 分字段更新 历史过期数据刷新5 痛点解决delta joinmerge-enginehol…

Datawhale AI夏令营---coze空间共学

1.进入coze空间 2.点击免费使用 3.点击制作播客,微信上面选好链接 彻底搞懂深度学习-模型训练和推理(动图讲解) 4.运行过程 5.音频链接 https://lf-bot-studio-plugin-resource.coze.cn/obj/bot-studio-platform-plugin-tos/sami_podcast…

遥感机器学习入门实战教程|Sklearn案例⑥:网格搜索与超参数优化

在前几篇案例中,有同学在后台留言:“模型的参数到底怎么调?比如 SVM 的 C 和 γ,随机森林的树数和深度,要怎么选才能得到最优结果呢?”这是一个非常经典的问题:参数选不好,模型效果差…

论文精读(三)|智能合约漏洞检测技术综述

笔者链接:扑克中的黑桃A 专栏链接:论文精读 本文关键词:智能合约;合约安全;合约可靠性;合约质量保障;漏洞检测;合约程序分析 引 诸位技术同仁: 本系列将系统精读的方式,深入剖析计算机科学顶级期刊/会议论文&#…

YOLO --- YOLO11模型以及项目详解

YOLO — YOLO11模型以及项目详解 文章目录YOLO --- YOLO11模型以及项目详解一,开源地址二,重要模块2.1 C3K22.2 C2PSA2.3 检测头三,网络结构3.1 整体结构划分3.2 Backbone 结构分析(从下往上看)3.3 结构分析&#xff0…

Debezium监听MySQL binlog并实现有状态重启

Debezium实现MySQL数据监听了解Debezium​ 本期主要内容实现步骤1. 新建Maven工程2.导入依赖3.核心代码编写4.offset的存储5.OffsetBackingStore实现jdbc模式6.运行结果总结了解Debezium 官网:https://debezium.io/ Debezium是一组分布式服务,用于捕获数…

InfluxDB 存储优化:TSM 文件管理与空间回收(一)

一、InfluxDB 与 TSM 文件初相识**在数字化时代,数据量呈爆发式增长,尤其是时间序列数据,如服务器监控指标、传感器读数、金融交易记录等,它们都带有时间戳,记录着事物随时间的变化。InfluxDB 作为一款高性能的开源时序…

macos使用FFmpeg与SDL解码并播放H.265视频

效果: 安装依赖: brew install ffmpeg brew install sdl2 brew install x265 确认x265已启用 查看x265版本 工程CMakeLists.txt

C#开源库ACadSharp读取dwg图元的示例

文章目录介绍数据示例读取图元属性介绍 开源库ACadSharp的地址:https://github.com/DomCR/ACadSharp 可以在NuGet中搜索到该库并安装。 数据示例 数据是一个绘制了以下简单图元的dwg数据: 读取图元属性 创建了.net6控制台项目,通过NuG…

【UniApp打包鸿蒙APP全流程】如何配置并添加UniApp API所需的鸿蒙系统权限

一、前言:为什么选择 UniApp 打包鸿蒙应用? 随着鸿蒙生态的快速发展,越来越多开发者希望将现有跨平台项目快速接入鸿蒙系统。而 UniApp 作为国内领先的跨平台开发框架,凭借其“一次开发,多端发布”的特性,…

STM32-FreeRTOS快速入门指南(下)

第十一章 FreeRTOS事件标志组 1. 事件标志组简介 事件标志组与信号量一样属于任务间同步的机制,但是信号量一般用于任务间的单事件同步,对于任务间的多事件同步,仅使用信号量就显得力不从心了。 FreeRTOS 提供的事件标志组可以很好的处理多事…

KTH7812磁编码器芯片完全支持ABZ和UVW输出模式

KTH7812磁编码器芯片完全支持ABZ和UVW输出模式,具体功能细节如下:🔧 1. ABZ输出特性 分辨率可编程:支持 4~4096步/圈(对应1~1024个脉冲周期/圈),用户可通过配置寄存器自定义分辨率。 输出频率…

Android为ijkplayer设置音频发音类型usage

官方文档 多区音频路由 | Android Open Source Projecthttps://source.android.google.cn/docs/automotive/audio/audio-multizone-routing?hlzh-cn 背景 车机系统开发多分区(zone)功能,可以实现同一个app通过设置,在不同分…

C++ 循环:从入门到精通的深度解析

《C++ 循环:从入门到精通的深度解析》 目录 循环的本质与编程价值 三大基础循环结构详解 循环控制语句:break与continue的魔法 嵌套循环:构建复杂逻辑的基石 现代C++循环特性(C++11+) 循环性能优化与常见陷阱 实战案例:算法与工程中的循环应用 面试题深度解析与编程技巧…

| `cat /etc/os-release` | 发行版详细信息(如 Ubuntu、CentOS) |

在 Linux 或类 Unix 系统中,最简洁的命令查看操作系统类型是: uname -s✅ 输出示例: LinuxDarwin(macOS)FreeBSD 等🔍 说明: uname:显示系统信息-s:仅显示操作系统内核名…

Maya 3D建模:点、线、面、创建多边面

目录 一 点、线、面 二 创建多边面 一 点、线、面 鼠标放在模型上 按住鼠标右键:就可以选择点 线 面 shift 加选点线面 ctrl 减选点线面 顶点面:是一个检查模式,观察有无错误 选择面,单击一个面,按住shift键 同时…

CXR-LT 2024:一场关于基于胸部X线的长尾、多标签和零样本疾病分类的MICCAI挑战赛|文献速递-深度学习人工智能医疗图像

Title题目CXR-LT 2024: A MICCAI challenge on long-tailed, multi-label, and zero-shotdisease classification from chest X-rayCXR-LT 2024:一场关于基于胸部X线的长尾、多标签和零样本疾病分类的MICCAI挑战赛01文献速递介绍CXR-LT系列是一项由社区推动的计划&a…

拆解本地组策略编辑器 (gpedit.msc) 的界面和功能

我们来详细拆解本地组策略编辑器 (gpedit.msc) 的界面和功能。打开后,你会看到一个标准的微软管理控制台 (MMC) 窗口,主要分为三个部分。 这是一个典型的本地组策略编辑器界面,我们将其分为三个主要部分进行讲解: +-----------------------------------------------+----…

[NCTF2019]True XML cookbook

TRY 尝试XML外部实体注入 <?xml version"1.0" encoding"utf-8" ?> <!DOCTYPE user[<!ENTITY flag SYSTEM "file://./doLogin.php"> ]> <user><username> &flag; </username><password>1</pa…

嵌入式硬件篇---模块使用

在电子开发、自动化控制等领域&#xff0c;“模块” 是实现特定功能的标准化组件&#xff08;可以理解为 “功能积木”&#xff09;。不同模块分工明确&#xff0c;比如有的负责感知环境&#xff08;传感器&#xff09;&#xff0c;有的负责通信&#xff08;蓝牙 / WiFi&#x…