前言
閒暇之餘,簡單寫一個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 完善篇後續再補。