前言

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

Redis基础命令

Redis有两种方式可支持我们实现MQ功能,1、使用列表(List)相关命令特性;2、使用publish、subscribe命令特性;
这里我是采取列表相关命令实现。

使用列表(List)相关命令的特性实现

  • 压入数据(发布消息)

    使用列表(List)的 LPUSH RPUSH 命令可以从列表左边和右边压入数据;

LPUSH
将一个或多个值插入到列表头部(此处可以将列表想象成一个从左到右的链表数据结构,LPUSH就是将指定的值插入最左侧!)

如下命令,将多个元素压入list1的头部(最左侧)

LPUSH list1 测试1 测试2

执行结果如下:

上面是写入多个元素,我们也可以写入单个元素

LPUSH list1 测试3

  

需要留意,每次执行完LPUSH后,Redis会返回当前列表的长度。

RPUSH
在指定列表的尾部(相当于一个链表的最右侧)添加单个或多个元素

如下命令,还是在list1上添加多个元素,并查看执行后的list1元素信息

RPUSH list 测试4 测试5

  

同理,RPUSH也可直接写入单个元素,和LPUSH一样。

  • 拉取数据(消费数据)
    这里的拉取数据不单单是读取List内的元素,而是将元素从列表中取出来

BLPOP
移出并获取列表的第一个元素(从左至右), 如果列表没有元素会阻塞当前线程,直到等待超时或发现可弹出元素为止。

如下命令,从list1这个列表获取从左至右第一个元素,在100秒内如果获取则结束阻塞,否则阻塞到100秒之后。

BLPOP list1 100

  

执行结果如下:

需要留意的是BLPOP命令如果拉取到数据则会返回两行数据,1行为列表的key名称,1行为获取到的元素值。如果直到阻塞结束都没有获取到元素值则直接返回命令执行超时。如下图:

BRPOP
移出并获取列表的最后一个元素(从左至右), 如果列表没有元素会阻塞当前线程直到等待超时或发现可弹出元素为止。该命令与BLPOP除了获取的元素位置不同,其他特性全部一致。

LPOP
移出并获取列表的第一个元素(从左至右),如获取到元素则返回元素信息,没有元素则立即返回null。

如下命令:

LPOP list1

  

RPOP
移出并获取列表的最后一个元素(从左至右),如获取到元素则返回元素信息,没有元素则立即返回null。该命令与LPOP除了获取的元素位置不同其他特性全部一致;

RPOPLPUSH
移除列表的最后一个元素(最右侧的元素),并将该元素添加到另一个列表并返回。该命令如获取到元素则返回元素信息,否则返回错误信息。

可以通过RPOPLPUSH这个命令的特性对MQ内一致性要求较高的业务进行处理,在从列表获取元素成功后将该元素添加到一个备份列表,在业务处理完毕后再从备份列表将该元素删除。
执行下面命令测试下:

RPOPLPUSH list1 listback

  

BRPOPLPUSH
从列表中弹出一个值,将弹出的元素插入到另外一个列表中并返回它; 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。

该命令其实就是在BRPOP的基础上将LPUSH的功能加上了,依旧也保留了指定超时时间内未获取到元素则阻塞线程。
执行下面命令测试下:

BRPOPLPUSH list1 listback 10

  

执行结果如下:

完善代码

基于上面Redis的相关命令,我们再完善下上篇博客的代码。这里我们需要新增一个控制台启动项,将它作为消费服务,原来的控制台即订单保存的控制台作为消息发布的服务。
下单代码更改为下面的样子:

        /// <summary>
/// 异步方式触发订单相关事件
/// </summary>
public static void AsynEventHandle()
{
Guid userId = Guid.NewGuid();
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start(); var order = new OrderModel()
{
CreateTime = DateTime.Now,
Id = Guid.NewGuid(),
Money = (decimal)300.00,
Number = 1,
ProductName = "鲜花一束",
UserId = userId
};
Console.WriteLine($"模拟存储订单【采取Redis做消息队列的异步方式】");
Thread.Sleep(1000); FullRedis fullRedis = new FullRedis("127.0.0.1:6379", "", 1);
//这里尝试过使用redis 的订阅发布模式,在执行发布命令时候发现值但凡出现空格或者"符号则会异常...
fullRedis.LPUSH("orders", new OrderModel[] { order }); stopwatch.Stop();
Console.WriteLine($"下单总耗时:{stopwatch.ElapsedMilliseconds}毫秒");
Console.ReadLine();
}

  

