前言
閒暇之餘,簡單寫一個eventbus。
正文
什麼是eventbus?
eventbus 是一個開源的釋出訂閱模式的框架,用於簡化程式間不同元件的通訊。
它允許不同元件間松耦合通訊,元件之間不透過直接引用的方式,而是事件的方式進行訊息傳遞。
下面進行程式碼演示:
首先是釋出訂閱,那麼就應該有釋出方法和訂閱方法,因為是訊息傳遞,那麼就應該還有啟動消費訊息的方法。
public interface IEventBus : IDisposable
{
Task Publish<T>(T @event) where T : IntegrationEvent;
Task Subscribe<T>(IIntegrationEventHandler<T> handler)
where T : IntegrationEvent;
Task StartConsume();
}
大體我們要實現上面的功能。
然後我們可以定義事件的基礎資訊:
public class IntegrationEvent
{
public Guid Id { get; set; }
public DateTime OccurredOn { get; set; }
public IntegrationEvent()
{
Id = Guid.NewGuid();
OccurredOn = DateTime.Now;
}
}
比如說要有唯一的id,同時要有事件發生的時間。
訂閱的話,那麼需要指定處理的物件。
public interface IIntegrationEventHandler
{
}
public interface IIntegrationEventHandler<in TIntegrationEvent> :
IIntegrationEventHandler where TIntegrationEvent : IntegrationEvent
{
Task Handler(TIntegrationEvent @event);
}
處理物件設計也很簡單,就是需要建立一個有能夠處理IntegrationEvent的物件即可。
這裡很多人會疑惑,為什麼很多框架的泛型介面類,往往會建立一個非泛型的介面。
這個其實是為了進一步抽象,方便做集合處理,下面將會介紹到。
然後就可以寫一個記憶體型的eventbus。
public class InMemoryEventBus : IDisposable
{
private Dictionary<string, List<IIntegrationEventHandler>>
_dictionary = new Dictionary<string, List<IIntegrationEventHandler>>();
public async Task Publish<T>(T @event) where T : IntegrationEvent
{
var fullName = @event.GetType().FullName;
if (fullName == null)
{
return;
}
var handlers = _dictionary[fullName];
foreach (var integrationEventHandler in handlers)
{
if (integrationEventHandler is IIntegrationEventHandler<T> handler)
{
await handler.Handler(@event);
}
}
}
public async Task Subscribe<T>(IIntegrationEventHandler<T> handler)
where T : IntegrationEvent
{
var fullname = typeof(T).FullName;
if (fullname == null)
{
return;
}
if (_dictionary.ContainsKey(fullname))
{
var handlers = _dictionary[fullname];
handlers.Add(handler);
}
else
{
_dictionary.Add(fullname, new List<IIntegrationEventHandler>()
{
handler
});
}
}
public void Dispose()
{
// 移除相關連線等
}
}
裡面實現了eventbus的基本功能。可以看到上面的_dictionary,裡面就是IIntegrationEventHandler,
所以泛型介面會繼承一個非泛型的介面,是為了進一步抽象宣告,對一些集合處理是很方便的。
然後這裡為什麼沒有直接繼承Ieventbus呢? 而是實現eventbus的功能。
因為Ieventbus 其實是面向使用者的,繼承ieventbus只是一個門面,相當於介面卡。
而InMemoryEventBus 是為了實現功能。
可以理解為InMemoryEventBus 是我們電腦主機板、cpu等,然後我們只需要一個實現其介面的元件,從而和外部連線。
而不是整個核心系統和外部直連,那麼我們可以使用InMemoryEventBusClient 作為這個元件。
public class InMemoryEventBusClient : IEventBus
{
private readonly InMemoryEventBus _eventBus;
public InMemoryEventBusClient()
{
_eventBus = new InMemoryEventBus();
}
public void Dispose()
{
_eventBus.Dispose();
}
public async Task Publish<T>(T @event) where T : IntegrationEvent
{
await _eventBus.Publish(@event);
}
public async Task Subscribe<T>(IIntegrationEventHandler<T> handler) where T : IntegrationEvent
{
await _eventBus.Subscribe(@handler);
}
public Task StartConsume()
{
// 執行相關的消費
return Task.CompletedTask;
}
}
InMemoryEventBusClient 負責實現外部介面,InMemoryEventBus 負責實現功能。
從而達到解耦的目的。
同樣的例子還有polly,這個框架應該很出名了,其中他裡面就有很多衍生的元件,都是呼叫核心來適配其他框架定義的介面。
上面可以看到StartConsume什麼都沒有做,其功能被Publish給融合了。
只要publish就消費了。
如果我們擴充套件kafka的話,那麼consume其實就是拉取資料然後消費,publish其實就是推向kafka,中間就是序列號和反序列話的過程。
結
eventbus 完善篇後續再補。