• NopCommerce事件发布订阅机制详解


    Nop框架中,可以看到多处用到事件机制,特别是缓存的更新,有些人可能会疑惑,这么做解决了什么问题?
    假如我们有这么一个场景,一个客户注册后,我们会更新一下缓存,然后发送一封注册邮件,常规的做法是:

    void InsertCustomer()
    {
        1. 新增到数据库
        2. 插入到缓存
        3. 发送注册邮件
    }
    

    这样做本来没有什么问题,但这样的代码会紧紧的耦合在一起,如果想再加一个注册后发送优惠券的动作,可能就会再里面增加一个发送优惠券的动作,所以的业务都牢牢的绑定在了新增方法里面,Nop怎么做的呢

    void Insert()
    {
        1. 新增到数据库
        2. 发布一条新增消息
    }
    
    class CacheConsumer: IConsumer<EntityInsertedEvent<Customer>>
    {
        public void HandleEvent(EntityUpdatedEvent<Setting> eventMessage)
        {
            //新增缓存
        }
    }
    
    class MailConsumer: IConsumer<EntityInsertedEvent<Customer>>
    {
        public void HandleEvent(EntityUpdatedEvent<Setting> eventMessage)
        {
            //发送邮件
        }
    }
    

    具体来解释执行流程:

    1. 新增一条数据后,发布一条消息到消息管理器
    2. 所有订阅该新增消息的消费者都会收到消息
    3. 消费者执行具体的事件,比如新增缓存、发送邮件等

    Nop的技术实现可能会更复杂了一点,它没有采用传统的RabbitMQ等第三方消息队列,而是结合Autofac的依赖注入,巧妙的实现了这一设计:

    1. IEventPublisher事件发布接口
    2. EventPublisher实现IEventPublisher的类,主要功能包括发布事件并通知订阅者
    3. IConsumer消费者(事件订阅者)接口
    4. ISubscriptionService订阅者接口,解析所有的订阅者
    5. SubscriptionService的具体实现

    接下来我们看下具体的实现类,以CategoryService为例,已删除不必要的细节:

    public virtual void InsertCategory(Category category)
    {
        _categoryRepository.Insert(category);
        //发布一条消息
        _eventPublisher.EntityInserted(category);
    }
    

    而订阅该消息的类PriceCacheEventConsumer,代码如下:

    public partial class PriceCacheEventConsumer : IConsumer<EntityInsertedEvent<Category>>
    {
        public void HandleEvent(EntityInsertedEvent<Category> eventMessage)
        {
            _cacheManager.RemoveByPattern(NopCatalogDefaults.ProductCategoryIdsPatternCacheKey);
        }
    }
    

    上述代码已删除干扰因素,我们看到该类继承于IConsumer

    接下来我们看下,事件发布类EventPublisher做的哪些事情:

    /// <summary>
    /// 发布到消费者
    /// </summary>
    protected virtual void PublishToConsumer<T>(IConsumer<T> x, T eventMessage)
    {
        try
        {
            x.HandleEvent(eventMessage); //执行消费者订阅方法
        }
        catch (Exception exc)
        {
        }
    }
    
    /// <summary>
    /// 发布事件,注意,是整个实现的核心
    /// </summary>
    public virtual void Publish<T>(T eventMessage)
    {
        //利用控制反转IOC技术,查找所有消费者类
        var subscribers = _subscriptionService.GetSubscriptions<T>()
            .Where(subscriber => PluginManager.FindPlugin(subscriber.GetType())?.Installed ?? true).ToList();
    
        //调用PublishToConsumer,执行对应消费者类的方法,这个是关键
        subscribers.ForEach(subscriber => PublishToConsumer(subscriber, eventMessage));
    }
    

    事件发布服务:SubscriptionService,该类是个工具类,提供了根据类型查找消费者的方法GetSubscriptions

    public IList<IConsumer<T>> GetSubscriptions<T>()
    {
        return EngineContext.Current.ResolveAll<IConsumer<T>>().ToList();
    }
    

    上述类里面的EngineContext,Nop引擎管理器,Current指的是当前引擎,ResolveAll从容器中取出消费类,具体技术查看Autofac

    总结一下:

    1. 整个代码实现了解耦,不再相互依赖
    2. 发布消息订阅,并没有直接采用第三方消息队列RabbitMQ,所以没有单独的监控端
    3. 采用了控制反转技术,巧妙的解决了查找消费者的困难,如果采用反射,性能会下降
  • 相关阅读:
    Visual Studio 2008 完全卸载
    设置 Visual Studio 文件版权信息
    安装 Visual Studio 插件 Visual Assist
    下载 / 安装 Visual Studio
    Python help 函数
    Python next 函数
    Python oct 函数
    Python min 函数
    Python reload 函数
    numpy中matrix的特殊属性
  • 原文地址:https://www.cnblogs.com/honzhez/p/12699121.html
Copyright © 2020-2023  润新知