参考https://www.cnblogs.com/zxxyx/p/6202306.html
rabbittmq简介
一、常见的几种消息队列的比较
实际上,这个地方摘抄的,也不知道啥意思,就集中写在这里吧,万一哪天看懂了呢
https://blog.csdn.net/qq_35688140/article/details/102903589
1,activemq
- 具有优秀的api,是sun公司提出的jms规范的一种消息队列
- 不适合高并发场合,性能不足,适合中小型架构;
两种模式:master-slave模式 和 network模式
2,kafka
-
速度高:使用操作系统为memory mapper files方式,完成操作系统虚拟内存的作用,从而实现到内存地址的映射
3,rocketmq
- 对kafka的事务性和可靠性传输做了优化
- 商业版收费
nameserver就是相当于zookeeper,由于zookeeper太慢了,所以使用name server,几百行代码实现了name server。
4,rabbitmq
- 速度慢于kafka,快于activemq
- 良好的事务性
架构
-------------------------------------------------------------------------------------------
二、rabbitmq常见的五种模式
摘抄 https://blog.csdn.net/m0_37968982/article/details/103985291
按照投递规则和消费方式的不同,主要有5种常见的模式。
1,简单模式:simple/helloworld单生产单消费
投递规则:队列名称和routing key相同
原理:
生产者将消息交给默认的交换机,交换机获取消息后交给绑定这个生产者的队列,监听当前队列的消费者获取信息并执行消费逻辑。
场景:
有一个oa系统,用户接收手机验证码进行注册,页面上点击获取验证码后,将验证码放到消息队列,然后短信服务从队列中获取验证码,并发送给用户。
2,工作模式:work单发送多接收
原理:
生产者将消息交给交换机,交换机交给绑定的队列,队列有多个消费者监听,一条消息只能由一个消费者消费,这样就形成了资源竞争,谁的资源空闲大,争抢到的可能性就大。
场景:
有一个电商平台,有两个订单服务,用户下单的时候,任意一个订单服务消费用户的下单请求生成订单即可。不用两个订单服务同时消费用户的下单请求。
备注:保证资源竞争的代码为:channel.basicQos(1),告诉服务器,在消费者没有确认当前消息时,不要再发送新的消息。如果没有这行代码,消费者是轮询消费消息的。
3,发布订阅模式:public/subsribe
原理:
生产者将消息扔给交换机,交换机类型为fanout,不同的队列注册到交换机上,不同的消费注册在不同的队列上。所有消费者都会收到消息。
场景:
商城,添加一个商品后,可能同时需要去更新缓存和数据库。
注意:交换机没有保存消息的功能,如果没有消费者,则消息对丢失。
问题:为何这句话写这里呢?前几个不也存在这个问题么?
4,路由模式:routing
原理:
生产者将消息发送给交换机,消息携带具体的routingkey。交换机类型是direct,接收到消息中的routingkey,比对与之绑定的队列的routingkey,分发到不同的队列上。
场景:
商城中,新添加一个商品,实时性不是很高,值需要将其添加到数据库即可,不用刷新缓存。
5,主题(通配符)模式:topics,按topic发送接收
原理:
路由模式的一种,路由功能添加了模糊匹配。
星号*代表一个单词,# 代表一个或多个单词。
场景:
商城中,新添加一个产品,实时性不是很高,只需要添加到数据库即可,数据库包含了主库和从库,不用刷新缓存。
------------------------------------------------------实操环节到了-----------------------------------------------------------
rabbitmq+php
一、ubuntu下rabbitmq安装:
1,安装erlang
由于rabbitmq需要erlang语言的支持,所以安装rabbitmq之前先安装erlang
sudo apt-install erlang-nox
2,安装rabbitmq
#更新源
sudo apt-get update
#安装
sudo apt-get install rabbitmq-server
启动、停止、重启和状态rabbitmq命令
sudo rabbitmq-server start
sudo rabbitmq-server stop
sudo rabbitmq-server restart
sudo rabbitmqctl status
3,添加admin并赋予administrator权限
添加admin用户,密码设置为admin
#添加admin用户,密码设置为admin
sudo rabbitmqctl add_user admin admin
#赋予权限
sudo rabbitmqctl set_user_tags admin administrator
#赋予virtual host中所有资源的配置、写读权限以便管理其中的资源--不太懂,抄录一遍
sudo rabbitmqctl set permissions -p /admin.'*'.'*'.'*'
4,rabbitmq guid使用
1)官方提供的一个web管理工具(rabbitmq_management)
https://www.rabbitmq.com/management.html
2)安装了rabbitmq之后,默认页安装了该管理工具,执行命令即可启动
sudo rabbitmq-plugins enable rabbitmq_management
二、rabbitmq+php
上面的安装,其实我整完都乱七八糟的,运行自己的代码老报错误,原因就是扩展没安装成功,我也不知道咋回事。
咨询同事,他也是各种失败,最后找我们公司专门负责我们开发环境的老师安装的,他给我发了一份文件,一键式即可安装。
如下:
1)一份压缩包,拷贝到自己虚拟机上。
这里没法上传,需要的人可以联系我,等我上传到网盘上,再补充链接,那边有点问题,暂时上传不上去。
2)依次执行如下命令即可:
tar zxvf amqp-package.tar.gz
cd amqp-package
./install.sh
注意:最后的这个shell脚本也会上传到网盘上,之后补充。
内容就是下载,安装编译配置啥的。内容先粘贴出来:
apt-get update
apt-get install -y cmake
tar zxvf amqp-1.9.3.tgz
unzip rabbitmq-c-master.zip
cd rabbitmq-c-master
mkdir build && cd build
cmake ..
cmake --build .
make
make install
cd ../../
cd amqp-1.9.3
phpize
./configure --with-amqp
make
make install
echo "extension=amqp.so" > /etc/php5/mods-available/amqp.ini
php5enmod amqp
cd ..
rm -r amqp-1.9.3
rm -r rabbitmq-c-master
php -m|grep amqp
-----------------------------------------------------------------------------------------------------------------------------------
rabbitmq是一个流行的开源消息队列系统,用erlang语言开发,完整的实现了AMQP(高级消息队列协议)。
rabbitmq启动默认是没有任何配置的,需要客户端连接上去,设置交换机等才能工作。
step1: vhosts:虚拟主机
一个rabbitmq server为一个消息队列服务器实体(broker),可以有多个用户,而用户只能在虚拟主机的粒度进行权限控制,所以,rebbitmq需要有多个虚拟机主机,每个rabbitmq服务器都有个默认的虚拟机主机'/'。对于一般的php应用,不需要用户权限设定,直接使用默认的 '/'即可,用户可以使用默认存在的'guest',例如:
$configs = array(
'host'=>'127.0.0.1',
'port'=>'5672',
'login'=>'guest',
'password'=>'guest',
'vhost' =>'/'
);
step2: connection与channel:连接与通信
connection是指物理的连接,一个client与一个server之间有一个连接,一个连接上可以建立多个channel,可以理解为逻辑上的连接。一般情况下,有一个channel就够了,不需要创建更多的channel.
//创建连接和channel
$conn = new AMQPConnection($configs);
if(!$conn->connect()){
die('cannot connect to the broker\n');
}
$channel = new AMQPChannel($configs);
step3: exchange与routingkey:交换机与路由键
为了将不同类型的消息进行区分,设置了交换机与路由两个概念。比如将A类型的消息发送到名为C1的交换机,将B类型的消息发送到名为C2的交换机。当客户端连接C1处理队列消息时,取到的就是只有A类型的消息。
进一步,如果A类型的消息也非常多,需要进一步细化区分,比如某个客户端只处理A类型消息中针对K用户的消息,routingkey就是用来做这个用途的。
//交换机名
$e_name= 'e_linvo';
//路由key
$k_route = array(0=>'key_1',1=>'key_2');
//创建交换机
$ex = new AMQPExchange($channel);
$ex->setName($e_name);
$ex->setType(AMQP_EX_TYPE_DIRECT);//direct类型
$ex->setFlags(AMQP_DURABLE);//持久化
$message = 'hello world';
echo 'Exchange Status'.$ex->declare().'\n';
for($i=0;$i<5;$i++){
echo 'send message: '.$ex->publish($message,$k_route[$i%2]).'\n';
}
由以上代码可以看到,发送消息的时候,只要有交换机就足够了至于交换机后main有没有对应的处理队列,发送方是不用管的。routingkey可以是空的字符串。在示例中,我们使用了两个key交替发送消息,是为了更便于理解routingkey的作用。
对于交换机,有两个重要的给概念:
A . 类型
有三种类型:
- fanout类型最简单,这种模型忽略routingkey,只看exchange。
- direct类型是使用最多的,使用确定的exchange和routingkey。在这种模型下,接收消息时绑定key_1,则只接收key_1的消息。
- topic,与direct类似,但是支持通配符进行匹配。比如,key_*就会接收key_1和key_2,虽然看着美好但是可能导致不严谨,还是推荐使用direct。
比如,交换机类型使用常见的direct类型,则只要消息队列的exchange与routekey同发送方的exchange和routekey一样,就能接收到消息,不用管消息队列名称两边是否一样。
具体可见链接:
http://blog.sina.com.cn/s/blog_8ff955df0102yyww.html
B. 持久化
指定了持久化的交换机,在重新启动时才能重建,否则需要客户端重新声明生成才行。
需要特别明确的概念:交换机的持久化,并不等于消息的持久化。只有在持久化队列中的消息,才能持久化;如果没有队列,消息是没有地方存储的;消息本身在投递时也有一个持久化标志的,php中默认投递到持久化交换机就是持久的消息,不用特别指定。
step4:queue:队列
事实上,队列仅是针对接收方(consumer)的,由接收方根据需求创建的。只有队列创建了,交换机才会将新接收到的消息送到队列中,交换机是不会将在队列创建之前的消息放进来的。也就是,在建立队列之前,发出的所有消息都被丢弃了。
疑问:如果发布东西,需要重新启动队列,在启动过程中,会重新创建队列,即使名字一样,但是这个中间差的消息被丢弃了?????
注意,队列属于receive mesage部分。
下面代码为创建队列和接收消息的实例。
//
$e_name = 'e_linvo';//交换机名
$q_name = 'q_linvo';//队列名
$k_route = '';//路由key
//创建链接和channel
$conn = new AMQPConnection($configs);
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);
$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 processMesage($envelope,$queue){
var_dump($envelope->getRoutingKey);//这个函数不清楚对不对
$msg = $envelope->getBody();
echo $msg.'\n'; //处理的消息内容
}
从示例可以看到,交换机既可以由消息发送端创建,也可以由消息消费者创建。
创建一个队列后,需要将队列绑定到交换机上,队列才能工作。routingkey也是在这里指定的。
消息的处理是有两种方式:
A.一次性
用$q->get([......]),不管取到取不到消息都会立即返回,一般情况下使用轮询处理消息队列就要用这种方式;
B.阻塞
用$q->consume(callback, [.....])程序会进入持续侦听状态,每收到一个消息就会调用callback指定的函数一次,直到某个callback函数返回false才结束。
关于callback,php支持数组,比如 $c = new MyClass(); $c->counter = 100; $q->consume( array($c,'myfunc') ),这样就可以调用自己写的处理类,其中myfunc的参数与例子中的processMessage一样就行。
注意,在上面的实例中,$k_route为空,意味接收全部的消息。我们可以改为 $k_route='key_1',则结果中仅有设置routingkey为key_1的内容。
在程序设计上,需要规划好exchange的名称,以及如何使用key区分开不同类型的标记,在消息产生的地方插入发送消息代码。
后端处理,可以针对每一个key启动一个或者多个client,以提高消息处理的实时性。