RabbitMQ 6 种工作模式 – (三)Pub/Sub 订阅模式

  • Post author:
  • Post category:其他


概念:

在订阅模型中,多了一个 Exchange 角色,而且过程略有变化:

  1. P:生产者,也就是要发送消息的程序,但是不再发送到队列中,而是发给X(交换机)

  2. C:消费者,消息的接收者,会一直等待消息到来

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

  4. Exchange:交换机(X)。一方面,接收生产者发送的消息。另一方面,知道如何处理消息,例如递交给某个特别队列、递交给所有队列、或是将消息丢弃。到底如何操作,取决于Exchange的类型。

    Exchange有常见以下3种类型:

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

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

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

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

生产者 producer:

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_PubSub {
    public static void main(String[] args) throws IOException, TimeoutException {
        /**
         * 1、创建连接工厂
         * 2、设置参数
         * 3、创建连接 Connection
         * 4、创建Channel
         * 5、创建交换机
         * 6、创建队列
         * 7、绑定队列和交换机
         * 8、发送消息
         * 9、释放资源
         */
        // 1、创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        // 2、设置参数
        factory.setHost("192.168.16.62"); // ip 默认值 localhost
        factory.setPort(5672); // 端口 默认值 5672
        factory.setVirtualHost("/"); //虚拟机 默认值 /
        factory.setUsername("guest"); // 用户名 默认值 guest
        factory.setPassword("guest"); // 密码 默认值 guest
        // 3、创建连接 Connection
        Connection connection = factory.newConnection();
        // 4、创建Channel
        Channel channel = connection.createChannel();
        // 5、创建交换机
        /*
        exchangeDeclare(
            String exchange, //交换机名称
            BuiltinExchangeType type,
            //交换机类型  4种:
                DIRECT("direct"), //定向
                FANOUT("fanout"), //扇形(广播),发送消息到每一个队列
                TOPIC("topic"), //通配符方式
                HEADERS("headers"); //参数匹配
            boolean durable, //是否持久化
            boolean autoDelete, //自动删除
            boolean internal, //内部使用。一般false
            Map<String, Object> arguments //参数
        )
        */
        String exchangeName = "test_fanout";
        channel.exchangeDeclare(exchangeName, BuiltinExchangeType.FANOUT, true, false, false, null);
        // 6、创建队列
        /*
         queueDeclare(
            String queue,  //队列名称
            boolean durable,  //是否持久化, 当mq重启后,还在
            boolean exclusive,  //是否独占,只能有一个消费者监听此队列;当Connection关闭时,是否删除队列
            boolean autoDelete, // 是否自动删除。当没有Consumer时自动删除
            Map<String, Object> arguments //参数信息,删除的信息
         )
         */
        String queue1Name = "test_fanout_queue1";
        String queue2Name = "test_fanout_queue2";
        channel.queueDeclare(queue1Name, true, false, false, null);
        channel.queueDeclare(queue2Name, true, false, false, null);
        // 7、绑定队列和交换机
        /*
        queueBind(
            String queue, //队列名称
            String exchange, //交换机名称
            String routingKey, //路由键,绑定规则,如果交换机的类型为 FANOUT ,routingKey 设置为 ""
            Map<String, Object> arguments //
        )
        */
        channel.queueBind(queue1Name, exchangeName, "");
        channel.queueBind(queue2Name, exchangeName, "");
        // 8、发送消息
        /*
         basicPublish(
            String exchange, // 交换机名称。简单模式下使用默认交换机:""
            String routingKey, // 路由名称
            BasicProperties props, // 配置信息
            byte[] body // 发送消息数据
         )
        */
        String body = "日志信息:findALl  日志级别:info";
        channel.basicPublish(exchangeName,"",null,body.getBytes());
        // 9、释放资源
        channel.close();
        connection.close();
    }
}

消费者 consumer:

import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
 * 接收消息
 */
