RabbitMQ的安装,配置,监控 ,集群安装 心得

操作者  :包竹滨  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


  
  
  1. wget http://www.erlang.org/download/otp_src_17.1.tar.gz

  2. http://www.erlang.org/download.html  两种下载路径

tar zxf otp_src_17.1.tar.gz cd otp_src_17.1 ./configure

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


   
   
  1. wget https://pypi.python.org/packages/source/s/simplejson/simplejson-3.5.3.tar.gz#md5=d5f62dfa6b6dea31735d56c858361d48

  2. 进入http://www.rabbitmq.com/download.html选择最新的源码包   两种下载路径

tar zxf simplejson-3.5.3.tar.gz

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 




  1. [root@ptr165 rabbitmq_server-3.2.0]# ./rabbitmq-server   

  2.   

  3.               RabbitMQ 3.2.0. Copyright (C) 2007-2013 GoPivotal, Inc.  

  4.   ##  ##      Licensed under the MPL.  See http://www.rabbitmq.com/  

  5.   ##  ##  

  6.   ##########  Logs: sbin/../var/log/rabbitmq/rabbit@ptr165.log  

  7.   ######  ##        sbin/../var/log/rabbitmq/rabbit@ptr165-sasl.log  

  8.   ##########  

  9.               Starting broker... completed with 0 plugins.  

如果出现这样的输出说明rabbitmq安装成功


(三)RabbitMQ的基本管理

(1)控制台启动方式:./rabbitmq-server

通过控制台方式启动,可以CTRL-C命令关掉rabbitmq

(2)后台启动方式:sbin/rabbitmq-server -detached

可以通过rabbitmqctl stop命令关掉rabbitmq


  1. [root@ptr165 rabbitmq_server-3.2.0]# sbin/rabbitmqctl stop  

  2. Stopping and halting node rabbit@ptr165 ...  

  3. ...done.  

(3)指定rabbitmq节点的名字启动


默认情况下,rabbitmq节点的名字是rabbit@HOSTNAME

  1. rabbit@ptr165  

默认端口是5672


当我们运行rabbitmqctl stop命令时,由于没有指定节点名称,默认指向rabbit@HOSTNAME

我们可以通过指定不同的名字和端口


  1. RABBITMQ_NODE_PORT=5673 RABBITMQ_NODENAME=rabbit1 sbin/rabbitmq-server -detached  

此时运行rabbitmqctl命令时,要指定节点名称


  1. sbin/rabbitmqctl -n rabbit1@ptr164 status  

  2. Status of node rabbit1@ptr164 ...  

关闭rabbitmq



  1. sbin/rabbitmqctl -n rabbit1@ptr164 stop  

  2. Stopping and halting node rabbit1@ptr164 ...  

  3. ...done.  

一台单机上可以运行多个node节点。


(4)启动rabbitmq的管理插件

我们前面启动的rabbitmq没有启动任何插件



  1. 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


  1. ./rabbitmq-server   

  2.   

  3.               RabbitMQ 3.2.0. Copyright (C) 2007-2013 GoPivotal, Inc.  

  4.   ##  ##      Licensed under the MPL.  See http://www.rabbitmq.com/  

  5.   ##  ##  

  6.   ##########  Logs: sbin/../var/log/rabbitmq/rabbit@ptr164.log  

  7.   ######  ##        sbin/../var/log/rabbitmq/rabbit@ptr164-sasl.log  

  8.   ##########  

  9.               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



1.必需掌握的指令

首先创建

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 add_vhost dnt_mq

//注:删除虚拟主机 rabbitmqctl delete_vhost vhostpath

用下面指定就可以显示出所有虚拟主机信息: 

rabbitmqctl list_vhosts


添加用户:

rabbitmqctl add_user admin 123456

删除测试用户:

rabbitmqctl delete_user admin


//注:修改用户密码:rabbitmqctl change_password username newpassword 

添加权限:绑定用户权限:

./rabbitmqctl set_permissions -p / admin ".*" ".*" ".*"

列出用户权限:

rabbitmqctl list_user_permissions daizhj

//注:清除用户权限 rabbitmqctl clear_permissions [-p vhostpath] username      Listing permissions for user "daizhj" ...      dnt_mq  .*      .*      .*      client 


设置用户角色
rabbitmqctl  set_user_tags  User  Tag
Tag为角色名administrator,monitoring,policymaker,management或其他自定义名称

可给同一用户设置多个角色,如:
rabbitmqctl set_user_tags testmq monitoring policymaker administrator


rabbitmq-server 可带参数 -detached(后台运行) 这样server即启动 再执行 rabbitmqctl start_app 启动应用 这样就可往rabbitmq 发送消息 之后可以用 rabbitmqctl list_queues 查看队列信息 


 用下面指定就可以显示出所有虚拟主机信息:

所有指令列表(很简单的英文):

add_user        <UserName> <Password>

