简单写一个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 完善篇后续再补。

posted @ 2024-04-15 12:21  敖毛毛  阅读(530)  评论(2编辑  收藏  举报