Spring Event陷阱重重,我为何含泪放弃?

本文涉及的产品
容器镜像服务 ACR,镜像仓库100个 不限时长
Serverless 应用引擎免费试用套餐包,4320000 CU,有效期3个月
可观测可视化 Grafana 版,10个用户账号 1个月
简介: 网络上比较推崇使用Spring Event 优雅的实现观察者模式。我在调研后也确实觉得这种方式能实现业务逻辑上的解耦,但线上的一次事故,让我意识到 Spring Event远远没有那么简单。

网络上比较推崇使用Spring Event 优雅的实现观察者模式。我在调研后也确实觉得这种方式能实现业务逻辑上的解耦,但线上的一次事故,让我意识到 Spring Event远远没有那么简单。

1.png

前几天,线上系统出现两条异常日志Get Bean时找不到对应的bean,调用堆栈让我非常迷惑,为什么Get Bean找不到对应的Bean呢? 堆栈中的信息 解释了原因。

Do not request a bean from a BeanFactory in a destroy method implementation

在应用上下文关闭时,不得从上下文中Get Bean。恰好,问题出现在服务关闭期间..... 由于系统流量较高,日订单千万级,即便在低峰期单机的并发度也是比较高的,所以服务在关闭期间有少量流量进来或未处理完。

要明白问题原因和解决办法,需要先简单了解 Spring Event如何使用,总共分为三步。

  1. 声明事件
  2. 发布事件
  3. 监听事件

声明事件

自定义事件需要继承Spring ApplicationEvent。我选择使用泛型,支持子类可以灵活关联事件的内容。

public class BaseEvent<T> extends ApplicationEvent {
    private final T data;

    public BaseEvent(T source) {
        super(source);
        this.data = source;
    }

    public T getData() {
        return data;
    }
}

发布事件

使用Spring上下文 ApplicationContext发布事件

applicationContext.publishEvent(new BaseEvent<>(param));

Idea为Spring提供了跳转工具,点击绿色按钮位置,就可以 跳转到事件的监听器列表。

2.png

监听事件

监听器只需要 在方法上声明为 EventListener注解,Spring就会自动找到对应的监听器。Spring会根据方法入参的事件类型和 发布的事件类型 自动匹配。

@EventListener
public void handleEvent(BaseEvent<PerformParam> event) {
   
   
    //消费事件
}

我遇到的线上问题恰恰出现在发布事件后,Spring匹配Listener时,Spring需要从上下文中Get Bean,每发布一次事件需要Get Bean一次。正常情况下没事,在服务关闭期间,如果恰好发布了一个事件,就凉了。

在调研Spring Evnet期间,我重点考虑了使用方式,使用的优缺点,但确实没有想到Spring会有这个缺陷。如何避免这个问题呢?

  1. 弃用SpringEvent
  2. 允许Spring Event 出现异常,但上层调用捕获异常,上报MQ重试。
  3. 彻底服务优雅关闭。Rpc、Http、MQ入口在进程关闭前,先禁用流量。

结合三个方案的改动成本,我选择忍痛弃用Spring Event。

这件事让我明白了,全面调研真的很困难,某些框架确实很好用,但没准在哪个点上就有坑。有时候保守不是一件坏事,至少能保证线上环境能稳定运行啊。

目录
相关文章
|
2月前
|
监控 Java C#
Spring Event 的介绍
Spring Event 是 Spring 框架中的事件驱动机制,允许组件间进行同步或异步消息传递,无需直接依赖。它包括事件(Event)、事件发布者(Publisher)和事件监听器(Listener),通过 `ApplicationEventPublisher` 广播事件,实现松耦合通信,增强模块化和可维护性。Spring 还提供了多种内置事件,如 `ContextRefreshedEvent` 和 `ContextClosedEvent`,支持同步及异步处理,并具备良好的扩展性。
|
3月前
|
消息中间件 Java 开发工具
【Azure 事件中心】Spring Cloud Stream Event Hubs Binder 发送Event Hub消息遇见 Spec. Rule 1.3 - onSubscribe, onNext, onError and onComplete signaled to a Subscriber MUST be signaled serially 异常
【Azure 事件中心】Spring Cloud Stream Event Hubs Binder 发送Event Hub消息遇见 Spec. Rule 1.3 - onSubscribe, onNext, onError and onComplete signaled to a Subscriber MUST be signaled serially 异常
|
3月前
|
Java Spring
【Azure 事件中心】Spring Boot 集成 Event Hub(azure-spring-cloud-stream-binder-eventhubs)指定Partition Key有异常消息
【Azure 事件中心】Spring Boot 集成 Event Hub(azure-spring-cloud-stream-binder-eventhubs)指定Partition Key有异常消息
|
Java Spring
深入理解Spring IOC之扩展篇(十)、SpringBoot中重要event介绍,顺便简单讲下SB的启动流程(二)
深入理解Spring IOC之扩展篇(十)、SpringBoot中重要event介绍,顺便简单讲下SB的启动流程(二)
113 0
|
XML Java 数据格式
深入理解Spring IOC之扩展篇(九)、SpringBoot中重要event介绍,顺便简单讲下SB的启动流程(一)
深入理解Spring IOC之扩展篇(九)、SpringBoot中重要event介绍,顺便简单讲下SB的启动流程(一)
113 0
|
设计模式 缓存 Java
深入理解Spring IOC之扩展篇(七)、Spring中的event以及自定义event
深入理解Spring IOC之扩展篇(七)、Spring中的event以及自定义event
112 0
|
消息中间件 存储 架构师
【首席架构师看Event Hub】Kafka深挖 -第2部分:Kafka和Spring Cloud Stream
【首席架构师看Event Hub】Kafka深挖 -第2部分:Kafka和Spring Cloud Stream
|
消息中间件 缓存 前端开发
「首席看Event Hub」如何在您的Spring启动应用程序中使用Kafka
「首席看Event Hub」如何在您的Spring启动应用程序中使用Kafka
|
消息中间件 架构师 Java
「首席架构师看Event Hub」Kafka的Spring 深入挖掘 -第1部分
「首席架构师看Event Hub」Kafka的Spring 深入挖掘 -第1部分
|
消息中间件 JavaScript 小程序
Spring Event + DDD = 王炸!!下
Spring Event + DDD = 王炸!!下