当前位置: 首页 > article >正文

SpringBoot分布式项目实战:观察者模式的高阶应用与避坑指南

一、痛点场景:当观察者遇上分布式

在某电商平台重构项目中,我们遭遇了这样的困境:订单中心完成支付后需要触发库存扣减、积分结算、物流调度等12个后续操作。最初的实现采用了硬编码调用:

// 伪代码示例
public void paySuccess(Order order) {inventoryService.deduct(order);pointsService.calculate(order);logisticsService.schedule(order);// ...更多调用
}

这种实现方式带来的问题在分布式环境下被急剧放大:

  • 新增业务逻辑需要修改核心支付代码
  • 下游服务故障导致主流程阻塞
  • 响应时间随着调用链增长而线性增加
  • 跨服务事务难以协调

二、模式升级:观察者模式的分布式改造

2.1 传统观察者模式回顾

在单体应用中,Spring事件机制能很好实现解耦:

// 定义事件
public class OrderPaidEvent extends ApplicationEvent {public OrderPaidEvent(Order source) {super(source);}
}// 发布事件
applicationEventPublisher.publishEvent(new OrderPaidEvent(order));// 监听处理
@EventListener
public void handleOrderPaid(OrderPaidEvent event) {// 处理逻辑
}

但在分布式场景下存在三大挑战:

  1. 事件只能在本JVM内传播
  2. 缺乏可靠的事件存储
  3. 无法保证最终一致性

2.2 分布式观察者架构设计

我们采用分层架构:

  • 事件生产层:Spring Event + RabbitMQ
  • 事件路由层:消息队列主题交换器
  • 事件消费层:独立微服务+本地事务

三、实战代码:SpringBoot整合RabbitMQ实现分布式观察者

3.1 基础设施配置

# application.yml
spring:rabbitmq:host: rabbitmq-clusterport: 5672publisher-confirms: truepublisher-returns: true
@Configuration
public class EventConfig {// 定义业务专属交换机@Beanpublic TopicExchange businessExchange() {return new TopicExchange("business.exchange");}// 死信队列配置@Beanpublic Queue dlq() {return QueueBuilder.durable("business.dlq").withArgument("x-message-ttl", 86400000).build();}
}

3.2 增强型事件发布

@Component
@RequiredArgsConstructor
public class DomainEventPublisher {private final ApplicationEventPublisher localPublisher;private final RabbitTemplate rabbitTemplate;@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)public void publishDomainEvent(DomainEvent event) {// 本地监听localPublisher.publishEvent(event);// 分布式发布rabbitTemplate.convertAndSend("business.exchange", event.getEventType(),event,message -> {message.getMessageProperties().setHeader("retry_count", 0);return message;});}
}

3.3 可靠事件消费

@Component
@Slf4j
public class InventoryListener {@RabbitListener(bindings = @QueueBinding(value = @Queue(name = "inventory.queue", arguments = @Argument(name = "x-dead-letter-exchange", value = "business.dlq")),exchange = @Exchange(name = "business.exchange", type = "topic"),key = "order.paid"))@Transactional(rollbackFor = Exception.class)public void handleOrderPaid(OrderPaidEvent event, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long tag) {try {inventoryService.deduct(event.getOrder());channel.basicAck(tag, false);} catch (BusinessException e) {handleRetry(channel, tag, e);}}private void handleRetry(Channel channel, long tag, Exception e) {// 获取当前重试次数Integer retry = (Integer) channel.getHeader("retry_count");if (retry == null) retry = 0;if (retry < 3) {// 指数退避重试channel.basicNack(tag, false, true);Thread.sleep((long) (Math.pow(2, retry) * 1000));} else {// 进入死信队列channel.basicReject(tag, false);}}
}

四、进阶技巧:分布式场景下的特别处理

4.1 事件幂等性保障

// 使用Redis实现幂等锁
public boolean checkIdempotent(String eventId) {return redisTemplate.opsForValue().setIfAbsent("event:" + eventId, "processing", 1, TimeUnit.HOURS);
}

4.2 事件顺序性处理

// 在消息头中添加顺序标识
message.getMessageProperties().setHeader("sequence", System.currentTimeMillis());// 消费者单线程处理
@RabbitListener(concurrency = "1")

4.3 分布式事务补偿

// 实现Saga模式
public class InventorySaga {@SagaStartpublic void deductInventory() {// 正向操作}@Compensatepublic void compensateDeduct() {// 补偿操作}
}

五、性能优化实战数据

我们在压力测试中对比了不同实现方案的性能:

指标同步调用本地事件分布式事件
TPS120025001800
平均响应时间(ms)450180260
99%延迟(ms)1200500800
故障影响范围全局局部服务级

优化策略:

