消息队列 (3) RabbtMQ 发布/订阅 、Routing Key 模式、Topic 通配符模式

第三种模式:发布/订阅 一对多 每个消费者监听各自的队列 消息来了每个一个消费者 都可以收到

在订阅模式中,多了一个交换机 Exchange角色,而且过程略有变化。

P:生产者,发送消息的程序,但是不再发送到队列中,而是发给交换机。

Queue:消息队列,接收消息、缓存消息。

Exchange:交换机,一方面接收生产者发送来的消息。另一方面知道如何处理消息,例如交给特别的队列,或者全部的队列,或者将消息丢弃。到底如何操作取决于Exchange是哪种类型:

  1.Fanout:广播,将消息分发给所有绑定到交换机的队列。 

  2.Direct:定向,把消息交给符合特定routing key 的队列。

  3.Topic:通配符,吧消息交给符合routing pattern (路由模式)的队列。

Exchange 只负责转发消息,不具备存储消息的能力,因此如果没有任何队列与Exchange绑定,或者没有符合路由规则的队列,那么消息就会丢失!

一、编写代码:

发布方: fanout 分发消息

package com.david;

import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class Publish {
    private static String EXCHANGE_NAME = "pubsub";
    public static void main(String[] args) throws IOException, TimeoutException {
        //创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        //设置参数
        factory.setHost("10.211.55.4"); //id
        factory.setVirtualHost("local"); //虚拟机
        factory.setPort(5672); //端口 默认5672
        factory.setUsername("admin");
        factory.setPassword("admin");
        //创建connect
        Connection connection = factory.newConnection();
        //创建管道
        Channel channel = connection.createChannel();


        //创建交换机 fanout是分发 所有人都收到
        //channel.exchangeDeclare(EXCHANGE_NAME,"fanout");
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.FANOUT);

        //创建队列
        String queue1_name="test_fanout_queue1";
        String queue2_name="test_fanout_queue2";
        channel.queueDeclare(queue1_name,true,false,false,null);
        channel.queueDeclare(queue2_name,true,false,false,null);

        //绑定交换机
        channel.queueBind(queue1_name,EXCHANGE_NAME,"");
        channel.queueBind(queue2_name,EXCHANGE_NAME,"");


        for(int i = 0;i<10;i++){
            String message = "hello rabbitmq!" + i;
            channel.basicPublish(EXCHANGE_NAME,"",null,message.getBytes());
            System.out.println("Producer Send : " + message);
        }

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

消费者代码

import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class Sub1 {

    private static String QUEUE_NAME = "test_fanout_queue1";
    public static void main(String[] args) throws IOException, TimeoutException {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("10.211.55.4");
        factory.setUsername("admin");
        factory.setPassword("admin");
        factory.setPort(5672);
        factory.setVirtualHost("local");

        Connection connection = factory.newConnection();

        final Channel channel = connection.createChannel();

        channel.queueDeclare(QUEUE_NAME,true,false,false,null);

        System.out.println("work1 ready");

        //每次从队列获取的数量
        channel.basicQos(1);


        Consumer comsumer = 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("work1 收到了 :" + message);
                try {
                    doWork();
                }catch (Exception ex){
                    channel.abort();
                }finally {
                    System.out.println("work1 完成了");
                    channel.basicAck(envelope.getDeliveryTag(),false);
                }
            }
        };

        boolean autoAck = false;

        channel.basicConsume(QUEUE_NAME,autoAck,comsumer);

    }

    public static void doWork() throws InterruptedException {
        Thread.sleep(1000);
    }
}

上面完成了一个发布/订阅模式的消息队列  可以看到 当发布一个消息的时候 发送到交换机中 由交换机发送给每一个消费者 没有竞争 都得到了

第四种模式:Routing 路由模式

  上面我们采用了广播的模式进行消息的发送,现在我们采用路由的方式对不同的消息进行过滤。

新建发送端

 1 public class Producer_Routing {
 2 
 3     private static String EXCHANGE_NAME = "RoutingKey";
 4     public static void main(String[] args) throws IOException, TimeoutException {
 5         //创建连接工厂
 6         ConnectionFactory factory = new ConnectionFactory();
 7         //设置参数
 8         factory.setHost("10.211.55.4"); //id
 9         factory.setVirtualHost("local"); //虚拟机
10         factory.setPort(5672); //端口 默认5672
11         factory.setUsername("admin");
12         factory.setPassword("admin");
13         //创建connect
14         Connection connection = factory.newConnection();
15         //创建管道
16         Channel channel = connection.createChannel();
17 
18         //创建交换机 direct
19         channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);
20 
21         //创建队列
22         String queue1_name="test_direct_queue1";
23         String queue2_name="test_direct_queue2";
24         channel.queueDeclare(queue1_name,true,false,false,null);
25         channel.queueDeclare(queue2_name,true,false,false,null);
26 
27         //队列1绑定
28         channel.queueBind(queue1_name,EXCHANGE_NAME,"error");
29         //队列2绑定
30         channel.queueBind(queue2_name,EXCHANGE_NAME,"info");
31         channel.queueBind(queue2_name,EXCHANGE_NAME,"warning");
32         channel.queueBind(queue2_name,EXCHANGE_NAME,"error");
33 
34         for(int i = 0;i<10;i++){
35             String type = "";
36             if(i%2==0){
37                 type = "info";
38             }else{
39                 type = "error";
40             }
41             String message = type + i;
42             channel.basicPublish(EXCHANGE_NAME,type,null,message.getBytes());
43             System.out.println("Producer Send : " + message);
44         }
45 
46         channel.close();
47         connection.close();
48 
49     }
50 }

