EventBus:
基於觀察者模式實現的,本文基於同步模式來操作;這裏只介紹下幾個常用操作的代碼實現:
register:註冊觀察者實現:主要就是以把觀察者觀察的類key,同類觀察者的set集合爲value構成一個ConcurrenHashMap
代碼如下:
private final ConcurrentMap<Class<?>, CopyOnWriteArraySet<Subscriber>> subscribers =
Maps.newConcurrentMap();
這裏有兩步加鎖:map分段鎖,copyOnWriteArraySet讀寫鎖;
EventBus.post(Object):發送事件實現:根據事件的class找subscribers中的觀察者,通過反射執行所有觀察者的@Subscribe註解註釋的方法;
代碼如下:
public void post(Object event) {
Iterator<Subscriber> eventSubscribers = subscribers.getSubscribers(event);
if (eventSubscribers.hasNext()) {
dispatcher.dispatch(event, eventSubscribers);
} else if (!(event instanceof DeadEvent)) {
// the event had no subscribers and was not itself a DeadEvent
post(new DeadEvent(this, event));
}
}
本地測試結果:生產者生成100000條事件非常快(沒找到有拒絕策略,異步模式有線程池拒絕),如果動態增加大量生產者效率會下降主要是因爲兩個鎖,另外因爲消費者需要輪詢執行,如果消費者延遲高,會導致內存和cpu飆升,本地16g內存測試結果:
應用場景:ddd領域模型中jvm事件總線可以使用eventBus,建議使用異步的模式
Disruptor:
基礎介紹就不說了自己去看官方文檔,這裏只寫做事件總線怎麼做:
生產者:
// 1.ringBuffer 事件隊列 下一個槽
long sequence = ringBuffer.next();
//2.取出空的事件隊列
LongEvent longEvent = ringBuffer.get(sequence);
data = byteBuffer.getLong(0);
//3.獲取事件隊列傳遞的數據
longEvent.setValue(data);
ringBuffer.publish(sequence);
消費者(單線程):把消費者作爲事件轉發器,根據事件類型轉發到不同的自定義事件處理器(類似於Redis的事件處理機制)
每個事件處理器都在各自的業務線程池中執行(Hystrix的線程池隔離機制),避免某個延遲操作影響其他操作(線程池數不宜過多避免達到監控平臺的數量限制)
他是怎麼實現的線程安全的呢?
1.生產者cas替換ringbuffer的節點實現無鎖設計(如果競爭特別激烈會不會有某個線程一直不成功呢?肯定有但是還沒有那麼大併發量)
2.消費者通過維護自己的selection和ringbuffer的全局版本進行對比
這裏爲什麼把兩種方式比較呢?
其實大部分公司eventBus肯定夠用,但是他的所有的事件都是一個線程池,並沒有隔離,而且還有鎖,總的來說就是一個pull的模式。
disruptor是一個拉的模式,ringbuffer和消費者各自維護一個序號,用於記錄消費者消費的消費位置;另外disruptor支持多種消費模式:串行,並行(a1,a2之間並行,但是a1,b1需要串行),鏈式並行,菱形並行模式;
對於支付類的大型公司還是disruptor的無鎖效率更高,並且使用事件分派機制可以使各個業務操作在線程池層面隔離