RabbitMQ学习(三):工作模式——路由模式,主题模式,RPC模式

目录

1、路由模式

2、主题模式

3、RPC模式


阅读本篇文章之前建议先阅读前三个模式:RabbitMQ学习(二):工作模式——简单队列,工作队列,发布-订阅模式。能够更好的承上启下。消息确认机制:RabbitMQ学习(四):消息确认机制——事务模式,Confirm模式

1、路由模式

路由模式和发布订阅模式一样,都是通过交换机实现一条消息被多个消费者接收。不同的是,路由模式能够做到控制消息只被某一些满足条件的消费者所接收。

是怎么实现的呢?就是通过路由键(routingKey)来实现的,生产者发送消息会携带一个KEY,只有拥有这个KEY的消费者才会接收到对应消息。

生产者需要在发布订阅模式的基础上:

  • 修改交换机类型为direct
  • 设置每条消息的路由键
public class Producer {

    private static final String EXCHANGE_NAME = "exchange_direct";

    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = ConnectionUtils.getConnection();

        // 从连接中获取通道
        Channel channel = connection.createChannel();

        // 创建交换机,direct:处理路由键
        channel.exchangeDeclare(EXCHANGE_NAME, "direct");
        
        String msg = "hello error";

        // 指定路由键,并携带到消息中
        String routingKey = "error";
        channel.basicPublish(EXCHANGE_NAME, routingKey, null, msg.getBytes());
        System.out.println("send-->" + msg);

        // 第二条消息,更换路由键
        msg = "hello warning";
        routingKey = "warning";
        channel.basicPublish(EXCHANGE_NAME, routingKey, null, msg.getBytes());
        System.out.println("send-->" + msg);

        channel.close();
        connection.close();
    }
}

消费者需要指定自己能接收哪些路由键的消息

/**
 * 消费者2(error)
 */
public class Consumer1 {
    private static final String QUEUE_NAME = "queue_exchange_direct_email";
    private static final String EXCHANGE_NAME = "exchange_direct";

    public static void main(String[] args) throws IOException, TimeoutException {
        // 获取连接
        Connection connection = ConnectionUtils.getConnection();

        // 创建通道
        Channel channel = connection.createChannel();

        // 创建队列
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);

        // 绑定交换机
        String routingKey = "error";
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, routingKey);

        // 定义一个消费者
        DefaultConsumer 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("consumer1->" + msg);
            }
        };

        // 监听队列
        boolean autoAck = true;
        channel.basicConsume(QUEUE_NAME, autoAck, consumer);
    }
}

/**
 * 消费者2(error,info,warning)
 */
public class Consumer2 {
    private static final String QUEUE_NAME = "queue_exchange_direct_sms";
    private static final String EXCHANGE_NAME = "exchange_direct";

    public static void main(String[] args) throws IOException, TimeoutException {
        // 获取连接
        Connection connection = ConnectionUtils.getConnection();

        // 创建通道
        Channel channel = connection.createChannel();

        channel.queueDeclare(QUEUE_NAME, false, false, false, null);

        // 绑定交换机,多个就绑定多次
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "error");
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "info");
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "warning");

        // 定义一个消费者
        DefaultConsumer 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("consumer2->" + msg);
            }
        };

        // 监听队列
        boolean autoAck = true;
        channel.basicConsume(QUEUE_NAME, autoAck, consumer);
    }
}

从结果我们可以看到,error消息被两个消费者接收,而warning消息只被消费者2接收了。

控制台中的绑定情况。

2、主题模式

主题模式在路由模式的基础上更进一步,实现通配符进行模糊匹配的机制。

通配符:

  • #:任意多个字符。(如:goods.#匹配goods.addgoods.delete...
  • *:任意单个字符。

生产者需要做的是(代码只展示修改部分):

  • 修改交换机类型为topic
  • 指定路由键
// 创建交换机,topic:处理带通配符路由键
channel.exchangeDeclare(EXCHANGE_NAME, "topic");

// 指定路由键
String msg = "新商品";
channel.basicPublish(EXCHANGE_NAME, "goods.add", null, msg.getBytes());
System.out.println("send-->" + msg);

msg = "旧商品";
channel.basicPublish(EXCHANGE_NAME, "goods.delete", null, msg.getBytes());
System.out.println("send-->" + msg);

消费者则需要指定带路由键(可以带通配符)

// 消费者1
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "goods.add");

// 消费者2
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "goods.#");

可以看到,消费者一只接收到了goods.add的消息,而消费者2匹配的都接收到了

控制台绑定情况

3、RPC模式

两段程序不在同一个内存空间,无法直接通过方法名调用,就需要通过网络通信方式调用。对于RabbitMQ,本身就是用于消息通信。生产端发送消息,消费者获取消息并返回一个结果。实现一个请求——响应模型的RPC远程调用。

怎么实现?最简单的方法是,生产者和消费者在约定一个队列用于消费者回复消息。但是这样不利于解耦,必须要提前约定,如果修改两端都要修改。

RabbitMQ的RPC模式通过生产者直接将回复队列发送给消费者,并给消息绑定一个唯一标识,来确认回回复消息所对应的是哪条发送消息。从而实现解耦。

生产者需要做的是指定回复队列及唯一标识,这一步通过AMQP.BasicProperties来实现

public class Client {
    private static final String QUEUE_NAME = "queue_rpc";

    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = ConnectionUtils.getConnection();

        Channel channel = connection.createChannel();
        channel.queueDeclare(QUEUE_NAME, false, false, false,null);

        //声明了Server要回复的队列。队列名称由RabbitMQ自动创建。
        //这样做的好处是:每个客户端有属于自己的唯一回复队列,生命周期同客户端
        String replyQueue = channel.queueDeclare().getQueue();

        for (int i = 0; i < 2; i++) {
            // 每条消息绑定一个唯一的id来标识
            final String corrID = UUID.randomUUID().toString();

            // 指定回复队列和唯一id
            AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder()
                    .replyTo(replyQueue)
                    .correlationId(corrID)
                    .build();

            channel.basicPublish("", QUEUE_NAME, false, properties, "hello rpc".getBytes());
            System.out.println("send->" + corrID);
        }

        // 接收服务端回执的消息
        DefaultConsumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println(properties.getCorrelationId() + ":成功被接收");
            }
        };

        channel.basicConsume(replyQueue, true, consumer);
    }
}

消费者接收到消息后,从属性中去出回复队列,唯一标识,进行响应。

public class Server {
    private static final String QUEUE_NAME = "queue_rpc";

    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = ConnectionUtils.getConnection();

        Channel channel = connection.createChannel();

        DefaultConsumer c = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body)
                    throws IOException {
                AMQP.BasicProperties.Builder builder = new AMQP.BasicProperties.Builder();
                //我们在将要回复的消息属性中,放入从客户端传递过来的correlateId
                builder.correlationId(properties.getCorrelationId());
                AMQP.BasicProperties prop = builder.build();

                //因为RabbitMQ对于队列,始终存在一个默认exchange="",routingKey=队列名称的绑定关系
                channel.basicPublish("", properties.getReplyTo(), prop, (new String(body) + "-回复").getBytes());

            }
        };
        channel.basicConsume(QUEUE_NAME, true, c);
    }
}

可以看到,生产者收到了响应。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值