新建消费者1 用于接受error

public class Customer_RoutingKey1 {
    private static String QUEUE_NAME = "test_direct_queue1";
    public static void main(String[] args) throws IOException, TimeoutException {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("10.211.55.4");
        factory.setUsername("admin");
        factory.setPassword("admin");
        factory.setPort(5672);
        factory.setVirtualHost("local");

        Connection connection = factory.newConnection();

        final Channel channel = connection.createChannel();

        channel.queueDeclare(QUEUE_NAME,true,false,false,null);

        System.out.println("routing1 ready");

        Consumer comsumer = 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("routing1 收到了 :" + message);
            }
        };
        channel.basicConsume(QUEUE_NAME,true,comsumer);

    }

}

新建消费者2 用于接受info

public class Customer_RoutingKey2 {
    private static String QUEUE_NAME = "test_direct_queue2";
    public static void main(String[] args) throws IOException, TimeoutException {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("10.211.55.4");
        factory.setUsername("admin");
        factory.setPassword("admin");
        factory.setPort(5672);
        factory.setVirtualHost("local");

        Connection connection = factory.newConnection();

        final Channel channel = connection.createChannel();

        channel.queueDeclare(QUEUE_NAME,true,false,false,null);

        System.out.println("routing2 ready");

        Consumer comsumer = 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("routing2 收到了 :" + message);
            }
        };
        channel.basicConsume(QUEUE_NAME,true,comsumer);

    }
}

执行后 只有routingkey1 灰收到 1 3 5 7 9  routingkey2 可以收到全部

第五种模式:通配符模式 Topics

这种应该属于模糊匹配

*:可以替代一个词

#:可以替代0或者多个词

发送端:

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class TopicSend {
    private static final String EXCHANGE_NAME = "topic_logs";

    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = null;
        Channel channel = null;
        try {
            ConnectionFactory factory = new ConnectionFactory();
            factory.setHost("localhost");
            connection = factory.newConnection();
            channel = connection.createChannel();

            //声明一个匹配模式的交换机
            channel.exchangeDeclare(EXCHANGE_NAME, "topic");
            //待发送的消息
            String[] routingKeys = new String[]{
                    "quick.orange.rabbit",
                    "lazy.orange.elephant",
                    "quick.orange.fox",
                    "lazy.brown.fox",
                    "quick.brown.fox",
                    "quick.orange.male.rabbit",
                    "lazy.orange.male.rabbit"
            };
            //发送消息
            for (String severity : routingKeys) {
                String message = "From " + severity + " routingKey' s message!";
                channel.basicPublish(EXCHANGE_NAME, severity, null, message.getBytes());
                System.out.println("TopicSend Sent '" + severity + "':'" + message + "'");
            }
        } catch (Exception e) {
            e.printStackTrace();
            if (connection != null) {
                channel.close();
                connection.close();
            }
        } finally {
            if (connection != null) {
                channel.close();
                connection.close();
            }
        }
    }
}

消费者1 匹配*.orange.*的 

import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class ReceiveLogsTopic1 {
    private static final String EXCHANGE_NAME = "topic_logs";

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

        //声明一个匹配模式的交换机
        channel.exchangeDeclare(EXCHANGE_NAME, "topic");
        String queueName = channel.queueDeclare().getQueue();
        //路由关键字
        String[] routingKeys = new String[]{"*.orange.*"};
        //绑定路由
        for (String routingKey : routingKeys) {
            channel.queueBind(queueName, EXCHANGE_NAME, routingKey);
            System.out.println("ReceiveLogsTopic1 exchange:" + EXCHANGE_NAME + ", queue:" + queueName + ", BindRoutingKey:" + routingKey);
        }
        System.out.println("ReceiveLogsTopic1 Waiting for messages");

        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("ReceiveLogsTopic1 Received '" + envelope.getRoutingKey() + "':'" + message + "'");
            }
        };
        channel.basicConsume(queueName, true, consumer);
    }
}

消费者2 匹配*.*.rabit的 和lazy.# lazy开头的

import com.rabbitmq.client.*;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.concurrent.TimeoutException;

public class ReceiveLogsTopic2 {
    private static final String EXCHANGE_NAME = "topic_logs";

    public static void main(String[] argv) throws IOException, TimeoutException {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();
//      声明一个匹配模式的交换器
        channel.exchangeDeclare(EXCHANGE_NAME, "topic");
        String queueName = channel.queueDeclare().getQueue();
        // 路由关键字
        String[] routingKeys = new String[]{"*.*.rabbit", "lazy.#"};
//      绑定路由关键字
        for (String bindingKey : routingKeys) {
            channel.queueBind(queueName, EXCHANGE_NAME, bindingKey);
            System.out.println("ReceiveLogsTopic2 exchange:"+EXCHANGE_NAME+", queue:"+queueName+", BindRoutingKey:" + bindingKey);
        }

        System.out.println("ReceiveLogsTopic2 Waiting for messages");

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

原文地址:https://www.cnblogs.com/baidawei/p/9172464.html