RabbitMQ队列和交换机类型

文章目录

  • 队列
    • 1简单队列
      • 1.1 生产者Productor
      • 1.2 消费者Consumer
    • 2 工作队列work queues
      • 2.1 生产者
      • 2.2 两个消费者
      • 结论
    • 3 公平分发
      • 3.1 生产者
      • 3.2 消费者
      • 3.3 消息应答与消息持久化
        • 消息应答autoAck
        • 消息持久化
    • 4 订阅模式
      • 4.1 生产者
      • 4.2 消费者
  • 交换机
    • 5 Routing路由模式
    • 6 Topic模式

队列

1简单队列

RabbitMQ队列和交换机类型_第1张图片

  • P:生产者
  • 红色:队列Q
  • C:消费者

不足:

  • 耦合性高,生产者消费者一一对应,多个消费者不能消费同一个队列,队列名也需要同时变更。

1.1 生产者Productor

工具类,获取mq连接

/**
* 获取MQ连接
 * @return
 * @throws IOException
 * @throws TimeoutException
 */
public static Connection getConnection() throws IOException, TimeoutException {
    //定义工厂
    ConnectionFactory factory = new ConnectionFactory();
    //服务地址
    factory.setHost("127.0.0.1");
    //AMQP端口
    factory.setPort(5672);
    //vhost
    factory.setVirtualHost("/");
    //用户密码
    factory.setUsername("guest");
    factory.setPassword("guest");

    return factory.newConnection();
}
private static final String QUEUE_NAME="test_simple_queue";
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 msg = "hello simple";
    channel.basicPublish("",QUEUE_NAME,null,msg.getBytes());

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

出现队列
RabbitMQ队列和交换机类型_第2张图片
队列进行Get Message(s)取出消息,Message变为0。
RabbitMQ队列和交换机类型_第3张图片

1.2 消费者Consumer

public class Consumer {
    private static final String QUEUE_NAME="test_simple_queue";
    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);

        //消费者
        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, "utf-8");
                System.out.println("接收到的消息:" + msg);
            }
        };

        //监听队列
        channel.basicConsume(QUEUE_NAME,true,consumer);

    }
}

2 工作队列work queues

RabbitMQ队列和交换机类型_第4张图片

2.1 生产者

public class Product {
    private static final String QUEUE_NAME="test_work_queue";
    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
        Connection connection = ConnectionUtils.getConnection();
        Channel channel = connection.createChannel();

        /**
         * queue = QUEUE_NAME
         * durable = false
         * exclusive = false
         * autoDelete = false
         * arguments = null
         */
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);

        for (int i = 0; i < 50; i++) {
            String msg = "msg"+i;
            System.out.println("product: "+msg);

            /**
             * exchange = ""
             * routingKey = QUEUE_NAME
             * props = null
             * body = msg.getBytes()
             */
            channel.basicPublish("",QUEUE_NAME,null,msg.getBytes());
            Thread.sleep(100);
        }
        channel.close();
        connection.close();
    }
}

2.2 两个消费者

线程睡眠时长不同
消费者一

public class Consumer1 {
    private static final String QUEUE_NAME="test_work_queue";

    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);

        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);

                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally{
                    System.out.println("[consumer1]-------------");
                }
            }
        };

        /**
         * queue = QUEUE_NAME
         * autoAck = true
         * callback = consumer
         */
        channel.basicConsume(QUEUE_NAME,true,consumer);
    }
}

消费者二

public class Consumer2 {
    private static final String QUEUE_NAME="test_work_queue";

    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);

        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("c2: "+msg);

                try {
                    Thread.sleep(1500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally{
                    System.out.println("[consumer2]-------------");
                }
            }
        };

        /**
         * queue = QUEUE_NAME
         * autoAck = true
         * callback = consumer
         */
        channel.basicConsume(QUEUE_NAME,true,consumer);
    }
}

结论

  • 两个消费这尽管消费速度不同,但是消费信息的数量是相同的。
  • 这种方法叫做,轮询分发(round-robin)。不管谁忙谁空闲,都是轮着发消息。

3 公平分发

  • 使用公平分发必须关闭自动应答autoAck
  • 使用basicQos(perfetch=1)每次请求一条消息,消费完之后,返回给队列,获取新的消息,可以实现处理快的消费者多处理。
  • 能者多劳

3.1 生产者

//增加代码,对消息进行控制
int prefetchCount = 1;
channel.basicQos(prefetchCount);
public class Product {
    private static final String QUEUE_NAME="test_work_queue";
    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
        Connection connection = ConnectionUtils.getConnection();
        Channel channel = connection.createChannel();

        /**
         * queue = QUEUE_NAME
         * durable = false
         * exclusive = false
         * autoDelete = false
         * arguments = null
         */
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);

        //每个消费者发送确认消息前,消息队列不发在一个消息给消费者
        int prefetchCount = 1;
        channel.basicQos(prefetchCount);

        for (int i = 0; i < 50; i++) {
            String msg = "msg"+i;
            System.out.println("product: "+msg);

            /**
             * exchange = ""
             * routingKey = QUEUE_NAME
             * props = null
             * body = msg.getBytes()
             */
            channel.basicPublish("",QUEUE_NAME,null,msg.getBytes());
            Thread.sleep(100);
        }
        channel.close();
        connection.close();
    }
}

