簡單寫一個eventbus

前言

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章