信道、交换器和路由键、队列概念
参考:https://www.cnblogs.com/zhangxue/p/7699698.html
交换机和交换机类型
交换机是用来发送消息的AMQP实体。交换机拿到一个消息之后将它路由给一个或零个队列。它使用哪种路由算法是由交换机类型和被称作绑定(bindings)的规则所决定的。AMQP 0-9-1的代理提供了四种交换机
先上代码然后在解释
1、封装一个amqp类:
<?php
/**
* User: lvjinliang
* Date: 2019-10-17
* Time: 16:23
*/
namespace help;
class Amqp
{
/**
* @param $exchangeName
* @param $exchangeModel AMQP_EX_TYPE_DIRECT, AMQP_EX_TYPE_FANOUT, AMQP_EX_TYPE_HEADERS or AMQP_EX_TYPE_TOPIC.
* @param $message
* @return bool
* @throws \AMQPChannelException
* @throws \AMQPConnectionException
* @throws \AMQPExchangeException
*/
public function publisher($exchangeName, $exchangeModel, $message, $routeKey=null ) {
$conn_args = array(
'host' => 'rabbitmq',
'port' => '5672',
'login' => 'admin',
'password' => 'admin',
'vhost'=>'my_vhost'
);
//创建连接和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($exchangeName);
$ex->setType($exchangeModel); //direct类型
$ex->setFlags(AMQP_DURABLE); //持久化
$ex->declareExchange();
$status = $ex->publish($message, $routeKey);
return $status;
}
/**
* @param $exchangeName
* @param $exchangeModel
* @param $queuesName
* @param $routeKey
* @param $callback
* @throws \AMQPChannelException
* @throws \AMQPConnectionException
* @throws \AMQPEnvelopeException
* @throws \AMQPExchangeException
* @throws \AMQPQueueException
*/
public function receive ($exchangeName, $exchangeModel, $queuesName, $routeKey, $callback)
{
$conn_args = array(
'host' => 'rabbitmq',
'port' => '5672',
'login' => 'admin',
'password' => 'admin',
'vhost'=>'my_vhost'
);
//创建连接和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($exchangeName);
$ex->setType($exchangeModel); //direct类型
$ex->setFlags(AMQP_DURABLE); //持久化
$ex->declareExchange();
//创建队列
$q = new \AMQPQueue($channel);
$q->setName($queuesName);
$q->setFlags(AMQP_DURABLE); //持久化
$q->declareQueue();
$q->bind($exchangeName, $routeKey);
$q->consume($callback, AMQP_AUTOACK); //自动ACK应答
$conn->disconnect();
}
}
2、一个生产者类:
<?php
namespace application\index\controller;
use help\Amqp;
class Publisher
{
public function p_direct()
{
$Amqp = new Amqp();
$message = date('Y-m-d H:i:s')." this is test direct; \r\n";
try {
$status = $Amqp->publisher('direct_e', AMQP_EX_TYPE_DIRECT, $message, 'key_1' );
if ($status) {
echo '发送成功';
}
} catch (\Exception $e) {
echo $e->getMessage();
}
}
public function p_fanout()
{
$Amqp = new Amqp();
$message = date('Y-m-d H:i:s')." this is test fanout; \r\n";
try {
$status = $Amqp->publisher('fanout_e', AMQP_EX_TYPE_FANOUT, $message, 'key_1' );
if ($status) {
echo '发送成功';
}
} catch (\Exception $e) {
echo $e->getMessage();
}
}
public function p_topic()
{
$Amqp = new Amqp();
try {
$message = date('Y-m-d H:i:s')." this is test topic color red; \r\n";
$status = $Amqp->publisher('topic_e', AMQP_EX_TYPE_TOPIC, $message, 'red.color' );
if ($status) {
echo '发送成功';
}
//该消息顺序不对,已定两个消费者。收不到信息
$message = date('Y-m-d H:i:s')." this is test topic color blue; \r\n";
$status = $Amqp->publisher('topic_e', AMQP_EX_TYPE_TOPIC, $message, 'blue.color.aaa' );
if ($status) {
echo '发送成功';
}
$message = date('Y-m-d H:i:s')." this is test topic animal; \r\n";
$status = $Amqp->publisher('topic_e', AMQP_EX_TYPE_TOPIC, $message, 'dog.animal' );
if ($status) {
echo '发送成功';
}
} catch (\Exception $e) {
echo $e->getMessage();
}
}
}
3、一个消息者类:
<?php
namespace application\index\controller;
use help\Amqp;
class Consumer
{
public function c_direct()
{
$Amqp = new Amqp();
try {
$Amqp->receive('direct_e', AMQP_EX_TYPE_DIRECT, 'direct_q', 'key_1', [$this, 'processMessage']);
} catch (\Exception $e) {
echo $e->getMessage();
}
}
public function c_fanout1()
{
$Amqp = new Amqp();
try {
$Amqp->receive('fanout_e', AMQP_EX_TYPE_FANOUT, 'fanout_q1', 'key_1', [$this, 'processMessage']);
} catch (\Exception $e) {
echo $e->getMessage();
}
}
public function c_fanout2()
{
$Amqp = new Amqp();
try {
$Amqp->receive('fanout_e', AMQP_EX_TYPE_FANOUT, 'fanout_q2', 'key_1', [$this, 'processMessage']);
} catch (\Exception $e) {
echo $e->getMessage();
}
}
public function c_topic1()
{
$Amqp = new Amqp();
try {
// 为队列绑定主题词, *: 匹配0或多个任意主题词, #: 1个或多个任意主题词. 绑定主题词时, 主题词出现的位置也要匹配
$Amqp->receive('topic_e', AMQP_EX_TYPE_TOPIC, 'topic_q1', '#.animal', [$this, 'processMessage']);
} catch (\Exception $e) {
echo $e->getMessage();
}
}
public function c_topic2()
{
$Amqp = new Amqp();
try {
// 为队列绑定主题词, *: 匹配0或多个任意主题词, #: 1个或多个任意主题词. 绑定主题词时, 主题词出现的位置也要匹配
$Amqp->receive('topic_e', AMQP_EX_TYPE_TOPIC, 'topic_q2', '#.color', [$this, 'processMessage']);
} catch (\Exception $e) {
echo $e->getMessage();
}
}
/**
* 消费回调函数
* 处理消息
*/
public function processMessage($envelope, $queue) {
//var_dump($envelope);
//var_dump($queue);
$msg = $envelope->getBody();
echo $msg."\n"; //处理消息
}
}
直连交换机
直连型交换机(direct exchange)是根据消息携带的路由键(routing key)将消息投递给对应队列的。直连交换机用来处理消息的单播路由(unicast routing)(尽管它也可以处理多播路由)。下边介绍它是如何工作的:
将一个队列绑定到某个交换机上,同时赋予该绑定一个路由键(routing key)
当一个携带着路由键为R的消息被发送给直连交换机时,交换机会把它路由给绑定值同样为R的队列。
直连型交换机图例:
行运两个c_direct消费者:
第一次运行生产者:
第二次运行生产者:
直连交换机经常用来循环分发任务给多个工作者(workers)。由实验可见,在AMQP 0-9-1中,消息的负载均衡是发生在消费者(consumer)之间的,保证只有一个消费者能获取消息。
扇型交换机
扇型交换机(funout exchange)将消息路由给绑定到它身上的所有队列,而不理会绑定的路由键。如果N个队列绑定到某个扇型交换机上,当有消息发送给此扇型交换机时,交换机会将消息的拷贝分别发送给这所有的N个队列。扇型用来交换机处理消息的广播路由(broadcast routing)。
因为扇型交换机投递消息的拷贝到所有绑定到它的队列,所以他的应用案例都极其相似:
-
大规模多用户在线(MMO)游戏可以使用它来处理排行榜更新等全局事件
-
体育新闻网站可以用它来近乎实时地将比分更新分发给移动客户端
-
分发系统使用它来广播各种状态和配置更新
-
在群聊的时候,它被用来分发消息给参与群聊的用户。(AMQP没有内置presence的概念,因此XMPP可能会是个更好的选择)
扇型交换机图例:
运行两个fanout消费者,两个队列同时绑定一个路由
两个对列同时收到消息
主题交换机
主题交换机(topic exchanges)通过对消息的路由键和队列到交换机的绑定模式之间的匹配,将消息路由给一个或多个队列。主题交换机经常用来实现各种分发/订阅模式及其变种。主题交换机通常用来实现消息的多播路由(multicast routing)。
主题交换机拥有非常广泛的用户案例。无论何时,当一个问题涉及到那些想要有针对性的选择需要接收消息的多消费者/多应用(multiple consumers/applications) 的时候,主题交换机都可以被列入考虑范围。
使用案例:
-
分发有关于特定地理位置的数据,例如销售点
-
由多个工作者(workers)完成的后台任务,每个工作者负责处理某些特定的任务
-
股票价格更新(以及其他类型的金融数据更新)
-
涉及到分类或者标签的新闻更新(例如,针对特定的运动项目或者队伍)
-
云端的不同种类服务的协调
-
分布式架构/基于系统的软件封装,其中每个构建者仅能处理一个特定的架构或者系统。
此模式下交换机,在推送消息时, 会根据消息的主题词和队列的主题词决定将消息推送到哪个队列. 交换机只会为 Queue 分发符合其指定的主题的消息。
-
向交换机发送消息时,消息的routing key 就是主题关键词,主题词不能随意设置,必须由 "." 联结多个主题词 (如:log.error、log.warn) .
-
必须将队列显示的绑定到指定的交换机上.
-
为队列指定队列主题词时,可以使用通配符: "#": 表示 0 或多个主题词; "*": 表示 1 个主题词,且顺序也匹配。
简单的讲topic可以用匹配,fanout不能只是广播
头交换机
有时消息的路由操作会涉及到多个属性,此时使用消息头就比用路由键更容易表达,头交换机(headers exchange)就是为此而生的。头交换机使用多个消息属性来代替路由键建立路由规则。通过判断消息头的值能否与指定的绑定相匹配来确立路由规则。
我们可以绑定一个队列到头交换机上,并给他们之间的绑定使用多个用于匹配的头(header)。这个案例中,消息代理得从应用开发者那儿取到更多一段信息,换句话说,它需要考虑某条消息(message)是需要部分匹配还是全部匹配。上边说的“更多一段消息”就是"x-match"参数。当"x-match"设置为“any”时,消息头的任意一个值被匹配就可以满足条件,而当"x-match"设置为“all”的时候,就需要消息头的所有值都匹配成功。
头交换机可以视为直连交换机的另一种表现形式。头交换机能够像直连交换机一样工作,不同之处在于头交换机的路由规则是建立在头属性值之上,而不是路由键。路由键必须是一个字符串,而头属性值则没有这个约束,它们甚至可以是整数或者哈希值(字典)等。
这个模式我自己也没理解,你们自己试试吧,好像用的不多。读者有懂的可留言告诉我。
关注快乐程序员公众号,每日分享一点小知识。爱编程,爱生活!