RabbitMQ工作模式

rabbitMQ的工作模式有以下几种:

1、Work queues
2、Publish/Subscribe
3、Routing
4、Topics
5、Header
6、RPC

此外还有一个入门程序也是一种模式链接

Work queues

图示:
在这里插入图片描述
work queues与入门程序相比,多了一个消费端,两个消费端共同消费同一个队列中的消息。

使用场景

  • 对于任务过重或任务较多情况使用工作队列可以提高任务处理的速度。(因为消息只会被一个消费者接受,其他消费者就不会接受了。)

测试:
1、使用入门程序,启动多个消费者。
2、生产者发送多个消息。
结果:
1、一条消息只会被一个消费者接收;
2、rabbit采用轮询的方式将消息是平均发送给消费者的,也就是说每一个连接都会接受;
3、消费者在处理完某条消息后,才会收到下一条消息。

Publish/subscribe

图示:
在这里插入图片描述
使用场景:
1、每个消费者监听自己的队列。
2、生产者将消息发给broker,由交换机将消息转发到绑定此交换机的每个队列,每个绑定交换机的队列都将接收到消息。

ps:其实在第一种workqueues模式中也有一个交换机,只是使用的默认的

发布订阅模式中的生产者:

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 Producer02_publish {

    private static final String QUEUE_INFORM_EMAIL = "queue_info_email";
    private static final String QUEUE_INFORM_SMS = "queue_info_sms";
    private static final String EXCHANGE_FANOUT_INFORM = "exchange_info-fanout";

    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = null;
        Channel channel = null;
        try {
            ConnectionFactory connectionFactory = new ConnectionFactory();
            connectionFactory.setHost("127.0.0.1");
            connectionFactory.setPort(5672);
            connectionFactory.setUsername("guest");
            connectionFactory.setPassword("guest");
            //使用默认的虚拟机,虚拟机相当于一个独立的mq服务器
            connectionFactory.setVirtualHost("/");
            connection = connectionFactory.newConnection();
            channel = connection.createChannel();
            /**
             * 声明队列,如果Rabbit中没有此队列将自动创建
             * param1:队列名称
             * param2:是否持久化
             * param3:队列是否独占此连接
             * param4:队列不再使用时是否自动删除此队列
             * param5:队列参数
             */
            //声明两个队列

            channel.queueDeclare(QUEUE_INFORM_EMAIL, true, false, false, null);
            channel.queueDeclare(QUEUE_INFORM_SMS, true, false, false, null);
            //声明交换机
            /**
             * 参数明细
             * 1、交换机名称
             * 2、交换机类型,fanout、topic、direct、headers
             */
            channel.exchangeDeclare(EXCHANGE_FANOUT_INFORM,BuiltinExchangeType.FANOUT);
            //交換机和队列绑定
            /**
             * 参数明细
             * 1、队列名称
             * 2、交换机名称
             * 3、路由key
             */
            channel.queueBind(QUEUE_INFORM_SMS,EXCHANGE_FANOUT_INFORM,"");
            channel.queueBind(QUEUE_INFORM_EMAIL,EXCHANGE_FANOUT_INFORM,"");
            /**
             * 消息发布方法
             * param1:Exchange的名称,如果没有指定,则使用Default Exchange
             * param2:routingKey,消息的路由Key,是用于Exchange(交换机)将消息转发到指定的消息队列
             * param3:消息包含的属性
             * param4:消息体
             */
            for (int i = 0; i < 5; i++) {
                String message = "send message is :" + i ;
                channel.basicPublish(EXCHANGE_FANOUT_INFORM, "", null, message.getBytes());

                System.out.println("Producer发布了消息" + message);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //关闭资源
            //先关里面的channel
            if (channel != null) {
                channel.close();
            }
            //再关外面的connection
            if (connection != null) {
                connection.close();
            }

        }

    }
}


这个和work Queues不同的是,这个需要使用channel声明一个交换机和多个队列,而且队列需要和交换机相互绑定,这样,消息才可以通过交换机发送到队列中被消费者所使用。

