前言

最近一直奔波于面试,面了几家公司的研发。有让我受益颇多的面试经验,也有让我感觉浪费时间的面试经历~
因为疫情原因,最近宅在家里也没事,就想着使用Redis配合事件总线去实现下具体的业务。

  • 需求

    一个简单的电商,有几个重要的需求点

    商品下单后TODO

    • 存储订单信息
    • 锁定商品库存
    • 消息推送商家端

    订单支付后TODO

    • 存储订单支付信息
    • 商品库存减少
    • 消息推送商家端
    • 会员积分调整

技术思路

这里用控制台实现上面的业务功能外,自行编写一个基于C#反射特性的事件总线,方便具体业务事件的后续扩展,比如订单支付后后续还要加会员消息推送啥的。使用Redis的发布订阅模式对事件处理进行异步化,提升执行性能。
所以最终技术架构就是 事件总线+Redis发布订阅。

完成事件总线

这里先不急着将上面的订单、支付、会员 等进行建模。先将事件总线的架子搭好。首先需要理解事件总线在业务系统的目的是什么。
事件总线存在目的最重要的就是解耦 。我们需要实现的效果就是针对指定事件源对象触发事件后,但凡注册了该事件参数的事件处理类则开始执行相关代码。

下图可以看出我们的事件处理类均需要引用事件参数,所有事件处理类都是基于对事件参数处理的需求上来的。

aaarticlea/png;base64,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" alt="9d8bb75ee6ad2747e082d625ce99549f.png">

但是!并不是意味创建了事件处理类就一定会去执行!能否执行除了取决于事件源的触发外就是必须有一层注册(也可称映射)。
在WinForm程序里处处可见事件的绑定,如 this.button1.OnClick+=button1OnClick;
那么在这里我将绑定事件放置到一个字典里。C#的字典Dictionary是个key value的键值对数据集合,键和值都可以是任意数据类型。
我们可以将事件处理类EventHandle和事件参数EventData作为键和值存储到字典里。在事件源触发时根据EventData反向找出所有的EventHandle

思路就是这样,开始编码了。
定义事件参数接口,后续具体业务的事件参数接口均要继承它。

    /// <summary>
/// 事件参数接口
/// </summary>
public interface IEventData
{
/// <summary>
/// 事件源对象
/// </summary>
object Source { get; set; } ///// <summary>
///// 事件发生的数据
///// </summary>
//TDataModel Data { get; set; } /// <summary>
/// 事件发生时间
/// </summary>
DateTime Time { get; set; }
}

  

需要一个事件处理接口,后续具体业务的事件处理接口均需要继承它

    /// <summary>
/// 事件实现接口
/// </summary>
public interface IEventHandle<T> where T : IEventData
{
/// <summary>
/// 处理等级
/// 方便事件总线触发时候可以有序的执行相应
/// </summary>
/// <returns></returns>
int ExecuteLevel { get; } /// <summary>
/// 事件执行
/// </summary>
/// <param name="eventData">事件参数</param>
void Execute(T eventData);
}

  

现在已经将事件参数和事件处理都抽象出来了,接下来是要实现上面说的注册容器的实现了。

   /// <summary>
/// 事件仓库
/// </summary>
public interface IEventStore
{
/// <summary>
/// 事件注册
/// </summary>
/// <param name="handle">事件实现对象</param>
/// <param name="data">事件参数</param>
void EventRegister(Type handle, Type data); /// <summary>
/// 事件取消注册
/// </summary>
/// <param name="handle">事件实现对象</param>
void EventUnRegister(Type handle); /// <summary>
/// 获取事件处理对象
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
Type GetEventHandle(Type data); /// <summary>
/// 根据事件参数获取事件处理集合
/// </summary>
/// <typeparam name="TEventData">事件参数类型</typeparam>
/// <param name="data">事件参数</param>
/// <returns></returns>
IEnumerable<Type> GetEventHandleList<TEventData>(TEventData data);
}

  

实现上面的接口

    /// <summary>
