rabbitMQ的五种消息模式

1、simple


        producer-------------------->|queue|---------------->consumer   

producer:

public static void main(String[] args) throws Exception {
        Connection connection = ConnectUtil.getConnection();
        Channel channel = connection.createChannel();

        //声明队列
        String queryName="simple_queue";
        channel.queueDeclare(queryName, false, false, false,null);

        //发送消息
        String msg="hello world";
        channel.basicPublish("", queryName,null, msg.getBytes());
        System.out.println("Sender:" + msg);

        //关闭通道和连接
        channel.close();
        connection.close();
    }

consumer:

public static void main(String[] args) throws Exception {
        Connection connection = ConnectUtil.getConnection();
        Channel channel = connection.createChannel();

        //声明队列
        String queryName="simple_queue";
        channel.queueDeclare(queryName, false, false, false,null);
        Consumer consumer = new DefaultConsumer(channel){
          
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String msg = new String(body);
                // int i=6/0;
                System.out.println(" received : " + msg + "!");
                 //true:将小于当前消息的下标的消息全处理掉   false:反之
                channel.basicAck(envelope.getDeliveryTag(), false);
            }
        };
        //第二个参数 手动ack
        //ack=true: 如果消费信息过程中出现中出现异常 消息也会被销毁  false:出现异常不会被销毁
        channel.basicConsume(queryName, true, consumer);
    }


    2、work:多个消费者消费同一个队列


        producer-------------------->|queue|---------------->多consumer(消费同一个队列)

producer:

 public static void main(String[] args) throws Exception {
        Connection connection = ConnectUtil.getConnection();
        Channel channel = connection.createChannel();

        //声明队列
        String queryName="work_queue";
        channel.queueDeclare(queryName, false, false, false,null);

        //发送消息   多发送几条 观察消费者的处理消息情况
        for (int i = 0; i < 50; i++) {
            String msg="hello world---"+i;
            channel.basicPublish("", queryName,null, msg.getBytes());
            System.out.println("Sender:" + msg);
        }

        //关闭通道和连接
        channel.close();
        connection.close();
    }

consumer1:处理过程中睡2秒 模仿性能低的一个消费者

 public static void main(String[] args) throws Exception {
        Connection connection = ConnectUtil.getConnection();
        Channel channel = connection.createChannel();

        //声明队列
        String queryName="work_queue";
        channel.queueDeclare(queryName, false, false, false,null);
        //能者多劳
        channel.basicQos(1);
        Consumer consumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                try {
                    String msg = new String(body);
                    System.out.println(" received1 : " + msg + "!");
                    //true:将小于当前消息的下标的消息全处理掉   false:反之
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                channel.basicAck(envelope.getDeliveryTag(), false);
            }
        };
        //第二个参数 手动ack
        //ack=true: 如果消费信息过程中出现中出现异常 消息也会被销毁  false:出现异常不会被销毁
        channel.basicConsume(queryName, false, consumer);
    }

consumer2:消费同一个队列,模仿没有延迟的性能高的消费者

 public static void main(String[] args) throws Exception {
        Connection connection = ConnectUtil.getConnection();
        Channel channel = connection.createChannel();

        //声明队列
        String queryName="work_queue";
        channel.queueDeclare(queryName, false, false, false,null);
        channel.basicQos(1);
        Consumer consumer = new DefaultConsumer(channel){
rows IOException

            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String msg = new String(body);              
                System.out.println(" received2 : " + msg + "!");
                 //true:将小于当前消息的下标的消息全处理掉   false:反之
                channel.basicAck(envelope.getDeliveryTag(), false);
            }
        };
        //第二个参数 手动ack
        //ack=true: 如果消费信息过程中出现中出现异常 消息也会被销毁  false:出现异常不会被销毁
        channel.basicConsume(queryName, false, consumer);
    }

结果:由于添加了channel.basicQos(1);能者多劳

consumer1 只处理了一条信息,consumer2处理了其余的四十九条信息。

    3、fanout: 多个队列,消息发送不在发送到队列,直接发送到交换机上


                                             bind
        producer------------->exchange---------------->|多queue|---------------->多consumer

        注意:exchange只负责分发消息,若没有queue绑定到exchange则消息会被丢弃

producer:

public static void main(String[] args) throws Exception {
        Connection connection = ConnectUtil.getConnection();
        Channel channel = connection.createChannel();

        //声明交换机   不再需要声明队列
        String exchange = "fanout_exchange";
        // 交换机名  交换机类型
        channel.exchangeDeclare(exchange, "fanout");
        //声明队列
        // String queryName="work_queue";
        // channel.queueDeclare(queryName, false, false, false,null);

        //发送消息
        String msg="hello world---";
        //                交换机名   队列名  basicproperties  msg
        channel.basicPublish(exchange, "",null, msg.getBytes());
        System.out.println("Sender:" + msg);

        //关闭通道和连接
        channel.close();
        connection.close();
    }

consumer1:

 public static void main(String[] args) throws Exception {
        Connection connection = ConnectUtil.getConnection();
        Channel channel = connection.createChannel();

        String queryName="fanout_queue_1";
        String exchange="fanout_exchange";
        //声明队列
        channel.queueDeclare(queryName, false, false, false,null);
        //将队列绑定到交换机上
        channel.queueBind(queryName, exchange, "");
        Consumer consumer = new DefaultConsumer(channel){

            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String msg = new String(body);

                System.out.println(" received1 : " + msg + "!");
                 //true:将小于当前消息的下标的消息全处理掉   false:反之
                channel.basicAck(envelope.getDeliveryTag(), false);
            }
        };
        //第二个参数 手动ack
        //ack=true: 如果消费信息过程中出现中出现异常 消息也会被销毁  false:出现异常不会被销毁
        channel.basicConsume(queryName, false, consumer);
    }

