从最开始的使用redis实现的单进程消费的异步任务系统到加入swoole的多进程消费模式,现在,我们的异步任务系统终于又能迈进一步。

因为有了前面两个简单系统的经验,这回基于RabbitMQ的异步任务系统设计的的更加完善,包括多进程消费,异常重试等。

系统介绍

消费端架构图

从图中可以看到,我们这个系统是一个基于事件的异步任务系统。就是说当一个事件产生时,生产者将事件抛给调度器,调度器负责查询事件下有哪些任务,然后将这些任务丢到相应的队列中,最后由消费者消费任务队列中的任务。

在整个系统中主要分为三大部分

1.事件生产者,即产生消息事件的一方。
2.任务调度器(Scheduler),负责注册事件并调度任务。
3.消费者(Worker),负责消费任务队列中的任务。

事件生产者

事件生产者很简单,在业务系统中直接调用即可,代码如下。

<?php

require_once DIR.'/../autoload.php';

use Asynclib\Ebats\Event;

try{

    $event = new Event('order_paied');  //定义事件

    $event->setOptions(['order_id' => 'FB138020392193312']); //事件产生的参数

    $event->publish();

}catch (Exception $exc){

    echo $exc->getMessage();

}

  

任务调度器

调度器主要做两件事,一是注册事件,另一个是调度任务。

注册事件代码如下:

//注册事件

EventManager::register('order_create', 'closeOrder', 'demo', 10);//关闭未付款订单(延迟任务)

EventManager::register('order_paied', 'virtualShipping', 'demo'); //虚拟商品自动发货

  

这样就注册了两个事件,事件下各有一个任务。

具体调度部分代码很简单,就不多赘述,有兴趣的可以去看代码。

消费者

重头戏来了,一个异步任务系统最重要的就是消费端了,现在让我们来看下Worker的流程图。

一个完整的消费进程

可以看到,在这里我们采用了两个交换器和两个队列,一个负责处理正常的任务即ntask,另一个负责处理需要延迟执行的任务即dtask。简单描述下一个任务的生命周期。

正常任务

1、task产生,进入正常任务的交换器Exchange[ebats_core_ntask]
2、交换器根据topic将任务分发到对应的队列中
3、子进程ntask阻塞等待成功获取到task,并执行该任务
4、执行失败,需要重试时抛出RetryException,不需要重试时抛出TaskException
5、子进程ntask捕获到重试异常将任务抛给延迟任务的交换器Exchange[ebats_core_dtask]
6、将任务执行信息回调给上层开发者以便保存查看

延迟任务

1、子进程dtask阻塞等待成功获取到task,并执行该任务
2、执行失败,需要重试时抛出RetryException,不需要重试时抛出TaskException
3、子进程dtask捕获到重试异常将任务抛给延迟任务的交换器Exchange[ebats_core_dtask]
4、将任务执行信息回调给上层开发者以便保存查看

消费者代码如下:

require_once DIR.'/../autoload.php';

require_once DIR.'/task/TaskDemoModel.php';

use Asynclib\Ebats\Worker;

//执行结果回调函数

$callback = function ($topic, $taskid, $taskname, $params, $timeuse, $message){

};

$worker = new Worker($callback);  //支持多进程消费默认为1

$worker->setQueue('demo');  //队列名和事件的topic一一对应

$worker->run();

  

自定义调度器

一般来说这是一个基于事件的任务系统,那么能不能直接产生任务呢。答案是肯定的。

只需要创建一个自定义调度器,由您自行实现调度逻辑,最终生成一个任务即可。代码如下:

<?php

require_once DIR.'/../autoload.php';

use Asynclib\Ebats\Task;

use Asynclib\Core\Consumer;

use Asynclib\Amq\ExchangeTypes;

use Asynclib\Exception\ExceptionInterface;

/** 

 * 本示例演示了如何创建一个自定义调度器,开发者可以根据自身需求开发自己的任务调度器

 */

try{

    $worker = new Consumer();

    $worker->setExchange('order_fanout', ExchangeTypes::TOPIC);

    $worker->setQueue('shzf_order_paied', ['*.*.WAIT_SELLER_SEND_GOODS']);

    $worker->run(function($key, $msg){

        $order_data = json_encode($msg);

        echo " [$key] $order_data \n";

        Task::create('demo', 'orderAsync', $msg);//创建任务,之后消息将作为参数由任务接管处理

    });

}catch (ExceptionInterface $exc){

    echo $exc->getMessage();

}

  

这样,当接收到消息时就会产生一个orderAsync的任务,您只需要启动一个用来消费这个Topic的Worker即可。

也许你会觉得这里直接写业务逻辑的代码就可以了,实际上也确实可以。当你可以忍受一个进程慢慢消费的时候是可以这样做的。但大多数情况下我们还是希望它能够尽快的消费掉,所以建议这里只负责创建任务,具体任务的业务逻辑由worker去执行。

以上就是基于RabbitMQ和Swoole实现的一个完整的异步任务系统的详细内容

视频教学:九年架构师讲解php+swoole+rabbitMQ实现异步任务多进程消费

更多内容请访问

怎么从一名码农成为架构师的必看知识点:目录大全(持续更新)50W年薪挑战!