/// 基于反射实现的事件仓储
/// 存储事件处理对象和事件参数
/// </summary>
public class ReflectEventStore : IEventStore
{
private static Dictionary<Type, Type> StoreLst; public ReflectEventStore()
{
StoreLst = new Dictionary<Type, Type>();
} public void EventRegister(Type handle, Type data)
{
if (handle == null || data == null) throw new NullReferenceException();
if (StoreLst.Keys.Contains(handle))
throw new ArgumentException($"事件总线已注册类型为{nameof(handle)} !"); if (!StoreLst.TryAdd(handle, data))
throw new Exception($"注册{nameof(handle)}类型到事件总线失败!");
} public void EventUnRegister(Type handle)
{
if (handle == null) throw new NullReferenceException();
StoreLst.Remove(handle);
} public Type GetEventHandle(Type data)
{
if (data == null) throw new NullReferenceException();
Type handle = StoreLst.FirstOrDefault(p => p.Value == data).Key;
return handle;
} public IEnumerable<Type> GetEventHandleList<TEventData>(TEventData data)
{
if (data == null) throw new NullReferenceException();
var items = StoreLst.Where(p => p.Value == data.GetType())
.Select(k => k.Key);
return items;
}
}

  

根据上面代码可以看出来,我们存储到Dictionary内的是Type类型,GetEventHandleList方法最终获取的是一个List<Type>的集合。
我们需要在下面创建的EventBus类里循环List<Type>并且执行这个事件处理类的Execute方法。

实现EventBus

    /// <summary>
/// 事件总线服务
/// </summary>
public class EventBus : ReflectEventStore
{ public void Trigger<TEventData>(TEventData data, SortType sort = SortType.Asc) where TEventData : IEventData
{
// 这里如需保证顺序执行则必须循环两次 - -....
var items = GetEventHandleList(data).ToList();
Dictionary<object, Tuple<Type, int>> ds = new Dictionary<object, Tuple<Type, int>>(); foreach (var item in items)
{
var instance = Activator.CreateInstance(item);
MethodInfo method = item.GetMethod("get_ExecuteLevel");
int value = (int)method.Invoke(instance, null);
ds.Add(instance, new Tuple<Type, int>(item, value));
} var lst = sort == SortType.Asc ? ds.OrderBy(p => p.Value.Item2).ToList() : ds.OrderByDescending(p => p.Value.Item2).ToList(); foreach (var k in lst)
{
MethodInfo method = k.Value.Item1.GetMethod("Execute");
method.Invoke(k.Key, new object[] { data });
}
}
}

  

上面可以看到,我们的事件总线是支持对绑定的事件处理对象进行有序处理,需要依赖下面这个枚举

    /// <summary>
/// 排序类型
/// </summary>
public enum SortType
{
/// <summary>
/// 升序
/// </summary>
Asc = 1,
/// <summary>
/// 降序
/// </summary>
Desc = 2
}

  

好了,至此,我们的简易版的事件总线就出来了~ 接下来就是去建模、实现相应的事件参数和事件处理类了。
创建订单模型:

   /// <summary>
/// 订单模型
/// </summary>
public class OrderModel
{
/// <summary>
/// 订单ID
/// </summary>
public Guid Id { get; set; } /// <summary>
/// 用户ID
/// </summary>
public Guid UserId { get; set; } /// <summary>
/// 订单创建时间
/// </summary>
public DateTime CreateTime { get; set; } /// <summary>
/// 商品名称
/// </summary>
public string ProductName { get; set; } /// <summary>
/// 购买数量
/// </summary>
public int Number { get; set; } /// <summary>
/// 订单金额
/// </summary>
public decimal Money { get; set; }
}

  

创建订单下单事件参数

    public interface IOrderCreateEventData : IEventData
{
/// <summary>
/// 订单信息
/// </summary>
OrderModel Order { get; set; }
} /// <summary>
/// 订单创建事件参数
/// </summary>
public class OrderCreateEventData : IOrderCreateEventData
{
public OrderModel Order { get; set; }
public object Source { get; set; }
public DateTime Time { get; set; }
}

