C#&.NET implements a distributed event bus from 0 (1)

The event bus can provide a set of publish and subscribe mechanisms from the perspective of the system as a whole. Students who have used ABP framework development, or studied DDD, or understood Rebus, should know the many benefits brought by the application of event bus, and it can bring us an improvement in programming thinking.

Source code & Nuget

By learning some open source component libraries and starting from my own experience, I designed a distributed event bus based on RabbitMq from 0. You can go to my Azure Devops warehouse to view the source code.

And I published it as a Nuget package, which can be installed and used by everyone

Install-Package MaH.EventBus -Version 0.7.0

Let's look at a simple application first:

//定义一个订单更新事件
namespace MaH.SimpleLocalEventBus.Console
{
    public class UpdateOrderEvent
    {
        public DateTime Timestamp { get; set; }

        public UpdateOrderEvent()
        {
            Timestamp = DateTime.Now;
        }
    }
}
//创建一个订单更新事件处理程序, 继承:IEventHandler<UpdateOrderEvent>
namespace MaH.SimpleLocalEventBus.Console
{
    public class UpdateOrderHandler : IEventHandler<UpdateOrderEvent>
    {
        public Task InvokeAsync(UpdateOrderEvent eventData)
        {
            System.Console.WriteLine($"Update-{eventData.Timestamp}");

            return Task.CompletedTask;
        }
    }
}
using System.Threading.Tasks;
//在EventBus中注册处理程序,当使用eventBus.PublishAsync()发布一个事件后,会根据事件类型自动触发处理程序
namespace MaH.SimpleLocalEventBus.Console
{
    class Program
    {
        static async Task Main(string[] args)
        {
            ILocalEventBus eventBus = new LocalEventBus();

            UpdateOrderHandler updateOrderHandler = new UpdateOrderHandler();

            eventBus.Subscribe(updateOrderHandler);

            await eventBus.PublishAsync(new UpdateOrderEvent());

            System.Console.ReadKey();
        }
    }
}

local event bus

The above example shows a simple application of the local event bus, and the implementation method is also very basic. You can go to the Basic-1.0 branch of MaH.EventBus  to view the specific implementation.

 public class LocalEventBus : ILocalEventBus
    {
        private ConcurrentDictionary<Type, List<IHandler>> handlers;

        public LocalEventBus()
        {
            handlers = new ConcurrentDictionary<Type, List<IHandler>>();
        }
        public async Task PublishAsync<TEvent>(TEvent eventData) where TEvent : class
        {
            await TriggerEventAsync(GetOrAddHandlers(typeof(TEvent)), eventData);
        }

        public void Subscribe<TEvent>(IEventHandler<TEvent> handler) where TEvent : class
        {
            GetOrAddHandlers(typeof(TEvent)).Add(handler);
        }
}

 

We can notice that the implementation here is done by registering a specific Handler instance object. If we want to control the life cycle of the handler, or in other words, we want the handler to be obtained from the IOC container, what should we do?

The answer is to introduce the factory pattern and use the factory method to get the handler.

Get Handler from IOC container

We can switch the branch to  Basic-2.0 of MaH.EventBus to see the specific implementation:

// LocalEventBus不再直接保存handler的引用,而是保存相应的IEventHandlerFactory
public class LocalEventBus : ILocalEventBus
    {
        private ConcurrentDictionary<Type, List<IEventHandlerFactory>> handlers;

        public LocalEventBus()
        {
            handlers = new ConcurrentDictionary<Type, List<IEventHandlerFactory>>();
        }
        
        public void Subscribe(Type eventType, IEventHandlerFactory handlerFactory)
        {
            GetOrAddHandlers(eventType).Add(handlerFactory);
        }
    }
namespace MaH.SimpleLocalEventBus
{
    public interface IEventHandlerFactory
    {
        IHandler GetHandler();
    }
}

I made two implementations of IEventHandlerFactory, one is SingletonEventHandlerFactory, which directly maintains a reference to the handler instance:

 public class SingletonEventHandlerFactory : IEventHandlerFactory
    {
        private readonly IHandler _handlerInstance;

        public SingletonEventHandlerFactory(IHandler handlerInstance)
        {
            _handlerInstance = handlerInstance;
        }

        public IHandler GetHandler() => _handlerInstance;
    }

The other is based on the IOC container, saving the handler type and a reference to IServiceScopeFactory:

public class DefaultEventHandlerFactory : IEventHandlerFactory
    {
        private readonly Type _handlerType;

        private readonly IServiceScopeFactory _serviceScopeFactory;

        public DefaultEventHandlerFactory(Type handlerType, IServiceScopeFactory serviceScopeFactory)
        {
            _handlerType = handlerType;
            _serviceScopeFactory = serviceScopeFactory;
        }

        public IHandler GetHandler()
        {
            var scope = _serviceScopeFactory.CreateScope();
            return (IHandler) scope.ServiceProvider.GetRequiredService(_handlerType);
        }
    }

In this way, we can use the event bus in the .NET project using IOC technology! Of course, an excellent event bus framework should also provide an automatic subscription mechanism, which is not implemented here.

 public void ConfigureServices(IServiceCollection services)
        {
            services.AddTransient<WeatherForecastHandler>();
            services.AddSingleton<ILocalEventBus>(sp =>
            {
                var eventBus = new LocalEventBus();
                eventBus.Subscribe(typeof(WeatherUpdateEvent), new DefaultEventHandlerFactory(typeof(WeatherForecastHandler), sp.GetRequiredService<IServiceScopeFactory>()));
                return eventBus;
            });
        }

Distributed Event Bus

It is not enough to implement the local event bus, because in the microservice design, it often involves communication between multiple systems. For example, the payment system issues an order payment notification, and the SMS site needs to send the payment success SMS according to the order information, etc. wait.

Therefore, we need to further upgrade the current event bus design to implement a distributed event bus with the help of RabbitMQ. Of course, you can also use Kafka, database, etc.

Guess you like

Origin blog.csdn.net/qq_40404477/article/details/116763534