delete_user     <UserName>

change_password <UserName> <NewPassword>

list_users

add_vhost    <VHostPath>

delete_vhost <VHostPath>

list_vhosts

set_permissions   [-p <VHostPath>] <UserName> <Regexp> <Regexp> <Regexp>

clear_permissions [-p <VHostPath>] <UserName>

list_permissions  [-p <VHostPath>]

list_user_permissions <UserName>

list_queues    [-p <VHostPath>] [<QueueInfoItem> ...]

list_exchanges [-p <VHostPath>] [<ExchangeInfoItem> ...]

list_bindings  [-p <VHostPath>] 

list_connections [<ConnectionInfoItem> ...]

       2.vhost  / 不能删除

 

      删除/以后,新建立的vhost不能正常使用(即便不删除/,新建立的vhost也是不能正常使用).不知道为什么,有待研究.

 



cd /usr/local/rabbitmq/sbin/


1        ./rabbitmqctl delete_user  guest
2./rabbitmqctl add_user admin 123456
3./rabbitmqctl set_user_tags admin administrator

当然如果你执意要使用guest进行远程访问,建议修改密码,如

1rabbitmqctl change_password guest 123456

然后在配置文件/etc/rabbitmq/rabbitmq.config中(可能不存在,新建即可使用)添加:

1[{rabbit, [{loopback_users, []}]}].

然后重启rabbitmq-service服务

1/usr/local/rabbitmq/sbin/rabbitmq-server start

这个时候在浏览器输入http://{server_name}:15672/进行访问,使用刚刚设置的账号登陆就可以进行管理了



(四)RabbitMQ集群部署

我们测试用的两台机器分别为192.168.1.164和192.168.1.165。

(1)配置hostname

在两台机器的host分别配置vi /etc/hosts


  1. 122.11.45.165   ptr165  

  2. 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


  1. [root@ptr165 rabbitmq_server-3.2.0]# sbin/rabbitmqctl cluster_status  

  2. Cluster status of node rabbit@ptr165 ...  

  3. [{nodes,[{disc,[rabbit@ptr164]},{ram,[rabbit@ptr165]}]},  

  4.  {running_nodes,[rabbit@ptr164,rabbit@ptr165]},  

  5.  {partitions,[]}]  

  6. ...done.  


  1. [root@ptr164 rabbitmq_server-3.2.0]# sbin/rabbitmqctl cluster_status  

  2. Cluster status of node rabbit@ptr164 ...  

  3. [{nodes,[{disc,[rabbit@ptr164]},{ram,[rabbit@ptr165]}]},  

  4.  {running_nodes,[rabbit@ptr165,rabbit@ptr164]},  

  5.  {partitions,[]}]  

  6. ...done.  

两个节点得到相同的输出,其中rabbit@ptr164节点形式是disc,rabbit@ptr165节点形式是ram。这和执行join_cluster命令是的选项--ram是一致的。


(5)改变节点形式


  1. [root@ptr165 rabbitmq_server-3.2.0]# ./rabbitmqctl stop_app  

  2. Stopping node rabbit@ptr165 ...  

  3. ...done.  

  4. [root@ptr165 rabbitmq_server-3.2.0]# ./rabbitmqctl change_cluster_node_type disc  

  5. Turning rabbit@ptr165 into a disc node ...  

  6. ...done.  

  7. [root@ptr165 rabbitmq_server-3.2.0]# ./rabbitmqctl start_app  

  8. Starting node rabbit@ptr165 ...  

  9. ...done.  


我们将rabbit@ptr165的节点改为disc



  1. [root@ptr164 rabbitmq_server-3.2.0]# sbin/rabbitmqctl cluster_status  

  2. Cluster status of node rabbit@ptr164 ...  

  3. [{nodes,[{disc,[rabbit@ptr164,rabbit@ptr165]}]},  

  4.  {running_nodes,[rabbit@ptr165,rabbit@ptr164]},  

  5.  {partitions,[]}]  

  6. ...done.  


两个节点都是disc的形式

(6)从cluster中分离


  1. [root@ptr165 rabbitmq_server-3.2.0]# ./rabbitmqctl stop_app  

  2. Stopping node rabbit@ptr165 ...  

  3. ...done.  

  4. [root@ptr165 rabbitmq_server-3.2.0]# ./rabbitmqctl reset  

  5. Resetting node rabbit@ptr165 ...  

  6. ...done.  

  7. [root@ptr165 rabbitmq_server-3.2.0]# ./rabbitmqctl start_app  

  8. Starting node rabbit@ptr165 ...  


通过查看集群状态发现,两个节点都成了孤立的节点


  1. Cluster status of node rabbit@ptr165 ...  

  2. [{nodes,[{disc,[rabbit@ptr165]}]},  

  3.  {running_nodes,[rabbit@ptr165]},  

  4.  {partitions,[]}]  

  5. ...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);
        }
         }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值