消费者:

import com.rabbitmq.client.*;

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

public class Consumer02_subscribe_email {
    private static final String QUEUE_INFORM_EMAIL = "queue_info_email";
    private static final String QUEUE_INFORM_SMS = "queue_info_sms";
    private static final String EXCHANGE_FANOUT_INFORM = "exchange_info-fanout";
    public static void main(String[] args) {
        Connection connection = null;
        Channel channel = null;
        try {
            ConnectionFactory connectionFactory = new ConnectionFactory();
            connectionFactory.setHost("127.0.0.1");
            connectionFactory.setPort(5672);
            connectionFactory.setUsername("guest");
            connectionFactory.setPassword("guest");
            connectionFactory.setVirtualHost("/");
            connection = connectionFactory.newConnection();
            channel = connection.createChannel();
            //声明交换机
            channel.exchangeDeclare(EXCHANGE_FANOUT_INFORM,BuiltinExchangeType.FANOUT);
            //交换机和队列绑定:
            channel.queueBind(QUEUE_INFORM_EMAIL,EXCHANGE_FANOUT_INFORM,"");
            DefaultConsumer consumer = new DefaultConsumer(channel) {
                /**
                 * 消费者接收消息调用此方法
                 * @param consumerTag 消费者的标签,在channel.basicConsume()去指定
                 * @param envelope 消息包的内容,可从中获取消息id,消息routingkey,交换机,消息和重传标志(收到消息失败后是否需要重新发送)
                 * @param properties
                 * @param body  消息体
                 * @throws IOException
                 */
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                    long deliveryTag = envelope.getDeliveryTag();
                    String exchange = envelope.getExchange();
                    String routingKey = envelope.getRoutingKey();
                    String msg = new String(body,"utf-8");
                    System.out.println("consumer recieve msg:"+msg);
                }
            };
            channel.queueDeclare(QUEUE_INFORM_EMAIL, true, false, false, null);
            /**
             * 监听队列String queue, boolean autoAck,Consumer callback
             * 参数明细
             * 1、队列名称
             * 2、是否自动回复,设置为true为表示消息接收到自动向mq回复接收到了,mq接收到回复会删除消息,设置
             为false则需要手动回复
             * 3、消费消息的方法,消费者接收到消息后调用此方法
             */

            channel.basicConsume(QUEUE_INFORM_EMAIL, true, consumer);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        }
    }
}

消费者同样都要声明交换机和队列,防止出现异常。

PS:在此种模式的情况下,同样可以使用work queues的方式,即也可以达到相同的效果。

下面说说两种模式的异同:

不同:

1)work queues不用定义交换机,而publish/subscribe需要定义交换机。
2)publish/subscribe的生产方是面向交换机发送消息,work queues的生产方是面向队列发送消息(底层使用默认交换机)。
3)publish/subscribe需要设置队列和交换机的绑定,work queues不需要设置,实质上work queues会将队列绑定到默认的交换机 。

相同:

所以两者实现的发布/订阅的效果是一样的,多个消费端监听同一个队列不会重复消费消息。

Routing

图示:

在这里插入图片描述
路由模式:
1、每个消费者监听自己的队列,并且设置routingkey。
2、生产者将消息发给交换机,由交换机根据routingkey来转发消息到指定的队列。

场景:
适用于有时候需要根据不同的需要动态的选择何种路线。

代码

