文章出處

回到目錄

讓大叔興奮的自動化注冊

對于領域事件之前說過,在程序啟動時訂閱(注冊)一些事件處理程序,然后在程序的具體位置去發布(觸發)它,這是傳統的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序列化的方式,在進行發布時,不能成功回調"訂閱"的代碼,原因我目前還不清楚,需要大家一起去研究!

 

感謝各位的閱讀!

回到目錄


文章列表




Avast logo

Avast 防毒軟體已檢查此封電子郵件的病毒。
www.avast.com


arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()