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<T>消费者(事件订阅者)接口
  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. 采用了控制反转技术,巧妙的解决了查找消费者的困难,如果采用反射,性能会下降
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,992评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,212评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,535评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,197评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,310评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,383评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,409评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,191评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,621评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,910评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,084评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,763评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,403评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,083评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,318评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,946评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,967评论 2 351