1、生产者

  • 声明exchange_routing_inform交换机。
  • 声明两个队列并且绑定到此交换机,绑定时需要指定routingkey
  • 发送消息时需要指定routingkey
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 Producer03_routting {

    private static final String QUEUE_INFORM_EMAIL = "queue_info_email";
    private static final String QUEUE_INFORM_SMS = "queue_info_sms";
    private static final String EXCHANGE_ROUTTING_INFORM = "exchange_info-routting";
    private static final String ROUTTING_KEY_EMAIL = "routting_email";
    private static final String ROUTTING_KEY_SMS = "routting_sms";


    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = null;
        Channel channel = null;
        try {
            ConnectionFactory connectionFactory = new ConnectionFactory();
            connectionFactory.setHost("127.0.0.1");
            connectionFactory.setPort(5672);
            connectionFactory.setUsername("guest");
            connectionFactory.setPassword("guest");
            //使用默认的虚拟机,虚拟机相当于一个独立的mq服务器
            connectionFactory.setVirtualHost("/");
            connection = connectionFactory.newConnection();
            channel = connection.createChannel();
            /**
             * 声明队列,如果Rabbit中没有此队列将自动创建
             * param1:队列名称
             * param2:是否持久化
             * param3:队列是否独占此连接
             * param4:队列不再使用时是否自动删除此队列
             * param5:队列参数
             */
            //声明两个队列

            channel.queueDeclare(QUEUE_INFORM_EMAIL, true, false, false, null);
            channel.queueDeclare(QUEUE_INFORM_SMS, true, false, false, null);
            //声明交换机
            /**
             * 参数明细
             * 1、交换机名称
             * 2、交换机类型,fanout、topic、direct、headers
             */
            channel.exchangeDeclare(EXCHANGE_ROUTTING_INFORM, BuiltinExchangeType.DIRECT);
            //交換机和队列绑定
            /**
             * 参数明细
             * 1、队列名称
             * 2、交换机名称
             * 3、路由key
             */
            channel.queueBind(QUEUE_INFORM_SMS, EXCHANGE_ROUTTING_INFORM, ROUTTING_KEY_SMS);
            channel.queueBind(QUEUE_INFORM_SMS, EXCHANGE_ROUTTING_INFORM, "test");
            channel.queueBind(QUEUE_INFORM_EMAIL, EXCHANGE_ROUTTING_INFORM, ROUTTING_KEY_EMAIL);
            channel.queueBind(QUEUE_INFORM_EMAIL, EXCHANGE_ROUTTING_INFORM, "test");
            /**
             * 消息发布方法
             * param1:Exchange的名称,如果没有指定,则使用Default Exchange
             * param2:routingKey,消息的路由Key,是用于Exchange(交换机)将消息转发到指定的消息队列
             * param3:消息包含的属性
             * param4:消息体
             */

           /* //发送给sms
            for (int i = 0; i < 5; i++) {
                String message = "send sms message is :" + i;
                channel.basicPublish(EXCHANGE_ROUTTING_INFORM, ROUTTING_KEY_SMS, null, message.getBytes());
                System.out.println("Producer发布了消息" + message);
            }
            //发送给email
            for (int i = 0; i < 5; i++) {
                String message = "send email message is :" + i;
                channel.basicPublish(EXCHANGE_ROUTTING_INFORM, ROUTTING_KEY_EMAIL, null, message.getBytes());
                System.out.println("Producer发布了消息" + message);
            }*/
            //发送公共的消息
            for (int i = 0; i < 5; i++) {
                String message = "send inform message is :" + i;
                channel.basicPublish(EXCHANGE_ROUTTING_INFORM, "test", null, message.getBytes());
                System.out.println("Producer发布了消息" + message);
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //关闭资源
            //先关里面的channel
            if (channel != null) {
                channel.close();
            }
            //再关外面的connection
            if (connection != null) {
                connection.close();
            }

        }

    }
}

消费者:

import com.rabbitmq.client.*;

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

