RabbitMQ+PHP消息队列系统

 MQ全称为Message Queue, 消息队列 (MQ)是一种应用程序对应用程序的通信方法。应用程序通过读写出入队列的消息(针对应用程序的数据)来通信,而无需专用连接来链接它们。消息传递指的是程序之间通过在消息中发送数据进行通信,而不是通过直接调用彼此来通信,直接调用通常是用于诸如 远程过程调用 的技术。排队指的是应用程序通过 队列来通信。队列的使用除去了接收和发送应用程序同时执行的要求。其中较为成熟的MQ产品有IBM WEBSPHERE MQ等等。

MQ特点

编辑
MQ 是消费-生产者模型的一个典型的代表,一端往 消息队列 中不断写入消息,而另一端则可以读取或者订阅队列中的消息。MQ和 JMS 类似,但不同的是JMS是SUN JAVA 消息中间件 服务的一个标准和API定义,而MQ则是遵循了AMQP协议的具体实现和产品。

使用场景

编辑
在项目中,将一些无需即时返回且耗时的操作提取出来,进行了 异步处理 ,而这种异步处理的方式大大的节省了服务器的 请求响应时间 ,从而提高了系统的吞吐量。

含义

编辑
RabbitMQ 是一个在 AMQP 基础上完成的,可复用的企业消息系统。他遵循 Mozilla Public License 开源协议

几个概念

编辑
Exchange:交换机,决定了消息路由规则;
Queue:消息队列;
Channel:进行消息读写的通道;
Bind:绑定了Queue和Exchange,意即为符合什么样路由规则的消息,将会放置入哪一个 消息队列

消息持久

编辑
1) 将交换机置为可持久;
2) 将通道置为可持久
3) 消息发送时设置可持久。
当我们“生产”了一条可持久化的消息,尝试中断MQ服务,启动消费者获取消息,消息依然能够恢复。相反,则抛出异常。

入门介绍

编辑

基本概念

RabbitMQ是流行的开源消息队列系统,用erlang语言开发。RabbitMQ是AMQP(高级消息队列协议)的标准实现。如果不熟悉AMQP,直接看RabbitMQ的文档会比较困难。不过它也只有几个关键概念,这里简单介绍。
RabbitMQ的结构图如下:
RabbitMQ+PHP消息队列系统_第1张图片
几个概念说明:
       Broker:简单来说就是消息队列服务器实体。
  Exchange:消息交换机,它指定消息按什么规则,路由到哪个队列。
  Queue:消息队列载体,每个消息都会被投入到一个或多个队列。
  Binding:绑定,它的作用就是把exchange和queue按照路由规则绑定起来。
  Routing Key:路由关键字,exchange根据这个关键字进行消息投递。
  vhost:虚拟主机,一个broker里可以开设多个vhost,用作不同用户的权限分离。
  producer:消息生产者,就是投递消息的程序。
  consumer:消息消费者,就是接受消息的程序。
  channel:消息通道,在客户端的每个连接里,可建立多个channel,每个channel代表一个会话任务。
消息队列的使用过程大概如下:
(1)客户端连接到消息队列服务器,打开一个channel。
  (2)客户端声明一个exchange,并设置相关属性。
  (3)客户端声明一个queue,并设置相关属性。
  (4)客户端使用routing key,在exchange和queue之间建立好绑定关系。
  (5)客户端投递消息到exchange。
exchange接收到消息后,就根据消息的key和已经设置的binding,进行消息路由,将消息投递到一个或多个队列里。
exchange也有几个类型,完全根据key进行投递的叫做Direct交换机,例如,绑定时设置了routing key为”abc”,那么客户端提交的消息,只有设置了key为”abc”的才会投递到队列。对key进行模式匹配后进行投递的叫做Topic交换机,符号”#”匹配一个或多个词,符号”*”匹配正好一个词。例如”abc.#”匹配”abc.def.ghi”,”abc.*”只匹配”abc.def”。还有一种不需要key的,叫做Fanout交换机,它采取广播模式,一个消息进来时,投递到与该交换机绑定的所有队列。
RabbitMQ支持消息的持久化,也就是数据写在磁盘上,为了数据安全考虑,我想大多数用户都会选择持久化。消息队列持久化包括3个部分:
  (1)exchange持久化,在声明时指定durable => 1
  (2)queue持久化,在声明时指定durable => 1
  (3)消息持久化,在投递时指定delivery_mode => 2(1是非持久化)