public class Consumer_PubSub1 {
    public static void main(String[] args) throws IOException, TimeoutException {
        /**
         * 1、创建连接工厂
         * 2、设置参数
         * 3、创建连接 Connection
         * 4、创建Channel
         * 5、创建队列
         * 6、接收消息
         */
        // 1、创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        // 2、设置参数
        // ip 默认值 localhost, 192.168.16.62 :启动MQ服务器的ip
        factory.setHost("192.168.16.62");
        factory.setPort(5672); // 端口 默认值 5672
        factory.setVirtualHost("/"); //虚拟机 默认值 /
        factory.setUsername("guest"); // 用户名 默认值 guest
        factory.setPassword("guest"); // 密码 默认值 guest
        // 3、创建连接 Connection
        Connection connection = factory.newConnection();
        // 4、创建Channel
        Channel channel = connection.createChannel();
        // 5、创建队列, 生产者已经创建,无需再声明

        String queue1Name = "test_fanout_queue1";
        String queue2Name = "test_fanout_queue2";

        //6、接收消息
        /*
          basicConsume(
            String queue, // 队列名称
            boolean autoAck, // 是否自动确认
            Consumer callback // 回调对象
          )
         */
        Consumer consumer = new DefaultConsumer(channel){
            /*
              回调方法,当收到消息后,会自动执行该方法
              handleDelivery(
                String consumerTag, // 标识
                Envelope envelope, // 获取一些信息,交换机,路由key
                AMQP.BasicProperties properties, //配置信息
                byte[] body) // 数据
             */
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
//                System.out.println("consumerTag : "+consumerTag);
//                System.out.println("envelope : "+envelope);
//                System.out.println("properties : "+properties);
                System.out.println("控制台:" + new String(body));
            }
        };
        channel.basicConsume(queue1Name,true,consumer);
    }
}
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
 * 接收消息
 */
public class Consumer_PubSub2 {
    public static void main(String[] args) throws IOException, TimeoutException {
        /**
         * 1、创建连接工厂
         * 2、设置参数
         * 3、创建连接 Connection
         * 4、创建Channel
         * 5、创建队列
         * 6、接收消息
         */
        // 1、创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        // 2、设置参数
        // ip 默认值 localhost, 192.168.16.62 :启动MQ服务器的ip
        factory.setHost("192.168.16.62");
        factory.setPort(5672); // 端口 默认值 5672
        factory.setVirtualHost("/"); //虚拟机 默认值 /
        factory.setUsername("guest"); // 用户名 默认值 guest
        factory.setPassword("guest"); // 密码 默认值 guest
        // 3、创建连接 Connection
        Connection connection = factory.newConnection();
        // 4、创建Channel
        Channel channel = connection.createChannel();
        // 5、创建队列, 生产者已经创建,无需再声明

        String queue1Name = "test_fanout_queue1";
        String queue2Name = "test_fanout_queue2";

        //6、接收消息
        /*
          basicConsume(
            String queue, // 队列名称
            boolean autoAck, // 是否自动确认
            Consumer callback // 回调对象
          )
         */
        Consumer consumer = new DefaultConsumer(channel){
            /*
              回调方法,当收到消息后,会自动执行该方法
              handleDelivery(
                String consumerTag, // 标识
                Envelope envelope, // 获取一些信息,交换机,路由key
                AMQP.BasicProperties properties, //配置信息
                byte[] body) // 数据
             */
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
//                System.out.println("consumerTag : "+consumerTag);
//                System.out.println("envelope : "+envelope);
//                System.out.println("properties : "+properties);
                System.out.println("保存数据库:" + new String(body));
            }
        };
        channel.basicConsume(queue2Name,true,consumer);
    }
}

结果:

小结:

  1. 交换机需要与队列进行绑定,绑定之后;一个消息可以被多个消费者都收到。

  2. 发布订阅模式与工作队列模式的区别:

    1. 工作队列模式不用定义交换机,而发布/订阅模式需要定义交换机

    2. 发布/订阅模式的生产方是面向交换机发送消息,工作队列模式的生产方是面向队列发送消息(底层使用默认交换机)

    3. 发布/订阅模式需要设置队列和交换机的绑定,工作队列模式不需要设置,实际上工作队列模式会将队列绑 定到默认的交换机



版权声明:本文为sxs1051310123原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。