public class Consumer03_routting_email {
    private static final String QUEUE_INFORM_EMAIL = "queue_info_email";
    private static final String EXCHANGE_ROUTTING_INFORM = "exchange_info-routting";
    private static final String ROUTTING_KEY = "routting_email";
    public static void main(String[] args) {
        Connection connection = null;
        Channel channel = null;
        try {
            ConnectionFactory connectionFactory = new ConnectionFactory();
            connectionFactory.setHost("127.0.0.1");
            connectionFactory.setPort(5672);
            connectionFactory.setUsername("guest");
            connectionFactory.setPassword("guest");
            connectionFactory.setVirtualHost("/");
            connection = connectionFactory.newConnection();
            channel = connection.createChannel();
            //声明交换机
            channel.exchangeDeclare(EXCHANGE_ROUTTING_INFORM,BuiltinExchangeType.DIRECT);
            //交换机和队列绑定:
            channel.queueBind(QUEUE_INFORM_EMAIL,EXCHANGE_ROUTTING_INFORM,ROUTTING_KEY);
            DefaultConsumer consumer = new DefaultConsumer(channel) {
                /**
                 * 消费者接收消息调用此方法
                 * @param consumerTag 消费者的标签,在channel.basicConsume()去指定
                 * @param envelope 消息包的内容,可从中获取消息id,消息routingkey,交换机,消息和重传标志(收到消息失败后是否需要重新发送)
                 * @param properties
                 * @param body  消息体
                 * @throws IOException
                 */
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                    long deliveryTag = envelope.getDeliveryTag();
                    String exchange = envelope.getExchange();
                    String routingKey = envelope.getRoutingKey();
                    String msg = new String(body,"utf-8");
                    System.out.println("consumer recieve msg:"+msg);
                }
            };
            channel.queueDeclare(QUEUE_INFORM_EMAIL, true, false, false, null);
            /**
             * 监听队列String queue, boolean autoAck,Consumer callback
             * 参数明细
             * 1、队列名称
             * 2、是否自动回复,设置为true为表示消息接收到自动向mq回复接收到了,mq接收到回复会删除消息,设置
             为false则需要手动回复
             * 3、消费消息的方法,消费者接收到消息后调用此方法
             */

            channel.basicConsume(QUEUE_INFORM_EMAIL, true, consumer);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        }
    }
    public int get(){
        return Integer.parseInt(null);
    }
}

import com.rabbitmq.client.*;

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

public class Consumer03_routting_sms {
    private static final String QUEUE_INFORM_SMS = "queue_info_sms";
    private static final String EXCHANGE_ROUTTING_INFORM = "exchange_info-routting";
    private static final String ROUTTING_KEY = "routting_sms";
    public static void main(String[] args) {
        Connection connection = null;
        Channel channel = null;
        try {
            ConnectionFactory connectionFactory = new ConnectionFactory();
            connectionFactory.setHost("127.0.0.1");
            connectionFactory.setPort(5672);
            connectionFactory.setUsername("guest");
            connectionFactory.setPassword("guest");
            connectionFactory.setVirtualHost("/");
            connection = connectionFactory.newConnection();
            channel = connection.createChannel();
            //声明交换机
            channel.exchangeDeclare(EXCHANGE_ROUTTING_INFORM,BuiltinExchangeType.DIRECT);
            //交换机和队列绑定:
            channel.queueBind(QUEUE_INFORM_SMS,EXCHANGE_ROUTTING_INFORM,ROUTTING_KEY);
            DefaultConsumer consumer = new DefaultConsumer(channel) {
                /**
                 * 消费者接收消息调用此方法
                 * @param consumerTag 消费者的标签,在channel.basicConsume()去指定
                 * @param envelope 消息包的内容,可从中获取消息id,消息routingkey,交换机,消息和重传标志(收到消息失败后是否需要重新发送)
                 * @param properties
                 * @param body  消息体
                 * @throws IOException
                 */
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                    long deliveryTag = envelope.getDeliveryTag();
                    String exchange = envelope.getExchange();
                    String routingKey = envelope.getRoutingKey();
                    String msg = new String(body,"utf-8");
                    System.out.println("consumer recieve msg:"+msg);
                }
            };
            channel.queueDeclare(QUEUE_INFORM_SMS, true, false, false, null);
            /**
             * 监听队列String queue, boolean autoAck,Consumer callback
             * 参数明细
             * 1、队列名称
             * 2、是否自动回复,设置为true为表示消息接收到自动向mq回复接收到了,mq接收到回复会删除消息,设置
             为false则需要手动回复
             * 3、消费消息的方法,消费者接收到消息后调用此方法
             */

            channel.basicConsume(QUEUE_INFORM_SMS, true, consumer);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        }
    }
}