OK~接下来就是实现我们上面需求上的那些功能了。

  • 存储订单信息
  • 锁定商品库存
  • 消息推送商家端
    这里我不实现存储订单信息的事件处理对象,我默认此业务必须同步处理,至于后面两个则可以采取异步处理。通过下面代码创建相应的事件处理类。
    订单创建事件之消息推送商家端处理类。
    /// <summary>
/// 订单创建事件之消息处理类
/// </summary>
public class OrderCreateEventNotifyHandle : IEventHandle<IOrderCreateEventData>
{
public int ExecuteLevel { get; private set; } public OrderCreateEventNotifyHandle()
{
Console.WriteLine($"创建OrderCreateEventNotifyHandle对象");
this.ExecuteLevel = 2;
} public void Execute(IOrderCreateEventData eventData)
{
Thread.Sleep(1000);
Console.WriteLine($"执行订单创建事件之消息推送!订单ID:{eventData.Order.Id.ToString()},商品名称:{eventData.Order.ProductName}");
} }

  

订单创建消息之锁定库存处理类

   /// <summary>
/// 订单创建事件 锁定库存 处理类
/// </summary>
public class OrderCreateEventStockLockHandle : IEventHandle<IOrderCreateEventData>
{
public int ExecuteLevel { get; private set; } public OrderCreateEventStockLockHandle()
{
Console.WriteLine($"创建OrderCreateEventStockLockHandle对象");
this.ExecuteLevel = 1;
} public void Execute(IOrderCreateEventData eventData)
{
Thread.Sleep(1000);
Console.WriteLine($"执行订单创建事件之库存锁定!订单ID:{eventData.Order.Id.ToString()},商品名称:{eventData.Order.ProductName}");
}
}

  

OK~ 到main方法下开始执行订单创建相关代码。

        static void Main(string[] args)
{ Guid userId = Guid.NewGuid();
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
EventBus eventBus = new EventBus();
eventBus.EventRegister(typeof(OrderCreateEventNotifyHandle), typeof(OrderCreateEventData));
eventBus.EventRegister(typeof(OrderCreateEventStockLockHandle), typeof(OrderCreateEventData));
var order = new Order.OrderModel()
{
CreateTime = DateTime.Now,
Id = Guid.NewGuid(),
Money = (decimal)300.00,
Number = 1,
ProductName = "鲜花一束",
UserId = userId
};
Console.WriteLine($"模拟存储订单");
Thread.Sleep(1000);
eventBus.Trigger(new OrderCreateEventData()
{
Order = order
});
stopwatch.Stop();
Console.WriteLine($"下单总耗时:{stopwatch.ElapsedMilliseconds}毫秒");
Console.ReadLine();
}

  至此,我们采取事件总线的方式成功将需求实现了,执行后结果如下:

可以看到我们的下单总耗时是3038毫秒,如您所见,我们解决了代码的耦合性但是没有解决代码的执行效率。
下一章,将我们的Redis的发布订阅模式再加入进来,看是否能改善我们的代码执行效率~~

源码在下一篇博客上提供下载地址(毕竟现在才完成一半~)

