RabbitMQ工作模式

人走茶凉 提交于 2019-11-28 03:33:02

 

1、简单模式HelloWorld: 一个生产者、一个消费者,不需要设置交换机(使用默认的交换机)

  

消息生产者

import com.rabbitmq.client.Channel;import com.rabbitmq.client.Connection;import com.rabbitmq.client.ConnectionFactory;/*    消息生产者:发送消息到队列 */public class Producer {   //队列名    public static final String QUEUE_NAME = "simple_queue";    public static void main(String[] args) throws Exception {        //1. 创建连接工厂        ConnectionFactory connectionFactory = new ConnectionFactory();        //设置连接参数        connectionFactory.setHost("localhost");     //主机地址        connectionFactory.setPort(5672);           //连接端口        connectionFactory.setVirtualHost("/pomelo");//虚拟主机        connectionFactory.setUsername("pomelo");     //用户名        connectionFactory.setPassword("pomelo");     //密码        //2. 创建连接        Connection connection = connectionFactory.newConnection();        //3. 创建频道        Channel channel = connection.createChannel();        /**         * 4. 声明(创建)队列         * 参数1:队列名称         * 参数2:是否定义持久化队列         * 参数3:是否独占本次连接         * 参数4:是否在不使用的时候自动删除队列         * 参数5:队列其它参数         */        channel.queueDeclare(QUEUE_NAME,true,false,false,null);        //5. 发送消息        String message = "你好,小兔子";        /**         * 参数1:交换机名称,如果没有指定则使用默认Default Exchage         * 参数2:路由key,简单模式可以传递队列名称         * 参数3:消息其它属性         * 参数4:消息内容         */        channel.basicPublish("", QUEUE_NAME, null, message.getBytes());        System.out.println("已发送消息:" + message);        // 6. 关闭资源        channel.close();        connection.close();    }}
//消息消费者public class Consumer {   //设置队列名    public static final String QUEUE_NAME = "simple_queue";    public static void main(String[] args) throws Exception {    //1. 创建连接工厂        ConnectionFactory connectionFactory = new ConnectionFactory();        //设置连接参数,如果没有指定的话,会有默认值        connectionFactory.setHost("localhost");             connectionFactory.setPort(5672);                   connectionFactory.setVirtualHost("/pomelo");        connectionFactory.setUsername("pomelo");             connectionFactory.setPassword("pomelo");            //2. 创建连接        Connection connection = connectionFactory.getConnection();        //3. 创建频道        Channel channel = connection.createChannel();        //4. 声明队列        channel.queueDeclare(Producer.QUEUE_NAME,true,false,false,null);        //5. 创建消息消费者        DefaultConsumer consumer = new DefaultConsumer(channel){            @Override            /**             * consumerTag 消费者标签,在channel.basicConsume时候可以指定             * envelope 消息包的内容,可从中获取消息id,消息routingkey,交换机,消息和重传标志(收到消息失败后是否需要重新发送)             * properties 属性信息             * body 消息             */            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {                //路由key                System.out.println("路由key: "+envelope.getRoutingKey());                //交换机                System.out.println("交换机:  "+envelope.getExchange());                //消息id                System.out.println("消息id:  "+envelope.getDeliveryTag());                //接受到的消息                System.out.println("收到的消息:  "+new String(body,"utf-8"));            }        };        //监听消息        /**         * 参数1:队列名称         * 参数2:是否自动确认,设置为true为表示消息接收到自动向mq回复接收到了,         *       mq接收到回复会删除消息,设置为false则需要手动确认         * 参数3:消息接收到后回调         */        channel.basicConsume(Producer.QUEUE_NAME, true, consumer);        //不关闭资源,应该一直监听消息    }}2、工作队列模式Work Queue: 一个生产者、多个消费者(竞争关系),不需要设置交换机(使用默认的交换机)在同一个队列中可以有多个消费者监听消息,消费者之间对于消息是竞争关系工作队列模式:一个消息只能被一个消费者接收,其它消费者不能接收到

/*    消息生产者 : 发送30个消息到队列 */public class Producer {    public static final String QUEUE_NAME = "simple_queue";    public static void main(String[] args) throws Exception {        //1. 创建连接        Connection connection = ConnectionUtil.getConnection();        //2. 创建频道        Channel channel = connection.createChannel();        //3. 声明队列        channel.queueDeclare(QUEUE_NAME,true,false,false,null);        //4. 发送消息        for(int i = 0 ; i < 30 ; i++){            String message = "你好,小兔子! "+i;            channel.basicPublish("",QUEUE_NAME,null,message.getBytes());            System.out.println("已发送消息: "+message);        }        //5. 关闭资源        channel.close();        connection.close();    }}
//消息消费者1public class Consumer1 {    static final String QUEUE_NAME = "simple_queue";    public static void main(String[] args) throws Exception {        //1. 创建连接        Connection connection = ConnectionUtil.getConnection();  //ConnectionUtil工具类创建连接工厂        //2. 创建频道        Channel channel = connection.createChannel();        //3. 声明队列        channel.queueDeclare(Producer.QUEUE_NAME,true,false,false,null);        //4. 创建消息消费者        DefaultConsumer consumer = new DefaultConsumer(channel){            @Override            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {                //路由key                System.out.println("路由key: "+envelope.getRoutingKey());                //交换机                System.out.println("交换机:  "+envelope.getExchange());                //消息id                System.out.println("消息id:  "+envelope.getDeliveryTag());                //接受到的消息                System.out.println("收到的消息:  "+new String(body,"utf-8"));            }        };        channel.basicConsume(Producer.QUEUE_NAME, true, consumer);             //不关闭资源,应该一直监听消息    }}//消息消费者2public class Consumer2 {    static final String QUEUE_NAME = "simple_queue";    public static void main(String[] args) throws Exception {        //1. 创建连接        Connection connection = ConnectionUtil.getConnection();        //2. 创建频道        Channel channel = connection.createChannel();        //3. 声明队列        channel.queueDeclare(Producer.QUEUE_NAME,true,false,false,null);        //4. 创建消息消费者        DefaultConsumer consumer = new DefaultConsumer(channel){            @Override            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {                //路由key                System.out.println("路由key: "+envelope.getRoutingKey());                //交换机                System.out.println("交换机:  "+envelope.getExchange());                //消息id                System.out.println("消息id:  "+envelope.getDeliveryTag());                //接受到的消息                System.out.println("收到的消息:  "+new String(body,"utf-8"));            }        };        channel.basicConsume(Producer.QUEUE_NAME, true, consumer);             //不关闭资源,应该一直监听消息    }}前面2个案例中,只有3个角色:​	P:生产者,也就是要发送消息的程序​	C:消费者:消息的接受者,会一直等待消息到来。​	queue:消息队列,图中红色部分而在订阅模式中,多了一个exchange角色,而且过程略有变化:  P:生产者,也就是要发送消息的程序,但是不再发送到队列中,而是发给X(交换机)  C:消费者,消息的接受者,会一直等待消息到来  Q : 消息队列,接收消息、缓存消息  X : 交换机,接收数据并决定如何将数据转发投递到不同的队列,消息的投递决定于交换机的类型Exchange有常见以下3种类型:  Fanout:广播,将消息交给所有绑定到交换机的队列  Direct:定向,把消息交给符合指定routing key 的队列  Topic:通配符,把消息交给符合routing pattern(路由模式) 的队列Exchange(交换机)只负责转发消息,不具备存储消息的能力,因此如果没有任何队列与Exchange绑定,或者没有符合路由规则的队列,那么消息会丢失!3、发布订阅模式Publish/subscribe,需要设置类型为fanout的交换机,并且交换机和队列进行绑定,当发送消息到交换机后,交换机会将消息发送到绑定的队列

 

/*    消息生产者:发布订阅模式    发布订阅模式Publish/subscribe        1.需要设置类型为fanout的交换机        2.并且交换机和队列进行绑定,当发送消息到交换机后,交换机会将消息发送到绑定的队列 */public class Producer {    // 交换机名称    public static final String FANOUT_EXCHANGE = "fanout_exchange";    // 队列名称1    public static final String FANOUT_QUEUE_1 = "fanout_queue_1";    // 队列名称2    public static final String FANOUT_QUEUE_2 = "fanout_queue_2";    public static void main(String[] args) throws Exception {        // 1.创建连接        Connection connection = ConnectionUtil.getConnection();        // 2.创建频道        Channel channel = connection.createChannel();        /**         * 3.声明交换机         * 参数1:交换机名称         * 参数2:交换机类型:fanout、direct、topic、headers         */        channel.exchangeDeclare(FANOUT_EXCHANGE, BuiltinExchangeType.FANOUT);        /**         * 4.声明(创建)队列         * 参数1:队列名称         * 参数2:是否定义持久化队列         * 参数3:是否独占本次连接         * 参数4:是否在不使用的时候自动删除队列         * 参数5:队列其它参数         */        channel.queueDeclare(FANOUT_QUEUE_1,true,false,false,null);        channel.queueDeclare(FANOUT_QUEUE_2,true,false,false,null);        // 5.队列绑定交换机        channel.queueBind(FANOUT_QUEUE_1,FANOUT_EXCHANGE,"");        channel.queueBind(FANOUT_QUEUE_2,FANOUT_EXCHANGE,"");        // 6. 发送多个消息        for (int i=1; i<=10; i++) {            // 要发送的信息            String message = "你好;小兔子!"+i;            /**             * 参数1:交换机名称,如果没有指定则使用默认Default Exchage             * 参数2:路由key,简单模式可以传递队列名称             * 参数3:消息其它属性             * 参数4:消息内容             */            channel.basicPublish(FANOUT_EXCHANGE, "", null, message.getBytes());            System.out.println("已发送消息:" + message);        }        // 7. 关闭资源        channel.close();        connection.close();    }}
/** * 消息消费者1: 消费队列1中的消息 */public class Consumer1 {    public static void main(String[] args) throws Exception {        // 1.创建连接        Connection connection = ConnectionUtil.getConnection();        // 2.创建频道        Channel channel = connection.createChannel();        // 3.创建交换机        channel.exchangeDeclare(Producer.FANOUT_EXCHANGE,BuiltinExchangeType.FANOUT);        // 4.声明(创建)队列        channel.queueDeclare(Producer.FANOUT_QUEUE_1, true, false, false, null);        // 5.队列绑定到交换机        channel.queueBind(Producer.FANOUT_QUEUE_1,Producer.FANOUT_EXCHANGE,"");        // 6.创建消费者;并设置消息处理        DefaultConsumer consumer = new DefaultConsumer(channel){            @Override            public void handleDelivery(String consumerTag, Envelope envelope,                                       AMQP.BasicProperties properties, byte[] body) throws IOException {                System.out.println("路由key为:" + envelope.getRoutingKey());                System.out.println("交换机为:" + envelope.getExchange());                System.out.println("消息id为:" + envelope.getDeliveryTag());                System.out.println("接收到的消息为:" + new String(body, "utf-8"));            }        };        // 7.监听消息        channel.basicConsume(Producer.FANOUT_QUEUE_1, true, consumer);    }}

** * 消息消费者2: 消费队列2中的消息 */public class Consumer2 {    public static void main(String[] args) throws Exception {        // 1.创建连接        Connection connection = ConnectionUtil.getConnection();        // 2.创建频道        Channel channel = connection.createChannel();        // 3.创建交换机        channel.exchangeDeclare(Producer.FANOUT_EXCHANGE,BuiltinExchangeType.FANOUT);        // 4.声明(创建)队列        channel.queueDeclare(Producer.FANOUT_QUEUE_2, true, false, false, null);        // 5.队列绑定到交换机        channel.queueBind(Producer.FANOUT_QUEUE_2,Producer.FANOUT_EXCHANGE,"");        // 6.创建消费者;并设置消息处理        DefaultConsumer consumer = new DefaultConsumer(channel){            @Override            public void handleDelivery(String consumerTag, Envelope envelope,                                       AMQP.BasicProperties properties, byte[] body) throws IOException {                System.out.println("路由key为:" + envelope.getRoutingKey());                System.out.println("交换机为:" + envelope.getExchange());                System.out.println("消息id为:" + envelope.getDeliveryTag());                System.out.println("接收到的消息为:" + new String(body, "utf-8"));            }        };        // 7.监听消息        channel.basicConsume(Producer.FANOUT_QUEUE_2, true, consumer);    }}
发布与订阅:一个消息可以被多个消费者接收,使用了 fanout(广播)类型的交换机4、路由模式Routing 需要设置类型为direct的交换机,交换机和队列进行绑定,并且指定routing key,当发送消息到交换机后,交换机会根据routing key将消息发送到对应的队列

 

路由模式特点:

    • 队列与交换机的绑定,不能是任意绑定了,而是要指定一个 RoutingKey (路由key)

    • 消息的发送方在 向Exchange发送消息时,也必须指定消息的  RoutingKey 。

    • Exchange不再把消息交给每一个绑定的队列,而是根据消息的 Routing Key 进行判断,只有队列的
      Routingkey 与消息的  Routing key 完全一致,才会接收到消息

/*    路由消息生产者:消息发送到交换机 */public class Producer {    //交换机名    public static final String DIRECT_EXCHANGE = "direct_exchange";    //队列名    public static final String DIRECT_QUEUE_INSERT = "direct_queue_insert";    public static final String DIRECT_QUEUE_UPDATE = "direct_queue_update";    public static void main(String[] args) throws Exception {        //1. 创建连接        Connection connection = ConnectionUtil.getConnection();        //2. 创建频道        Channel channel = connection.createChannel();             3.声明交换机        channel.exchangeDeclare(DIRECT_EXCHANGE, BuiltinExchangeType.DIRECT);                //4.声明(创建)队列        channel.queueDeclare(DIRECT_QUEUE_INSERT,true,false,false,null);        channel.queueDeclare(DIRECT_QUEUE_UPDATE,true,false,false,null);            // 5.队列绑定交换机        channel.queueBind(DIRECT_QUEUE_INSERT,DIRECT_EXCHANGE,"insert");        channel.queueBind(DIRECT_QUEUE_UPDATE,DIRECT_EXCHANGE,"update");            // 6.发送消息        String message = "新增了商品,路由模式;routing key 为 insert ";        channel.basicPublish(DIRECT_EXCHANGE,"insert",null,message.getBytes());        System.out.println("已发送消息:" + message);        message = "修改了商品,路由模式;routing key 为 update ";        channel.basicPublish(DIRECT_EXCHANGE,"update",null,message.getBytes());        System.out.println("已发送消息:" + message);        // 7.关闭资源        channel.close();        connection.close();    }}
/** * 消息消费者: 消费队列中的消息 */public class Consumer1 {    public static void main(String[] args) throws Exception {        // 1.创建连接        Connection connection = ConnectionUtil.getConnection();        // 2.创建频道        Channel channel = connection.createChannel();        // 3.创建交换机        channel.exchangeDeclare(Producer.DIRECT_EXCHANGE,BuiltinExchangeType.DIRECT);        // 4.声明(创建)队列        channel.queueDeclare(Producer.DIRECT_QUEUE_INSERT, true, false, false, null);        // 5.队列绑定到交换机        channel.queueBind(Producer.DIRECT_QUEUE_INSERT,Producer.DIRECT_EXCHANGE,"insert");        // 6.创建消费者;并设置消息处理        DefaultConsumer consumer = new DefaultConsumer(channel){            @Override            public void handleDelivery(String consumerTag, Envelope envelope,                                       AMQP.BasicProperties properties, byte[] body) throws IOException {                System.out.println("路由key为:" + envelope.getRoutingKey());                System.out.println("交换机为:" + envelope.getExchange());                System.out.println("消息id为:" + envelope.getDeliveryTag());                System.out.println("接收到的消息为:" + new String(body, "utf-8"));            }        };        // 7.监听消息        channel.basicConsume(Producer.DIRECT_QUEUE_INSERT, true, consumer);    }}
/** * 消息消费者2: 消费队列中的消息 */public class Consumer2 {    public static void main(String[] args) throws Exception {        // 1.创建连接        Connection connection = ConnectionUtil.getConnection();        // 2.创建频道        Channel channel = connection.createChannel();        // 3.创建交换机        channel.exchangeDeclare(Producer.DIRECT_EXCHANGE,BuiltinExchangeType.DIRECT);        // 4.声明(创建)队列        channel.queueDeclare(Producer.DIRECT_QUEUE_UPDATE, true, false, false, null);        // 5.队列绑定到交换机        channel.queueBind(Producer.DIRECT_QUEUE_UPDATE,Producer.DIRECT_EXCHANGE,"update");        // 6.创建消费者;并设置消息处理        DefaultConsumer consumer = new DefaultConsumer(channel){            @Override            public void handleDelivery(String consumerTag, Envelope envelope,                                       AMQP.BasicProperties properties, byte[] body) throws IOException {                System.out.println("路由key为:" + envelope.getRoutingKey());                System.out.println("交换机为:" + envelope.getExchange());                System.out.println("消息id为:" + envelope.getDeliveryTag());                System.out.println("接收到的消息为:" + new String(body, "utf-8"));            }        };        // 7.监听消息        channel.basicConsume(Producer.DIRECT_QUEUE_UPDATE, true, consumer);    }}小结:路由模式:可以根据消息的路由key传递到不同的队列,然后实现消息的定向投递到不同消费者。
5、通配符模式Topic需要设置类型为topic的交换机,交换机和队列进行绑定,并且指定通配符方式routing key,当发送消息到交换机后,交换机会根据routing key将消息发送到对应的队列

 

Topic 类型与 Direct 相比,都是可以根据 RoutingKey 把消息路由到不同的队列。只不过 Topic 类型 Exchange 可
以让队列在绑定 Routing key  的时候使用通配符!

Routingkey  一般都是有一个或多个单词组成,多个单词之间以”.”分割,例如:  item.insert

案例分析

  • 生产者:发送包含有item.insert,item.update,item.delete的3种路由key的消息

  • 消费者1:一个队列绑定到交换机的routing key是:item.*

  • 消费者2:一个队列绑定到交换机的routing key是:item.update、item.delete

//Topic通配符模型消息生产者:消息发送到交换机public class Producer {    // 交换机名称    public static final String TOPIC_EXCHANGE = "topic_exchange";    // 队列名称    public static final String TOPIC_QUEUE_1 = "topic_queue_1";    // 队列名称    public static final String TOPIC_QUEUE_2 = "topic_queue_2";    public static void main(String[] args) throws Exception {        //1. 创建连接        Connection connection = ConnectionUtil.getConnection();        //2. 创建频道        Channel channel = connection.createChannel();        //3. 声明交换机  参数1(交换机名) 参数2(交换机类型)        channel.exchangeDeclare(TOPIC_EXCHANGE, BuiltinExchangeType.TOPIC);        //4.发送消息        String message = "新增了商品,Topic模式,路由key为item.insert";        channel.basicPublish(TOPIC_EXCHANGE,"item.insert",null,message.getBytes());        System.out.println("已发送消息:" + message);        message = "修改了商品,Topic模式,路由key为item.update";        channel.basicPublish(TOPIC_EXCHANGE,"item.update",null,message.getBytes());        System.out.println("已发送消息:" + message);        message = "删除了商品,Topic模式,路由key为item.delete";        channel.basicPublish(TOPIC_EXCHANGE,"item.delete",null,message.getBytes());        System.out.println("已发送消息:" + message);        // 7.关闭资源        channel.close();        connection.close();    }}
/** * 消息消费者: 消费队列中的消息 */public class Consumer2 {    public static void main(String[] args) throws Exception {        // 1.创建连接        Connection connection = ConnectionUtil.getConnection();        // 2.创建频道        Channel channel = connection.createChannel();        // 3.创建交换机        channel.exchangeDeclare(Producer.TOPIC_EXCHANGE,BuiltinExchangeType.TOPIC);        // 4.声明(创建)队列        channel.queueDeclare(Producer.TOPIC_QUEUE_2, true, false, false, null);        // 5.队列绑定到交换机        channel.queueBind(Producer.TOPIC_QUEUE_2, Producer.TOPIC_EXCHANGE,"item.update");        channel.queueBind(Producer.TOPIC_QUEUE_2, Producer.TOPIC_EXCHANGE,"item.delete");        // 6.创建消费者;并设置消息处理        DefaultConsumer consumer = new DefaultConsumer(channel){            @Override            public void handleDelivery(String consumerTag, Envelope envelope,                                       AMQP.BasicProperties properties, byte[] body) throws IOException {                System.out.println("路由key为:" + envelope.getRoutingKey());                System.out.println("交换机为:" + envelope.getExchange());                System.out.println("消息id为:" + envelope.getDeliveryTag());                System.out.println("接收到的消息为:" + new String(body, "utf-8"));            }        };        // 7.监听消息        channel.basicConsume(Producer.TOPIC_QUEUE_2, true, consumer);    }}
public class Consumer1 {    public static void main(String[] args) throws Exception {        //1. 创建连接        Connection connection = ConnectionUtil.getConnection();        //2. 创建频道        Channel channel = connection.createChannel();        //3. 声明交换机  参数1(交换机名) 参数2(交换机类型)        channel.exchangeDeclare(Producer.TOPIC_EXCHANGE, BuiltinExchangeType.TOPIC);        //4. 声明队列        channel.queueDeclare(Producer.TOPIC_QUEUE_1, true, false, false, null);        // 5.队列绑定到交换机        channel.queueBind(Producer.TOPIC_QUEUE_1, Producer.TOPIC_EXCHANGE,"item.*");        // 6.创建消费者;并设置消息处理        DefaultConsumer consumer = new DefaultConsumer(channel){            @Override            public void handleDelivery(String consumerTag, Envelope envelope,                                       AMQP.BasicProperties properties, byte[] body) throws IOException {                System.out.println("路由key为:" + envelope.getRoutingKey());                System.out.println("交换机为:" + envelope.getExchange());                System.out.println("消息id为:" + envelope.getDeliveryTag());                System.out.println("接收到的消息为:" + new String(body, "utf-8"));            }        };        // 7.监听消息        channel.basicConsume(Producer.TOPIC_QUEUE_1, true, consumer);    }}小结 Topic通配符模式:可以根据routing key将消息传递到队列中;其中routing key可以使用 * 和 #,使用了通配符则对应路由key的配置更加灵活。

 

总结

  • 不使用Exchange交换机(默认交换机)

    1. simple简单模式:一个生产者发送消息到队列中由一个消费者接收。

    2. work工作队列模式:一个生产者发送消息到队列中可由多个消费者接收;多个消费者之间消息是竞争接收。

  • 使用Exchange交换机;订阅模式(广播fanout,定向direct,通配符topic)

    1. 发布与订阅模式:使用了fanout类型的交换机,可以将一个消息发送到所有与交换机绑定的队列并被消费者接收。

    2. 路由模式:使用了direct类型的交换机,可以将一个消息发送到routing key相关的队列并被消费者接收。

    3. 通配符模式:使用了topic类型的交换机,可以将一个消息发送到routing key(*,#)相关的队列并被消费者接收。

 




  

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!