我稱其為單塊架構(gòu)的利器
前言
在設(shè)計(jì)模式中敏晤, 有一種叫做發(fā)布/訂閱模式茂洒, 即某事件被發(fā)布, 訂閱該事件的角色將自動(dòng)更新采缚。
那么訂閱者和發(fā)布者直接耦合针炉, 也就是說在發(fā)布者內(nèi)要通知訂閱者說我這邊有東西發(fā)布了, 你收一下扳抽。
正如在rxJava中篡帕, 主要就是發(fā)布訂閱設(shè)計(jì)模式
Observable.just(1).subscribe(new Subsriber(){
@Override
public void onCompleted() {
System.out.println("onCompleted ");
}
@Override
public void onError(Throwable arg0) {
}
@Override
public void onNext(Long arg0) {
System.out.println("onNext " + arg0);
}
})
我們可以看到, 發(fā)布者發(fā)布一個(gè)數(shù)字1摔蓝, 訂閱者訂閱了這個(gè)
而加入eventBus赂苗, 發(fā)布者與生產(chǎn)者之間的耦合性就降低了。因?yàn)檫@時(shí)候我們?nèi)ス芾韊ventbus就可以贮尉, 發(fā)布者只要向eventbus發(fā)送信息就可以拌滋, 而不需要關(guān)心有多少訂閱者訂閱了此消息。模型如下:
為什么說eventBus 是單塊架構(gòu)的利器呢猜谚?
首先單塊架構(gòu)就是在一個(gè)進(jìn)程內(nèi)败砂, 在一個(gè)進(jìn)程內(nèi), 我們還是希望模塊與模塊之間(功能與功能之間)是松耦合的魏铅,而在一個(gè)模塊中是高度內(nèi)聚的昌犹, 如何降低一定的耦合, 使得代碼更加有結(jié)構(gòu)览芳, guava eventbus就是支持進(jìn)程內(nèi)通訊的橋梁斜姥。
想象一下以下業(yè)務(wù)
我們希望在數(shù)據(jù)到來之后, 進(jìn)行入庫沧竟, 同時(shí)能夠?qū)?shù)據(jù)進(jìn)行報(bào)警預(yù)測(cè)铸敏, 當(dāng)發(fā)生報(bào)警了, 能夠有以下幾個(gè)動(dòng)作悟泵, 向手機(jī)端發(fā)送推送杈笔, 向web端發(fā)送推送, 向手機(jī)端發(fā)送短信糕非。
在一般情況下我們可以這樣實(shí)現(xiàn): (偽代碼如下)
processData(data){
insertintoDB(data); //執(zhí)行入庫操作
predictWarning(data); // 執(zhí)行報(bào)警預(yù)測(cè)
}
在predictWarning(data)中{
if(data reaches warning line){
sendNotification2App(data); //向手機(jī)端發(fā)送推送
sendNotification2Web(data); // 向web端發(fā)送推送
sendSMS2APP(data); //手機(jī)端發(fā)送短信
}
}
在這里我不去講具體是如何向web端發(fā)送推送蒙具, 如何發(fā)送短信球榆。主要用到第三方平臺(tái)
分析
入庫和報(bào)警預(yù)測(cè)是沒有直接聯(lián)系,或者是不分先后順序的禁筏, 同樣在報(bào)警模塊中持钉, 向3個(gè)客戶端發(fā)送信息也應(yīng)該是沒有聯(lián)系的, 所以以上雖然可以實(shí)現(xiàn)功能融师, 但不符合代碼的合理性右钾。
應(yīng)該是怎么樣的邏輯呢? 如下圖
當(dāng)數(shù)據(jù)事件觸發(fā)旱爆, 發(fā)布到data EventBus 上舀射, 入庫和預(yù)警分別訂閱這個(gè)eventBus, 就會(huì)觸發(fā)這兩個(gè)事件, 而在預(yù)警事件中怀伦, 將事件發(fā)送到warning EventBus 中脆烟, 由下列3個(gè)訂閱的客戶端進(jìn)行發(fā)送消息。
如何實(shí)現(xiàn)
先來講同步房待, 即訂閱者收到事件后依次執(zhí)行, 下面都是偽代碼邢羔, 具體的入庫細(xì)節(jié)等我在這里不提供。
@Component
public class DataHandler{
@Subscribe
public void handleDataPersisist(Data data){
daoImpl.insertData2Mysql(data);
}
@Subscribe
public void predictWarning(Data data){
if(data is warning){ // pseudo code 如果預(yù)警
Warning warning = createWarningEvent(data); // 根據(jù)data創(chuàng)建一個(gè)Warning事件
postWarningEvent(warning)
}
}
protected postWarningEvent(Warning warning){
EventBusManager.warningEventBus.post(warning);// 發(fā)布到warning event 上
}
@PostConstruct // 由spring 在初始化bean后執(zhí)行
public void init(){
register2DataEventBus();
}
// 將自己注冊(cè)到eventBus中
protected void register2DataEventBus(){
EventBusManager.dataEventBus.register(this);
}
}
@Component
public class WarningHandler{
@Subscribe
public void sendNotification2AppClient(Warning warning){
JpushUtils.sendNotification(warning);
}
@Subscribe
public void sendSMS(Warning warning){
SMSUtils.sendSMS(warning);
}
@Subscribe
public void send2WebUsingWebSocket(Warning warning){
WebsocketUtils.sendWarning(warning);
}
@PostConstruct // 由spring 在初始化bean后執(zhí)行
public void init(){
register2WarningEventBus();
}
// 將自己注冊(cè)到eventBus中
protected void register2DataEventBus(){
EventBusManager.warningEventBus.register(this);
}
}
/**
* 管理所有的eventBus
**/
public class EventBusManager{
public final static EventBus dataEventBus = new EventBus();
public final static EventBus warningEventBus = new EventBus();
}
簡(jiǎn)化
// 我們發(fā)現(xiàn)每一個(gè)Handler都要進(jìn)行注冊(cè)桑孩,
public abstract class BaseEventBusHandler{
@PostConstruct
public void init(){
register2EventBus();
}
private void register2EventBus(){
getEventBus().register(this);
}
protected abstract EventBus getEventBus();
}
這樣在寫自己的eventBus只需要
@Component
public class MyEventBus extends BaseEventBusHandler{
@Override
protected abstract EventBus getEventBus(){
retrun EventBusManager.myEventBus;
}
}
在目前的應(yīng)用場(chǎng)景下拜鹤, 同步是我們不希望的, 異步場(chǎng)景也很容易實(shí)現(xiàn)流椒。
只需要將EventBus 改成
AsyncEventBus warningEvent = new AsyncEventBus(Executors.newFixedThreadPool(1))
AsyncEventBus dataEventBus = new AsyncEventBus(Executors.newFixedThreadPool(3))
當(dāng)然還有很多很多場(chǎng)景我們利用發(fā)布和訂閱模式敏簿, 在一定時(shí)候巧妙結(jié)合guavaEventBus, 這樣我們單塊架構(gòu)的結(jié)構(gòu)更加合理。
至于分布式的架構(gòu)
宣虾, 我們可以使用MQ 如rabbitMq, kafka惯裕。 這里的篇章我會(huì)在后續(xù)給出。
如果大家感興趣的話: 可以多去看看響應(yīng)式編程绣硝, 有很多特性會(huì)吸引著你蜻势。
如果大家想對(duì)發(fā)布和訂閱設(shè)計(jì)模式是如何實(shí)現(xiàn)的, 其實(shí)也簡(jiǎn)單鹉胖, 后續(xù)我會(huì)簡(jiǎn)單實(shí)現(xiàn)這種設(shè)計(jì)模式握玛。
在本篇最后, 還推薦大家去學(xué)習(xí)一下vert.x 甫菠, 后續(xù)我也會(huì)去補(bǔ)充败许。
在vert.x 中, 用到了eventbus(非guava eventBus)淑蔚。
圖片引用:
圖一: 來自百度百科