应用场景:客户支付成功后,需要发送短信、邮件告知客户订单支付成功(短信、邮件由不同模块实现)
实现方法: 1. 定义支付成功 OrderPaidEvent 事件。
2. 定义短信,邮箱两个消费者共同监听 OrderPaidEvent 事件,并实现相关业务。
3. 当客户支付成功后生产者发送 OrderPaidEvent 事件。
4. 消费者接收到 OrderPaidEvent 事件后,短信和邮箱消费者分别执行自己的业务。
nop 事件机制使用到 "生产者 / 消费者" 模式。生产者只负责发布事件,并不需要关心谁来处理,相反消费者只用来处理事件。那生产者和消费者是如何进行关联的呢?nop 实现是非常简单的,通过泛型来定义一个事件类,如果生产者和消费者都使用同一个事件类,那么就关联到一起了称之为订阅。负责实现事件机制的部分称之为缓冲区,缓冲区的作用是通过解耦的方式实现消息机制。生产者和消费者是一对多的关系。下图简单介绍下生产者消费者关系。
生产者接口:Nop.Services.Events.IEventPublisher
消费者接口:Nop.Services.Events.IConsumer<T>
事件订阅接口:Nop.Services.Events.ISubscriptionService
IEventPublisher 接口 Publish<T>(T eventMessage) 方法用于发布事件(生产者)。
IConsumer<T> 接口 HandleEvent(T eventMessage) 方法用于处理事件(消费者)。
两者之间的关系由 T 泛型来关联,称之为事件,简单的说 T 类型相同则两者关联订阅成功。
ISubscriptionService 接口 GetSubscriptions<T>() 方法返回 IList<IConsumer<T>> 集合,该集合保存了消费者。
接口实现如下图:
- 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(IConsumer 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();
- //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 eventMessage) {
- var subscriptions = _subscriptionService.GetSubscriptions(); //获取订阅消费者
- subscriptions.ToList().ForEach(x = >PublishToConsumer(x, eventMessage));
- }
- }
- }
- 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 > GetSubscriptions() {
- return EngineContext.Current.ResolveAll > ();
- }
- }
- }
应用启动时 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 {
- 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 {
- 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.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
来源: http://www.cnblogs.com/yaoshangjin/p/7234522.html