这样,只要生产者在发送队列的时候声明在那一条线上,就会根据routtingkey发送到对应的队列中

通配符模式

图示:

在这里插入图片描述

通配符模式:

1、每个消费者监听自己的队列,并且设置带统配符的routingkey。
2、生产者将消息发给broker,由交换机根据routingkey来转发消息到指定的队列。
3、通配符#表示匹配多个字符。
4、通配符*表示匹配一个字符。

说明:topics模式是在路由模式的基础上增加了对于通配符匹配的模式,这样在发送队列的时候会更加的灵活。

代码

生产者:


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 Producer04_topics {

    private static final String QUEUE_INFORM_EMAIL = "queue_info_email";
    private static final String QUEUE_INFORM_SMS = "queue_info_sms";
    private static final String EXCHANGE_TOPIC_INFORM = "exchange_info-topics";
    private static final String TOPICS_KEY_SMS= "inform.#.sms.#";
    private static final String TOPICS_KEY_EMAIL= "inform.#.email.#";


    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = null;
        Channel channel = null;
        try {
            ConnectionFactory connectionFactory = new ConnectionFactory();
            connectionFactory.setHost("127.0.0.1");
            connectionFactory.setPort(5672);
            connectionFactory.setUsername("guest");
            connectionFactory.setPassword("guest");
            //使用默认的虚拟机,虚拟机相当于一个独立的mq服务器
            connectionFactory.setVirtualHost("/");
            connection = connectionFactory.newConnection();
            channel = connection.createChannel();
            /**
             * 声明队列,如果Rabbit中没有此队列将自动创建
             * param1:队列名称
             * param2:是否持久化
             * param3:队列是否独占此连接
             * param4:队列不再使用时是否自动删除此队列
             * param5:队列参数
             */
            //声明两个队列

            channel.queueDeclare(QUEUE_INFORM_EMAIL, true, false, false, null);
            channel.queueDeclare(QUEUE_INFORM_SMS, true, false, false, null);
            //声明交换机
            /**
             * 参数明细
             * 1、交换机名称
             * 2、交换机类型,fanout、topic、direct、headers
             */
            channel.exchangeDeclare(EXCHANGE_TOPIC_INFORM, BuiltinExchangeType.TOPIC);
            //交換机和队列绑定
            /**
             * 参数明细
             * 1、队列名称
             * 2、交换机名称
             * 3、路由key
             */
            channel.queueBind(QUEUE_INFORM_SMS, EXCHANGE_TOPIC_INFORM, TOPICS_KEY_SMS);
            channel.queueBind(QUEUE_INFORM_EMAIL, EXCHANGE_TOPIC_INFORM, TOPICS_KEY_EMAIL);
            /**
             * 消息发布方法
             * param1:Exchange的名称,如果没有指定,则使用Default Exchange
             * param2:routingKey,消息的路由Key,是用于Exchange(交换机)将消息转发到指定的消息队列
             * param3:消息包含的属性
             * param4:消息体
             */

           /* //发送给sms
            for (int i = 0; i < 5; i++) {
                String message = "send sms message is :" + i;
                channel.basicPublish(EXCHANGE_TOPIC_INFORM, TOPICS_KEY_SMS, null, message.getBytes());
                System.out.println("Producer发布了消息" + message);
            }*/
            //发送给email
            for (int i = 0; i < 5; i++) {
                String message = "send email message is :" + i;
                channel.basicPublish(EXCHANGE_TOPIC_INFORM, "inform.email", null, message.getBytes());
                System.out.println("Producer发布了消息" + message);
            }
            //发送公共的消息
/*            for (int i = 0; i < 5; i++) {
                String message = "send sms message is :" + i;
                channel.basicPublish(EXCHANGE_TOPIC_INFORM, "inform.sms", null, message.getBytes());
                System.out.println("Producer发布了消息" + message);
            }*/

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //关闭资源
            //先关里面的channel
            if (channel != null) {
                channel.close();
            }
            //再关外面的connection
            if (connection != null) {
                connection.close();
            }

        }

    }
}