3.2 消费者

增加语句

//开始的时候增加
channel.basicQos(1);
//处理消息时,手动回执
channel.basicAck(envelope.getDeliveryTag(),false);
public class Consumer2 {
    private static final String QUEUE_NAME="test_work_queue";

    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.basicQos(1);

        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("c2: "+msg);

                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    /**
                     * deliverTag = envelop.getDeliveryTag()
                     * multiple = false
                     */
                    System.out.println("------------");
                    channel.basicAck(envelope.getDeliveryTag(),false);
                }
            }
        };

        /**
         * queue = QUEUE_NAME
         * autoAck = true
         * callback = consumer
         */
        boolean autoAck = false;
        channel.basicConsume(QUEUE_NAME,autoAck,consumer);
    }
}

3.3 消息应答与消息持久化

消息应答autoAck

/**
* queue = QUEUE_NAME
* autoAck = true
* callback = consumer
*/
boolean autoAck = false;
channel.basicConsume(QUEUE_NAME,autoAck,consumer);
  • true:自动确认模式,一旦rabbitmq将消息分发给消费者,就会对消息进行删除。如果消费者被杀死,就会丢失正在处理的消息
  • false:手动模式,一个消费者被杀死,就会交付给其他消费者。消费者处理完消息后,进行应答,才会删除消息。

消息持久化

/**
 * queue = QUEUE_NAME
 * durable = false
 * exclusive = false
 * autoDelete = false
 * arguments = null
 */
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
  • durable:持久化,只有在声明队列的时候才能修改(在已经有了队列之后再修改会报错)

4 订阅模式

RabbitMQ队列和交换机类型_第5张图片

生产者将消息发送给交换机而不是队列中,通过交换机(exchange)将消息发送到不同的队列。每个队列都要绑定到交换机上,消费者有自己的队列,以实现一个消息被多个消费者消费。

4.1 生产者

区别:

  • 在basicPublish中添加交换机项
public class product {
    private static final String EXCHANGE_NAME = "exchange_fanout";
    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = ConnectionUtils.getConnection();
        Channel channel = connection.createChannel();

        //声明交换机
        //fanout 分发
        channel.exchangeDeclare(EXCHANGE_NAME,"fanout");

        String msg = "hello exchange";

        /**
         * exchange = EXCHANGE_NAME
         * routingKey = ""
         * props = null
         * body = msg.getBytes()
         */
        channel.basicPublish(EXCHANGE_NAME,"",null,msg.getBytes());
        System.out.println("Send: "+msg);

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

在这里插入图片描述

4.2 消费者

消费者中自己声明交换机(但是需要和生产者中的交换机相同),先启动消费者监听,防止交换机不存在报错

public class Consumer1 {
    private static final String EXCHANGE_NAME = "exchange_fanout";
    private static final String QUEUE_NAME = "queue_fanout";

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

        /**
         * queue = QUEUE_NAME
         * durable = false
         * exclusive = false
         * autoDelete = false
         * arguments = null
         */
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);

        //消费者中自己声明交换机(但是需要和生产者中的交换机相同),先启动消费者监听,防止交换机不存在报错
        channel.exchangeDeclare(EXCHANGE_NAME,"fanout");
        //绑定队列到交换机
        channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,"");

        channel.basicQos(1);

        boolean autoAck = false;
        //直接匿名内部类,实现消费
        channel.basicConsume(QUEUE_NAME,autoAck,new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String msg = new String(body, StandardCharsets.UTF_8);
                System.out.println("[1]Recv: "+ msg);
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    System.out.println("---------");
                    //手动回执
                    channel.basicAck(envelope.getDeliveryTag(),false);
                }
            }
        });
    }
}

RabbitMQ队列和交换机类型_第6张图片

交换机

  • “”匿名转发
  • Fanout不处理路由键
  • Direct处理路由键routing key

5 Routing路由模式

交换机选择direct类型,在进行消息转发时对比routing key向值相同的队列转发消息。

消费者

channel.exchangeDeclare(EXCHANGE_NAME,"direct",true);
String msg = "direct msg sms";
//设置routing key
channel.basicPublish(EXCHANGE_NAME,"sms",null,msg.getBytes());

生产者
如果有多个routing key多次进行绑定即可。

channel.exchangeDeclare(EXCHANGE_NAME,"direct",true);
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,"email");
channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,"sms");

6 Topic模式

topic交换机将路由键和模式相匹配。将路由键切分成单词,用.隔开,使用通配符进行表示#匹配一个或多个单词,*匹配一个单词
RabbitMQ队列和交换机类型_第7张图片
RabbitMQ队列和交换机类型_第8张图片

//匹配所有email开头的routingkey
channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,"email.#");

你可能感兴趣的:(消息队列,rabbitmq,rabbitmq)