操作者 :包竹滨 QQ:249910820@qq.com
参考网站:
原理篇:
http://blog.csdn.net/anzhsoft/article/details/19563091
http://www.2cto.com/kf/201402/278429.html
http://blog.csdn.net/lmj623565791/article/details/37620057
安装篇:
http://www.linuxidc.com/Linux/2014-12/110449.htm
http://blog.csdn.net/zdq0394123/article/details/14226531
http://www.cnblogs.com/DanielChow/p/3372664.html
http://blog.csdn.net/historyasamirror/article/details/6827870
http://nonfu.me/p/8833.html
#安装erlang
tar zxf otp_src_17.1.tar.gz cd otp_src_17.1 ./configure
wget http://www.erlang.org/download/otp_src_17.1.tar.gz
make && make install
安装完成以后,执行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().
#安装simplejson
tar zxf simplejson-3.5.3.tar.gz
wget https://pypi.python.org/packages/source/s/simplejson/simplejson-3.5.3.tar.gz#md5=d5f62dfa6b6dea31735d56c858361d48
进入http://www.rabbitmq.com/download.html选择最新的源码包 两种下载路径
cd simplejson-3.5.3
python setup.py build
python setup.py install
#安装rabbitmq
进入http://www.rabbitmq.com/download.html选择最新的源码包
#wget http://www.rabbitmq.com/releases/rabbitmq-server/v3.3.4/rabbitmq-server-3.3.4.tar.gz
#tar zxvf rabbitmq-server-3.3.4.tar.gz
请先执行 yum install xmlto
#cd rabbitmq-server-3.3.4
make TARGET_DIR=/apps/svr/rabbitmq SBIN_DIR=/apps/svr/rabbitmq/sbin MAN_DIR=/apps/svr/rabbitmq/man install
出现 unzip 命令找不到,,,
请yum install unzip ok
make TARGET_DIR=/usr/local/rabbitmq SBIN_DIR=/usr/local/rabbitmq/sbin MAN_DIR=/usr/local/rabbitmq/man DOC_INSTALL_DIR=/usr/local/rabbitmq/doc install
echo “Put your EZs here and use rabbitmq-plugins to enable them.” > plugins/README rm -f plugins/rabbit_common*.ez Please set TARGET_DIR. Please set SBIN_DIR. Please set MAN_DIR. Please set DOC_INSTALL_DIR. make: *** [install_dirs] 错误 1
说让添加环境变量,解决方法: [root@localhost rabbitmq-server-3.2.4]# export TARGET_DIR=/apps/svr/rabbitmq [root@localhost rabbitmq-server-3.2.4]# export SBIN_DIR=/apps/svr/rabbitmq/sbin [root@localhost rabbitmq-server-3.2.4]# export MAN_DIR=/apps/svr/rabbitmq/man [root@localhost rabbitmq-server-3.2.4]# export DOC_INSTALL_DIR=/apps/svr/rabbitmq/doc
#配置
mkdir /etc/rabbitmq cp /apps/svr/rabbitmq/doc/rabbitmq.config.example /etc/rabbitmq/rabbitmq.config#插件
/apps/svr/rabbitmq/sbin/rabbitmq-plugins enable rabbitmq_management /apps/svr/rabbitmq/sbin/rabbitmq-plugins enable rabbitmq_mqtt
#启动
/apps/svr/rabbitmq/sbin/rabbitmq-server &关闭rabbitmq:
#
/apps/svr/rabbitmq/sbin/rabbitmqctl stop
4、查看RabbitMQ服务启动的状态,并开启RabbitMQ的相应管理插件
[root@localhost sbin]# ./rabbitmqctl status
[root@ptr165 rabbitmq_server-3.2.0]# ./rabbitmq-server
RabbitMQ 3.2.0. Copyright (C) 2007-2013 GoPivotal, Inc.
## ## Licensed under the MPL. See http://www.rabbitmq.com/
## ##
########## Logs: sbin/../var/log/rabbitmq/rabbit@ptr165.log
###### ## sbin/../var/log/rabbitmq/rabbit@ptr165-sasl.log
##########
Starting broker... completed with 0 plugins.
如果出现这样的输出说明rabbitmq安装成功
(1)控制台启动方式:./rabbitmq-server
通过控制台方式启动,可以CTRL-C命令关掉rabbitmq
(2)后台启动方式:sbin/rabbitmq-server -detached
可以通过rabbitmqctl stop命令关掉rabbitmq
[root@ptr165 rabbitmq_server-3.2.0]# sbin/rabbitmqctl stop
Stopping and halting node rabbit@ptr165 ...
...done.
(3)指定rabbitmq节点的名字启动
默认情况下,rabbitmq节点的名字是rabbit@HOSTNAME
rabbit@ptr165
默认端口是5672
当我们运行rabbitmqctl stop命令时,由于没有指定节点名称,默认指向rabbit@HOSTNAME
我们可以通过指定不同的名字和端口
RABBITMQ_NODE_PORT=5673 RABBITMQ_NODENAME=rabbit1 sbin/rabbitmq-server -detached
此时运行rabbitmqctl命令时,要指定节点名称
sbin/rabbitmqctl -n rabbit1@ptr164 status
Status of node rabbit1@ptr164 ...
关闭rabbitmq
sbin/rabbitmqctl -n rabbit1@ptr164 stop
Stopping and halting node rabbit1@ptr164 ...
...done.
一台单机上可以运行多个node节点。
(4)启动rabbitmq的管理插件
我们前面启动的rabbitmq没有启动任何插件
Starting broker... completed with 0 plugins.
首先启动rabbitmq
然后运行命令
./rabbitmq-plugins enable rabbitmq_management
如果/etc/rabbitmq不存在会报如下错误:
Error: {cannot_write_enabled_plugins_file,”/etc/rabbitmq/enabled_plugins”, enoent}
mkdir /etc/rabbitmq
cd /apps/svr/rabbitmq/sbin/
启用此插件
1
. /rabbitmq-plugins enable rabbitmq_management如果要禁用用此命令即可
1
. /rabbitmq-plugins disable rabbitmq_management查看已经安装的插件
. /rabbitmq-plugins list
[e ] amqp_client 0.0.0
[ ] eldap 0.0.0-git
[ ] erlando 0.0.0
[e ] mochiweb 1.3-rmq0.0.0-git
[ ] rabbitmq_auth_backend_ldap 0.0.0
[ ] rabbitmq_auth_mechanism_ssl 0.0.0
[ ] rabbitmq_consistent_hash_exchange 0.0.0
[ ] rabbitmq_federation 0.0.0
[ ] rabbitmq_jsonrpc 0.0.0
[ ] rabbitmq_jsonrpc_channel 0.0.0
[ ] rabbitmq_jsonrpc_channel_examples 0.0.0
[E ] rabbitmq_management 0.0.0
[e ] rabbitmq_management_agent 0.0.0
[ ] rabbitmq_management_visualiser 0.0.0
[e ] rabbitmq_mochiweb 0.0.0
[ ] rabbitmq_shovel 0.0.0
[ ] rabbitmq_shovel_management 0.0.0
[ ] rabbitmq_stomp 0.0.0
[ ] rabbitmq_tracing 0.0.0
[ ] rfc4627_jsonrpc 0.0.0-git
[e ] webmachine 1.7.0-rmq0.0.0-hg停止RabbitMQ
1
. /rabbitmqctl stop启动RabbitMQ
1
. /rabbitmq-server -detached
./rabbitmq-server
RabbitMQ 3.2.0. Copyright (C) 2007-2013 GoPivotal, Inc.
## ## Licensed under the MPL. See http://www.rabbitmq.com/
## ##
########## Logs: sbin/../var/log/rabbitmq/rabbit@ptr164.log
###### ## sbin/../var/log/rabbitmq/rabbit@ptr164-sasl.log
##########
Starting broker... completed with 6 plugins.
可以在浏览器中输入:http://192.168.1.164:15672
登陆失败
以上似乎要结束了,等等可能当你输入geust账号和密码登陆时发现不能登陆,其它程序调用连接时也无法连接,这是因为出于安全原因,官方默认是禁止guest进行远程登陆访问的(仅限于localhost,参见:http://www.rabbitmq.com/access-control.html)。
上面的链接中官方也很明确的说明的建议删除guest用户创建一个新用户,如
权限参考网站:
http://rainbird.blog.51cto.com/211214/525523/
http://blog.csdn.net/zyz511919766/article/details/42292655
http://www.linuxidc.com/Linux/2014-12/110449.htm
首先创建
vhosts
,命令如下:
添加创建虚拟主机:
D:\rabbitmq\sbin>./rabbitmqctl add_vhost dnt_mq
//
注:删除虚拟主机
./rabbitmqctl delete_vhost vhostpath
用下面指定就可以显示出所有虚拟主机信息:
D:\rabbitmq\sbin>./rabbitmqctl list_vhosts
Listing vhosts ...
/
(根目录)
首先创建
vhosts
,命令如下:
添加创建虚拟主机:
D:\rabbitmq\sbin>./rabbitmqctl add_vhost dnt_mq
//
注:删除虚拟主机
rabbitmqctl delete_vhost vhostpath
用下面指定就可以显示出所有虚拟主机信息:
D:\rabbitmq\sbin>rabbitmqctl list_vhosts
Listing vhosts ...
/
(根目录)
首先创建
vhosts
,命令如下:
添加创建虚拟主机:
D:\rabbitmq\sbin>rabbitmqctl add_vhost dnt_mq
//
注:删除虚拟主机
rabbitmqctl delete_vhost vhostpath
用下面指定就可以显示出所有虚拟主机信息:
D:\rabbitmq\sbin>./rabbitmqctl list_vhosts
Listing vhosts ...
/
(根目录)
添加创建虚拟主机:设置用户角色
rabbitmqctl set_user_tags User Tag
Tag为角色名administrator,monitoring,policymaker,management或其他自定义名称可给同一用户设置多个角色,如:
rabbitmqctl set_user_tags testmq monitoring policymaker administrator
cd /usr/local/rabbitmq/sbin/
2
3
当然如果你执意要使用guest进行远程访问,建议修改密码,如
1
然后在配置文件/etc/rabbitmq/rabbitmq.config中(可能不存在,新建即可使用)添加:
1
然后重启rabbitmq-service服务
1
这个时候在浏览器输入http://{server_name}:15672/进行访问,使用刚刚设置的账号登陆就可以进行管理了
我们测试用的两台机器分别为192.168.1.164和192.168.1.165。
(1)配置hostname
在两台机器的host分别配置vi /etc/hosts
122.11.45.165 ptr165
122.11.45.164 ptr164
(2)确保两台机器的cookie一致:(可以参考erlang的分布式通信模型)
~/.erlang.cookie,可以为任意值。
(3)分别启动rabbitmq
[root@ptr164 rabbitmq_server-3.2.0]# ./rabbitmq-server -detached
[root@ptr165 rabbitmq_server-3.2.0]# ./rabbitmq-server -detached
(4)创建cluster
我们以rabbit@ptr164为主,将rabbit@ptr165加入到集群中
[root@ptr165 rabbitmq_server-3.2.0]# ./rabbitmqctl stop_app
[root@ptr165 rabbitmq_server-3.2.0]# ./rabbitmqctl join_cluster --ram rabbit@ptr164
[root@ptr165 rabbitmq_server-3.2.0]# ./rabbitmqctl start_app
查看集群状态
[root@ptr165 rabbitmq_server-3.2.0]# sbin/rabbitmqctl cluster_status
[root@ptr165 rabbitmq_server-3.2.0]# sbin/rabbitmqctl cluster_status
Cluster status of node rabbit@ptr165 ...
[{nodes,[{disc,[rabbit@ptr164]},{ram,[rabbit@ptr165]}]},
{running_nodes,[rabbit@ptr164,rabbit@ptr165]},
{partitions,[]}]
...done.
[root@ptr164 rabbitmq_server-3.2.0]# sbin/rabbitmqctl cluster_status
Cluster status of node rabbit@ptr164 ...
[{nodes,[{disc,[rabbit@ptr164]},{ram,[rabbit@ptr165]}]},
{running_nodes,[rabbit@ptr165,rabbit@ptr164]},
{partitions,[]}]
...done.
两个节点得到相同的输出,其中rabbit@ptr164节点形式是disc,rabbit@ptr165节点形式是ram。这和执行join_cluster命令是的选项--ram是一致的。
(5)改变节点形式
[root@ptr165 rabbitmq_server-3.2.0]# ./rabbitmqctl stop_app
Stopping node rabbit@ptr165 ...
...done.
[root@ptr165 rabbitmq_server-3.2.0]# ./rabbitmqctl change_cluster_node_type disc
Turning rabbit@ptr165 into a disc node ...
...done.
[root@ptr165 rabbitmq_server-3.2.0]# ./rabbitmqctl start_app
Starting node rabbit@ptr165 ...
...done.
我们将rabbit@ptr165的节点改为disc
[root@ptr164 rabbitmq_server-3.2.0]# sbin/rabbitmqctl cluster_status
Cluster status of node rabbit@ptr164 ...
[{nodes,[{disc,[rabbit@ptr164,rabbit@ptr165]}]},
{running_nodes,[rabbit@ptr165,rabbit@ptr164]},
{partitions,[]}]
...done.
两个节点都是disc的形式
(6)从cluster中分离
[root@ptr165 rabbitmq_server-3.2.0]# ./rabbitmqctl stop_app
Stopping node rabbit@ptr165 ...
...done.
[root@ptr165 rabbitmq_server-3.2.0]# ./rabbitmqctl reset
Resetting node rabbit@ptr165 ...
...done.
[root@ptr165 rabbitmq_server-3.2.0]# ./rabbitmqctl start_app
Starting node rabbit@ptr165 ...
通过查看集群状态发现,两个节点都成了孤立的节点
Cluster status of node rabbit@ptr165 ...
[{nodes,[{disc,[rabbit@ptr165]}]},
{running_nodes,[rabbit@ptr165]},
{partitions,[]}]
...done.
php rabbitmq 扩展
主要是两个包
1.rabbitmq-c的包
2.amqp的包
wget https://github.com/alanxz/rabbitmq-c/releases/download/v0.8.0/rabbitmq-c-0.8.0.tar.gz
wget http://pecl.php.net/get/amqp-1.6.1.tgz
安装有先后顺序的~ 先安装 rabbitmq-c 在安装 ampqp
tar zxvf rabbitmq-c-0.8.0.tar.gz
cdrabbitmq-c-0.8.0
./configure --prefix=/usr/local/rabbitmq-c
make && make install
tar zxvf amqp-1.6.1.tgz
cd amqp-1.6.1
/apps/svr/php/bin/phpize
./configure --with-php-config=/apps/svr/php/bin/php-config --with-amqp --with-librabbitmq-dir=/usr/local/rabbitmq-c/
make && make install
vim /apps/svr/php/etc/php.ini extension="amqp.so" extension_dir = "/usr/local/php/lib/php/extensions/no-debug-non-zts-20090626/"
# php.ini 可以根据自己位置定
重启 !!!
send.php:<?php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
$conn_args = array(
'host' => '192.168.1.109',
'port' => '5672',
'login' => 'xiaobao',
'password' => '123456',
'vhost'=>'/'
);
$e_name = 'e_linvo'; //交换机名
//$q_name = 'q_linvo'; //无需队列名
$k_route = 'key_1'; //路由key
//创建连接和channel
$conn = new AMQPConnection($conn_args);
if (!$conn->connect()) {
die("Cannot connect to the broker!\n");
}
$channel = new AMQPChannel($conn);
//消息内容
$message = "TEST MESSAGE! okkkkk";
//创建交换机对象
$ex = new AMQPExchange($channel);
$ex->setName($e_name);
//发送消息
//$channel->startTransaction(); //开始事务
for($i=0; $i<5; ++$i){
echo "Send Message:".$ex->publish($message, $k_route)."\n";
}
//$channel->commitTransaction(); //提交事务
$conn->disconnect();
?>
receiver.php
<?php
<?php
/*************************************
* PHP amqp(RabbitMQ) Demo - consumer
* Author: Linvo
* Date: 2012/7/30
*************************************/
//配置信息
$conn_args = array(
'host' => '192.168.1.109',
'port' => '5672',
'login' => 'xiaobao',
'password' => '123456',
'vhost'=>'/'
);
$e_name = 'e_linvo'; //交换机名
$q_name = 'q_linvo'; //队列名
$k_route = 'key_1'; //路由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 "Message Total:".$q->declare()."\n";
//绑定交换机与队列,并指定路由键
echo 'Queue Bind: '.$q->bind($e_name, $k_route)."\n";
//阻塞模式接收消息
echo "Message:\n";
while(True){
$q->consume('processMessage');
//$q->consume('processMessage', AMQP_AUTOACK); //自动ACK应答
}
$conn->disconnect();
/**
* 消费回调函数
* 处理消息
*/
function processMessage($envelope, $queue) {
$msg = $envelope->getBody();
echo $msg."\n"; //处理消息
$queue->ack($envelope->getDeliveryTag()); //手动发送ACK应答
}
/**
* 消费回调函数
* 处理消息
*/
function processMessage($envelope, $queue) {
$msg = $envelope->getBody();
echo $msg."\n"; //处理消息
$queue->ack($envelope->getDeliveryTag()); //手动发送ACK应答
}
//阻塞模式接收消息
echo "Message:\n";
while(True){
$q->consume('processMessage');
//$q->consume('processMessage', AMQP_AUTOACK); //自动ACK应答
}
$conn->disconnect();
/**
* 消费回调函数
* 处理消息
*/
function processMessage($envelope, $queue) {
$msg = $envelope->getBody();
echo $msg."\n"; //处理消息
$queue->ack($envelope->getDeliveryTag()); //手动发送ACK应答
}
AMPQ协议为了能够满足各种消息队列需求,在概念上比较复杂。首先,rabbitMQ启动默认是没有任何配置的,需要客户端连接上去,设置交换机等才能工作。不把这些基础概念弄清楚,后面程序设计就容易产生问题。
1.vhosts : 虚拟主机。
一个RabbitMQ的实体上可以有多个vhosts,用户与权限设置就是依附于vhosts。对一般PHP应用,不需要用户权限设定,直接使用默认就存在的"/"就可以了,用户可以使用默认就存在的"guest"。一个简单的配置示例:
$conn_args = array( 'host' => '127.0.0.1', 'port' => '5672', 'login' => 'guest', 'password' => 'guest', 'vhost'=>'/' );2.connection 与 channel : 连接与信道
connection是指物理的连接,一个client与一个server之间有一个连接;一个连接上可以建立多个channel,可以理解为逻辑上的连接。一般应用的情况下,有一个channel就够用了,不需要创建更多的channel。示例代码:
//创建连接和channel $conn = new AMQPConnection($conn_args); if (!$conn->connect()) { die("Cannot connect to the broker!\n"); } $channel = new AMQPChannel($conn);3.exchange 与 routingkey : 交换机与路由键
为了将不同类型的消息进行区分,设置了交换机与路由两个概念。比如,将A类型的消息发送到名为‘C1’的交换机,将类型为B的发送到'C2'的交换机。当客户端连接C1处理队列消息时,取到的就只是A类型消息。进一步的,如果A类型消息也非常多,需要进一步细化区分,比如某个客户端只处理A类型消息中针对K用户的消息,routingkey就是来做这个用途的。
$e_name = 'e_linvo'; //交换机名 $k_route = array(0=> 'key_1', 1=> 'key_2'); //路由key //创建交换机 $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"; for($i=0; $i<5; ++$i){ echo "Send Message:".$ex->publish($message . date('H:i:s'), $k_route[i%2])."\n"; }由以上代码可以看到,发送消息时,只要有“交换机”就够了。至于交换机后面有没有对应的处理队列,发送方是不用管的。routingkey可以是空的字符串。在示例中,我使用了两个key交替发送消息,是为了下面更便于理解routingkey的作用。
对于交换机,有两个重要的概念:
A,类型。有三种类型: Fanout类型最简单,这种模型忽略routingkey;Direct类型是使用最多的,使用确定的routingkey。这种模型下,接收消息时绑定'key_1'则只接收key_1的消息;最后一种是Topic,这种模式与Direct类似,但是支持通配符进行匹配,比如: 'key_*',就会接受key_1和key_2。Topic貌似美好,但是有可能导致不严谨,所以还是推荐使用Direct。
B,持久化。指定了持久化的交换机,在重新启动时才能重建,否则需要客户端重新声明生成才行。
需要特别明确的概念:交换机的持久化,并不等于消息的持久化。只有在持久化队列中的消息,才能持久化;如果没有队列,消息是没有地方存储的;消息本身在投递时也有一个持久化标志的,PHP中默认投递到持久化交换机就是持久的消息,不用特别指定。
4.queue: 队列
讲了这么多,才讲到队列呀。事实上,队列仅是针对接收方(consumer)的,由接收方根据需求创建的。只有队列创建了,交换机才会将新接受到的消息送到队列中,交换机是不会在队列创建之前的消息放进来的。换句话说,在建立队列之前,发出的所有消息都被丢弃了。下面这个图比RabbitMQ官方的图更清楚——Queue是属于ReceiveMessage的一部分。
接下来看一下创建队列及接收消息的示例:
$e_name = 'e_linvo'; //交换机名 $q_name = 'q_linvo'; //队列名 $k_route = ''; //路由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, $k_route)."\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"; //处理消息 }从上述示例中可以看到,交换机既可以由消息发送端创建,也可以由消息消费者创建。
创建一个队列(line:20)后,需要将队列绑定到交换机上(line:25)队列才能工作,routingkey也是在这里指定的。有的资料上写成bindingkey,其实一回事儿,弄两个名词反倒容易混淆。
消息的处理,是有两种方式:
A,一次性。用 $q->get([...]),不管取到取不到消息都会立即返回,一般情况下使用轮询处理消息队列就要用这种方式;
B,阻塞。用 $q->consum( callback, [...] ) 程序会进入持续侦听状态,每收到一个消息就会调用callback指定的函数一次,直到某个callback函数返回FALSE才结束。
关于callback,这里多说几句: PHP的call_back是支持使用数组的,比如: $c = new MyClass(); $c->counter = 100; $q->consume( array($c,'myfunc') ) 这样就可以调用自己写的处理类。MyClass中myfunc的参数定义,与上例中processMessage一样就行。
在上述示例中,使用的$routingkey = '', 意味着接收全部的消息。我们可以将其改为 $routingkey = 'key_1',可以看到结果中仅有设置routingkey为key_1的内容了。
注意: routingkey = 'key_1' 与 routingkey = 'key_2' 是两个不同的队列。假设: client1 与 client2 都连接到 key_1 的队列上,一个消息被client1处理之后,就不会被client2处理。而 routingkey = '' 是另类,client_all绑定到 '' 上,将消息全都处理后,client1和client2上也就没有消息了。
在程序设计上,需要规划好exchange的名称,以及如何使用key区分开不同类型的标记,在消息产生的地方插入发送消息代码。后端处理,可以针对每一个key启动一个或多个client,以提高消息处理的实时性。如何使用PHP进行多线程的消息处理,将在下一节中讲述。
实战篇
计划任务crontab -uapps -e
* * * * * /home/rabbitmq.sh > /dev/null &
/home目录
#!/bin/bash
step=2 #间隔的秒数,不能大于60
for (( i = 0; i < 60; i=(i+step) )); do
# $(php '/home/fdipzone/php/crontab/tolog.php')
alive=`ps aux|grep \/sync\/recieve\/recieve |grep -v grep|wc -l`
if [ $alive -eq 0 ]
then
php /opt/wwwroot/www.custom_service.cn/public/index.php /sync/recieve/recieve > /dev/null &
php /opt/wwwroot/www.custom_service.cn/public/index.php /sync/recieve/recieve > /dev/null &
fi
sleep $step
done
exit 0
执行脚本php /opt/data/custom_service/public/index.php /sync/recieve/recieve > /dev/null &
php 代码
消费篇<?php
/**
* 队列管理控制器
* @copyright Copyright (c) 2016 http://www.shanxinhui.com All rights reserved.
* @author testbao <249910820@qq.com>
* @version v1.0
*/
namespace app\sync\controller;
use \think\Controller;
class Recieve extends Controller{
//读取rabbitmq
public function recieve(){
//rabbitmq
try {
$rabbit = new \app\common\controller\Rabbit();
$result = $rabbit->run(array(new Recieve,'processMessage'),false);
} catch ( \Exception $e) {
$catch = $e->getMessage();
if($catch && !empty($catch)){
$Rredis = new \app\sync\controller\Rredis();
$Rredis->sync_sms();
}
}
}
function processMessage($envelope, $queue) {
$msg = $envelope->getBody();
if(!empty($msg))
{
//解码后的消息数组
$decode_msg = json_decode($msg,true);
//type 为1表示 客服系统登录 2--默认就是发消息
if(isset($decode_msg['type']) && $decode_msg['type'] == 2 ){
//手机号码
$phone = $decode_msg['phone'];
//替换-隔开的手机号码 如:135-1656-5566
$phone = str_replace('-', '', $phone);
//手机号码验证表达式
$pattern = '/^(13[0-9]\d{8}|15[0-3|5-9]\d{8}|18[0-9]\d{8}|17[0-9]\d{8}|14[57]\d{8})$/';
//短信内容
$content = isset($decode_msg['content']) ? $decode_msg['content'] :'';
$code = isset($decode_msg['code']) ? $decode_msg['code']:'';
$uid = isset($decode_msg['user_id']) ? $decode_msg['user_id'] : 0;
$ip = isset($decode_msg['ip_address']) ? $decode_msg['ip_address']: 0;
$matching_id = isset($decode_msg['matching_id']) ? $decode_msg['matching_id']: 0;
$title = isset($decode_msg['title']) ? $decode_msg['title']: '';
$status = isset($decode_msg['status']) ? $decode_msg['status']: 1;
//验证手机号码是否合法
if( preg_match($pattern,$phone))
{
//发送短信失败,重新加入到短信队列
$rs = sendSms($phone,$content);
}else{
$title = "手机格式错误";
$rs = true;
}
//短信写入数据库
if( isset($rs) && !empty($rs))
{
//手动回复--ack
$envelopeID = $envelope->getDeliveryTag();
/* $pid = posix_getpid(); */
$queue->ack($envelopeID);
//插入客服系统
$data = [
'user_id' => $uid,
'phone' => $phone,
'code' => $code,
'content' => $content,
'title' => $title,
'status' => $status,
'ip_address' => $ip,
'matching_id' => $matching_id,
'create_time' => time(),
'update_time' => time()
];
$table_postfix = date('Y',$data['create_time']) . '_' . ceil(date('m',$data['create_time'])/3);
//取得表名
$table = "sxh_user_sms_{$table_postfix}";
//加入到数据库
\think\Db::table($table)->insert($data);
}
}else{
//手机号码
$phone = $decode_msg['mobile'];
//替换-隔开的手机号码 如:135-1656-5566
$phone = str_replace('-', '', $phone);
//手机号码验证表达式
$pattern = '/^(13[0-9]\d{8}|15[0-3|5-9]\d{8}|18[0-9]\d{8}|17[0-9]\d{8}|14[57]\d{8})$/';
//短信内容
$content = isset($decode_msg['msg']) ? $decode_msg['msg'] :'';
$code = isset($decode_msg['code']) ? $decode_msg['code']:'';
$uid = isset($decode_msg['user_id']) ? $decode_msg['user_id'] : '';
$ip = isset($decode_msg['ip_address']) ? $decode_msg['ip_address']: '';
//验证手机号码是否合法
if( preg_match($pattern,$phone))
{
//发送短信失败,重新加入到短信队列
$rs = sendSms($phone,$content);
}else{
$title = "手机格式错误";
$rs = true;
}
//短信写入数据库
if( isset($rs) && !empty($rs))
{
//手动回复--ack
$envelopeID = $envelope->getDeliveryTag();
/* $pid = posix_getpid(); */
$queue->ack($envelopeID);
//插入客服系统
$data = [
'sys_uid' => $uid,
'mobile' => $phone,
'code' => $code,
'ip' => $ip,
'msg' => $content,
'create_time' => time(),
'update_time' => time()
];
\think\Db::table('sxh_sys_sms')->insert($data);
}
}
}
}
}
生产篇try {
//开启双写 先rabbitmq 在redis 与rabbimq为主'kefu_clinvo','kefu_qlinvo','kefu_lkey'
$rabbit = new \app\common\controller\Rabbit();
$result = $rabbit->send($data,1);
} catch ( \Exception $e) {
$catch = $e->getMessage();
if($catch){
//添加到队列 redis
$redis = new \app\common\controller\SmsRedis();
$result = $redis->dispatch_sms($data,1);
}
}