如果exchange和queue都是持久化的,那么它们之间的binding也是持久化的。如果exchange和queue两者之间有一个持久化,一个非持久化,就不允许建立绑定。

依赖包安装

yum install ncurses-devel unixODBC unixODBC-devel

erlang环境

wget http://erlang.org/download/otp_src_18.1.tar.gztar -zxvf otp_src_18.1.tar.gzcd otp_src_18.1./configure --prefix=/usr/local/erlangmakemake install# 配置erlang环境变量vim /etc/profile# 增加内容:export PATH="$PATH:/usr/local/erlang/bin"# 保存退出,并刷新变量source /etc/profile# 测试erlang是否安装成功# 安装完成以后,执行erl看是否能打开eshell,用’halt().’退出,注意后面的点号,那是erlang的结束符。[root@localhost src]# erl Erlang/OTP 17 [erts-6.1] [source] [64-bit] [async-threads:10] [hipe] [kernel-poll:false]Eshell V6.1 (abort with ^G) 2> 9+3. 12 3> halt().

安装rabbitmq依赖文件,安装rabbitmq

# 安装rabbitmq依赖包yum install xmlto# 安装rabbitmq服务端wget http://www.rabbitmq.com/releases/rabbitmq-server/v3.5.7/rabbitmq-server-3.5.7.tar.gztar zxvf rabbitmq-server-3.5.7.tar.gzcd rabbitmq-server-3.5.7/makemake install TARGET_DIR=/usr/local/rabbitmq SBIN_DIR=/usr/local/rabbitmq/sbin MAN_DIR=/usr/local/rabbitmq/man DOC_INSTALL_DIR=/usr/local/rabbitmq/doc# 配置hostsvim /etc/hosts# 增加一行内容# 当前IP地址 绑定HOSTNAME名称(vim /etc/sysconfig/network)192.168.2.208 localhost.localdomain# 这种会提示错误(Warning: PID file not written; -detached was passed.)/usr/local/rabbitmq/sbin/rabbitmq-server -detached 启动rabbitmq/usr/local/rabbitmq/sbin/rabbitmqctl status 查看状态/usr/local/rabbitmq/sbin/rabbitmqctl stop 关闭rabbitmq# 目前我自己使用/usr/local/rabbitmq/sbin/rabbitmq-server start & 启动rabbitmq/usr/local/rabbitmq/sbin/rabbitmqctl status 查看状态/usr/local/rabbitmq/sbin/rabbitmqctl stop 关闭rabbitmq

启用管理插件

mkdir /etc/rabbitmq/usr/local/rabbitmq/sbin/rabbitmq-plugins list 查看插件列表/usr/local/rabbitmq/sbin/rabbitmq-plugins enable rabbitmq_management (启用插件)/usr/local/rabbitmq/sbin/rabbitmq-plugins disable rabbitmq_management (禁用插件)# 重启rabbitmq# 访问 http://127.0.0.1:15672/# 如果有iptablesvim /etc/sysconfig/iptables# 增加一下内容-A INPUT -m state --state NEW -m tcp -p tcp --dport 15672 -j ACCEPT# 重启动iptableservice iptables restart

开机自启动配置

#!/bin/sh#start rabbitMqsudo /usr/local/rabbitmq/sbin/rabbitmq-server & > /usr/local/rabbitmq/logs/rabbitmq.log 2>&1

RabbitMQ PHP扩展安装