可以看到,我们已经将事件总线相关代码给移除了,上面代码除了向Redis的队列(List)里写入元素外就只是对订单进行了持久化动作,所以看代码就知道执行效率的提升了。
接下来,看消费服务的代码。

        static void Main(string[] args)
{
XTrace.UseConsole();
Console.WriteLine("进入Redis消息订阅者模式订单消息推送订阅者客户端!"); EventBus eventBus = new EventBus();
eventBus.EventRegister(typeof(OrderCreateEventNotifyHandle), typeof(OrderCreateEventData));
eventBus.EventRegister(typeof(OrderCreateEventStockLockHandle), typeof(OrderCreateEventData)); FullRedis fullRedis = new FullRedis("127.0.0.1:6379", "", 1);
fullRedis.Log = XTrace.Log;
fullRedis.Timeout = 30000;
OrderModel order = null;
while (order == null)
{
order = fullRedis.BLPOP<OrderModel>("orders", 20);
if (order != null)
{
Console.WriteLine($"得到订单信息:{JsonConvert.SerializeObject(order)}");
//执行相关事件
eventBus.Trigger(new OrderCreateEventData()
{
Order = order,
});
//再次设置为null方便循环读取
order = null;
} }
Console.ReadLine();
}

  

消费服务首先从Redis里通过BLPOP从orders列表中获取元素,再触发事件总线,执行订单保存相关业务处理。

最终看下执行效率如何?
消息发布的执行效率(订单保存)

消息消费

可以看到目前消息发布的执行效率下单总耗时间为1170毫秒,我们再改为同步的测试下结果:

可以看到,同步执行的结果是3035毫秒。

小结

两种方式相差了将近2000毫秒~ 而且后续如果再继续扩展订单存储相关处理的话同步执行的响应时间会更加拉长,而采取Redis MQ的方式配合事件总线我们可以将整个业务拆分为独立的应用,采取分布式的方式提高响应效率,同时事件总线的加入方便我们后续业务的扩展。

消息发布端将订单信息写入到列表后如果消息消费者在拉取到数据后业务执行过程中代码出现异常导致无法满足业务的完整性如何处理

答:可以使用上述Redis命令中的RPOPLPUSH或BRPOPLPUSH在拉取元素后写入到一个备份的列表中,在我们的逻辑代码执行完毕后在将备份列表中的该元素值移除。

上述代码已发布到Github,有需要的自行下载。

地址为:https://github.com/QQ897878763/OrderRedisSample

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

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

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

  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. 浅谈vue响应式原理及发布订阅模式和观察者模式

    一.Vue响应式原理 首先要了解几个概念: 数据响应式:数据模型仅仅是普通的Javascript对象,而我们修改数据时,视图会进行更新,避免了繁琐的DOM操作,提高开发效率. 双向绑定:数据改变,视图 ...

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

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

随机推荐

  1. 吴裕雄--天生自然C语言开发:enum(枚举)

    enum DAY { MON=, TUE, WED, THU, FRI, SAT, SUN }; enum DAY { MON=, TUE, WED, THU, FRI, SAT, SUN }; en ...

  2. 吴裕雄--天生自然C语言开发:判断

    if(boolean_expression) { /* 如果布尔表达式为真将执行的语句 */ } #include <stdio.h> int main () { /* 局部变量定义 */ ...

  3. PAT甲级——1008 Elevator

    PATA1008 Elevator The highest building in our city has only one elevator. A request list is made up ...

  4. java replaceall 用法:处理特殊字符

    public class TryDotRegEx { public static void main(String[] args) { // TODO Auto-generated method st ...

  5. linux系统--C语言程序开发的基本步骤(包含gcc的基本步骤)

    1.使用vi或者vim编写程序文件 2.使用gcc把所有的源文件翻译成计算机认识的格式(编译) 3.使用./a.out作为命令执行得到的可执行文件 gcc编译器的工作步骤: 1.处理所有的预处理指令 ...

  6. Spring-IOC(DI)的三种注入方式

    spring为方便不同的需求,为我们提供了3中不同的注入方式分别是set.get方法注入,构造注入还有p命名空间注入,老规矩,直接上代码 首先创建实体类Student public class Stu ...

  7. 使用mybatis的动态sql解析能力生成sql

    需求: 计算平台,有很多表,打算提供一个基于sql的服务接口, sql不能完全在配置页面写死, 要能根据参数不同执行不同的语义,防止sql个数爆炸 把mybatis原码down下来, 改造一下测试用例 ...

  8. http、https的压测工具——apacheab 、webbench

    http的压测工具 搞清楚不同的安装方法,执行文件.配置文件的路径. yum安装: 执行文件在/usr/bin 下,一般为执行文件 配置文件在/etc目录下 conf文件 源码安装: 执行文件在安装文 ...

  9. .net和JAVA面向对象,继承有趣的细节

    原型是同事间讨论的一道面试题.估计这题秒杀了不少人,LZ也被秒了. 但这个题里隐藏了一个很有趣的细节,这个细节不说清楚,不少人会其实死的冤枉. 这是C#的代码. class Program { sta ...

  10. mysql-5.7.14-winx64解压版配置

    1.下载最新的MySQL文件并且解压 我的位置是 F:\mysql-5.7.14-winx64 2.F:\mysql-5.7.14-winx64\bin; 添加到环境变量-系统变量-PATH下 3.复 ...