4

一. RabbitMQ消息处理
在上一节我们讲到了RabbitMQ的简单架构,我们先回顾一下RabbitMQ中消息的流转流程,这个概念会在本节中使用到。
image.png

1.生产者生产消息后,会绑定一个路由键将消息投递到交换器中
2.队列通过路由键绑定到交换器
3.交换器通过消息的路由键去查找绑定在它上面的队列,通过不同的规则去匹配队列绑定的路由键,匹配成功后将消息路由到队列上,遍历所有队列后都没有匹配的消息将无法路由到队列
4.消费者监听队列,一旦有消息路由到队列,消费者就会从队列中取出消息或者是RabbitMQ将消息推送给消费者进行消费

上面的流程是在正常情况下的消息处理流程,对于投递失败的消息和消费失败的消息,我们会在后续的知识点中讲到


二. 交换器类型
不同类型的交换器有不同的路由键键匹配规则,我们先来看看RabbitMQ中有哪几种交换器
1 Direct
只有当路由键完全匹配时,才会将消息投递到相应的队列中
image.png

2 Fanout
消息广播到绑定的队列,即该类型的交换器不会根据路由键去匹配队列,只要有消息投递到该交换器,它就会将消息投递到所有绑定在该交换器上的队列
image.png

3 Topic
Topic交换器可以使用通配符("*" 和 "#")使来自不同源头的消息到达统一队列。”.”将路由键分为了几个标识符," * "匹配1个,"#"匹配一个或多个

eg:有四个队列,queue1绑定路由键"usa.#",queue2绑定路由键"#.news",queue3绑定路由键"*.weather",queue4绑定路由键"europe.*"

四条消息:message1绑定的路由键为"usa.news",message2绑定的路由键为"usa.weather",message3绑定的路由键为"europe.news",message4绑定的路由键为"errope.weather"

路由后:
queue1能收到消息message1,message2
queue2能收到消息message1,message3
queue3能收到消息message2,message4
queue4能收到消息message3,message4

需要注意一点是,"*"是匹配一个,"#"匹配多个。若是消息绑定路由键为usa.weather.cloud,那么"usa.*"无法匹配该路由键,"usa.#"能匹配,此时也可以使用"usa.*.*"匹配
image.png

4 Headers
由于headers交换器基本不用,这里就不做讲解


三. 使用RabbitMQ原生API
使用RabbitMQ原生API来发送和消费消息时,按照如下流程

生产者:
获取连接工厂
ConnectionFactory factory = new ConnectionFactory();

新建连接
public Connection newConnection();

创建信道
Channel createChannel();

声明交换器
Exchange.DeclareOk exchangeDeclare(String exchange, BuiltinExchangeType type)
args1 交换器名称,args2交换器类型

发送消息
void basicPublish(String exchange, String routingKey, boolean mandatory, BasicProperties props, byte[] body)
args1 交换器名,args2 路由键,args3 消息无法路由时的处理,为true时生产者可以通过回调函数获取消息状态,args4 消息属性,args5 消息体

消费者:
获取连接工厂
ConnectionFactory factory = new ConnectionFactory();

新建连接
public Connection newConnection();

创建信道
Channel createChannel();

声明交换器
Exchange.DeclareOk exchangeDeclare(String exchange, BuiltinExchangeType type)
args1 交换器名称,args2交换器类型

声明队列
Queue.DeclareOk queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments)
args1 队列名,arg2 是否持久化,args3 是否独占队列,args4 自动删除,args5 其他属性

队列绑定
Queue.BindOk queueBind(String queue, String exchange, String routingKey, Map<String, Object> arguments)
args1 队列名,args2 交换器名,args3 路由键,arg4 其他属性

消息消费
String basicConsume(String queue, boolean autoAck, Consumer callback)
args1 队列名,args2 是否自动签收,args3 消费者


四. 使用不同的交换器完成消息处理

  1. Direct Exchange

DirectProducer Demo

public class DirectProducer {

    public static final String EXCHANGE_NAME = "direct_exchange";

    public static final String ROUTING_KEY = "direct_key";

    public static void main(String[] args) throws Exception{
        // 创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("127.0.0.1");
        factory.setVirtualHost("/");

        // 新建连接
        Connection connection = factory.newConnection();

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

        // 声明交换器
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);

        String msg = "hello rabbitmq";
        // 发送消息
        channel.basicPublish(EXCHANGE_NAME, ROUTING_KEY,null, msg.getBytes());
        System.out.println("消息投递完成,消息:" + msg + ", 路由键:" + ROUTING_KEY);

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

生产者端结果
image.png

DirectConsumer Demo

public class DirectConsumer {