consumer2:

代码部分只是变成:        queryName="fanout_queue_2"

结果:producer发送消息  consumer1和consumer2都能接收到消息

    4、direct:指定routingkey 分发消息


                                        bind(routingkey)
        producer------------->exchange-------------------->|多queue|---------------->多consumer

        routingkey:灵活控制消息分发

producer:

public static void main(String[] args) throws Exception {
        Connection connection = ConnectUtil.getConnection();
        Channel channel = connection.createChannel();

        //声明交换机   不再需要声明队列
        String exchange = "direct-exchange";
        // 交换机名  交换机类型      routingKey
        channel.exchangeDeclare(exchange, "direct");

        //发送消息
        String msg="hello world";
        //交换机名 队列名  basicproperties  msg
        channel.basicPublish(exchange, "test",null, msg.getBytes());
        System.out.println("Sender:" + msg);

        //关闭通道和连接
        channel.close();
        connection.close();
    }

consumer1:

 public static void main(String[] args) throws Exception {
        Connection connection = ConnectUtil.getConnection();
        Channel channel = connection.createChannel();

        String queryName="direct_queue_1";
        String exchange="direct-exchange";
        //声明队列
        channel.queueDeclare(queryName, false, false, false,null);
        //将队列绑定到交换机上
        channel.queueBind(queryName, exchange, "");
        Consumer consumer = new DefaultConsumer(channel){

            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String msg = new String(body);
                System.out.println(" received1 : " + msg + "!");
                 //true:将小于当前消息的下标的消息全处理掉   false:反之
                channel.basicAck(envelope.getDeliveryTag(), false);
            }
        };
        //第二个参数 手动ack
        //ack=true: 如果消费信息过程中出现中出现异常 消息也会被销毁  false:出现异常不会被销毁
        channel.basicConsume(queryName, false, consumer);
    }

consumer2:

相对于consumer1修改部分:

        1)队列名称:queryName="direct_queue_2";

        2)channel.queueBind(queryName, exchange, "test");

结果:producer发送的时候携带的routingkey时“test”,consumer1不匹配,consumer2匹配

consumer2接收到了消息,但是consumer1没有

    5、topic:通配符匹配,接受分发的消息


                                        bind(#.routingkey.*)
        producer------------->exchange-------------------->|多queue|---------------->多consumer

        通配符:
            #:匹配n个单词
            *:匹配1个单词

producer:

public static void main(String[] args) throws Exception {
        Connection connection = ConnectUtil.getConnection();
        Channel channel = connection.createChannel();

        //声明交换机   不再需要声明队列
        String exchange = "topic-exchange";
        // 交换机名  交换机类型      routingKey
        channel.exchangeDeclare(exchange, "topic");

        //发送消息
        String msg="hello world";
        //交换机名 队列名  basicproperties  msg
        channel.basicPublish(exchange, "topic.ww.add",null, msg.getBytes());
        System.out.println("Sender:" + msg);

        //关闭通道和连接
        channel.close();
        connection.close();
    }

consumer1:

  public static void main(String[] args) throws Exception {
        Connection connection = ConnectUtil.getConnection();
        Channel channel = connection.createChannel();

        String queryName="topic_queue_1";
        String exchange="topic-exchange";
        //声明队列
        channel.queueDeclare(queryName, false, false, false,null);
        //将队列绑定到交换机上
        channel.queueBind(queryName, exchange, "*.add");
        Consumer consumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String msg = new String(body);
                System.out.println(" received1 : " + msg + "!");
                 //true:将小于当前消息的下标的消息全处理掉   false:反之
                channel.basicAck(envelope.getDeliveryTag(), false);
            }
        };
        //第二个参数 手动ack
        //ack=true: 如果消费信息过程中出现中出现异常 消息也会被销毁  false:出现异常不会被销毁
        channel.basicConsume(queryName, false, consumer);
    }

consumer2:

相对于consumer1修改部分:

        1)队列名称:queryName="topic_queue_2";

        2)channel.queueBind(queryName, exchange, "#.add");

结果:consumer1没有接收到消息,consumer2接收到了。

  问题1:如何保证消费者把消息成功消费?手动ack(理论)

    手动ack:
            channel.basicConsume(, false, );//不自动ack 

                // true:就会处理掉比当前信息下标小的所有信息

            try {
                //业务逻辑
                channel.basicAck(envelope.getDeliveryTag(), false);//手动ack

                // true:无论是否出现异常执行之后都会将消息处理掉
            }catch (Exception e){
                e.printStackTrace();
            }

    问题2:如何防止消息堆积?能者多劳+多consumer

        能者多劳:
            channel.basicQos(1);

   问题3:再修改和routingKey有关的代码,需要先删除原来的通配符匹配规则

修改后的匹配规则,不会覆盖原来的,而是追加。会对匹配规则造成影响。 

     问题4:如果交换机没有创建,但是开启消费者会报错

 

可以先执行生产者发送一个空消息,建立好交换机之后再启动消费者。 

问题5:为什么使用rabbitmq

        1)解耦:分布式项目中,一个模块需要调用其他模块的接口(商品,订单),当新增加业务时(聊天),就需要这个调用其他模块的内容进修添加修改,耦合度很高。使用消息队列之后:所有模块信息包括 --”新增加业务模块“-- 全发送到消息队列中,调用者直接接收队列的消息即可,不需要修改任何原来模块的代码,只需要将新的消息发送到队列中即可。

        2)异步:消息写入队列之后,队列处理一些非必要业务逻辑采用异步的方式,加快响应时间。

        3)削峰:数据库无法保证在高并发的情况下正常运行。中间件会根据数据库可以处理请求的最大并发量,有序的从队列中拉取数据。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值