nopCommerce 3.9 大波浪系列 之 事件机制(生产者、消费者)
2017-07-25 15:47
656 查看
一.nop事件机制简介
应用场景:客户支付成功后,需要发送短信、邮件告知客户订单支付成功(短信、邮件由不同模块实现)
实现方法: 1.定义支付成功OrderPaidEvent事件。
2.定义短信,邮箱两个消费者共同监听OrderPaidEvent事件,并实现相关业务。
3.当客户支付成功后生产者发送OrderPaidEvent事件。
4.消费者接收到OrderPaidEvent事件后,短信和邮箱消费者分别执行自己的业务。
nop事件机制使用到“生产者/消费者”模式。生产者只负责发布事件,并不需要关心谁来处理,相反消费者只用来处理事件。那生产者和消费者是如何进行关联的呢?nop实现是非常简单的,通过泛型来定义一个事件类,如果生产者和消费者都使用同一个事件类,那么就关联到一起了称之为订阅。负责实现事件机制的部分称之为缓冲区,缓冲区的作用是通过解耦的方式实现消息机制。生产者和消费者是一对多的关系。下图简单介绍下生产者消费者关系。
using System; using System.Linq; using Nop.Core.Infrastructure; using Nop.Core.Plugins; using Nop.Services.Logging; namespace Nop.Services.Events { /// <summary> /// Evnt publisher /// </summary> public class EventPublisher : IEventPublisher { private readonly ISubscriptionService _subscriptionService; /// <summary> /// Ctor /// </summary> /// <param name="subscriptionService"></param> public EventPublisher(ISubscriptionService subscriptionService) { _subscriptionService = subscriptionService; } /// <summary> /// Publish to cunsumer /// </summary> /// <typeparam name="T">Type</typeparam> /// <param name="x">Event consumer</param> /// <param name="eventMessage">Event message</param> protected virtual void PublishToConsumer<T>(IConsumer<T> x, T eventMessage) { //Ignore not installed plugins var plugin = FindPlugin(x.GetType()); if (plugin != null && !plugin.Installed) return; try { //消费者处理方法 x.HandleEvent(eventMessage); } catch (Exception exc) { //log error var logger = EngineContext.Current.Resolve<ILogger>(); //we put in to nested try-catch to prevent possible cyclic (if some error occurs) try { logger.Error(exc.Message, exc); } catch (Exception) { //do nothing } } } /// <summary> /// Find a plugin descriptor by some type which is located into its assembly /// </summary> /// <param name="providerType">Provider type</param> /// <returns>Plugin descriptor</returns> protected virtual PluginDescriptor FindPlugin(Type providerType) { if (providerType == null) throw new ArgumentNullException("providerType"); if (PluginManager.ReferencedPlugins == null) return null; foreach (var plugin in PluginManager.ReferencedPlugins) { if (plugin.ReferencedAssembly == null) continue; if (plugin.ReferencedAssembly.FullName == providerType.Assembly.FullName) return plugin; } return null; } /// <summary> /// 发送事件 /// </summary> /// <typeparam name="T">Type</typeparam> /// <param name="eventMessage">Event message</param> public virtual void Publish<T>(T eventMessage) { var subscriptions = _subscriptionService.GetSubscriptions<T>();//获取订阅消费者 subscriptions.ToList().ForEach(x => PublishToConsumer(x, eventMessage)); } } }
EventPublisher
using System.Collections.Generic; using Nop.Core.Infrastructure; namespace Nop.Services.Events { /// <summary> /// 事件订阅服务 /// </summary> public class SubscriptionService : ISubscriptionService { /// <summary> /// 获取事件订阅 /// </summary> /// <typeparam name="T">Type</typeparam> /// <returns>Event consumers</returns> public IList<IConsumer<T>> GetSubscriptions<T>() { return EngineContext.Current.ResolveAll<IConsumer<T>>(); } } }
二.消费者IConsermer<T>注册
应用启动时Nop.Web.Framework.DependencyRegistrar中将所有实现IConsumer<T>接口的类注册到ioc容器中。
通过EngineContext.Current.ResolveAll<IConsumer<T>>(),就可以获取到某个消息(T)的订阅了。
//注册事件消费者 var consumers = typeFinder.FindClassesOfType(typeof(IConsumer<>)).ToList(); foreach (var consumer in consumers) { builder.RegisterType(consumer) .As(consumer.FindInterfaces((type, criteria) => { var isMatch = type.IsGenericType && ((Type)criteria).IsAssignableFrom(type.GetGenericTypeDefinition()); return isMatch; }, typeof(IConsumer<>))) .InstancePerLifetimeScope(); }
三.创建消费者
结合上边提到的应用场景,我们创建订阅OrderPaidEvent事件来处理短信通知的消费者。
创建OrderPaidSMSEventConsumer类
using System; using Nop.Core; using Nop.Core.Domain.Orders; using Nop.Core.Plugins; using Nop.Services.Events; using Nop.Services.Orders; namespace Nop.Plugin.SMS { public class OrderPaidSMSEventConsumer : IConsumer<OrderPaidEvent> { private readonly IOrderService _orderService; public OrderPaidSMSEventConsumer( IOrderService orderService, IStoreContext storeContext) { this._orderService = orderService; this._storeContext = storeContext; } /// <summary> /// 事件处理. /// </summary> /// <param name="eventMessage">The event message.</param> public void HandleEvent(OrderPaidEvent eventMessage) { var order = eventMessage.Order;//获取订单 //发送短息通知代码 //.................... } } }
OrderPaidSMSEventConsumer类继承 IConsumer<OrderPaidEvent>,OrderPaidEvent就是事件类,维护生产者与消费者之间的订阅关系。事件类名称可以自定义,代表了一个事件。
接下来我们再创建一个邮件处理的消费者OrderPaidEmailEventConsumer类,同样继承了ICnsumer<OrderPaidEvent>,说明我们订阅的是也是OrderPaidEvent事件。
using System; using Nop.Core; using Nop.Core.Domain.Orders; using Nop.Core.Plugins; using Nop.Services.Events; using Nop.Services.Orders; namespace Nop.Plugin.Email { public class OrderPaidEmailEventConsumer : IConsumer<OrderPaidEvent> { private readonly IOrderService _orderService; private readonly IStoreContext _storeContext; public OrderPaidEmailEventConsumer( IOrderService orderService, IStoreContext storeContext) { this._orderService = orderService; this._storeContext = storeContext; } /// <summary> /// 邮件处理 /// </summary> /// <param name="eventMessage">The event message.</param> public void HandleEvent(OrderPaidEvent eventMessage) { var order = eventMessage.Order; //发送邮件通知客户 //............................ } } }
四.生产消息
我们已经创建了两个订阅了OrderPaidEvent事件的消费者,现在我们看看当客户支付完成时我们是如何通知消费者的。
Nop.Services.OrderProcessingService类中
_eventPublisher.Publish(new OrderPaidEvent(order))方法发送了OrderPaidEvent事件。这时候上边订阅OrderPaidEvent事件的消费(短信、邮件)就会处理消息了。
五.nop中常用的事件整理
消费者,主要还是处理缓存
Nop.Web.Infrastructure.Cache.ModelCacheEventConsumer:前台模型相关
Nop.Admin.Infrastructure.Cache.ModelCacheEventConsumer:后台模型相关
Nop.Services.Discounts.Cache.DiscountEventConsumer:折扣相关
Nop.Services.Catalog.Cache.PriceCacheEventConsumer:价格相关
Nop.Services.Customers.Cache.CustomerCacheEventConsumer:密码修改
生产者,下图总结了nop 3.9 源码中自带的事件及所在的类,大部分是未实现对应的消费者。
nop只是在相关的地方留下事件位置,方便我们二次开发的时候进行扩展。
六.总结
1.生产者需要继承IEventPublisher接口。
2.消费者需要继承IConsumer<T>接口。
3.消费者通过事件类订阅到生产者,订阅实现参见ISubscriptionService接口。
nop事件机制实现很简单,有兴趣的朋友可以用RabbitMQ进行消息的扩展。
文中有错误的理解和不正确的观点,请留言,一起交流共同进步。
本文地址:http://www.cnblogs.com/yaoshangjin/p/7234522.html
本文为大波浪原创、转载请注明出处。
相关文章推荐
- nopCommerce 3.9 大波浪系列 之 事件机制(生产者、消费者)
- nopCommerce 3.9 大波浪系列 之 可退款的支付宝插件(上)
- nopCommerce 3.9 大波浪系列 之 global.asax
- nopCommerce 3.9 大波浪系列 之 路由扩展 [多语言Seo的实现]
- nopCommerce 3.9 大波浪系列 之 网页加载Widgets插件原理
- nopCommerce 3.9 大波浪系列 之 NUnit 配置调试环境
- nopCommerce 3.9 大波浪系列 之 汉化-中文语言
- nopCommerce 3.9 大波浪系列 之 微信公众平台登录插件
- nopCommerce 3.9 大波浪系列 之 汉化-Roxy Fileman
- nopCommerce 3.9 大波浪系列 之 IWebHelper
- nopCommerce 3.9 大波浪系列 之 开发支持多店的插件
- nopCommerce 3.9 大波浪系列 之 路由扩展 [多语言Seo的实现]
- nopCommerce 3.9 大波浪系列 之 引擎 NopEngine
- nopCommerce 3.9 大波浪系列 之 引擎 NopEngine
- nopCommerce 3.9 大波浪系列 之 网页加载Widgets插件原理
- 总结系列-触摸屏事件的传递机制
- Java等待/通知机制:生产者-消费者问题
- 多线程_生产者消费者之等待唤醒机制代码优化
- 多线程 等待唤醒机制 生产者消费者 (Lock jdk1.5版)
- 线程间通信、等待唤醒机制、生产者消费者问题(Lock,Condition)、停止线程和守护线程、线程优先级