PHP消息队列实现及应用详解【队列处理订单系统和配送系统】

本文实例讲述了PHP消息队列实现及应用。分享给大家供大家参考,具体如下:

在互联网项目开发者经常会遇到『给用户群发短信』、『订单系统有大量的日志需要记录』或者在秒杀业务的时候服务器无法承受瞬间并发的压力。

这种情况下,我们怎么保证系统正常有效的运行呢?

这个时候,我们可以引入一个叫『消息队列』的概念来解决上面的需求。

消息队列的概念、原理和场景

在高并发的时候,程序往往无法做到及时的处理。我们引入一个中间的系统,来进行分流和减压。

所以从本质上讲:消息队列就是一个队列结构的中间件。也就是说,你把消息和内容放入这个容器之后就可以直接返回,不用等它后期处理的结果。另外会有一个程序,读取这些数据并按照顺序处理。

1、队列结构的中间件
2、消息放入后,不必立即处理
3、由订阅者/消费者按顺序处理

也就是说:当遇到一个比较大或者耗时比较长的环节的时候,而同时你的业务又不需要立即知道这个环节的结果,使用消息队列是好的选择。

核心结构如下面:

消息队列 适用场景

一、数据需要冗余的时候
比如订单系统中,后续需要进行数据的转换和记录。消息队列可以把这些数据持久化的存储在队列中,然后由订单后期处理程序进行处理,处理完成之后再把这条记录从队列中删除。

二、系统的解耦
消息队列解决了2套系统之间深度耦合的问题。
使用消息队列后,入队的系统和出队的系统没有直接的关系。
入队系统和出队系统,其中一个崩溃之后不会影响另外一个的正常运行。

三、流量削峰
就是秒杀和抢购的时候,会出现明显的流量剧增,对服务器的压力非常大。
实际项目开发中,配合缓存来使用消息队列,一种很好的方案。

四、异步通信
消息队列本身就实现了程序的异步操作,因此只要适合于异步的场景都可以使用消息队列

五、扩展性
比如订单系统,订单入队之后,后期或许还有财务系统处理,但是如果还要加一个配货系统。
只需要让这个配货系统 订阅这个 消息队列 即可。

六、排序保证
在有些场景下,数据的处理顺序是非常重要的,队列本身就可以做成单线程的单进单出的系统。
从而有效的保证数据按照顺序进行处理。

常见 队列实现 的优缺点

队列介质:

Mysql:可靠性高、易实现、速度慢
Redis:速度快,单条大消息包时效率低
消息系统:专业性强、可靠,学习成本高(比如:RabbtiMQ)

消息处理的触发机制:

死循环方式读取:易实现,故障时无法及时恢复;
定时任务:压力均分,有处理量上限。(最大的缺陷:定位任务时间的间隔和处理的数据需要精准把握,不能上一个任务还没有处理完成,下一个认为就已经启动了)
守护进程:类似于PHP-FPM和PHP-CGI,需要shell知识

解耦案列:队列处理 订单系统和配送系统

我们在前面了解过消息队列的使用场景

这里,我们要来处理其中一个场景:系统的解耦。

在电商项目中,当客户提交了一个订单之后,客户在个人中心可以看到订单处于配送中。
这个时候就要参与进来一个系统,叫做『配送系统』。如果我们在做架构的时候,把订单系统和配送系统设计在一起的话就会出现一些问题:订单系统的压力比较大,但是配送系统没有必要对这些压力做及时的反应;我们不需要订单系统出现故障之后导致配送系统故障。

所以我们需要把这2个系统分开,通过一个中间的队列表来实现这2个系统的沟通。

如下图架构:

具体到我们的程序代码大致逻辑如下图:

大致流程:order.php来接收用户订单,生成订单号并对订单进行处理(订单系统);在订单系统会把配送系统所需要的数据放入队列表中;我们的配送系统goods.php会有个定时脚本每分钟执行一次,处理队列表中的数据。

简单设计队列表order_queue:

CREATE TABLE `order_queue` (
 `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
 `order_id` int(11) unsigned NOT NULL COMMENT '订单ID(从订单系统来的)',
 `user_info` varchar(255) NOT NULL DEFAULT '' COMMENT '可以是用户手机号/用户id等(这里只是演示)',
 `created_at` datetime NOT NULL COMMENT '订单创建时间',
 `updated_at` datetime NOT NULL COMMENT '本记录最后处理完成时间',
 `status` tinyint(2) NOT NULL COMMENT '0未处理,1已处理,2处理中',
 PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

mysql订单队列

前面我们已经分析清楚了逻辑,剩下的就是代码实现了。

注意:我这里只是演示代码,单纯为了展示实现过程。

1、接收订单,处理订单order.php

<?php
// 这个文件是用来接收用户的订单信息 并写入队列的一个文件
if(!empty($_GET['user_info'])){
  // 验证 过滤 接收的数据
  // todo...
  // 这里是应该首先是订单中心的处理流程
  // 因为订单系统是一套单独的系统 这里就不编写这个系统了
  // todo...
  $order_id = rand(100000,99999); // 正常的订单号从 订单系统来,我们这里只是演示
  // 把配送系统需要的订单数据存入队列表中
  $insert_data = array(
    'order_id'=>$order_id,
    'user_info'=>$_GET['user_info'],
    'created_at'=>date('Y-m-d H:i:s',time()),
    'status'=>0
  );
  // 把上面的数据 插入到order_queue表中
  // insert into order_queue
}

2、配送系统goods.php

<?php
// 这个文件主要是配送系统处理队列表中的订单并进行标记的文件
//分析:
//第一步:先把要处理的记录更新为『等待处理』
//第二步:选择刚刚标记为『等待处理』的记录,然后进行配送系统的处理
//第三步:把上面前面处理过的程序标记『已完成』
/////////////////////这里很重要,你一定要明白哦//////////////////////////////////////////////
//疑问:为什么不直接处理最后更新为『已完成』,多了先标记为『等待处理』?
//这是因为配送系统很可能不是及时完成的,它中间会有一段处理的时间,如果还在处理中有其他程序来进行读取和操作,就冲突了。
//这样设计其实也是一个锁的机制
//1、
$waiting = array('status'=>0);
$lock = array('status'=>2);
//把状态为0的记录标记为2,每次更新3条(具体每次几条看情况)
$sql = "update order_queue set status=2 where status=0 limit 3";
//2、
if(上面update成功){
  // 选择出要处理订单内容
  // select * from order_queue where status = 2;
  // 然后由配货系统进行处理
  // todo...
  //3、处理完成把订单状态更新为已完成
  $success = array(
    'status'=>1,
    'updated_at'=>date('Y-m-d H:i:s',time())
  );
}else{
  echo 'All Finished';
}

3、linux服务器 定时任务

写个shell脚本:goods.sh

#!/bin/bash
date "+%G-%m-%d %H:%M:%S"
cd /var/www/
php goods.php

这个脚本就是去执行orders.php这个程序的。

在linux服务器部署定时任务:

crontab -e

*/1 * * * * /var/www/goods.sh >> /var/www/goods_shell.log 2>$1

每分钟执行一次goods.sh文件,并记录日志到goods_shell.log文件(在对应目录新建该文件)

更多关于PHP相关内容感兴趣的读者可查看本站专题:《PHP数据结构与算法教程》、《php程序设计算法总结》、《php字符串(string)用法总结》、《PHP数组(Array)操作技巧大全》、《PHP常用遍历算法与技巧总结》及《PHP数学运算技巧总结》

希望本文所述对大家PHP程序设计有所帮助。

(0)

相关推荐

  • phpredis提高消息队列的实时性方法(推荐)

    数据库存贮都用list形式 要存2个队列 1个用作消息队列保存到数据 还有个 就是用来实时读取数据在redis $redis->lpush($queenkey, json_encode($array)); $redis->lpush($listkey, json_encode($array)); /*消息队列实例*/ public function insertinfo() { $infos = array('info1' => mt_rand(10,100), 'info2' =>

  • PHP使用redis消息队列发布微博的方法示例

    本文实例讲述了PHP使用redis消息队列发布微博的方法.分享给大家供大家参考,具体如下: 在一些用户发布内容应用中,可能出现1秒上万个用户同时发布消息的情况,此时使用mysql可能会出现" too many connections"错误,当然把Mysql的max_connections参数设置为更大数,不过这是一个治标不治本的方法.而使用redis的消息队列,把用户发布的消息暂时存储在消息队列中,然后使用多个cron程序把消息队列中的数据插入到Mysql.这样就有效的降低了Mysql

  • PHP基于Redis消息队列实现发布微博的方法

    本文实例讲述了PHP基于Redis消息队列实现发布微博的方法.分享给大家供大家参考,具体如下: phpRedisAdmin :github地址  图形化管理界面 git clone [url]https://github.com/ErikDubbelboer/phpRedisAdmin.git[/url] cd phpRedisAdmin git clone [url]https://github.com/nrk/predis.git[/url] vendor 首先安装上述的Redis图形化管理

  • PHP+memcache实现消息队列案例分享

    memche消息队列的原理就是在key上做文章,用以做一个连续的数字加上前缀记录序列化以后消息或者日志.然后通过定时程序将内容落地到文件或者数据库. php实现消息队列的用处比如在做发送邮件时发送大量邮件很费时间的问题,那么可以采取队列.方便实现队列的轻量级队列服务器是:starling支持memcache协议的轻量级持久化服务器https://github.com/starling/starlingBeanstalkd轻量.高效,支持持久化,每秒可处理3000左右的队列http://kr.gi

  • PHP下操作Linux消息队列完成进程间通信的方法

    关于Linux系统进程通信的概念及实现可查看:http://www.ibm.com/developerworks/cn/linux/l-ipc/ 关于Linux系统消息队列的概念及实现可查看:http://www.ibm.com/developerworks/cn/linux/l-ipc/part4/ PHP的sysvmsg模块是对Linux系统支持的System V IPC中的System V消息队列函数族的封装.我们需要利用sysvmsg模块提供的函数来进进程间通信.先来看一段示例代码_1:

  • PHP+Redis 消息队列 实现高并发下注册人数统计的实例

    前言 现在越来越多的网站开始注重统计和用户行为分析,作为网站经常使用的功能,如何让统计性能更加高,这也是我们需要考虑的事情.本篇通过Redis来优化统计功能(以注册人数统计为例). 传统的统计功能都是直接操作数据库把数据插入表中.这样做,对数据库的性能消耗就会比较大. 思路: 这里我们用到了redis的队列,注册的时候先添加到队列,然后在处理的时候出队,并且把人数添加redis里. 代码: <?php //register.php $redis = new Redis(); $redis->c

  • PHP+RabbitMQ实现消息队列的完整代码

    前言 为什么使用RabbitMq而不是ActiveMq或者RocketMq? 首先,从业务上来讲,我并不要求消息的100%接受率,并且,我需要结合php开发,RabbitMq相较RocketMq,延迟较低(微妙级).至于ActiveMq,貌似问题较多.RabbitMq对各种语言的支持较好,所以选择RabbitMq. 先安装PHP对应的RabbitMQ,这里用的是 php_amqp 不同的扩展实现方式会有细微的差异. php扩展地址: http://pecl.php.net/package/amq

  • php+redis消息队列实现抢购功能

    本文实例为大家分享了php+redis消息队列实现抢购的具体代码,供大家参考,具体内容如下 实现功能: 1. 基于redis队列,防止高并发的超卖 2. 基于mysql的事务加排它锁,防止高并发的超卖 基于redis队列工作流程: 1. 管理员根据goods表中的库存,创建redis商品库存队列 2. 客户端访问秒杀API 3. web服务器先从redis的商品库存队列中查询剩余库存重点内容 4. redis队列中有剩余,则在mysql中创建订单,去库存,抢购成功 5. redis队列中没有剩余

  • PHP的Laravel框架中使用消息队列queue及异步队列的方法

    queue配置 首先说明一下我之前的项目中如何使用queue的. 我们现在的项目都是用的symfony,老一点的项目用的symfony1.4,新一点的项目用的都是symfony2.symfony用起来整体感觉还是很爽的,尤其symfony2,整体上来讲使用了很多java里面框架的设计思想.但是他不支持queue.在symfony,我们使用queue也经历了几个过程.最开始使用张堰同学的httpsqs.这个简单使用,但是存在单点.毕竟我们的项目还是正式对外服务的,所以我们研究了Apache旗下的开

  • PHP使用php-resque库配合Redis实现MQ消息队列的教程

    消息队列处理后台任务带来的问题 项目中经常会有后台运行任务的需求,比如发送邮件时,因为要连接邮件服务器,往往需要5-10秒甚至更长时间,如果能先给用户一个成功的提示信息,然后在后台慢慢处理发送邮件的操作,显然会有更好的用户体验. 为了实现类似的需求,Web项目中一般的实现方法是使用消息队列(Message Queue),比如MemcacheQ,RabbitMQ等等,都是很著名的产品. 消息队列说白了就是一个最简单的先进先出队列,队列的一个成员就是一段文本.正是因为消息队列实在太简单了,当拿着消息

随机推荐