解构EventBus框架(一)从一次业务解耦说起

业务背景

笔者所从事的电信业务中,经常涉及到宽带新开户的业务。在办理宽带开户的同时,会涉及到很多其它的业务,比如通知工单服务(预约工程师上门装机),通知资管服务(申请光猫设备)以及通知消息推送服务(给用户发短信)等。

针对这些业务,我们做一个最基本的实现

//R1
private WorkOrderService workOrderService;
private ResourceService resourceService;
private PushService pushService;

public void openUser() {
    User user = new User();//T1
    save(user);
    
    //开户成功之后调用
    workOrderService.openUserHandler(user);//T2
    resourceService.openUserHandler(user);//T3
    pushService.openUserHandler(user);//T4
    
}
复制代码

针对以上代码的实现,会引发什么问题呢?如果我们要再增加一个开户之后的业务,比如开户之后需要将用户缴费纳入到账务流程中,那我们需要修改openUser方法,这显然违背了代码设计的原则。鉴于这个问题,我们引入事件监听机制(或者叫观察者模式,生产者消费者模式等)解耦业务代码。

基于事件的实现

定义事件

public interface Event<T> {
    void reg(EventHandler eventHandler);
    void remove(EventHandler eventHandler);
    void notify(T t);
}
复制代码

定义事件的接收者

public interface EventHandler<T> {
    void handle(T t);
}
复制代码

开户事件

public class OpenUserEvent implements Event<User> {
    List<EventHandler<User>> eventHandlers = new ArrayList<EventHandler<User>>();

    public void reg(EventHandler eventHandler) {
        eventHandlers.add(eventHandler);
    }

    public void remove(EventHandler eventHandler) {
        eventHandlers.remove(eventHandler);
    }
	//R2-notify
    public void notify(User user) {
        for (EventHandler eventHandler : eventHandlers) {
            eventHandler.handle(user);
        }
    }
}
复制代码

开户事件处理器-短信推送服务

public class OpenUserEventHandler4SmsPush implements EventHandler<User> {
    
    private SmsPushService pushService = new SmsPushService();

    public void handle(User user) {
        pushService.openUserHandler(user);
    }
}
复制代码

开户事件处理器-工单服务

public class OpenUserEventHandler4WorkOrder implements EventHandler<User> {
    
    private WorkOrderService workOrderService = new WorkOrderService();

    public void handle(User user) {
        workOrderService.openUserHandler(user);
    }
}
复制代码

开户事件处理器-资源服务

public class OpenUserEventHandler4Resource implements EventHandler<User> {

    private ResourceService resourceService = new ResourceService();
    
    public void handle(User user) {
        resourceService.openUserHandler(user);
    }
}
复制代码

以上我们定义了几个角色:

  • 事件(Event),具有注册(register),unregister(取消注册),分发(notify)的功能

  • 事件处理(EventHandler),事件的接收处理者

开始模拟执行主流程

//初始化以及注册流程
final OpenUserEvent openUserEvent = new OpenUserEvent();
OpenUserEventHandler4SmsPush o1 = new OpenUserEventHandler4SmsPush();
OpenUserEventHandler4Resource o2 = new OpenUserEventHandler4Resource();
OpenUserEventHandler4WorkOrder o3 = new OpenUserEventHandler4WorkOrder();
openUserEvent.reg(o1);
openUserEvent.reg(o2);
openUserEvent.reg(o3);


//开户线程
new Thread(new Runnable() {
    public void run() {
        User user1 = new User();
        System.out.println("进入开户");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        user1.setId("1");
        user1.setName("jack");
        System.out.println("开户完成");

        //将开户事件通知给各个事件接收者
        openUserEvent.notify(user1);
    }
}).start();
复制代码

以上我们基于事件实现了业务解耦,让我们回到最初提出的问题,如果在开户时再增加一种业务,基于现有的框架就很简单了,不需要修改原有业务流程代码,只需要增加一个EventHandler就可以了。

解决阻塞问题

针对现有代码,我们做个优化,引入异步执行。

对比文章开头R1代码段,不难计算出总执行时长T

T=T1+T2+T3+T4
复制代码

在我们改造之后的代码(参考R2-notify代码段),执行时间实际上仍是T,并没有发生改变。现在我们将代码引入线程池,解决阻塞的问题。

定义一个简单任务

//任务包含了一个事件处理器以及处理器依赖对象
class SimpleTask<T> implements Runnable {

    private T t;
    private EventHandler<T> eventHandler;

    public SimpleTask(EventHandler<T> eventHandler, T t) {
        this.eventHandler = eventHandler;
        this.t = t;
    }

    public void run() {
        eventHandler.handle(t);
    }
}
复制代码

原有事件中引入线程池

public class OpenUserAsynEvent implements Event<User> {
    //定义一个线程池,实际应用中线程池可以是全局的或者业务模块级的
    private Executor executor = Executors.newFixedThreadPool(10);
    List<EventHandler<User>> eventHandlers = new ArrayList<EventHandler<User>>();

    public void reg(EventHandler eventHandler) {
        eventHandlers.add(eventHandler);
    }

    public void remove(EventHandler eventHandler) {
        eventHandlers.remove(eventHandler);
    }
	
    //R3-notify
    public void notify(User user) {
        for (EventHandler eventHandler : eventHandlers) {
            executor.execute(new SimpleTask<User>(eventHandler,user));
        }
    }
}
复制代码

对照R3-notify代码块,通过引入线程池解决了阻塞执行的问题。

写到这里,利用事件监听,我们已经完成了业务的解耦。好吧,我们现在又遇到了新的问题,现在系统又增加了修改密码(触发更新缓存),注销账户(触发回收设备资源、用户资料归档),账户充值(触发账单核销、信用控制)的业务,基于现有代码结构,那么我们就需要定义

ModifyPasswordEvent:

public class ModifyPasswordEvent implements Event<User> {
    List<EventHandler<User>> eventHandlers = new ArrayList<EventHandler<User>>();
    public void reg(EventHandler eventHandler) {}
    public void remove(EventHandler eventHandler) {}
    public void notify(User user) {}
}
复制代码

ModifyPasswordHandler:

public class ModifyPasswordEventHandler implements EventHandler<User> {
    private ModifyPasswordService service = new ModifyPasswordService();
    public void handle(User user) {
    }
}
复制代码

CancelEvent(CancelResourceHandler,CancelDocumentHandler)....

是不是看起来头很大,每个事件都实现了注册和分发, 太多的“事件-处理器”组,这都给代码维护带来了麻烦。这时候,我们需要引入框架,隐藏这些实现的细节,完成统一的注册和分发,让业务专注于业务。

下期,我们引入Google的EventBus框架来解决问题

猜你喜欢

转载自juejin.im/post/5eeec6686fb9a0586f5d7e7d