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)
相关文章:
SpringBoot - Google EventBus、AsyncEventBus
介绍 EventBus 顾名思义,事件总线,是一个轻量级的发布/订阅模式的应用模式,最初设计及应用源与 google guava 库。 相比于各种 MQ 中间件更加简洁、轻量,它可以在单体非分布式的小型应用模块内部使用(即同一个JVM范围…...
Tauri打包windows应用配置中文界面
使用 Tauri Rust 开发桌面应用,在 windows 系统上,打包后安装包名称后缀、安装界面、相关说明默认都是英文的。如果要默认显示为中文,则需要在 tauri.conf.json 中配置相应参数。 前言 默认情况下,在 windows 系统打完的 mis 包…...
深度丨Serverless + AIGC,一场围绕加速创新的升维布局
作者:褚杏娟 上图来源于基于函数计算部署 SD实现光影效果 前言: Serverless 在中国发展这些年,经历了高潮、低谷、现在重新回到大众视野。很多企业都非常感兴趣,部分企业开始大规模应用;也有一些企业对在生产环境真正…...
flask日志
您可以使用 Python 自带的 logging 模块来实现 Flask 日志记录功能。以下是一个简单的示例: import os import logging from logging.handlers import TimedRotatingFileHandler from flask import Flask, requestapp Flask(__name__)# 创建日志目录 if not os.pa…...
新能源汽车动力总成系统及技术
需要动力系统总成的请联:shbinzer 拆车邦 需要动力系统总成的请联:shbinzer 拆车邦 需要动力系统总成的请联:shbinzer 拆车邦 需要动力系统总成的请联:shbinzer 拆车邦 需要动力系统总成的请联:shbinzer …...
在 WSL2 中使用 NVIDIA Docker 进行全栈开发和深度学习 TensorFlow pytorch GPU 加速
在 WSL2 中使用 NVIDIA Docker 进行全栈开发和深度学习 TensorFlow pytorch GPU 加速 0. 背景 0.1 起源 生产环境都是在 k8d pod 中运行,直接在容器中开发不好嘛?每次换电脑,都要配配配,呸呸呸新电脑只安装日常用的软件不好嘛&…...
模拟实现应用层协议
模拟实现应用层协议 文章目录 模拟实现应用层协议应用层再谈协议 序列化和反序列化 网络版计算器自定义协议利用Json进行序列化和反序列化json库的安装条件编译 应用层 应用层(Application layer)是OSI模型的第七层。应用层直接和应用程序接口并提供常见…...
SAP-MM-冲销凭证布局变更
业务场景: 仓管员在冲销物料凭证时MBST,显示行很少,只有7行,提出需求调整布局为多行,但是MBST没有调整布局功能, 解决:点击“定制本地布局-选项-字体设置”调整字体大小 跟据需求调整字体&…...
事务方法中保证数据只插入一次方案探究
需求场景 在项目的接口请求中,我们有一个接口A需要事务支持,在接口A中调用了方法B,方法B也需要事务支持,两者都带有Transactional注解。在B方法中是这个一个逻辑,查询本地数据库是否包含属性值为一个特定值的字段&…...
高通开发系列 - 5G网络之QTI守护进程服务介绍
By: fulinux E-mail: fulinux@sina.com Blog: https://blog.csdn.net/fulinus 喜欢的盆友欢迎点赞和订阅! 你的喜欢就是我写作的动力! 返回:专栏总目录 目录 代码位置和依赖关系功能介绍代码逻辑讲解外设节点关注的目录socket服务端初始化DPM客户端监听守护关键的数据结构体…...
Ansible学习笔记3
ansible模块: ansible是基于模块来工作的,本身没有批量部署的能力,真正具有批量部署的是ansible所运行的模块,ansible只是提供一个框架。 ansible支持的模块非常多,我们并不需要把每个模块记住,而只需要熟…...
DP读书:鲲鹏处理器 架构与编程(十)鲲鹏软件生态与云服务
十秒带你了解鲲鹏软件生态与云服务 鲲鹏软件生态与云服务ARM授权机制在传统的PC领域,半导体厂商的业务类型主要分为两种:在移动领域, ARM服务器生态鲲鹏服务器软件生态1. 鲲鹏计算产业2. 鲲鹏软件生态兼容性3. openEluer操作系统4. 鲲鹏软件栈…...
CSS_IOS适配状态栏和IOS底部安全区域
状态栏 var(--status-bar-height)计算属性 height: calc(var(--status-bar-height) 343px);底部安全区 先constant再env constant(safe-area-inset-bottom) env(safe-area-inset-bottom)计算属性 height: calc(132px constant(safe-area-inset-bottom)); height: calc(1…...
中央仓库更新失败,IDEA报错repository is non-nexus repo, or does not indexed
某个仓库未被识别为 Nexus 仓库,或者没有被正确地索引。导致引入依赖一直爆红,找不到。只有本地仓库的依赖没报错,因为下载过了,添加新的依赖就需要到远程仓库找就爆红。 解决 去阿里云Maven官网看了一下,发现阿里云…...
设计模式--代理模式
笔记来源:尚硅谷Java设计模式(图解框架源码剖析) 代理模式 1、代理模式的基本介绍 1)代理模式:为一个对象提供一个替身,以控制对这个对象的访问。即通过代理对象访问目标对象2)这样做的好处是…...
链路聚合原理
文章目录 一、定义二、功能三、负载分担四、分类五、常用命令 首先可以看下思维导图,以便更好的理解接下来的内容。 一、定义 在网络中,端口聚合是一种将连接到同一台交换机的多个物理端口捆绑在一起,形成一个逻辑端口的技术。通过端口聚合&…...
el-table表尾添加合计行,自动合计,且特殊列自定义计算展示
效果如图 1.element-ui的table表格有合计功能,但是功能却不完善,会有不显示和计算出现错误的问题,项目中有遇到,所以记录下 show-summary:自动合计 getSummaries():对合计行进行特…...
uview ui 1.x ActonSheet项太多,设置滚动(亲测有效)
问题:ActionSheet滚动不了。 使用uview ui :u-action-sheet, 但是item太多,超出屏幕了, 查了一下文档,并没有设置滚动的地方。 官方文档:ActionSheet 操作菜单 | uView - 多平台快速开发的UI框架 - uni-a…...
STM32 Cubemx 同名外设中断及回调
文章目录 前言示例工程个人理解 前言 最近在学习STM32,采用HAL库开发方式。记录一下同名外设中断及回调。 这里提及的同名外设指USART1/2之类的相同外设,但不是同一个instance。 示例工程 以使用cubemx配置两个同名外设EXTI0/EXT4为例。 在NVIC配置…...
储能辅助电力系统调峰的容量需求研究(matlab代码)
目录 1 主要内容 2 部分代码 3 程序结果 4 下载链接 1 主要内容 该程序参考文献《储能辅助电力系统调峰的容量需求研究》,是一个很常规很经典的matlab优化代码,主要是对火电、风电和储能等电力设备主体进行优化调度,在调峰能力达不到时采…...
8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂
蛋白质结合剂(如抗体、抑制肽)在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上,高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术,但这类方法普遍面临资源消耗巨大、研发周期冗长…...
FastAPI 教程:从入门到实践
FastAPI 是一个现代、快速(高性能)的 Web 框架,用于构建 API,支持 Python 3.6。它基于标准 Python 类型提示,易于学习且功能强大。以下是一个完整的 FastAPI 入门教程,涵盖从环境搭建到创建并运行一个简单的…...
React Native在HarmonyOS 5.0阅读类应用开发中的实践
一、技术选型背景 随着HarmonyOS 5.0对Web兼容层的增强,React Native作为跨平台框架可通过重新编译ArkTS组件实现85%以上的代码复用率。阅读类应用具有UI复杂度低、数据流清晰的特点。 二、核心实现方案 1. 环境配置 (1)使用React Native…...
抖音增长新引擎:品融电商,一站式全案代运营领跑者
抖音增长新引擎:品融电商,一站式全案代运营领跑者 在抖音这个日活超7亿的流量汪洋中,品牌如何破浪前行?自建团队成本高、效果难控;碎片化运营又难成合力——这正是许多企业面临的增长困局。品融电商以「抖音全案代运营…...
智能在线客服平台:数字化时代企业连接用户的 AI 中枢
随着互联网技术的飞速发展,消费者期望能够随时随地与企业进行交流。在线客服平台作为连接企业与客户的重要桥梁,不仅优化了客户体验,还提升了企业的服务效率和市场竞争力。本文将探讨在线客服平台的重要性、技术进展、实际应用,并…...
使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装
以下是基于 vant-ui(适配 Vue2 版本 )实现截图中照片上传预览、删除功能,并封装成可复用组件的完整代码,包含样式和逻辑实现,可直接在 Vue2 项目中使用: 1. 封装的图片上传组件 ImageUploader.vue <te…...
论文浅尝 | 基于判别指令微调生成式大语言模型的知识图谱补全方法(ISWC2024)
笔记整理:刘治强,浙江大学硕士生,研究方向为知识图谱表示学习,大语言模型 论文链接:http://arxiv.org/abs/2407.16127 发表会议:ISWC 2024 1. 动机 传统的知识图谱补全(KGC)模型通过…...
Python爬虫(一):爬虫伪装
一、网站防爬机制概述 在当今互联网环境中,具有一定规模或盈利性质的网站几乎都实施了各种防爬措施。这些措施主要分为两大类: 身份验证机制:直接将未经授权的爬虫阻挡在外反爬技术体系:通过各种技术手段增加爬虫获取数据的难度…...
css3笔记 (1) 自用
outline: none 用于移除元素获得焦点时默认的轮廓线 broder:0 用于移除边框 font-size:0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格ÿ…...
项目部署到Linux上时遇到的错误(Redis,MySQL,无法正确连接,地址占用问题)
Redis无法正确连接 在运行jar包时出现了这样的错误 查询得知问题核心在于Redis连接失败,具体原因是客户端发送了密码认证请求,但Redis服务器未设置密码 1.为Redis设置密码(匹配客户端配置) 步骤: 1).修…...
