• C#编程实践—EventBroker简单实现


    前言

    话说EventBroker这玩意已经不是什么新鲜货了,记得第一次接触这玩意是在进第二家公司的时候,公司产品基础架构层中集成了分布式消息中间件,在.net基础服务层中使用EventBroker的模式将消息组装成事件,支持同域、跨域和跨机器进行事件的发布和订阅,后来才知道这玩意叫做EventBroker。不得不承认,这是一个非常聪明的东西,它在内部高度封装了消息和事件的处理,将上层应用的事件和委托的依赖进行解耦,并且提供非常简洁的方式进行开发。OK,本篇文章只是实现了一个简化版本的EventBroker,功能非常有限,也没有集成消息组件,所以也不支持跨域和跨机器的分布式应用了,甚至没有经过严格测试(比如并发处理、跨线程处理等等),社区上更好的实现很多。所以必须声明:只是实验性代码,纯粹练手,带着学习的态度写代码,coding不易,大家不喜勿喷

    准备

    本篇主要使用.net C#技术,包括委托、反射、弱引用和弱事件模式,写代码也是为了学习这些,找找码感。说明:为了不影响阅读,所贴代码均有一些阉割,如需源码,后续我把附件传上来,不过这源码真没啥东西,我上传就是自己做一个记录,存储下来

    思路

    思路不用多说,网上搜一下会有很多信息,我比较懒,直接贴一张网上的图可以明确表现出系统的交互场景和交互流程

    EventBroker.png

    应用

    在应用层使用EventBroker,需要在应用中针对对象进行Register,一般中大型项目使用IOC容器都提供在对象创建时注入特征,比如:

    // 示范代码
    private void OnObjectBuild(ObjectModel builder, object instance)
    {
        var broker = DIContainer.Resolve<IEventBroker>();
        broker.Register(instance);
    }

    这样比较方便,避免在程序中手动Register,不过我这里只是简单实现,不涉及IOC和任何设计的东西,注册对象后,在我们的类中可以像如下的方式实现:

    [EventPublication("topic://eventbreaker/winform/message", EventScope = EventScope.Local)]
    public event EventHandler<EventModelArgs<string>> MessageArrived;

    以上是事件定义,我们可以把事件委托引用的方法定义在另一个类,而且两者完全不用依赖彼此

    [EventSubscription("topic://eventbreaker/winform/message", EventScope = EventScope.Local)]
    public void MessageReceived(object sender, EventModelArgs<string> message)
    {
        PrintOut(message.Data);
    }

    源码

    • 源码目录结构,看看是不是比别的版本精简了?

    code

    • EventBroker,我暂且把这个叫事件总线,内部封装EventTopic,事件的发布者和订阅者信息都间接存储在总线上,除此之外,总线向外部提供统一的API接口,方便调用,相关接口如下:
    namespace TinyEventBroker
    {
        public class EventBroker
        {
            private readonly EventTopicCollection topics
                = new EventTopicCollection();
    
            /// <summary>
            /// 事件主题集合列表
            /// </summary>
            public EventTopicCollection Topics
            {
                get { return topics; }
            }
    
            /// <summary>
            /// 事件场景
            /// </summary>
            public virtual EventScope EventScope { get { return EventScope.Local; } }
    
            /// <summary>
            /// 将对象注入到事件总线上
            /// </summary>
            public void Register(object target)
            {
                BindingFlags bingdings = BindingFlags.Instance | BindingFlags.Static
                    | BindingFlags.Public | BindingFlags.NonPublic;
                RegisterPublication(target, bingdings);
                RegisterSubscription(target, bingdings);
            }
    
            /// <summary>
            /// 注册发布者
            /// </summary>
            private void RegisterPublication(object target, BindingFlags bindings)
            {
                // ……
            }
    
            /// <summary>
            /// 注册订阅者
            /// </summary>
            /// <param name="target"></param>
            /// <param name="bindings"></param>
            private void RegisterSubscription(object target, BindingFlags bindings)
            {
                // ……
            }
        }
    }
    • EventTopic,这个我把它称为事件主题,一个主题可以包含多个发布者和订阅者并提供一些处理,主题管理器内部将事件进行桥接,并动态调用订阅者方法,相关接口如下:
    public class EventTopic
    {
        public EventTopic() { }
        public EventTopic(string name)
            : this()
        {
            this.Name = name;
        }
    
        /// <summary>
        /// 事件主题标识
        /// </summary>
        public string Name { get; set; }
    
        private List<EventPublication> publishers = new List<EventPublication>();
        /// <summary>
        /// 发布者列表
        /// </summary>
        internal List<EventPublication> Publishers
        {
            get { return publishers; }
            set { publishers = value; }
        }
    
        private List<EventSubscription> subscribers = new List<EventSubscription>();
        /// <summary>
        /// 订阅者列表
        /// </summary>
        internal List<EventSubscription> Subscribers
        {
            get { return subscribers; }
            set { subscribers = value; }
        }
    
        /// <summary>
        /// 增加发布者
        /// </summary>
        internal void AddPublication(EventPublication publisher)
        {
            EventContext.Instance.WriteTo("主题:{0} 添加发布者{1}", Name, publisher);
            publisher.EventFired += OnEventFired;
            publishers.Add(publisher);
        }
    
        /// <summary>
        /// 增加订阅者
        /// </summary>
        internal void AddSubscription(EventSubscription subscriber)
        {
            EventContext.Instance.WriteTo("主题:{0} 添加订阅者{1}", Name, subscriber);
            subscribers.Add(subscriber);
        }
    
        /// <summary>
        /// 移除发布者
        /// </summary>
        internal void RemovePublication(EventPublication publisher)
        {
            EventContext.Instance.WriteTo("主题:{0} 移除发布者{1}", Name, publisher);
            publishers.Remove(publisher);
        }
    
        /// <summary>
        /// 移除订阅者
        /// </summary>
        internal void RemoveSubscription(EventSubscription subscriber)
        {
            EventContext.Instance.WriteTo("主题:{0} 移除订阅者{1}", Name, subscriber);
            subscribers.Remove(subscriber);
        }
    
        internal void OnEventFired(EventPublication publication, object sender, EventArgs args)
        {
            // ......
        }
    
        private void CheckInvalidPublications()
        {
            // ......
        }
    
        private void CheckInvalidSubscriptions()
        {
            // ......
        }
    }
    • Publisher,订阅者,使用弱引用来存储目标对象,防止目标对象无法释放造成内存泄露
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Reflection;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace TinyEventBroker
    {
        /// <summary>
        /// 发布者
        /// </summary>
        internal class EventPublication
        {
            /// <summary>
            /// 弱引用对象包装器
            /// </summary>
            private readonly WeakReference wrapper;
            /// <summary>
            /// 事件名称
            /// </summary>
            private readonly string eventName;
            /// <summary>
            /// 事件处理委托类型
            /// </summary>
            private readonly Type eventHandleType;
    
            /// <summary>
            /// 对象名称,用于显示
            /// </summary>
            private readonly string targetName;
    
            /// <summary>
            /// 事件定义,用来桥接对象的Event
            /// </summary>
            public event TopicEventHandler EventFired;
    
            public EventPublication(object target, string eventName)
            {
                wrapper = new WeakReference(target);
                this.eventName = eventName;
                this.targetName = target.GetType().Name;
    
                EventInfo info = target.GetType().GetEvent(EventName);
                eventHandleType = info.EventHandlerType;
    
                Delegate handler = Delegate.CreateDelegate(
                   EventHandleType, this,
                   this.GetType().GetMethod("OnEventFired"));
                info.AddEventHandler(target, handler);
            }
    
            /// <summary>
            /// 获取对象引用
            /// </summary>
            public object Target
            {
                get { return wrapper.Target; }
            }
    
            /// <summary>
            /// 对象名称,用于显示
            /// </summary>
            public string TargetName
            {
                get { return targetName; }
            }
    
            /// <summary>
            /// 判断对象是否存活
            /// </summary>
            public bool IsAlive
            {
                get { return wrapper.IsAlive; }
            }
    
            /// <summary>
            /// 事件名称
            /// </summary>
            public string EventName
            {
                get { return eventName; }
            }
    
            /// <summary>
            /// 事件处理类型
            /// </summary>
            public Type EventHandleType
            {
                get { return eventHandleType; }
            }
    
            public virtual void OnEventFired(object sender, EventArgs e)
            {
                var handle = EventFired;
                if (handle != null)
                    handle(this, sender, e);
            }
    
            public override string ToString()
            {
                return string.Format("[{0} - {1}]", targetName, eventName);
            }
        }
    }
    • 订阅者,依然使用弱引用来保存目标对象
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace TinyEventBroker
    {
        /// <summary>
        /// 订阅者
        /// </summary>
        public class EventSubscription
        {
            /// <summary>
            /// 弱引用对象包装器
            /// </summary>
            private readonly WeakReference wrapper;
            /// <summary>
            /// 方法名
            /// </summary>
            private readonly string methodName;
    
            /// <summary>
            /// 对象名称,仅作显示
            /// </summary>
            private readonly string targetName;
    
            public EventSubscription(object target, string methodName)
            {
                this.wrapper = new WeakReference(target);
                this.methodName = methodName;
                targetName = target.GetType().Name;
            }
    
            /// <summary>
            /// 对象是否存活
            /// </summary>
            public bool IsAlive
            {
                get { return wrapper.IsAlive; }
            }
    
            /// <summary>
            /// 订阅者对象方法名
            /// </summary>
            public string MethodName { get { return methodName; } }
            /// <summary>
            /// 订阅者对象,通过弱引用包装访问
            /// </summary>
            public object Target { get { return wrapper.Target; } }
            /// <summary>
            /// 订阅者对象显式名
            /// </summary>
            public string TargetName { get { return targetName; } }
    
            internal virtual void HandleEvent(EventPublication publication, object sender, EventArgs args)
            {
                Delegate handler = Delegate.CreateDelegate(
                    publication.EventHandleType, Target, methodName);
    
                handler.DynamicInvoke(sender, args);
            }
    
            public override string ToString()
            {
                return string.Format("[{0} - {1}]", targetName, methodName);
            }
        }
    }
    • EventScope,这东西只是保留属性,以后扩展的时候用

    初步测试结果

    windemo

    引用

    针对弱引用这个东东,其实直接使用弱事件模式,微软已经封装好了的

    Weak Event PatternsWeak Event in C#

    结语

    本篇只是EventBroker的简单实现,纯粹就是学习练手,我想,如果后续我有充足的时间,或者工作环境允许,我可以实现得更好,比如可以在内部封装多钟策略,异常处理策略、异步事件策略、处理并发、线程安全的考虑等等。更而甚之,可以将它进行抽象和扩展,加入消息中间件,实现分布式发布者和订阅者,那样就比较有实用价值了。不过写代码水平和效率有限,时间和精力也有限,工作是工作学习是学习,区区一文,聊表我的热情和开发,让代码和生活可以持续下去吧!

    源码下载

  • 相关阅读:
    爬取诗词名句网数据并做简单数据分析
    python3 协程Unclosed client session client_session: <aiohttp.client.ClientSession object at 0x0918BAC0>
    scrapy框架中多个spider,tiems,pipelines的使用及运行方法
    python爬虫模拟登录的图片验证码处理和会话维持
    python使用selenium驱动chromium防止浏览器自动升级失效!
    爬虫篇-博客园搜索爬取
    复合索引,不走索引情况
    去掉Oracle连接超时限制
    C# 正则表达式
    Clean Code
  • 原文地址:https://www.cnblogs.com/fecktty2013/p/eventbroker-1.html
Copyright © 2020-2023  润新知