# 安装rabbitmq-c依赖包yum install libtool autoconf# 安装rabbitmq-c ( 最好下载 0.5的,0.6安装可能会报错)# 版本下载:https://github.com/alanxz/rabbitmq-c/releases/tag/v0.5.0wget https://github.com/alanxz/rabbitmq-c/releases/download/v0.5.0/rabbitmq-c-0.5.0.tar.gztar -zxvf v0.5.0cd rabbitmq-c-0.5.0/autoreconf -i./configure --prefix=/usr/local/rabbitmq-cmakemake install# 安装PHP扩展 amqpwget http://pecl.php.net/get/amqp-1.6.1.tgztar zxvf amqp-1.6.1.tgzcd amqp-1.6.1/usr/local/php/bin/phpize./configure --with-php-config=/usr/local/php/bin/php-config --with-amqp --with-librabbitmq-dir=/usr/local/rabbitmq-cmakemake install# 编辑php.ini文件,增加amqp扩展支持vim /usr/local/php/etc/php.ini# 增加下面内容; rabbitmq扩展支持extension=amqp.so# 重启php-fpm/etc/init.d/php-fpm restart

验证是否成功 phpinfo()查看下是否支持amqp扩展


相关配置

hostname mq // 设置hostname名称vim /etc/sysconfig/network // 设置hostnamevim /etc/hosts // 编辑hosts./rabbitmqctl add_user admin admin // 添加用户./rabbitmqctl set_user_tags admin administrator // 添加admin 到 administrator分组./rabbitmqctl set_permissions -p / admin "*." "*." "*." // 添加权限

创建配置文件

#在/usr/rabbitmq/sbin/rabbitmq-defaults 查看config文件路径# 创建配置文件 touch/usr/rabbitmq/sbin#vm_memory_high_watermark 内存低水位线,若低于该水位线,则开启流控机制,阻止所有请求,默认值是0.4,即内存总量的40%,#vm_memory_high_watermark_paging_ratio 内存低水位线的多少百分比开始通过写入磁盘文件来释放内存vi /usr/rabbitmq/sbin/rabbitmq.config 输入[{rabbit, [{vm_memory_high_watermark_paging_ratio, 0.75}, {vm_memory_high_watermark, 0.7}]}].

创建环境文件

touch /etc/rabbitmq/rabbitmq-env.conf#输入 RABBITMQ_NODENAME=FZTEC-240088 节点名称 RABBITMQ_NODE_IP_ADDRESS=127.0.0.1 监听IP RABBITMQ_NODE_PORT=5672 监听端口 RABBITMQ_LOG_BASE=/data/rabbitmq/log 日志目录 RABBITMQ_PLUGINS_DIR=/data/rabbitmq/plugins 插件目录 RABBITMQ_MNESIA_BASE=/data/rabbitmq/mnesia 后端存储目录

操作命令

查看exchange信息 /usr/rabbitmq/sbin/rabbitmqctl list_exchanges name type durable auto_delete arguments 查看队列信息 /usr/rabbitmq/sbin/rabbitmqctl list_queues name durable auto_delete messages consumers me 查看绑定信息 /usr/rabbitmq/sbin/rabbitmqctl list_bindings 查看连接信息 /usr/rabbitmq/sbin/rabbitmqctl list_connections

php的server端脚本

$routingkey='key';

//设置你的连接

$conn_args = array('host' => 'localhost', 'port' => '5672', 'login' => 'guest', 'password' => 'guest');

$conn = new AMQPConnection($conn_args);

if ($conn->connect()) {

    echo "Established a connection to the broker \n";

}

else {

    echo "Cannot connect to the broker \n ";

}

//你的消息

$message = json_encode(array('Hello World3!','php3','c++3:'));

//创建channel

$channel = new AMQPChannel($conn);

//创建exchange

$ex = new AMQPExchange($channel);

$ex->setName('exchange');//创建名字

$ex->setType(AMQP_EX_TYPE_DIRECT);

$ex->setFlags(AMQP_DURABLE);

//$ex->setFlags(AMQP_AUTODELETE);

//echo "exchange status:".$ex->declare();

echo "exchange status:".$ex->declareExchange();

echo "\n";

for($i=0;$i<100;$i++){

        if($routingkey=='key2'){

                $routingkey='key';

        }else{

                $routingkey='key2';

        }

        $ex->publish($message,$routingkey);

}

