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序列化的方式,在进行发布时,不能成功回调"订阅"的代码,原因我目前还不清楚,需要大家一起去研究!

 

感谢各位的阅读!

回到目录

posted @ 2016-06-12 16:00  张占岭  阅读(...)  评论(...编辑  收藏