RabbitMQ学习之三: 发布/订阅(广播方式fanout)

参考http://blog.csdn.net/lmj623565791/article/details/37620057和RabbitMQ官网,加之自己部分修改和实验,因是新手自学,难免有不对之处,欢迎大家指正,小弟先谢过了.

在上一篇文章中,我们学习了关于RabbitMQ工作队列,了解其分发消息的一些方式和消息反馈机制等等,并且假定一个任务只给一个消费者,但是在实际工作中”一对一”模式显然是满足不了需求的,在这篇文章里,我们将来实验一些尽量和实际相关度较高的例子——将一个任务发给所有消费者,然后消费者决定如何处理这些消息.

现在开始今天的第一个概念: exchange (转发器/路由器)
先摘录一段官网的话
The core idea in the messaging model in RabbitMQ is that the producer never sends any messages directly to a queue. Actually, quite often the producer doesn’t even know if a message will be delivered to any queue at all.

Instead, the producer can only send messages to an exchange. An exchange is a very simple thing. On one side it receives messages from producers and the other side it pushes them to queues. The exchange must know exactly what to do with a message it receives. Should it be appended to a particular queue? Should it be appended to many queues? Or should it get discarded. The rules for that are defined by the exchange type.
大概翻译一下:在RabbitMQ的消息模式设计中最核心的理念就是producer永远都不直接发送任何消息给队列,实际上大部分时候,producer甚至都不知道消息将会被传递到哪个队列.相反,producer只是将消息发送给exchange(转换器/路由器).其实exchange说起来只是做了一个简单的工作:
1.接收从producer发来的消息
2.把接收到的消息push给队列
有这样的工作特点,说明exchange必须清楚的知道该对它接到的消息做怎杨的处理: 是不是该把消息发送到一个特定的队列? 还是发送到很多的队列? 还是直接忽略掉? 到底该如何做,取决于你如何定义exchange的类型.

exchage类型:

 1. direct
 2. topic
 3. headers 
 4. fanout

说到这,细心的同学肯定发现了,之前的例子中并没有去声明一个exchange,但是我们的代码不是走的好好的么?别奇怪,在rabbitMQ中,有一个默认的exchange,它的名字就是”“,而这里的queneName是RoutingKey,下次再讨论.

channel.basicPublish("", queneName, null, msg.getBytes());

今天先学习下最简单的fanout方式,即广播方式,exchange把接收到的消息发给所有的消费者.

声明一个exchange

// 声明一个名称为"exchange_fanout"的exchange
channel.exchangeDeclare("exchange_fanout", "fanout");
// 将消息发送给exchange
channel.basicPublish("exchange_fanout", "", null, msg.getBytes());

第二个概念: 临时队列(Temporary queues)

之前的例子里,我们的队列都是有具体的名称的(如:”workquene1”),其实当我们需要在producer和consumer之间共享消息,给队列命名还是很有必要的,但是这篇文章暂时不讨论这个,我们今天的任务是把消息发给所有的消费者.恰好,服务器能给我们提供一个 non-durable(不持久化的), exclusive(单独的), autodelete(随着消费者的消亡自动删除的),random(随机命名)的一个队列.

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

第三个概念: 绑定(Bindings)
上面我们已经创建好了exchange,并且定义了一个临时队列,现在我要把消息从exchange推送到所有队列上,是不是该把exchange和队列绑定一下?不然exchange如何知道把消息发哪去呢.

// binding
channel.queueBind("队列名", "exchange名", "");

好了,fanout类型这块说的差不多了,可以上代码了.

生产者Producer

public class Producer {

    private final static String EXCHANGE_NAME = "exchange_fanout";

    public static void main(String[] args) throws IOException {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        // 创建connection
        Connection conn = factory.newConnection();
        // 创建channel
        Channel channel = conn.createChannel();
        // 声明该channel是fanout类型
        channel.exchangeDeclare(EXCHANGE_NAME, "fanout");
        Date nowDate = new Date();
        String msg = nowDate.getTime() + " have log sth...";
        // 将消息发送给exchange
        channel.basicPublish(EXCHANGE_NAME, "", null, msg.getBytes());
        System.out.println(nowDate + " 已经生成一条日志...");
        channel.close();
        conn.close();
    }
}

消费者Consumer

public class Consumer1 {

    private final static String EXCHANGE_NAME = "exchange_fanout";

    public static void main(String[] args) throws IOException, ShutdownSignalException, ConsumerCancelledException, InterruptedException {
        // 获取不同的pid,方便标识不同的消费者
        String name = ManagementFactory.getRuntimeMXBean().getName();  
        String pid = name.split("@")[0];  
        // 创建连接和channel
        ConnectionFactory factory = new ConnectionFactory();
        Connection conn = factory.newConnection();
        Channel channel = conn.createChannel();
        channel.exchangeDeclare(EXCHANGE_NAME, "fanout");
        // 由RabbitMQ自行创建的临时队列,唯一且随消费者的中止而自动删除的队列
        String queueName = channel.queueDeclare().getQueue();
        // binding
        channel.queueBind(queueName, EXCHANGE_NAME, "");

        System.out.println(pid + "已经创建,正在等待消息...");

        QueueingConsumer consumer = new QueueingConsumer(channel);
        // 指定队列消费者
        channel.basicConsume(queueName, true, consumer);

        while (true) {
            // Delivery : 封装了消息,消息的载体
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();
            String recieveMsg = new String(delivery.getBody());
            System.out.println(pid + "接收到了消息: " + recieveMsg);
        }
    }
}

输出结果:


生产者生产了下面两条数据:

1450345916316 have log sth…
1450345918852 have log sth…


消费者2980

2980接收到了消息: 1450345916316 have log sth…
2980接收到了消息: 1450345918852 have log sth…

消费者484

484接收到了消息: 1450345916316 have log sth…
484接收到了消息: 1450345918852 have log sth…

你可能感兴趣的:(RabbitMQ学习)