/*

$ex->publish($message,$routingkey);

创建队列

$q = new AMQPQueue($channel);

设置队列名字 如果不存在则添加

$q->setName('queue');

$q->setFlags(AMQP_DURABLE | AMQP_AUTODELETE);

echo "queue status: ".$q->declare();

echo "\n";

echo 'queue bind: '.$q->bind('exchange','route.key');

将你的队列绑定到routingKey

echo "\n";


$channel->startTransaction();

echo "send: ".$ex->publish($message, 'route.key'); //将你的消息通过制定routingKey发送

$channel->commitTransaction();

$conn->disconnect();

*/

php客户端脚本

$bindingkey='key2';

//连接RabbitMQ

$conn_args = array( 'host'=>'127.0.0.1' , 'port'=> '5672', 'login'=>'guest' , 'password'=> 'guest','vhost' =>'/');

$conn = new AMQPConnection($conn_args);

$conn->connect();

//设置queue名称,使用exchange,绑定routingkey

$channel = new AMQPChannel($conn);

$q = new AMQPQueue($channel);

$q->setName('queue2');

$q->setFlags(AMQP_DURABLE);

$q->declare();

$q->bind('exchange',$bindingkey);

//消息获取

$messages = $q->get(AMQP_AUTOACK) ;

if ($messages){

var_dump(json_decode($messages->getBody(), true ));

}

$conn->disconnect();

?>

今天用一个简单的案例来实现 RabbitMQ + PHP 这个消息队列的运行机制。

主要分为两个部分:

第一:发送者(publisher)

第二:消费者(consumer)
(一)生产者 (创建一个rabbit_publisher.php的文件)

$conn_args = array(

    'host' => '127.0.0.1',

    'port' => '5672',

    'login' => 'guest',

    'password' => 'guest',

    'vhost'=>'/'

);


//创建连接和channel

$conn = new AMQPConnection($conn_args);

if (!$conn->connect()) {

    die("Cannot connect to the broker!\n");

}

$channel = new AMQPChannel($conn);


//创建交换机

$e_name = 'e_linvo'; //交换机名

$ex = new AMQPExchange($channel);

$ex->setName($e_name);

$ex->setType(AMQP_EX_TYPE_DIRECT); //direct类型

$ex->setFlags(AMQP_DURABLE); //持久化

echo "Exchange Status:".$ex->declare()."\n";



echo "Send Message:".$ex->publish("TEST MESSAGE,key_1 by xust" . date('H:i:s', time()), 'key_1')."\n";

echo "Send Message:".$ex->publish("TEST MESSAGE,key_2 by xust" . date('H:i:s', time()), 'key_2')."\n";

(二)消费者(创建一个rabbit_consumer.php的文件)

  创建连接-->创建channel-->创建交换机-->创建队列-->绑定交换机/队列/路由键-->接收消息

$conn_args = array(

    'host' => '127.0.0.1',

    'port' => '5672',

    'login' => 'guest',

    'password' => 'guest',

    'vhost'=>'/'

);


$e_name = 'e_linvo'; //交换机名

$q_name = 'q_linvo'; //队列名

$k_route = 'key_2'; //路由key


//创建连接和channel

$conn = new AMQPConnection($conn_args);

if (!$conn->connect()) {

    die("Cannot connect to the broker!\n");

}

$channel = new AMQPChannel($conn);


//创建交换机

$ex = new AMQPExchange($channel);

$ex->setName($e_name);

$ex->setType(AMQP_EX_TYPE_DIRECT); //direct类型

$ex->setFlags(AMQP_DURABLE); //持久化

echo "Exchange Status:".$ex->declare()."\n";


//创建队列

$q = new AMQPQueue($channel);

$q->setName($q_name);

$q->setFlags(AMQP_DURABLE); //持久化


//绑定交换机与队列,并指定路由键

echo 'Queue Bind: '.$q->bind($e_name, 'key_2')."\n"; 


//阻塞模式接收消息

echo "Message:\n";

$q->consume('processMessage', AMQP_AUTOACK); //自动ACK应答


$conn->disconnect();


/**

 * 消费回调函数

 * 处理消息

 */

function processMessage($envelope, $queue) {

    var_dump($envelope->getRoutingKey);

    $msg = $envelope->getBody();

    echo $msg."\n"; //处理消息

  

你可能感兴趣的:(php)