    public static void main(String[] args) throws Exception{
        // 创建连接工厂
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("127.0.0.1");
        connectionFactory.setVirtualHost("/");

        // 创建连接
        Connection connection = connectionFactory.newConnection();

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

        // 声明交换器
        channel.exchangeDeclare(DirectProducer.EXCHANGE_NAME, BuiltinExchangeType.DIRECT);

        String queueName = "direct_queue";

        // 声明队列
        channel.queueDeclare(queueName, false, false, false, null);

        // 将队列绑定到交换器
        channel.queueBind(queueName, DirectProducer.EXCHANGE_NAME, DirectProducer.ROUTING_KEY);

        // 声明消费者
        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, "UTF-8");
                System.out.println("收到的消息:" + msg + ", 路由键:" + envelope.getRoutingKey());
            }
        };

        // 消费者消费指定队列的消息
        channel.basicConsume(queueName, true, consumer);
    }
}

消费端结果:
image.png

  1. Fanout Exchange

FanoutProducer Demo

public class FanoutProducer {

    // 交换器
    public static final String EXCHANGE_NAME = "fanout_exchange";

    // 路由键
    public static final String ROUTING_KEY = "fanout_key";

    public static void main(String[] args) throws Exception{
        // 获取连接工厂
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setVirtualHost("/");
        connectionFactory.setHost("127.0.0.1");
        connectionFactory.setPort(5672);

        // 获取连接
        Connection connection = connectionFactory.newConnection();

        // 声明信道
        Channel channel = connection.createChannel();

        // 声明交换器
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.FANOUT);

        // 发送消息
        String msg = "hello rabbitmq";
        channel.basicPublish(FanoutProducer.EXCHANGE_NAME, ROUTING_KEY, null, msg.getBytes());
        System.out.println("发送消息:" + msg + ",路由键:" + ROUTING_KEY);

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

生产端结果:
image.png

FanoutConsumer Demo

public class FanoutConsumer {

    public static void main(String[] args) throws Exception{
        // 创建连接工厂
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("127.0.0.1");
        connectionFactory.setVirtualHost("/");

        // 创建连接
        Connection connection = connectionFactory.newConnection();

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

        // 声明交换器
        channel.exchangeDeclare(FanoutProducer.EXCHANGE_NAME, BuiltinExchangeType.FANOUT);

        // 队列
        String[] queueNames = {"queue1", "queue2", "queue3"};

        // 路由键
        String[] routingKey = {"fanout_key", "fanout_key1", "fanout_key2"};

        // 分别使用不同的路由键声明不同的队列
        for (int i = 0; i < 3; i++){
            channel.queueDeclare(queueNames[i], false, false, false, null);
            channel.queueBind(queueNames[i], FanoutProducer.EXCHANGE_NAME, routingKey[i]);

            // 消费消息
            channel.basicConsume(queueNames[i], true, new DefaultConsumer(channel){
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                    String msg = new String(body, "UTF-8");
                    System.out.println("消息:" + msg + ",路由键:" + envelope.getRoutingKey());
                }
            });
        }
    }
}

消费端结果:
image.png

  1. Topic Exchange

TopicProducer Demo

public class TopicProducer {

    // 交换器
    public static final String EXCHANGE_NAME = "topic_exchange";

    public static void main(String[] args) throws Exception{
        // 创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("127.0.0.1");
        factory.setVirtualHost("/");

        // 新建连接
        Connection connection = factory.newConnection();

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

        // 声明交换器
        channel.exchangeDeclare(TopicProducer.EXCHANGE_NAME, BuiltinExchangeType.TOPIC);

        // 发送消息
        String msg = "hello rabbitmq";
        String[] routingKey = {"topic.key1", "topic.key2", "topic.key2.test"};

        for (int i = 0; i < 3; i++){
            channel.basicPublish(TopicProducer.EXCHANGE_NAME, routingKey[i], null, msg.getBytes());
            System.out.println("发送消息:" + msg + ", 路由键:" + routingKey[i]);
        }

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

生产端结果:
image.png

TopicConsumer Demo

public class TopicConsumer {

    public static void main(String[] args) throws Exception{
        // 创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("127.0.0.1");
        factory.setVirtualHost("/");

        // 新建连接
        Connection connection = factory.newConnection();

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

        // 声明交换器
        channel.exchangeDeclare(TopicProducer.EXCHANGE_NAME, BuiltinExchangeType.TOPIC);

        String queueName = "topic_queue";

        // 声明队列
        channel.queueDeclare(queueName, false, false, false, null);

        // 队列绑定
        channel.queueBind(queueName, TopicProducer.EXCHANGE_NAME, "topic.*");

        // 消息消费
        channel.basicConsume(queueName, true, new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String msg = new String(body, "UTF-8");
                System.out.println("消息:" + msg + ", 路由键:" + envelope.getRoutingKey());
            }
        });
    }
}

消费端结果:
image.png


Lemon
22 声望2 粉丝