SpringBoot封装EventBus

65 阅读3分钟

前言

EventBus 是 Guava 的事件处理机制,是观察者模式(生产/消费模型)的一种实现。

观察者模式在我们日常开发中使用非常广泛,例如在订单系统中,订单状态或者物流信息的变更会向用户发送APP推送、短信、通知卖家、买家等等;审批系统中,审批单的流程流转会通知发起审批用户、审批的领导等等。

Observer模式也是 JDK 中自带就支持的,其在 1.0 版本就已经存在 Observer,不过随着 Java 版本的飞速升级,其使用方式一直没有变化,许多程序库提供了更加简单的实现,例如 Guava EventBus、RxJava、EventBus 等

一、为什么要用 Observer模式以及 EventBus 优点 ?

EventBus 优点

  • 相比 Observer 编程简单方便
  • 通过自定义参数可实现同步、异步操作以及异常处理
  • 单进程使用,无网络影响

缺点

  • 只能单进程使用
  • 项目异常重启或者退出不保证消息持久化

如果需要分布式使用还是需要使用 MQ

二、EventBus 使用步骤

导入对应的依赖

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>19.0</version>
</dependency>

编写线程池配置

@Data
@ConfigurationProperties(prefix = "eventbus.pool")
public class EventBusExecutorProperites {
    /**
     * 核心线程数
     */
    private int corePoolSize = 5;
    /**
     * 最大线程数
     */
    private int maxPoolSize = 20;
    /**
     * 空闲线程销毁时间
     */
    private int keepAliveSeconds = 60;
​
    /**
     * 任务队列容量
     */
    private int queueCapacity = 1000;
​
    /**
     * 是否允许核心线程空闲退出
     */
    private boolean allowCoreThreadTimeOut = false;
​
    /**
     * 线程前缀
     */
    private String threadNamePrefix = "event-bus-pool-";
}

EventBus中事件订阅者的方法必需只能接受一个参数,声明基础参数类

@Data
public abstract class BaseCallableBean {
    private String oper;
    private String opeTime;
}

声明订阅者基础类

public abstract class BaseCallable<T extends BaseCallableBean> {
    public abstract void call(T t);
}

定义了事件监听者类,并定义参数类

注意:使用@Subscribe注解

@Data
@ToString
@AllArgsConstructor
public class TestBean extends BaseCallableBean{
    private String id;
    private String name;
}
@Slf4j
@Component
public class TestBaseCallable extends BaseCallable<TestBean> {
    @Override
    @Subscribe
    public void call(TestBean testBean) {
        log.info("hello");
        log.info(testBean.toString());
    }
}

进行EventBus配置

@Slf4j
@Configuration
@EnableConfigurationProperties(EventBusExecutorProperites.class)
public class EventBusConfiguration implements ApplicationListener<ContextRefreshedEvent> {
​
    @Autowired
    private EventBusExecutorProperites executorProperites;
​
    @Bean("eventBusExecutor")
    @Primary
    public Executor getThreadPoolTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 核心线程
        executor.setCorePoolSize(executorProperites.getCorePoolSize());
        // 最大线程数
        executor.setMaxPoolSize(executorProperites.getMaxPoolSize());
        // 任务队列容量
        executor.setQueueCapacity(executorProperites.getQueueCapacity());
        // 线程前缀
        executor.setThreadNamePrefix(executorProperites.getThreadNamePrefix());
        // 是否允许核心线程空闲退出
        executor.setAllowCoreThreadTimeOut(executorProperites.isAllowCoreThreadTimeOut());
        // 空闲线程销毁时间
        executor.setKeepAliveSeconds(executorProperites.getKeepAliveSeconds());
        // 任务装饰器-增强器,类似于aop
        executor.setTaskDecorator(new MDCTaskDecorator());
        // 不丢弃任务 由调用线程处理该任务
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        // 如果为true 线程池shutdown false 为 shutdownNow
        executor.setWaitForTasksToCompleteOnShutdown(true);
        executor.initialize();
        return executor;
    }
​
    static class MDCTaskDecorator implements TaskDecorator {
        @Override
        public Runnable decorate(Runnable runnable) {
            // 获取当前线程的MDC上下文数据副本,并存储在 mdcContext 中。
            Map<String, String> contextMap = MDC.getCopyOfContextMap();
            return () -> {
                if (contextMap != null) {
                    // 将 mdcContext 中的数据重新设置回当前线程的MDC,以恢复或传递上下文数据给其他线程或任务。
                    MDC.setContextMap(contextMap);
                }
                runnable.run();
            };
        }
    }
​
    @Bean("eventBus")
    public EventBus eventBus() {
        return new AsyncEventBus(getThreadPoolTaskExecutor());
    }
​
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        registerSubscribes(event.getApplicationContext());
    }
​
    private void registerSubscribes(ApplicationContext context) {
        if (null == context) {
            return;
        }
        // 根据上下文获取类型为 BaseCallable 的bean
        String[] beanDefinitionNames = context.getBeanNamesForType(BaseCallable.class, false, true);
        // 根据上下文获取eventbus的bean,用于注册
        EventBus eventBus = context.getBean(EventBus.class);
        for (String beanDefinitionName : beanDefinitionNames) {
            Object bean = context.getBean(beanDefinitionName);
            try {
                Class<?> aClass = bean.getClass();
                Method call = aClass.getMethod("call", BaseCallableBean.class);
​
                if (null == call) {
                    continue;
                }
                log.info("EventBus method-Subscribe register bean[{}]", beanDefinitionName);
            } catch (NoSuchMethodException e) {
                log.info("EventBus method-Subscribe register bean Exception : {}", e.getMessage());
                e.printStackTrace();
            }
            eventBus.register(bean);
        }
    }
}

测试

@RestController
@SpringBootApplication
public class DemoApplication {
    @Autowired
    private EventBus eventBus;
​
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
    
    @GetMapping("testEventBus")
    public String testEventBus(){
        TestBean testBean = new TestBean("1","hello");
        eventBus.post(testBean);
        return "ok";
    }   
}

测试结果

image-20231107143728624.png