基于RabbitMQ和Swoole实现的一个完整的异步任务系统的更多相关文章

  1. 基于 Vue + Koa2 + MongoDB + Redis 实现一个完整的登录注册

    项目地址:https://github.com/caochangkui/vue-element-responsive-demo/tree/login-register 通过 vue-cli3.0 + ...

  2. Go/Python/Erlang编程语言对比分析及示例 基于RabbitMQ.Client组件实现RabbitMQ可复用的 ConnectionPool(连接池) 封装一个基于NLog+NLog.Mongo的日志记录工具类LogUtil 分享基于MemoryCache(内存缓存)的缓存工具类,C# B/S 、C/S项目均可以使用!

    Go/Python/Erlang编程语言对比分析及示例   本文主要是介绍Go,从语言对比分析的角度切入.之所以选择与Python.Erlang对比,是因为做为高级语言,它们语言特性上有较大的相似性, ...

  3. Python分布式爬虫打造搜索引擎完整版-基于Scrapy、Redis、elasticsearch和django打造一个完整的搜索引擎网站

    Python分布式爬虫打造搜索引擎 基于Scrapy.Redis.elasticsearch和django打造一个完整的搜索引擎网站 https://github.com/mtianyan/Artic ...

  4. 一个基于RabbitMQ的可复用的事务消息方案

    前提 分布式事务是微服务实践中一个比较棘手的问题,在笔者所实施的微服务实践方案中,都采用了折中或者规避强一致性的方案.参考Ebay多年前提出的本地消息表方案,基于RabbitMQ和MySQL(JDBC ...

  5. SpringBoot | 第三十八章:基于RabbitMQ实现消息延迟队列方案

    前言 前段时间在编写通用的消息通知服务时,由于需要实现类似通知失败时,需要延后几分钟再次进行发送,进行多次尝试后,进入定时发送机制.此机制,在原先对接银联支付时,银联的异步通知也是类似的,在第一次通知 ...

  6. 【如何快速的开发一个完整的iOS直播app】(美颜篇)

    原文转自:袁峥Seemygo    感谢分享.自我学习 前言 在看这篇之前,如果您还不了解直播原理,请查看这篇文章如何快速的开发一个完整的iOS直播app(原理篇) 开发一款直播app,美颜功能是很重 ...

  7. 【如何快速的开发一个完整的iOS直播app】(播放篇)

    原文转自:袁峥Seemygo    感谢分享.自我学习 前言 在看这篇之前,如果您还不了解直播原理,请查看上篇文章如何快速的开发一个完整的iOS直播app(原理篇) 开发一款直播app,集成ijkpl ...

  8. 【如何快速的开发一个完整的iOS直播app】(原理篇)

    原文转自:袁峥Seemygo    感谢分享.自我学习 目录 [如何快速的开发一个完整的iOS直播app](原理篇) [如何快速的开发一个完整的iOS直播app](播放篇) [如何快速的开发一个完整的 ...

  9. 【如何快速的开发一个完整的 iOS 直播 app】(美颜篇)

    来源:袁峥Seemygo 链接:http://www.jianshu.com/p/4646894245ba 前言 在看这篇之前,如果您还不了解直播原理,请查看这篇文章如何快速的开发一个完整的iOS直播 ...

随机推荐

  1. String的compareTo用法

    String的compareTo其实就是依次比较两个字符串ASC码.如果两个字符的ASC码相等则继续后续比较,否则直接返回两个ASC的差值.如果两个字符串完全一样,则返回0.来看一下代码. publi ...

  2. 虚拟机apache启动

    /usr/local/apache2/bin/apachectl restart 重启 当启动也行 尝试过进入目录运行,比较奇怪,www目录竟然不一致,直接使用 server httpd start ...

  3. caffe之mac环境下通过XCode调试C++程序

    caffe log输出参考:http://blog.csdn.net/langb2014/article/details/50482150mac下用xcode开发caffe:http://coldmo ...

  4. js中escape的用法

    escape() 方法,它用于转义不能用明文正确发送的任何字符.比如,电话号码中的空格将被转换成字符 %20,从而能够在 URL 中传递这些字符.   var s="http://local ...

  5. <JZOJ1329>旅行

    贪心大水题 #include<cstdio> #include<iostream> #include<cstring> #include<algorithm& ...

  6. Java集合 - 明的博客

    "In this world there are only two tragedies. One is not getting what one wants, and the other i ...

  7. windows 右键新建html文档

    1.win+R 输入 regedit 启动注册表 2.HKEY_CLASSES_ROOT->.html 3.右键新建-项 名为:ShellNew 4.在右侧空白区右键新建字符串值FileName ...

  8. e代驾推出新产品“e代喝”,能否实现前者的社交梦?

    近日,关于e代驾推出e代喝的新闻不断出现在各大媒体的新闻报道之中,看似好像是替人排扰解难的征服酒局的又一利器.但事实真的如此吗?首先要弄清楚的,是目前e代驾在行业中的处境.作为代驾行业的先驱者,e代驾 ...

  9. Cisco2960 交换机密码破解方法

    1. 启用超级终端并出现连接界面2. 拔掉交换机的电源3. 按下交换机上的 Mode按钮, 与此同时重新插上交换机的电源线4. 当交换机左边的sys 灯会慢慢的闪动, ( 细心的话会发现部分灯红色的闪 ...

  10. 事务以及Spring的事务管理

    一.什么是事务? 事务是逻辑上的一组操作,要么都执行,要么都不执行 二.事务的特性(ACID) 原子性: 事务是最小的执行单位,不允许分割.事务的原子性确保动作要么全部完成,要么完全不起作用: 一致性 ...