SpringBoot - Google EventBus、AsyncEventBus

介绍

EventBus 顾名思义,事件总线,是一个轻量级的发布/订阅模式的应用模式,最初设计及应用源与 google guava 库。

相比于各种 MQ 中间件更加简洁、轻量,它可以在单体非分布式的小型应用模块内部使用(即同一个JVM范围)。

我们也可以把它和 MQ 中间件结合起来使用,使用 EventBus 作为当前应用程序接收中间件 MQ 消息的统一入口,然后应用内部基于 EventBus 进行分发订阅,以达到高内聚低耦合的目的(当应用内部需要消费多种不同 MQ 中间件消息时,不需要在当前应用的好多不同代码位置都编写 MQ 消费代码)。

EventBus 整体设计和流程比较简单,由注册、发布和订阅三个要点组成,如下:
在这里插入图片描述

注意事项

本文对 google guava 库中的 EventBus 进行实例说明,注意事项要先进行特别说明。

  • EventBus 默认为同步调用,同一个 EventBus 中注册的多个订阅处理,再事件下发后是被总线串行逐个调用的,如果其中一个方法占用事件较长,则同一个 EventBus 中的其他事件处于等待状态,且发送消息事件的代码调用处也是同步调用等待的状态。
  • 同一个 EventBus 对象,不仅仅在同一个 post 调用中串行执行,在多次并发 post 调用时,多个 post 调用之间也是串行等待执行的关系,这个要特别注意,应用不当会导致严重的消息消费处理性能瓶颈问题!

所以推荐使用异步的方式处理,异步处理主要包括 “EventBus 使用线程池统一异步” 和 “订阅消费处理代码自己使用线程异步” 两种方式。这里我更推荐使用前者,因为后者对开发者有一定的要求,加入开发者某个耗时的业务订阅实现没有自行使用线程异步处理,则会影响其他处的订阅处理。

代码示例

1、添加 pom 依赖

        <!-- google EvengBus 在 guava 包中 --><dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>32.1.2-jre</version></dependency><!-- lombok 非必须,其作用你懂得 --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.28</version><scope>provided</scope></dependency>

2、创建一个Java接口用于自动注册

package com.example.demospringbean.eventbus;/*** 用于自动注册事件订阅类的接口* * @author shanhy* @date 2023-08-30 12:06*/
public interface EventBusListener {
}

3、编写总配置类

