说下你可能没用过的EventBus(一)

wg204wg
发布于 2022-6-10 16:01
浏览
0收藏

 

最近在Code Review的时候发现了这样一个业务场景,某个业务处理完成之后需要通知审核人员,通知的方式包含短信和邮件,所以代码大致是这样:

//业务校验
validate();
//处理业务逻辑
doBusiness();
//发送邮件或者发送其他类型消息
sendMsg(); 

这个对不对呢?

基于这种普遍的业务场景来说,一般首先我们会考虑同步或者异步发送的问题。

同步的话对接口RT有影响,而且和业务逻辑耦合在一起,这样的做法肯定不太好。

一般情况下,我们会做成异步的方式,使用MQ自己发送自己消费,或者说一个线程池搞定,这样的话不影响主业务逻辑,可以提高性能,并且代码做到了解耦。

然后还有就是数据一致性的问题,邮件一定要发送成功吗?

大多数时候其实我们并不要求邮件一定100%发送成功,失败了就失败好了,监控告警打点做好失败率不要超过阈值就好,还有就是消息服务一旦收到请求应该自己保证消息能够投递。

所以总的来说,使用MQ发送消息自己消费处理,或者线程池异步处理,最后自己搞个补偿的逻辑就能处理好这类问题。

那么,今天要说的是这两个解决方案之外的处理方式,对于这种场景其实我们可以用EventBus来解决。

EventBus使用
看名字就知道,EventBus是事件总线的意思,它是Google Guava库的一个工具,基于观察者模式可以做到进程内的代码解耦作用。

就拿上面的例子来说,引入一个MQ太重了,其实不太需要这样做,EventBus也能达到这个效果,和MQ相比他只能提供进程内的消息事件传递,这对于我们这种业务场景来说足够了不是吗?

我们先看EventBus怎么来使用,一般先创建一个EventBus实例。

//1.创建EventBus
private static EventBus eventBus = new EventBus();

第二步,创建一个事件消息订阅者,处理方式非常简单,只要在我们希望去处理事件的方法上加上@Subscribe注解即可。

形参只能有一个,如果定义0个或者多个的话运行就会报错。

public class EmailMsgHandler {

    @Subscribe
    public void handle(Long businessId) {
        System.out.println("send email msg" + businessId);
    }
}

第三步,注册事件。

eventBus.register(new EmailMsgHandler());

第四步,发送事件。

eventBus.post(1L);

这就是一个EventBus使用的最简单例子,下面我们看看结合开头说的例子怎么处理。

结合实际
比如上面说的案例,举例来说比如注册和用户下单的场景,都需要发送消息和邮件给用户。

EventBus并不强制说我们一定要用单例模式,因为他的创建销毁成本比较低,所以更多是根据我们的业务场景和上下文自己来选择。

public class UserService {
    private static EventBus eventBus = new EventBus();

    public void regist(){
        Long userId = 1L;
        eventBus.register(new EmailMsgHandler());
        eventBus.register(new SmsMsgHandler());
        eventBus.post(userId);
    }
}

public class BookingService {
    private static EventBus eventBus = new EventBus();

    public void booking(){
        //业务逻辑
        Long bookingId = 2L;
        eventBus.register(new EmailMsgHandler());
        eventBus.register(new SmsMsgHandler());
        eventBus.post(bookingId);
    }
}

 

然后在业务逻辑处理完成之后,分别去注册了邮件和短信两个事件订阅者。

public class EmailMsgHandler {

    @Subscribe
    public void handle(Long businessId) {
        System.out.println("send email msg" + businessId);
    }
}

public class SmsMsgHandler {

    @Subscribe
    public void handle(Long businessId) {
        System.out.println("send sms msg" + businessId);
    }
}

 

最后我们发送事件,用户注册我们发送了一个用户ID,下单成功我们发送了一个订单ID。

再写一个测试类去测试一下,分别创建两个service,然后分别调用方法。

public class EventBusTest {

    public static void main(String[] args) {
        UserService userService = new UserService();
        userService.regist();

        BookingService bookingService = new BookingService();
        bookingService.booking();

    }
}

执行测试类,我们可以看到输出,分别去执行了我们的事件订阅的方法。

send email msg1
send sms msg1
send email msg2
send sms msg2

使用起来你会发现非常简单,对于希望轻量级简单地做到解耦使用EventBus非常合适。

注意别踩坑
首先,注意一下例子中的参数都是Long类型,如果事件的参数是其他类型的话,那么消息是无法接受到的,比如我们把下单中发送的订单ID改成String类型然后会发现没有消费了,因为我们没有定义一个参数类型是String的方法。

public class BookingService {
    private static AsyncEventBus eventBus = new AsyncEventBus(Executors.newFixedThreadPool(3));

    public void booking(){
        //业务逻辑
        String bookingId = "2";
        eventBus.register(new EmailMsgHandler());
        eventBus.register(new SmsMsgHandler());
        eventBus.post(bookingId);
    }
}
//输出
send email msg1
send sms msg1

去EmailMsgHandler和SmsMsgHandler都新增一个接收String类型的订阅方法,这样就可以接收到了。

@Subscribe
public void handle(String businessId) {
 System.out.println("send email msg for string" + businessId);
}

@Subscribe
public void handle(String businessId) {
 System.out.println("send sms msg for string" + businessId);
}

//输出
send sms msg1
send email msg1
send email msg for string2
send sms msg for string2

 

文章转自公众号:艾小仙

分类
标签
已于2022-6-10 16:01:02修改
收藏
回复
举报
回复
    相关推荐