使用EventBus + Redis发布订阅模式提升业务执行性能的更多相关文章

  1. 使用EventBus + Redis发布订阅模式提升业务执行性能(下)

    前言 上一篇博客上已经实现了使用EventBus对具体事件行为的分发处理,某种程度上也算是基于事件驱动思想编程了.但是如上篇博客结尾处一样,我们源码的执行效率依然达不到心里预期.在下单流程里我们明显可 ...

  2. SpringBoot Redis 发布订阅模式 Pub/Sub

    SpringBoot Redis 发布订阅模式 Pub/Sub 注意:redis的发布订阅模式不可以将消息进行持久化,订阅者发生网络断开.宕机等可能导致错过消息. Redis命令行下使用发布订阅 pu ...

  3. redis发布/订阅模式

    其实在很多的MQ产品中都存在这样的一个模式,我们常听到的一个例子 就是邮件订阅的场景,什么意思呢,也就是说100个人订阅了你的博客,如果博主发表了文章,那么100个人就会同时收到通知邮件,除了这个 场 ...

  4. Redis - 发布/订阅模式

    Redis 提供了一组命令可以让开发者实现 “发布/订阅” 模式.“发布/订阅” 可以实现进程间的消息传递,其原理是这样的: “发布/订阅” 模式中包含两种角色,分别是发布者和订阅者.订阅者可以订阅一 ...

  5. redis 发布/订阅 模式

    发布/订阅模式的命令如下: * 进入发布订阅模式的客户端,不能执行除发布订阅模式以上命令的其他命令,否则出错.

  6. 把酒言欢话聊天,基于Vue3.0+Tornado6.1+Redis发布订阅(pubsub)模式打造异步非阻塞(aioredis)实时(websocket)通信聊天系统

    原文转载自「刘悦的技术博客」https://v3u.cn/a_id_202 "表达欲"是人类成长史上的强大"源动力",恩格斯早就直截了当地指出,处在蒙昧时代即低 ...

  7. Redis 发布订阅,小功能大用处,真没那么废材!

    今天小黑哥来跟大家介绍一下 Redis 发布/订阅功能. 也许有的小伙伴对这个功能比较陌生,不太清楚这个功能是干什么的,没关系小黑哥先来举个例子. 假设我们有这么一个业务场景,在网站下单支付以后,需要 ...

  8. Redis进阶篇:发布订阅模式原理与运用

    "65 哥,如果你交了个漂亮小姐姐做女朋友,你会通过什么方式将这个消息广而告之给你的微信好友?" "那不得拍点女朋友的美照 + 亲密照弄一个九宫格图文消息在朋友圈发布大肆 ...

  9. springboot集成redis实现消息发布订阅模式-双通道(跨多服务器)

    基础配置参考https://blog.csdn.net/llll234/article/details/80966952 查看了基础配置那么会遇到一下几个问题: 1.实际应用中可能会订阅多个通道,而一 ...

随机推荐

  1. MySQL出现错误1030-Got error 28 from storage engine

    磁盘空间不足引起的!1030-Got error 28 from storage engine df -h 清理空间

  2. Jupyter_Notebook

    TA-lib指标库地址 http://github.com/xingbuxing/TA-Lib-in-chinese 1.Jupter是基于网页端写代码,属于一种交互式的编程,除了在上面写代码之外还可 ...

  3. 二十七、rsync同步工具

    1.什么是rsync? Rsync是一款开源的.快速的,多功能的,可实现全量及增量的本地或者远程数据同步备份的优秀工具.windows和linux都可以. 官网:http:www.samba.org/ ...

  4. 吴裕雄--天生自然python机器学习:支持向量机SVM

    基于最大间隔分隔数据 import matplotlib import matplotlib.pyplot as plt from numpy import * xcord0 = [] ycord0 ...

  5. 扩增|feather evolution

    Wool vs feather 扩增方法:1.Gene Duplication2.Genome Duplication3.Cluster 哺乳动物毛发和鸟类的羽毛,都来自于角蛋白. 羽毛进化图 DNA ...

  6. 修改hosts文件不需要重启的方法

    显示DNS缓存内容: ipconfig /displaydns 更新DNS缓存内容: ipconfig /flushdns

  7. linux新装系统优化

    1:关掉不需要的服务 检查在3级别上哪些是自动启动的 chkconfig --list  |grep ‘3:on’

  8. spring security在异步线程的处理

    https://spring.io/guides/topicals/spring-security-architecture 在异步线程中使用SecurityContextHolder , 需要将父线 ...

  9. The 2019 Asia Nanchang First Round Online Programming Contest C(cf原题,线段树维护矩阵)

    题:https://nanti.jisuanke.com/t/41350 分析:先将字符串转置过来 状态转移,因为只有5个状态,所以 i 状态到 j 状态的最小代价就枚举[i][k]->[k][ ...

  10. springboot学习笔记:10.springboot+atomikos+mysql+mybatis+druid+分布式事务

    前言 上一篇文章我们整合了springboot+druid+mybatis+mysql+多数据源: 本篇文章大家主要跟随你们涛兄在上一届基础上配置一下多数据源情况下的分布式事务: 首先,到底啥是分布式 ...