package com.example.demospringbean.eventbus;import com.google.common.eventbus.EventBus;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import java.util.List;/*** EventBus 配置类** @author shanhy* @date 2023-08-30 11:11*/
@Configuration
public class EventBusConfiguration {/*** 实例化 EventBus 对象,并自动注册所有订阅类对象** @param eventListenerList 所有实现了 EventBusListener 接口的实现类* @return*/@Beanpublic EventBus eventBus(List<EventBusListener> eventListenerList){// 异步处理,按照自己需要,实现自己的 Executor 逻辑,例如为了防止线程长期占用需要增加超时机制等
//      EventBus eventBus = new AsyncEventBus(new Executor() {
//            public void execute(Runnable command) {
//                new Thread(command).start();
//            }
//        });EventBus eventBus = new EventBus();if(eventListenerList != null && !eventListenerList.isEmpty()) {eventListenerList.iterator().forEachRemaining(eventListener -> eventBus.register(eventListener));}return eventBus;}}

4、编写订阅测试类

package com.example.demospringbean.eventbus;import com.google.common.eventbus.Subscribe;
import org.springframework.stereotype.Component;import java.util.concurrent.TimeUnit;/*** @author shanhy* @date 2023-08-30 11:19*/
@Component
public class EventSub1 implements EventBusListener {@Subscribepublic void handlerEvent(String test) {System.out.println("11111>>>>>" + test);}@Subscribepublic void handlerEvent2(String test) throws InterruptedException {TimeUnit.SECONDS.sleep(5);System.out.println("22222>>>>>" + test);}}
package com.example.demospringbean.eventbus;import lombok.Builder;
import lombok.Data;/*** @author shanhy* @date 2023-08-30 13:19*/
@Data
@Builder
public class User {private String name;private int age;}
package com.example.demospringbean.eventbus;import com.google.common.eventbus.Subscribe;
import org.springframework.stereotype.Component;/*** @author shanhy* @date 2023-08-30 11:19*/
@Component
public class EventSub2 implements EventBusListener {@Subscribepublic void handlerEvent(String test){System.out.println("33333>>>>>" + test);}@Subscribepublic void handlerEvent2(User user){System.out.println("44444>>>>>" + user.getName());}}

5、编写消息事件发送测试

package com.example.demospringbean;import com.example.demospringbean.eventbus.User;
import com.google.common.eventbus.EventBus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;/*** 接口示例** @author shanhy* @date 2023-03-20 15:49*/
@RestController
@RequestMapping("/test")
public class TestController {@Autowiredprivate EventBus eventBus;@GetMapping("/testEvent1")public String testEvent1(){eventBus.post("Hello");return "OK";}@GetMapping("/testEvent2")public String testEvent2(){eventBus.post(User.builder().name("Tome").age(22).build());return "OK";}
}

代码说明:

1、以上代码使用的 EventBus、未使用 AsyncEventBus,并加入了线程 sleep,是为了运行代码可以观察其串行处理效果(浏览器开2个Tab同时调用 /testEvent1 观察输出),让你能更明显的感受到这种处理会给程序带来多大的性能问题(推荐实际业务生产中使用 AsyncEventBus)。

2、@Subscribe 注解修饰的事件处理方法,其参数和发送事件时的消息体会自动按类型关联对应。只有相同类型的消息体才会被消费处理。例如示例中 /testEvent1 接口发送的 “Hello” 字符串,不会触发 handlerEvent2(User user) 方法的执行,同理执行示例中 /testEvent2 接口发送 User 对象时,只会触发 handlerEvent2(User user) 方法。


(END)

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

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

相关文章

线上问诊:数仓开发(三)

系列文章目录 线上问诊&#xff1a;业务数据采集 线上问诊&#xff1a;数仓数据同步 线上问诊&#xff1a;数仓开发(一) 线上问诊&#xff1a;数仓开发(二) 线上问诊&#xff1a;数仓开发(三) 文章目录 系列文章目录前言一、ADS1.交易主题1.交易综合统计2.各医院交易统计3.各性…

js+html实现打字游戏v2

实现逻辑&#xff0c;看jshtml实现打字游戏v1&#xff0c;在此基础之上增加了从文件读取到的单词&#xff0c;随机选取10个单词。 效果演示 上代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8">&l…

window系统 bat脚本开启和关闭防火墙

前言 手动去关闭和开启防火墙太麻烦 命令 开始防火墙 netsh advfirewall set allprofiles state on关闭防火墙 netsh advfirewall set allprofiles state off

Maven高级

目录 1.分模块设计与开发 2.继承与聚合 ​编辑 3.私服 资源上传和下载 1.分模块设计与开发 将项目按照功能拆分成若干个子模块&#xff0c;方便项目的管理维护、扩展&#xff0c;也方便模块间的相互调用&#xff0c;资源共享。 分模块设计需要先针对模块功能进行设计&…

Python+Requests+Pytest+Excel+Allure 接口自动化测试项目实战【框架之间的对比】

--------UnitTest框架和PyTest框架的简单认识对比与项目实战-------- 定义&#xff1a; Unittest是Python标准库中自带的单元测试框架&#xff0c;Unittest有时候也被称为PyUnit&#xff0c;就像JUnit是Java语言的标准单元测试框架一样&#xff0c;Unittest则是Python语言的标…

MySQL——连接查询与子查询

一、连接查询 单表查询&#xff1a;在一张表当中查询数据&#xff0c;叫做单表查询。 连接查询&#xff0c;结合俩&#xff08;多&#xff09;张表&#xff0c;在俩张&#xff08;多&#xff09;表当中查询数据&#xff0c;在一张表当中查询一部分&#xff0c;在另一张表当中…

5个强大的Java分布式缓存框架推荐

在开发中大型Java软件项目时&#xff0c;很多Java架构师都会遇到数据库读写瓶颈&#xff0c;如果你在系统架构时并没有将缓存策略考虑进去&#xff0c;或者并没有选择更优的缓存策略&#xff0c;那么到时候重构起来将会是一个噩梦。 在开发中大型Java软件项目时&#xff0c;很…

C语言之练习题

欢迎来到我的&#xff1a;世界 希望作者的文章对你有所帮助&#xff0c;有不足的地方还请指正&#xff0c;大家一起学习交流 ! 目录 前言填空题&#xff1a;第一题第二题第三题 编程题&#xff1a;第一题&#xff1a;不用加减乘除做加法第二题&#xff1a;完全数计算第三题&am…

Redis快速入门

文章目录 1. Centos下Redis安装2. redis.conf配置文件介绍3. redis相关命令4. redis封装系统服务5. 问题与解决 1. Centos下Redis安装 Linux_Study 目录&#xff1a;5.2 https://blog.csdn.net/meini32/article/details/128562114 2. redis.conf配置文件介绍 https://blog.c…

【GPT引领前沿】GPT4技术与AI绘图

推荐阅读&#xff1a; 1、遥感云大数据在灾害、水体与湿地领域典型案例实践及GPT模型应用 2、GPT模型支持下的Python-GEE遥感云大数据分析、管理与可视化技术 GPT对于每个科研人员已经成为不可或缺的辅助工具&#xff0c;不同的研究领域和项目具有不同的需求。例如在科研编程…

ChatGPT AIGC 完成动态堆积面积图实例

先使用ChatGPT AIGC描述一下堆积面积图的功能与作用。 接下来一起看一下ChatGPT做出的动态可视化效果图: 这样的动态图案例代码使用ChatGPT AIGC完成。 将完整代码复制如下: <!DOCTYPE html> <html> <head><meta charset="utf-8"><tit…

Python Flask Web开发二:数据库创建和使用

前言 数据库在 Web 开发中起着至关重要的作用。它不仅提供了数据的持久化存储和管理功能&#xff0c;还支持数据的关联和连接&#xff0c;保证数据的一致性和安全性。通过合理地设计和使用数据库&#xff0c;开发人员可以构建强大、可靠的 Web 应用程序&#xff0c;满足用户的…

Ubuntu系统下使用宝塔面板实现一键搭建Z-Blog个人博客的方法和流程

文章目录 1.前言2.网站搭建2.1. 网页下载和安装2.2.网页测试2.3.cpolar的安装和注册 3.本地网页发布3.1.Cpolar临时数据隧道3.2.Cpolar稳定隧道&#xff08;云端设置&#xff09;3.3.Cpolar稳定隧道&#xff08;本地设置&#xff09; 4.公网访问测试5.结语 1.前言 Ubuntu系统作…

Ansible之playbook剧本

一、playbook概述1.1 playbook 介绍1.2 playbook 组成部分 二、playbook 示例2.1 playbook 启动及检测2.2 实例一2.3 vars 定义、引用变量2.4 指定远程主机sudo切换用户2.5 when条件判断2.6 迭代2.7 Templates 模块1.先准备一个以 .j2 为后缀的 template 模板文件&#xff0c;设…

算法leetcode|76. 最小覆盖子串(rust重拳出击)

文章目录 76. 最小覆盖子串&#xff1a;样例 1&#xff1a;样例 2&#xff1a;样例 3&#xff1a;提示&#xff1a;进阶&#xff1a; 分析&#xff1a;在这里插入图片描述 题解&#xff1a;rust&#xff1a;go&#xff1a;c&#xff1a;python&#xff1a;java&#xff1a; 76.…

若依新建模块

下面介绍如何在若依框架下新建一个子模块 第一步&#xff1a; 如图操作&#xff1a; 1. 2. 3. 4.在刚建立的子模块的pom.xml文件添加通用工具依赖 代码&#xff1a; <dependencies> <!-- 导入通用工具--><dependency><groupId>com.rchuing&l…

2023-09-04 Linux 让shell编译脚本里面设置的环境变量改变kernel里面驱动文件的宏定义值方法,我这里用来做修改固件版本

一、原生的读取版本接口是/proc/version&#xff0c;我这里需要提供获取固件版本号的api给app&#xff0c;因为版本号会经常需要修改&#xff0c;如果每次都到kernel下修改比较麻烦&#xff0c;我这里是想在编译脚本里面对版本号进行修改&#xff0c;这样方便一点。 二、主要修…

无需租云服务器,Linux本地搭建web服务,并内网穿透发布公网访问

文章目录 前言1. 本地搭建web站点2. 测试局域网访问3. 公开本地web网站3.1 安装cpolar内网穿透3.2 创建http隧道&#xff0c;指向本地80端口3.3 配置后台服务 4. 配置固定二级子域名5. 测试使用固定二级子域名访问本地web站点 前言 在web项目中,部署的web站点需要被外部访问,则…

2023 最新前端面试题 (HTML 篇)

1. src 和 href 的区别 src 用于替换当前元素&#xff08;引入&#xff09;&#xff0c;href 用于在当前文档和引用资源之间确立联系&#xff08;引用&#xff09; &#xff08;1&#xff09;src&#xff08;source&#xff09; 指向外部资源的位置&#xff0c;指向的内容将会嵌…

安卓 MeasureCache优化了什么?

安卓绘制原理概览_油炸板蓝根的博客-CSDN博客 搜了一下&#xff0c;全网居然没有人提过 measureCache。 在前文中提到过&#xff0c;measure的时候&#xff0c;如果命中了 measureCache&#xff0c;会跳过 onMeasure&#xff0c;同时会设置 PFLAG3_MEASURE_NEEDED_BEFORE_LAYOU…