消费者:



import com.rabbitmq.client.*;

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

public class Consumer04_topics_sms {
    private static final String QUEUE_INFORM_SMS = "queue_info_sms";
    private static final String EXCHANGE_TOPIC_INFORM = "exchange_info-topics";
    private static final String TOPICS_KEY_SMS= "inform.#.sms.#";
    public static void main(String[] args) {
        Connection connection = null;
        Channel channel = null;
        try {
            ConnectionFactory connectionFactory = new ConnectionFactory();
            connectionFactory.setHost("127.0.0.1");
            connectionFactory.setPort(5672);
            connectionFactory.setUsername("guest");
            connectionFactory.setPassword("guest");
            connectionFactory.setVirtualHost("/");
            connection = connectionFactory.newConnection();
            channel = connection.createChannel();
            //声明交换机
            channel.exchangeDeclare(EXCHANGE_TOPIC_INFORM,BuiltinExchangeType.TOPIC);
            //交换机和队列绑定:
            channel.queueBind(QUEUE_INFORM_SMS,EXCHANGE_TOPIC_INFORM,TOPICS_KEY_SMS);
            DefaultConsumer consumer = new DefaultConsumer(channel) {
                /**
                 * 消费者接收消息调用此方法
                 * @param consumerTag 消费者的标签,在channel.basicConsume()去指定
                 * @param envelope 消息包的内容,可从中获取消息id,消息routingkey,交换机,消息和重传标志(收到消息失败后是否需要重新发送)
                 * @param properties
                 * @param body  消息体
                 * @throws IOException
                 */
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                    long deliveryTag = envelope.getDeliveryTag();
                    String exchange = envelope.getExchange();
                    String routingKey = envelope.getRoutingKey();
                    String msg = new String(body,"utf-8");
                    System.out.println("consumer recieve msg:"+msg);
                }
            };
            channel.queueDeclare(QUEUE_INFORM_SMS, true, false, false, null);
            /**
             * 监听队列String queue, boolean autoAck,Consumer callback
             * 参数明细
             * 1、队列名称
             * 2、是否自动回复,设置为true为表示消息接收到自动向mq回复接收到了,mq接收到回复会删除消息,设置
             为false则需要手动回复
             * 3、消费消息的方法,消费者接收到消息后调用此方法
             */

            channel.basicConsume(QUEUE_INFORM_SMS, true, consumer);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        }
    }
}

import com.rabbitmq.client.*;

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

