.Net Core 5.x Api开发笔记 -- 消息队列RabbitMQ实现事件总线EventBus(一)

本文主要围绕RabbitMQ消息队列和EventBus事件总线做笔记,其中有很多自己的理解和观点,有不对之处还望大神指出,我也学习一下。

1,消息队列

什么是消息队列就不说了,这里只介绍为什么用它!!!

RabbitMQ提供了可靠的消息机制、跟踪机制和灵活的消息路由,支持消息集群和分布式部署。适用于排队算法、秒杀活动、消息分发、异步处理、数据同步、处理耗时任务、CQRS等应用场景。它的以上好处在高并发等三高场景是十分必要的。

前提:使用RabbitMQ必须考虑高可用性

1、高可用:如果使用消息队列,基本要配合集群的,因为如果MQ服务器崩了,那就整个服务灾难了。

2、数据安全:必须保证数据不能丢失,也就是要考虑好最终一致性,做好补偿机制。

3、合理的消费。

2,事件总线

事件总线是对发布-订阅模式的一种实现。它是一种集中式事件处理机制,允许不同的组件之间进行彼此通信而又不需要相互依赖,达到一种解耦的目的。

说人话就是:集中对消息队列中处理的事件进行订阅和绑定管理。

实现事件总线的关键是:

  • 事件总线维护一个事件源与事件处理的映射字典;
  • 通过单例模式,确保事件总线的唯一入口;
  • 利用反射完成事件源与事件处理的初始化绑定;
  • 提供统一的事件注册、取消注册和触发接口;
 1 public class EventBus
 2 {
 3     /// <summary>
 4     /// EventBus 单例
 5     /// </summary>
 6     public static EventBus Default { get; private set; }
 7 
 8     /// <summary>
 9     /// 事件与事件的处理程序映射
10     /// </summary>
11     private static readonly ConcurrentDictionary<Type, List<Type>> m_EventAndHandlersMapping = new ConcurrentDictionary<Type, List<Type>>();
12 
13     /// <summary>
14     /// 加锁对象
15     /// </summary>
16     private object m_lockObj;
17 
18     static EventBus()
19     {
20         Default = new EventBus();
21     }
22 
23     private EventBus()
24     {
25         m_lockObj = new object();
26     }
27 
28     /// <summary>
29     /// 注册事件源和事件处理程序
30     /// </summary>
31     /// <typeparam name="TEvent">事件类型</typeparam>
32     /// <typeparam name="THandler">处理程序类型</typeparam>
33     public void RegisterHandler<T, THandler>()
34         where T : EventBase
35         where THandler : IEventHandler<T>
36     {
37         AddMapping(typeof(T), typeof(THandler));
38     }
39 
40     /// <summary>
41     /// 新增映射 事件源+事件处理程序
42     /// </summary>
43     /// <param name="eventType"></param>
44     /// <param name="handlerType"></param>
45     private void AddMapping(Type eventType, Type handlerType)
46     {
47         var handlers = m_EventAndHandlersMapping.GetOrAdd(eventType, type => new List<Type>());
48         lock (m_lockObj)
49         {
50             if (!handlers.Contains(handlerType))
51             {
52                 handlers.Add(handlerType);
53             }
54         }
55     }
56   
57     /// <summary>
58     /// 异步触发事件
59     /// </summary>
60     /// <param name="e"></param>
61     /// <returns></returns>
62     public async Task TriggerAsync<TEvent>(TEvent e)
63         where TEvent : EventBase
64     {
65         List<Type> value;
66         if (m_EventAndHandlersMapping.TryGetValue(e.GetType(), out value))
67         {
68             foreach (var handler in value.OrderBy(p => p.Name))
69             {
70                 var instance = IocManager.ServiceProvider.GetService(handler) as IEventHandler<TEvent>;
71                 await instance?.HandleEvent(e);
72             }
73         }
74     }
75 }

上边就是一个简单的事件总线管理类,主要任务就是对一个事件源与事件处理的映射,包括事件处理程序的触发。

3,事件源+事件处理

为了集中对事件进行处理,就需要进一步提取基类来处理,如下所示:

 1 /// <summary>
 2 /// 事件处理程序基类
 3 /// </summary>
 4 public interface IBaseEventHandler
 5 {
 6 }
 7 
 8 /// <summary>
 9 /// 事件处理程序
10 /// 解释:where T : EventBase 表示传入的类型必须继承 EventBase类
11 /// </summary>
12 public interface IEventHandler<T> : IBaseEventHandler
13     where T : EventBase
14 {
15     /// <summary>
16     /// 处理事件
17     /// </summary>
18     /// <param name="e">事件传递的数据</param>
19     Task HandleEvent(T e);
20 }

然后还需要声明一个事件基类来传递数据信息,这里声明一个强类型的带数据的事件

 1 /// <summary>
 2 /// 事件基类
 3 /// </summary>
 4 public abstract class EventBase
 5 {
 6     protected EventBase()
 7         : this(null)
 8     { 
 9     }
10     protected EventBase(object source)
11     {
12         this.Source = source;
13     }
14     /// <summary>
15     /// 引发事件的源
16     /// </summary>
17     public object Source { get; private set; }
18 }
19 
20 /// <summary>
21 /// 声明一个强类型的带数据的事件 传递数据
22 /// </summary>
23 public class EventWithData<TData> : EventBase
24 {
25 
26     /// <summary>
27     /// 初始化 EventBase
28     /// </summary>
29     /// <param name="data">传递的数据</param>
30     public EventWithData(TData data)
31     {
32         this.Data = data;
33     }
34 
35     /// <summary>
36     /// 事件传递的数据
37     /// </summary>
38     public TData Data { get; private set; }
39 
40     /// <summary>
41     /// 创建 EventWithData'T'
42     /// </summary>
43     /// <param name="data">数据</param>
44     /// <returns></returns>
45     public static EventWithData<TData> New(TData data)
46     {
47         return new EventWithData<TData>(data);
48     }
49 }

到这里关于事件总线的设计就初步完成了,接下来是关于消息队列 发布+订阅的设计。

 

posted @ 2021-09-25 23:29  找.net工作(北京)  阅读(1166)  评论(1编辑  收藏  举报