RabbitMQ发布订阅

发布订阅
    这先前的教程,我们创建了一个工作队列。工作队列背后的设想是每一个任务都传送给确定的一个工作者。在这一部分,我们
将做一些完全不一样的事情--我们传送消息给多个消费者。这种普遍周知的发布订阅模式。
    为了解释这种模式,我们将创建一个简单的日志系统,由两个程序构成--第一个将发送日志消息,第二个将接收和打印这个消息。
    在我们的日志系统每一个运行赋值的接收程序都将获取到消息。这样我们就可以运行一个接收器并将日志指向磁盘,同时我们
能够运行另外的接收程序将日志显示在屏幕上。
    实质上,发布的日志消息将会广播到所有的接收者。

 

交换机

    在先前部分的教程,我们从一个队列中发送和接收消息,现在是时候介绍RabbitMQ完全的消息模式。让我们迅速回顾下先前部分教程学到的知识。

    生产者是用来发送消息的用户程序。

    队列是存储消息的缓存。
    消费者是用来接收消息的用户程序。
    RabbitMQ中的消息模型的核心思想是生产者从来都不会直接发送消息给队列,事实上,很多时候生产者设置不知道一条消息是否将会传送给队列。
    相反,生产者只能将消息传送给交换机,交换机是非常简单的东西,一方面它从生产者接收消息,另一方面它将消息推送给队列。交换机一定知道怎么处理接收的消息。消息是否应该加到要给特定的队列?是否应该加到许多队列?或者
是否应该丢弃。这些规则通过交换机的类型进行定义。


    有许多种交换机类型可以获取:direct,topic,headers和fanout。我们将关注最后
一个--fanout。我们创建一个这种类型的交换机,叫做logs

channel.exchangeDeclare("logs", "fanout");

    fanout交换机非常简单,正如你可以大概从它的名字猜出来,仅仅是将消息广播到它所有知道的队列哪里。这正是我们日志系统所需要的。

 

列举交换机

    你可以运行rabbitmqctl例举所有的交换机。

sudo rabbitmqctl list_exchanges

    在这个列表中,你有许多amq.*交换机和默认没有名字的交换机,这些是默认创建的,这个时候你不太可能需要用到它们。

没有名字的交换机。
    在先前部分的教程中,我们不知道交换机,但是依然可以发送消息给队列,那是因为我们使用默认的交换机,我们定义为空字符串“”
    回忆之前我们怎么样发布消息

channel.basicPublish("", "hello", null, message.getBytes());

    第一个参数是交换机的名字,空字符串表示默认没有名字的交换机,消息通过特定的名字被路由到队列,如果存在的话。
    现在我们可以发布我们命名的交换机

channel.basicPublish( "logs", "", null, message.getBytes());

临时队列
    你可能记得先前我们使用特定名字的队列(记得hello和task_queue吗?)能够命名一个队列对我们来说很重要--我们需要将工作者指向相同的队列当你需要在生长者和消费者中分享要给队列,给队列一个名字很重要。
    但这不是我们日志的情形,我们想要听到所有的日志消息,并不是仅仅是一个子集。我们也仅仅对当前流动的消息感兴趣,而不是以前的消息。为了解决这个问题,我们需要两个东西。
    第一,连接RabbitMQ的时候,我们需要要给全新的,空的队列,为了做这个事情,我们创建要给随机名字的队列,或者,更好让服务器选择要给随机的队列名。
    第二,一旦我们断开消费者,队列应该被自动删除。
    在java客户端,当我们不知道参数queueDeclare(),我们将会创建一个非持久化独立的,自动生成名字和删除的队列。

String queueName = channel.queueDeclare().getQueue();

    从 guide on queues中,你可以学习更多exclusive标记和其他队列属性。
    在这种情况下,queueName包含一个随机的队列名字,例如长成这样子:amq.gen-JzTY20BRgKO-HjmUJj0wLg.
 

绑定

    我们已经创建了一个fanout交换机和队列,现在我们需要告诉交换机发送消息给我们的队列,这个关于交换机和队列的关系叫做绑定。

channel.queueBind(queueName, "logs", "")

    现在开始,我们的日志交换机将会发送消息给我们队列。
例举绑定
    你猜对了(猜你妹),你可以例举存在的绑定。

rabbitmqctl list_bindings

将它们都放在一起

    生产者程序,用于发送日志消息,和之前的并没有什么不同。最重要的改变是我们现在将消息发送欸logs交换机而不是没有名字的交换机。发送的时候,我们需要指定指定一个routingkey,但是它的值将会被fanout交换机忽略,下面是EmitLog.java程序
 

import java.io.IOException;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Channel;

public class EmitLog {

    private static final String EXCHANGE_NAME = "logs";

    public static void main(String[] argv)
                  throws java.io.IOException {

        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();

        channel.exchangeDeclare(EXCHANGE_NAME, "fanout");

        String message = getMessage(argv);

        channel.basicPublish(EXCHANGE_NAME, "", null, message.getBytes());
        System.out.println(" [x] Sent '" + message + "'");

        channel.close();
        connection.close();
    }
}

    正如你看到的,在创建连接之后我们声明了交换机。这一步是必须的,因为发布给要给不存在的交换机是禁止的。
    如果没有队列绑定到交换机上,我们的消息将会丢失,然后这对我们来说是没有问题的,如果还没有消费者监听,我们可以放心地丢掉消息。
ReceiveLogs.java的代码

import com.rabbitmq.client.*;

import java.io.IOException;

public class ReceiveLogs {
  private static final String EXCHANGE_NAME = "logs";

  public static void main(String[] argv) throws Exception {
    ConnectionFactory factory = new ConnectionFactory();
    factory.setHost("localhost");
    Connection connection = factory.newConnection();
    Channel channel = connection.createChannel();

    channel.exchangeDeclare(EXCHANGE_NAME, "fanout");
    String queueName = channel.queueDeclare().getQueue();
    channel.queueBind(queueName, EXCHANGE_NAME, "");

    System.out.println(" [*] Waiting for messages. To exit press CTRL+C");

    Consumer consumer = new DefaultConsumer(channel) {
      @Override
      public void handleDelivery(String consumerTag, Envelope envelope,
                                 AMQP.BasicProperties properties, byte[] body) throws IOException {
        String message = new String(body, "UTF-8");
        System.out.println(" [x] Received '" + message + "'");
      }
    };
    channel.basicConsume(queueName, true, consumer);
  }
}

    像我们之前一样编译代码
 

javac -cp $CP EmitLog.java ReceiveLogs.java

    如果你想要将日志保存到一个文件,你可以打开控制台,敲:

java -cp $CP ReceiveLogs > logs_from_rabbit.log

    如果你希望在你的屏幕上看到日志,新打开一个终端运行:

java -cp $CP ReceiveLogs

    当然,发送日志,敲:

java -cp $CP EmitLog

    使用rabbitmqctl list_bindings,你可以验证,代码确实产生了我们需要的绑定和队列。运行两个ReceiveLogs.java程序,你
应该看到:

sudo rabbitmqctl list_bindings
# => Listing bindings ...
# => logs    exchange        amq.gen-JzTY20BRgKO-HjmUJj0wLg  queue           []
# => logs    exchange        amq.gen-vso0PVvyiRIL2WoV3i48Yg  queue           []
# => ...done.

    结果的解析很简单,来自交换机logs的消息发送给两个服务器指定名字的队列。这正是我们想要的。
    移动到教程4学习如何监听消息的一个子集。

相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页