Lind.DDD.Events事件总线~自动化注册
让大叔兴奋的自动化注册
对于领域事件之前说过,在程序启动时订阅(注册)一些事件处理程序,然后在程序的具体位置去发布(触发)它,这是传统的pub/sub模式的体现,当然也没有什么问题,为了让它支持分布式的场景,我们引用了redis这种存储介质,这一切都早已集成到了Lind.DDD架构中,对些没什么好说的,而今天的重点在于"事件的自动过注册"的理念,这个概念真实在ABP架构中出现了,大叔觉得很不错,所以也集成到了自己的架构中,为些也兴奋了一段时间,其中有解决问题的
Redis只是一种分布式存储介质
对于第一版将事件总线放到内存的情况来说,使用redis这种存储介质确实解决了分布式的事件问题,它可以在更多场合下使用,不用考虑WEB端的负载均衡,不用考虑服务端的存储压力,不用考虑并发时的吞吐量,确实,redis是个存储效率非常高的产物,大叔redis里的事件的Key采用了当前EventData的名字加上自定义的前缀,这样可以同时在多个项目中使用.
/// <summary> /// 对于事件数据的存储,目前采用内存字典 /// </summary> private readonly IRedisClient _redisClient = RedisManager.GetClient(); /// <summary> /// redis事件总线的Key /// </summary> private string redisKey = ConfigConstants.ConfigManager.Config.DomainEvent.RedisKey; /// <summary> /// 得到当前redis-eventbus-key /// </summary> /// <typeparam name="TEvent"></typeparam> /// <returns></returns> private string GetCurrentRedisKey<TEvent>() { return redisKey + "_" + typeof(TEvent).FullName; } /// <summary> ///得到非泛型版本的值 /// </summary> /// <param name="tEvent"></param> /// <returns></returns> private string GetCurrentRedisKey(Type tEvent) { return redisKey + "_" + tEvent.FullName; }
结构图
主角是SubscribeAll这个方法
对于当前应用程序下的所有DLL进行反射,拿到所有实现了IEventHandler的类型,然后对这么类型(事件处理程序)进行注册即可.
核心代码(Memory版):
/// <summary> /// 需要过滤的接口 /// </summary> string[] Excepts = { "IEventHandler`1", "ActionDelegatedEventHandler`1" }; /// <summary> /// 全局统一注册所有事件处理程序,实现了IEventHandlers的 /// </summary> public void SubscribeAll() { var types = AppDomain.CurrentDomain.GetAssemblies() .SelectMany(a => a.GetTypes() .Where(t => t.GetInterfaces().Contains(typeof(IEventHandlers)))) .Where(i => !Excepts.Contains(i.Name)) .ToArray(); foreach (var item in types) { if (!item.ContainsGenericParameters) { var en = Activator.CreateInstance(item); foreach (var t in item.GetInterfaces().Where(i => i.Name != "IEventHandlers")) { Subscribe(t, en); } } } } /// <summary> /// 订阅非泛型版 /// </summary> /// <param name="type"></param> /// <param name="eventHandler"></param> void Subscribe(Type type, object eventHandler) { lock (_objLock) { var eventType = type.GetGenericArguments()[0]; //var eventType = type.GetType().GenericTypeArguments[0]; if (_eventHandlers.ContainsKey(eventType)) { var handlers = _eventHandlers[eventType]; if (handlers != null) { if (!handlers.Exists(deh => _eventHandlerEquals(deh, eventHandler))) handlers.Add(eventHandler); } else { handlers = new List<object>(); handlers.Add(eventHandler); } } else _eventHandlers.Add(eventType, new List<object> { eventHandler }); } }
对于这种仓储,在Redis里事实上是以二进制的格式存储的,所以要求你的EventData和EventHandler需要标记为可序列化,我经过测试,对于Json序列化的方式,在进行发布时,不能成功回调"订阅"的代码,原因我目前还不清楚,需要大家一起去研究!
感谢各位的阅读!