  1. 使用批量事件合并发送
  2. 采用Protobuf序列化
  3. 实施消费者动态扩缩容

六、踩坑实录:血泪教训总结

  1. 事件风暴:某次大促时MQ积压导致服务雪崩

    • 解决方案:实施分级熔断 + 动态流量控制
  2. 幽灵事件:事务回滚后事件已发送

    • 修复方案:使用TransactionalEventListener
  3. 版本兼容:事件结构变更导致消费者异常

    • 最佳实践:添加version头 + 兼容性测试
  4. 监控黑洞:无法追踪完整事件链路

    • 完善方案:集成SkyWalking + 自定义事件ID

七、架构演进:观察者模式的未来

在云原生时代,我们可以进一步优化:

  1. 采用Serverless架构实现事件处理弹性
  2. 使用Event Sourcing模式构建完整审计追踪
  3. 集成AI进行事件异常预测
  4. 采用Webhook实现跨系统通知
事件源
Event Mesh
Kubernetes Service
Serverless Function
Legacy System

结语

观察者模式在分布式系统中的真正价值,不在于简单的代码解耦,而在于构建出弹性、可观测、自愈合的业务生态。当我们在SpringBoot项目中熟练运用事件驱动架构时,实际上是在为系统植入面向未来的基因。记住:优秀的事件设计,应该让系统如同生物神经系统般,具备自主感知和反应能力。

相关文章:

SpringBoot分布式项目实战:观察者模式的高阶应用与避坑指南

一、痛点场景&#xff1a;当观察者遇上分布式 在某电商平台重构项目中&#xff0c;我们遭遇了这样的困境&#xff1a;订单中心完成支付后需要触发库存扣减、积分结算、物流调度等12个后续操作。最初的实现采用了硬编码调用&#xff1a; // 伪代码示例 public void paySuccess…...

How to use pgbench to test performance for PostgreSQL?

pgbench 是一个用于测试 PostgreSQL 数据库性能的基准测试工具。通过模拟多个客户端并发执行 SQL 查询&#xff0c;它可以帮助你评估数据库的性能。以下是使用 pgbench 的基本步骤&#xff1a; 安装 pgbench pgbench 是 PostgreSQL 的一部分&#xff0c;因此在安装 PostgreSQ…...

C#基础学习(五)函数中的ref和out

1. 引言&#xff1a;为什么需要ref和out&#xff1f; ​问题背景&#xff1a;函数参数默认按值传递&#xff0c;值类型在函数内修改不影响外部变量&#xff1b;引用类型重新赋值时外部对象不变。​核心作用&#xff1a;允许函数内部修改外部变量的值&#xff0c;实现“双向传参…...

从零构建大语言模型全栈开发指南:第二部分:模型架构设计与实现-2.2.2文本生成逻辑:Top-k采样与温度控制

👉 点击关注不迷路 👉 点击关注不迷路 👉 点击关注不迷路 文章大纲 2.2.2 文本生成逻辑:Top-k采样与温度控制1. 文本生成的核心挑战与数学框架1.1 自回归生成的基本流程2. `Top-k`采样原理与工程实现2.1 数学定义与算法流程2.2 PyTorch实现优化3. 温度控制的数学本质与参…...

关于CodeJava的学习笔记——9

一、IO流 1、定义 IInput输入 OOutput输出 流 : 数据从源点传输到汇点的"管道"而已 2、流的分类 按照方向分: 输入流 输出流 *:参照物当前Java程序为参照物 按照单位分: 字节流 字符流 按照功能分: 节点流 过滤流(包装流 处…...

LeetCode算法题(Go语言实现)_11

题目 给定字符串 s 和 t &#xff0c;判断 s 是否为 t 的子序列。 字符串的一个子序列是原始字符串删除一些&#xff08;也可以不删除&#xff09;字符而不改变剩余字符相对位置形成的新字符串。&#xff08;例如&#xff0c;"ace"是"abcde"的一个子序列&a…...

Python----数据分析(足球运动员数据分析)

一、数据展示 1.1、数据 1.2、列名 字段名备注Name姓名Nationality国籍National_Position国家队位置National_Kit国家队号码Club所在俱乐部Club_Position所在俱乐部位置Club_Kit俱乐部号码Club_Joining加入俱乐部时间Contract_Expiry合同到期时间Rating评分Height身高Weight体…...

Day38 | 1365. 有多少小于当前数字的数字、941. 有效的山脉数组、1207. 独一无二的出现次数、283. 移动零、189. 轮转数组

1365. 有多少小于当前数字的数字 题目链接&#xff1a;1365. 有多少小、于当前数字的数字 - 力扣&#xff08;LeetCode&#xff09; 题目难度&#xff1a;简单 代码&#xff1a; class Solution {public int[] smallerNumbersThanCurrent(int[] nums) {Map<Integer,Inte…...

Docker-清理容器空间prune

docker system prune -a 是一个非常有用的命令&#xff0c;用于清理 Docker 系统中未使用的资源&#xff0c;包括停止的容器、未使用的网络、卷以及未被任何容器引用的镜像&#xff08;悬空镜像和所有未使用的镜像&#xff09;。以下是关于该命令的详细说明&#xff1a; 命令格…...

matplotlib——南丁格尔玫瑰

南丁格尔玫瑰图&#xff08;Nightingale Rose Chart&#xff09;&#xff0c;是一种特殊形式的柱状图&#xff0c;它以南丁格尔&#xff08;Florence Nightingale&#xff09;命名&#xff0c;她在1858年首次使用这种图表来展示战争期间士兵死亡原因的数据。 它将数据绘制在极坐…...

Django与网页表单

我叫补三补四&#xff0c;很高兴见到大家&#xff0c;欢迎一起学习交流和进步 今天来讲一讲网页表单 网页表单又叫做HTML表单&#xff0c;用来处理用户从页面输入发送到服务器的数据&#xff0c;页面表单通常会提供复选框、单选按钮和文本字段&#xff0c;方便用户填写各种形式…...

Rust从入门到精通之入门篇:10.包和模块

包和模块 在本章中&#xff0c;我们将学习 Rust 的包和模块系统&#xff0c;它们是组织和重用代码的重要工具。随着项目规模的增长&#xff0c;良好的代码组织变得越来越重要&#xff0c;Rust 提供了一套强大的机制来管理代码结构。 包和 Crate Crate Crate 是 Rust 中最高…...

ChatDBA VS DeepSeek:快速诊断 OceanBase 集群新租户数据同步异常

社区王牌专栏《一问一实验&#xff1a;AI 版》改版以来已发布多期&#xff08;51-60&#xff09;&#xff0c;展现了 ChatDBA 在多种场景下解决问题的效果。 下面让我们正式进入《一问一实验&#xff1a;AI 版》第 62 期&#xff0c;看看 ChatDBA 最新效果以及与热门大模型 De…...

dify忘记密码

特别好&#xff0c;非常好&#xff0c;一把年纪忘了dify的账号、密码了&#xff0c;very good&#xff01;&#xff01;&#xff01; 参考如下教程 https://zhuanlan.zhihu.com/p/24515387167 rootbae577d82ec7:/# psql -U postgres psql: error: connection to server on so…...

Python----计算机视觉处理(Opencv:图像边缘检测:非极大值抑制,双阈值筛选)

一、 高斯滤波 边缘检测本身属于锐化操作&#xff0c;对噪点比较敏感&#xff0c;所以需要进行平滑处理。这里使用的是一个5*5的高斯 核对图像进行消除噪声。 二、计算图像的梯度和方向 三、非极大值抑制 在得到每个边缘的方向之后&#xff0c;其实把它们连起来边缘检测就算完了…...

vue3(笔记)5.0--pinia工具的知识扩展

pinia工具 defineStore(创建pinia) 作用&#xff1a;用于定义一个 Pinia store。 用法&#xff1a; 接收一个唯一的 ID 和一个配置对象&#xff0c;配置对象中可以定义 state、getters 和 actions。state 是一个函数&#xff0c;返回初始状态。getters 类似于 Vue 组件中的计…...

基于Kubernetes部署Prometheus监控平台

#作者&#xff1a;stackofumbrella 文章目录 prometheus和k8s集群版本对照表架构Prometheus Operator简介kube-prometheus下载地址 安装修改镜像地址修改Prometheus的service修改Grafana的service修改Alertmanager的service数据持久化执行安装 Prometheus验证Grafana验证解决C…...

往期项目shader着色器实践效果应用合集

1、管路混色 2、水管水流效果 3、水管流入到流完效果 4、加热冷却 两 色混色 示意 XX、毒蘑菇测试效果...

如何在 React 项目中使用React.lazy和Suspense实现组件的懒加载?

大白话如何在 React 项目中使用React.lazy和Suspense实现组件的懒加载&#xff1f; 在 React 项目里&#xff0c;有时候组件功能多、体积大&#xff0c;要是一次性把所有组件都加载进来&#xff0c;网页加载速度就会变慢。而 React 提供了 React.lazy 和 Suspense 这两个好东西…...

绿色暴政:Relax Max如何用军工科技定义环保新标准

《绿色暴政&#xff1a;Relax Max如何用军工科技定义环保新标准》 ——从隐形战斗机涂层到零碳卫浴的降维打击 &#xff08;洛克希德马丁实验室&#xff0c;2023年&#xff09;当F-35战斗机的隐形涂料配方被改写为卫浴釉料时&#xff0c;环保产业迎来了最硬核的颠覆者。Relax…...

蓝桥杯刷题 Day 4 栈与链表

蓝桥杯刷题 Day 4 栈与链表 文章目录 蓝桥杯刷题 Day 4 栈与链表前言一、栈1. 解题思路2. 拆解代码&#xff08;不复杂&#xff0c;不拆了&#xff09; 二、链表1. 解题思路1.1 主函数1.2 自定义列表类1.2.1 插入操作1.2.2 删除操作1.2.3 按要求输出 三、 题后收获3.1 知识点 前…...

第十三届蓝桥杯单片机省赛程序设计试题

目录 试题 各程序块代码 init.c main.c other.h other.c key.c seg.c onewire.c部分 ds1302.c部分 试题 各程序块代码 init.c #include "other.h"void init74hc138(unsigned char n){P2(P2&0x1f)|(n<<5);P2&0x1f; } void init(){P00x00;in…...

QOpenGLWidget动态加载功能实现教程(Qt+OpenGL)

QOpenGLWidget动态加载功能实现教程 我需要在Qt里面使用QOpenGLWidget显示OpenGL窗口&#xff0c;并且需要实现加载模型后重新渲染更新窗口的功能&#xff0c;但是一直无法更新被卡住了&#xff0c;现在把问题解决了总结一下整个实现过程。 创建一个自己的OpenGLWidget类 QOp…...

机器学习正则化技术:Ridge、Lasso与ElasticNet全解析

机器学习中的正则化技术 在机器学习中&#xff0c;正则化技术&#xff08;如 Ridge 和 Lasso&#xff09;主要用于解决过拟合问题&#xff0c;通过限制模型复杂度提高泛化能力。以下是详细说明及实例代码&#xff1a; 一、正则化解决的问题 过拟合&#xff1a;模型在训练集表…...

数字转换(c++)

【题目描述】 如果一个数 xx 的约数和 yy &#xff08;不包括他本身&#xff09;比他本身小&#xff0c;那么 xx 可以变成 yy &#xff0c;yy 也可以变成 xx 。例如 44 可以变为 33 &#xff0c;11 可以变为 77 。限定所有数字变换在不超过 nn 的正整数范围内进行&#xff0c;…...

ESP32驱动BMP280和MQ4传感器

文章目录 前言 一、硬件准备 所需组件 连接方式&#xff1a; 二、软件实现 1.所需库 2.代码实现 效果演示 三、上传Qt端 前言 在物联网和环境监测应用中&#xff0c;传感器是获取环境数据的关键组件。本文将详细介绍如何使用ESP32微控制器同时驱动BMP280大气压力传感器…...

洛谷题单1-B2002 Hello,World!-python-流程图重构

题目描述 编写一个能够输出 Hello,World! 的程序。 提示&#xff1a; 使用英文标点符号&#xff1b;Hello,World! 逗号后面没有空格。H 和 W 为大写字母。 输入格式 无 输出格式 无 输入输出样例 #1 输入 #1 无输出 #1 Hello,World!方式-print() 代码 class Solut…...

MQTT协议笔记

消息格式 MQTT&#xff08;Message Queuing Telemetry Transport&#xff09;是一种轻量级的消息协议&#xff0c;专为低带宽、高延迟或不可靠的网络设计&#xff0c;广泛应用于物联网&#xff08;IoT&#xff09;设备之间的通信。MQTT消息体的结构遵循MQTT协议规范&#xff0…...

CentOS系统下安装tesseract-ocr5.x版本

CentOS系统下安装tesseract-ocr5.x版本 安装依赖包&#xff1a; yum update -y yum install autoconf automake libtool libjpeg-devel libpng-devel libtiff-devel zlib-devel yum install automake libtool bzip2 -y手动编译安装GCC&#xff08;因系统默认安装的GCC版本比较…...

“征服HTML引号恶魔:“完全解析手册”!!!(quot;表示双引号)

&#x1f6a8;&#x1f4e2; "征服HTML引号恶魔&#xff1a;“完全解析手册” &#x1f4e2;&#x1f6a8; &#x1f3af; 博客引言&#xff1a;当引号变成"恶魔" &#x1f631; 是否遇到过这种情况&#xff1a; 写HTML时满心欢喜输入<div title"他…...