public class Consumer04_topics_email {
    private static final String QUEUE_INFORM_EMAIL = "queue_info_email";
    private static final String EXCHANGE_TOPIC_INFORM = "exchange_info-topics";
    private static final String TOPICS_KEY_EMAIL= "inform.#.email.#";
    public static void main(String[] args) {
        Connection connection = null;
        Channel channel = null;
        try {
            ConnectionFactory connectionFactory = new ConnectionFactory();
            connectionFactory.setHost("127.0.0.1");
            connectionFactory.setPort(5672);
            connectionFactory.setUsername("guest");
            connectionFactory.setPassword("guest");
            connectionFactory.setVirtualHost("/");
            connection = connectionFactory.newConnection();
            channel = connection.createChannel();
            //声明交换机
            channel.exchangeDeclare(EXCHANGE_TOPIC_INFORM,BuiltinExchangeType.TOPIC);
            //交换机和队列绑定:
            channel.queueBind(QUEUE_INFORM_EMAIL,EXCHANGE_TOPIC_INFORM,TOPICS_KEY_EMAIL);
            DefaultConsumer consumer = new DefaultConsumer(channel) {
                /**
                 * 消费者接收消息调用此方法
                 * @param consumerTag 消费者的标签,在channel.basicConsume()去指定
                 * @param envelope 消息包的内容,可从中获取消息id,消息routingkey,交换机,消息和重传标志(收到消息失败后是否需要重新发送)
                 * @param properties
                 * @param body  消息体
                 * @throws IOException
                 */
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                    long deliveryTag = envelope.getDeliveryTag();
                    String exchange = envelope.getExchange();
                    String routingKey = envelope.getRoutingKey();
                    String msg = new String(body,"utf-8");
                    System.out.println("consumer recieve msg:"+msg);
                }
            };
            channel.queueDeclare(QUEUE_INFORM_EMAIL, true, false, false, null);
            /**
             * 监听队列String queue, boolean autoAck,Consumer callback
             * 参数明细
             * 1、队列名称
             * 2、是否自动回复,设置为true为表示消息接收到自动向mq回复接收到了,mq接收到回复会删除消息,设置
             为false则需要手动回复
             * 3、消费消息的方法,消费者接收到消息后调用此方法
             */

            channel.basicConsume(QUEUE_INFORM_EMAIL, true, consumer);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        }
    }
    public int get(){
        return Integer.parseInt(null);
    }
}

这样使用inform.#.sms.#可以达到将inform.sms和inform.email.sms的消息接受;
而使用inform.#.email.#可以达到将inform.email和inform.email.sms的消息接受。

ps:Topic模式更多加强大,它可以实现Routing、publish/subscirbe模式的功能。

Header 模式

header模式与routing不同的地方在于,header模式取消routingkey,使用header中的 key/value(键值对)匹配队列。

生产者:

队列与交换机绑定的代码与之前不同,如下:

Map<String, Object> headers_email = new Hashtable<String, Object>();
headers_email.put("inform_type", "email");
Map<String, Object> headers_sms = new Hashtable<String, Object>();
headers_sms.put("inform_type", "sms");
channel.queueBind(QUEUE_INFORM_EMAIL,EXCHANGE_HEADERS_INFORM,"",headers_email);
channel.queueBind(QUEUE_INFORM_SMS,EXCHANGE_HEADERS_INFORM,"",headers_sms);

通知:

String message = "email inform to user"+i;
Map<String,Object> headers =  new Hashtable<String, Object>();
headers.put("inform_type", "email");//匹配email通知消费者绑定的header
//headers.put("inform_type", "sms");//匹配sms通知消费者绑定的header
AMQP.BasicProperties.Builder properties = new AMQP.BasicProperties.Builder();
properties.headers(headers);
//Email通知
channel.basicPublish(EXCHANGE_HEADERS_INFORM, "", properties.build(), message.getBytes());

发送邮件的消费者:

channel.exchangeDeclare(EXCHANGE_HEADERS_INFORM, BuiltinExchangeType.HEADERS);
Map<String, Object> headers_email = new Hashtable<String, Object>();
headers_email.put("inform_email", "email");
//交换机和队列绑定
channel.queueBind(QUEUE_INFORM_EMAIL,EXCHANGE_HEADERS_INFORM,"",headers_email);
//指定消费队列
channel.basicConsume(QUEUE_INFORM_EMAIL, true, consumer);

RPC

图示:

在这里插入图片描述
RPC即客户端远程调用服务端的方法 ,使用MQ可以实现RPC的异步调用,基于Direct交换机实现,流程如下:
1、客户端即是生产者就是消费者,向RPC请求队列发送RPC调用消息,同时监听RPC响应队列。
2、服务端监听RPC请求队列的消息,收到消息后执行服务端的方法,得到方法返回的结果
3、服务端将RPC方法 的结果发送到RPC响应队列
4、客户端(RPC调用方)监听RPC响应队列,接收到RPC调用结果。

猜你喜欢

转载自blog.csdn.net/qq_38116774/article/details/88091266