• RabbitMQ学习笔记(4)----RabbitMQ Exchange(交换机)的使用


    1. fanout模式

    1.1 Publish/Subscribe(发布/订阅)结构图

      

      上图表示一个消费者消费消息之后,不讲消息直接存储到队列,而是使用两个消费者各自声明一个队列,将各自的对应的队列与交换机绑定。这样每个消费者都读取的是自身所对应的队列的所有消息,大达到了一个生产者生产消息,所有消费者都能消费的目的。

      将交换机类型设置为fanout即可实现Publish/Subscribe

    1.2 生产者代码

    package com.wangx.rabbitmq.sp;
    
    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 Producer {
    
        private static final String EXCHANGE_NAME = "exchange";
        public static void main(String[] args) throws IOException, TimeoutException {
    
            //创建连接工厂
            ConnectionFactory factory = new ConnectionFactory();
            //设置服务器主机
            factory.setHost("127.0.0.1");
            //设置用户名
            factory.setUsername("wangx");
            //设置密码
            factory.setPassword("wangx");
            //设置VirtualHost
            factory.setVirtualHost("/wangx");
            Connection connection = null;
            Channel channel = null;
            try {
                connection = factory.newConnection();
                channel = connection.createChannel();
    
                //声明交换机
                channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.FANOUT);
                String message = "Hello World!";
                //发送消息
                for (int i = 0; i < 10; i++) {
                    //发送消息
                    channel.basicPublish(EXCHANGE_NAME, "", null, (message + i).getBytes());
                    System.out.println(" [x] Sent '" + message + i + "'");
                }
            }catch (Exception e) {
    
            }finally {
                channel.close();
                connection.close();
            }
        }
    }

      与普通消息生产者不同的地方在于,发布订阅时必须要显示的声明一个交换机,并且在发送消息的时候,没有队列,必须设置交换机名称。

    1.3 消费者实现代码

    package com.wangx.rabbitmq.sp;
    
    import com.rabbitmq.client.*;
    
    import java.io.IOException;
    import java.util.concurrent.TimeoutException;
    
    public class Consumer1 {
        /**
         * 队列名字
         */
        private static String QUEUE_NAME = "queue1";
        private static final String EXCHANGE_NAME = "exchange";
        public static void main(String[] args){
    
            //创建连接工厂
            ConnectionFactory factory = new ConnectionFactory();
            //设置服务器主机
            factory.setHost("127.0.0.1");
            //设置用户名
            factory.setUsername("wangx");
            //设置密码
            factory.setPassword("wangx");
            //设置VirtualHost
            factory.setVirtualHost("/wangx");
            Connection connection = null;
            try {
                //创建连接
                connection = factory.newConnection();
                //创建消息通道
                final Channel  channel = connection.createChannel();
                //声明交换机
                channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.FANOUT);
                //声明队列
                channel.queueDeclare(QUEUE_NAME,false, false, false, null);
                //绑定队列与交换机
                channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "");
                //消息服务器每次只向消费者发送一条消息
    //            channel.basicQos(1);
                Consumer consumer = new DefaultConsumer(channel){
                    //重写DefaultConsumer中handleDelivery方法,在方法中获取消息
                    @Override
                    public void handleDelivery(String consumerTag, Envelope envelope,
                                               AMQP.BasicProperties properties, byte[] body) throws IOException{
                        try {
                            //消息沉睡一秒
                            Thread.sleep(1000);
                            String message = new String(body, "UTF-8");
                            System.out.println("consumer1 收到消息 '" + message + "'");
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }finally {
                            System.out.println("consumer1 消息消费完成....");
                        }
    
                    }
                };
                //监听消息
                channel.basicConsume(QUEUE_NAME, true,consumer);
            }catch (Exception e) {
                e.printStackTrace();
            }finally {
            }
        }
    }

      这里仍然使用的是两个不同的消费者,并且将两个不同的消费者分别声明不同的消息队列,然后将声明的队列与交换机进行绑定,使用channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "fanout");方法。启动两个消费者,将两个不同的消息队列注册并绑定上去。启动消息生产者发送消息,将会看到,两个不同的消费者均能接收到生产者发送的所有消息。

    2. Routing 模式

      结构图:

      

      发送消息时指定不同的key,交换机分发消息是根据key分发消息到不同的消费者队列中。

      将交换机模式改为DIRECT,在消费端设置了不同的key,相当于为消息分个类,以便于在交换机分发消息时,将消息分发给持有该key的消费者,生产者代码如下:

    package com.wangx.rabbitmq.routing;
    
    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 Producer {
    
        private static final String EXCHANGE_NAME = "exchange-routing";
        public static void main(String[] args) throws IOException, TimeoutException {
    
            //创建连接工厂
            ConnectionFactory factory = new ConnectionFactory();
            //设置服务器主机
            factory.setHost("127.0.0.1");
            //设置用户名
            factory.setUsername("wangx");
            //设置密码
            factory.setPassword("wangx");
            //设置VirtualHost
            factory.setVirtualHost("/wangx");
            Connection connection = null;
            Channel channel = null;
            try {
                connection = factory.newConnection();
                channel = connection.createChannel();
    
                //声明交换机
                channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);
                String message = "Hello World!";
                //发送消息
                for (int i = 0; i < 10; i++) {
                   if ( i % 2 == 0) {
                       //发送消息,指定key
                       channel.basicPublish(EXCHANGE_NAME, "key2", null, (message + i).getBytes());
                       System.out.println(" 偶数消息 '" + message + i + "'");
                   } else {
                       //发送消息
                       channel.basicPublish(EXCHANGE_NAME, "key1", null, (message + i).getBytes());
                       System.out.println(" 奇数消息 '" + message + i + "'");
                   }
                }
            }catch (Exception e) {
    
            }finally {
                channel.close();
                connection.close();
            }
        }
    }

     消费者代码如下:

    package com.wangx.rabbitmq.routing;
    
    import com.rabbitmq.client.*;
    
    import java.io.IOException;
    
    public class Consumer1 {
        /**
         * 队列名字
         */
        private static String QUEUE_NAME = "queue1";
        private static final String EXCHANGE_NAME = "exchange-routing";
        public static void main(String[] args){
    
            //创建连接工厂
            ConnectionFactory factory = new ConnectionFactory();
            //设置服务器主机
            factory.setHost("127.0.0.1");
            //设置用户名
            factory.setUsername("wangx");
            //设置密码
            factory.setPassword("wangx");
            //设置VirtualHost
            factory.setVirtualHost("/wangx");
            Connection connection = null;
            try {
                //创建连接
                connection = factory.newConnection();
                //创建消息通道
                final Channel  channel = connection.createChannel();
                //声明交换机
                channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);
                //声明队列
                channel.queueDeclare(QUEUE_NAME,false, false, false, null);
                //绑定队列与交换机,指定接收的消息的key
                channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "key2");
                //消息服务器每次只向消费者发送一条消息
    //            channel.basicQos(1);
                Consumer consumer = new DefaultConsumer(channel){
                    //重写DefaultConsumer中handleDelivery方法,在方法中获取消息
                    @Override
                    public void handleDelivery(String consumerTag, Envelope envelope,
                                               AMQP.BasicProperties properties, byte[] body) throws IOException{
                        try {
                            //消息沉睡一秒
                            Thread.sleep(1000);
                            String message = new String(body, "UTF-8");
                            System.out.println("consumer1 收到消息 '" + message + "'");
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }finally {
                            System.out.println("consumer1 消息消费完成....");
                        }
    
                    }
                };
                //监听消息
                channel.basicConsume(QUEUE_NAME, true,consumer);
            }catch (Exception e) {
                e.printStackTrace();
            }finally {
            }
        }
    }

      两个消费者一个使用接收奇数,一个接收偶数消息(根据发送消息时判断奇偶设置不同的key),接收时接收相应的key即可。

      这样就可以指定接收想要接收的类型的消息了,相当于前面学习的mq的消息的过滤。

    3. Topic模式

      将路由键和某模式进行匹配。此时队列需要绑定要一个模式上。符号“#”匹配一个或多个词,符号“*”匹配不多不少一个词。因此“audit.#”能够匹配到“audit.irs.corporate”,但是“audit.*” 只会匹配到“audit.irs”。

       生产者实现:

    package com.wangx.rabbitmq.topic;
    
    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 Producer {
    
        private static final String EXCHANGE_NAME = "exchange-topic";
        public static void main(String[] args) throws IOException, TimeoutException {
    
            //创建连接工厂
            ConnectionFactory factory = new ConnectionFactory();
            //设置服务器主机
            factory.setHost("127.0.0.1");
            //设置用户名
            factory.setUsername("wangx");
            //设置密码
            factory.setPassword("wangx");
            //设置VirtualHost
            factory.setVirtualHost("/wangx");
            Connection connection = null;
            Channel channel = null;
            try {
                connection = factory.newConnection();
                channel = connection.createChannel();
    
                //声明交换机
                channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC);
                String message = "Hello World!";
                //发送消息,绑定模式
                channel.basicPublish(EXCHANGE_NAME, "key.one", null, ("one -" + message).getBytes());
                channel.basicPublish(EXCHANGE_NAME, "key.two.msg", null, ("two -" + message).getBytes());
            }catch (Exception e) {
    
            }finally {
                channel.close();
                connection.close();
            }
        }
    }

      这里发送消息时,绑定了key.one和key.two.msg两种模式。接下来使用*号和#号两种通配符得消费者,如下:

    Consumer1

    package com.wangx.rabbitmq.topic;
    
    import com.rabbitmq.client.*;
    
    import java.io.IOException;
    
    public class Consumer1 {
        /**
         * 队列名字
         */
        private static String QUEUE_NAME = "queue-topic";
        private static final String EXCHANGE_NAME = "exchange-topic";
        public static void main(String[] args){
    
            //创建连接工厂
            ConnectionFactory factory = new ConnectionFactory();
            //设置服务器主机
            factory.setHost("127.0.0.1");
            //设置用户名
            factory.setUsername("wangx");
            //设置密码
            factory.setPassword("wangx");
            //设置VirtualHost
            factory.setVirtualHost("/wangx");
            Connection connection = null;
            try {
                //创建连接
                connection = factory.newConnection();
                //创建消息通道
                final Channel  channel = connection.createChannel();
                //声明交换机
                channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC);
                //声明队列
                channel.queueDeclare(QUEUE_NAME,false, false, false, null);
                //绑定队列与交换机,使用通配符key.* 表示只能匹配key下的一个路径,
                channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "key.*");
                //消息服务器每次只向消费者发送一条消息
    //            channel.basicQos(1);
                Consumer consumer = new DefaultConsumer(channel){
                    //重写DefaultConsumer中handleDelivery方法,在方法中获取消息
                    @Override
                    public void handleDelivery(String consumerTag, Envelope envelope,
                                               AMQP.BasicProperties properties, byte[] body) throws IOException{
                        try {
                            //消息沉睡一秒
                            Thread.sleep(1000);
                            String message = new String(body, "UTF-8");
                            System.out.println("consumer1 收到消息 '" + message + "'");
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }finally {
                            System.out.println("consumer1 消息消费完成....");
                        }
    
                    }
                };
                //监听消息
                channel.basicConsume(QUEUE_NAME, true,consumer);
            }catch (Exception e) {
                e.printStackTrace();
            }finally {
            }
        }
    }

      所以在本例中只能匹配到key.one的消息,控制台打印如下:

    consumer1 收到消息 'one -Hello World!'
    consumer1 消息消费完成....

      Consumer2实现:

    package com.wangx.rabbitmq.topic;
    
    import com.rabbitmq.client.*;
    
    import java.io.IOException;
    
    public class Consumer2 {
        /**
         * 队列名字
         */
        private static String QUEUE_NAME = "queue-topic-2";
        private static final String EXCHANGE_NAME = "exchange-topic";
        public static void main(String[] args){
            //创建连接工厂
            ConnectionFactory factory = new ConnectionFactory();
            //设置服务器主机
            factory.setHost("127.0.0.1");
            //设置用户名
            factory.setUsername("wangx");
            //设置密码
            factory.setPassword("wangx");
            //设置VirtualHost
            factory.setVirtualHost("/wangx");
            Connection connection = null;
            try {
                //创建连接
                connection = factory.newConnection();
                //创建消息通道
                final Channel  channel = connection.createChannel();
                channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC);
                //声明队列
                channel.queueDeclare(QUEUE_NAME,false, false, false, null);
                //这里使用#号通配符,表示能够匹配到key下的任意路径
                channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "key.#");
                Consumer consumer = new DefaultConsumer(channel){
                    //重写DefaultConsumer中handleDelivery方法,在方法中获取消息
                    @Override
                    public void handleDelivery(String consumerTag, Envelope envelope,
                                               AMQP.BasicProperties properties, byte[] body) throws IOException{
                        try {
                            //消息沉睡100ms
                            Thread.sleep(100);
                            String message = new String(body, "UTF-8");
                            System.out.println("consumer2 收到消息 '" + message + "'");
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }finally {
                            System.out.println("consumer2 消息消费完成....");
                        }
    
                    }
                };
                //监听消息,第二个参数为true时表示自动确认
               channel.basicConsume(QUEUE_NAME, true,consumer);
            }catch (Exception e) {
                e.printStackTrace();
            }finally {
            }
        }
    }

      这里使用了key.#表示匹配所有key.下的所有路径,所以能够接收到所有以key开头的消息,控制台打印如下:

    consumer2 收到消息 'one -Hello World!'
    consumer2 消息消费完成....
    consumer2 收到消息 'two -Hello World!'
    consumer2 消息消费完成....

    使用topic模式既可以轻易的实现fanout模式,也可以实现routing模式,同时提供了通配符的情况下,使得匹配更加灵活,使用方式更加简洁。

  • 相关阅读:
    常用CDN
    SQL语句小结
    jQuery源码解析----domManip
    服务治理 SpringCloud Eureka
    docker容器操作
    docker镜像操作常用命令
    Maven
    Centos6解决网络不可达
    MyBatis
    SpringMVC
  • 原文地址:https://www.cnblogs.com/Eternally-dream/p/9970564.html
Copyright © 2020-2023  润新知