RabbitMQ学习

后面慢慢补充

有道云笔记原版

消息队列解决了什么问题(应用场景)

  • 异步处理
  • 应用解耦
  • 流量消锋
  • 日志处理

简单队列

一个生产者对应一个消费者

模型

graph LR
生产者-->Queue
Queue-->消费者

特点:

  • Message Queue把请求的压力保存一下,逐渐释放出来,让处理者按照自己的节奏来处理。
  • Message Queue引入一下新的结点,让系统的可靠性会受Message Queue结点的影响。
  • Message Queue是异步单向的消息。发送消息设计成是不需要等待消息处理的完成

先启动消费者,再启动生产者

添加依赖


    <dependency>
      <groupId>com.rabbitmqgroupId>
      <artifactId>amqp-clientartifactId>
      <version>5.2.0version>
    dependency>

生产者

public class Producer {

    private static final String QUEUE_NAME = "hello";

    public static void main(String[] args) {
        // 初始化消息队列
        ConnectionFactory factory = new ConnectionFactory();
        // 设置IP
        factory.setHost("118.126.117.177");
        // 设置端口
        factory.setPort(5672);
        try {
            // 获取连接
            Connection connection = factory.newConnection();
            // 创建渠道
            Channel channel = connection.createChannel();
            // 声明一个队列
            channel.queueDeclare(QUEUE_NAME, false, false, false, null);
            // 添加消息
            for (int i = 0; i < 10; i++) {

                channel.basicPublish("", QUEUE_NAME, null, (UUID.randomUUID().toString() + "丈夫是否").getBytes("UTF-8"));
                System.out.println(" [x] Sent ");
            }
            // 释放资源
            channel.close();
            connection.close();
        } catch (IOException | TimeoutException e) {
            e.printStackTrace();

        }
    }

消费者

public class Recv {

    private static final String QUEUE_NAME="hello";
    public static void main(String[] args) {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("118.126.117.177");
        factory.setPort(5672);
        try {
            Connection connection = factory.newConnection();
            Channel channel = connection.createChannel();
            channel.queueDeclare(QUEUE_NAME,false,false,false,null);

            DeliverCallback deliverCallback = (consumerTag, delivery) -> {
                String message = new String(delivery.getBody(), "UTF-8");
                System.out.println(" [x] Received '" + message + "'");
            };
            channel.basicConsume(QUEUE_NAME, true, deliverCallback, consumerTag -> { });

            System.out.println("stop");
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        }
    }
}
简单队列的不足
  • 耦合性高:生产者一一对应消费者(如果多个消费者消费队列中的消息,就不行。);队列名变更时,需要同时改变

Work Queues 工作队列

一个生产者对应于多个消费者,但一个消息只有一个消费者可以处理,多个消费者不可以处理同一个消息

模型

graph LR
生产者-->消息队列
消息队列-->消费者A
消息队列-->消费者B
消息队列-->消费者C

为什么使用工作队列?

简单队列是意义对应的,而实际开发中消息的生产者发送消息是比较简单的,消费者需要在业务逻辑中使用,消费者接收到消息后就需要处理消息,可能会花费时间,这时候就会造成队列中的消息积压

轮训分发

当存在两个消费者是出现轮训分发

轮训分发:不管两个消费者是否空闲或忙碌,两个消费者都只消费所有消息的一半

消费者1
package com.springinaction.controller.MessageQueueWork;

import com.rabbitmq.client.*;

import java.io.IOException;

/**
 * @author lhc
 * @Title: Recv1
 * @ProjectName springinaction
 * @Description: TODO
 * @date 2018/12/9下午 5:50
 */
public class Recv1 {

    private static final String QUEUE_NAME = "test_work";

    public static void main(String[] args) {
        Connection connection = GetConnection.getConn();
        try {
            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 {
                    super.handleDelivery(consumerTag, envelope, properties, body);
                    String result = new String(body, "utf-8");
                    System.out.println("[*] result1 " + result);
                }
            };
            channel.basicConsume(QUEUE_NAME, true, consumer);

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

消费者2
package com.springinaction.controller.MessageQueueWork;

import com.rabbitmq.client.*;

import java.io.IOException;

/**
 * @author lhc
 * @Title: Recv2
 * @ProjectName springinaction
 * @Description: TODO
 * @date 2018/12/9下午 5:59
 */
public class Recv2 {
    private static final String QUEUE_NAME = "test_work";

    public static void main(String[] args) {
        Connection connection = GetConnection.getConn();
        try {
            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 {
                    super.handleDelivery(consumerTag, envelope, properties, body);
                    String result = new String(body, "utf-8");
                    System.out.println("[*] result2 " + result);
                }
            };
            channel.basicConsume(QUEUE_NAME, true, consumer);

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

公平分发(需改为手动应答)

能者多劳,即任何一个消费者在处理完上一个消息之后,向消息队列返回完成任务的标志,再由消息队列分配下一个给当前消费者

模型
sequenceDiagram
生产者->>消费者: 分配消息数据
消费者->>生产者: 处理完了,索要下一个消息数据
生产者
package com.springinaction.controller.MessageQueueWork;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 * @author lhc
 * @Title: Send
 * @ProjectName springinaction
 * @Description: 消息发送方
 * @date 2018/12/9下午 5:38
 */
public class Send {
    private static final String QUEUE_NAME = "test_work";

    public static void main(String[] args) {
        // 获取连接
        Connection connection = GetConnection.getConn();
        try {
            // 获取channel
            Channel channel = connection.createChannel();

            channel.basicQos(1);
            // 声明队列
            channel.queueDeclare(QUEUE_NAME, false, false, false, null);

            for (int i = 0; i < 100; i++) {
                String message = "hello" + i;
                // 发送消息
                channel.basicPublish("", QUEUE_NAME, null, message.getBytes());

            }
            channel.close();
            connection.close();
        } catch (IOException | TimeoutException e) {
            e.printStackTrace();
        }

    }
}

消费者1
package com.springinaction.controller.MessageQueueWork;

import com.rabbitmq.client.*;

import java.io.IOException;

/**
 * @author lhc
 * @Title: Recv1
 * @ProjectName springinaction
 * @Description: TODO
 * @date 2018/12/9下午 5:50
 */
public class Recv1 {

    private static final String QUEUE_NAME = "test_work";

    public static void main(String[] args) {
        Connection connection = GetConnection.getConn();
        try {
            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 {
                    super.handleDelivery(consumerTag, envelope, properties, body);
                    String result = new String(body, "utf-8");
                    System.out.println("[*] result1 " + result);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    finally {
                        channel.basicAck(envelope.getDeliveryTag(),false);
                    }
                }
            };
            // 自动应答改为false
            channel.basicConsume(QUEUE_NAME, false, consumer);

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

消费者2
package com.springinaction.controller.MessageQueueWork;

import com.rabbitmq.client.*;

import java.io.IOException;

/**
 * @author lhc
 * @Title: Recv2
 * @ProjectName springinaction
 * @Description: TODO
 * @date 2018/12/9下午 5:59
 */
public class Recv2 {
    private static final String QUEUE_NAME = "test_work";

    public static void main(String[] args) {
        Connection connection = GetConnection.getConn();
        try {
            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 {
                    super.handleDelivery(consumerTag, envelope, properties, body);
                    String result = new String(body, "utf-8");
                    System.out.println("[*] result2 " + result);
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    finally {
                        channel.basicAck(envelope.getDeliveryTag(),false);
                    }
                }
            };
            channel.basicConsume(QUEUE_NAME, false, consumer);

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

消息应答与消息持久化

// 默认为true
boolean autoask = false;
// 自动应答改为false
channel.basicConsume(QUEUE_NAME, autoask, consumer);
  • 当autoask为false时:当一个消费者挂掉之后,就会交付给其他消费者,rabbitmq支持消息应答,消费者发送一个消息应答告诉rabbitmq这个消息我已经处理完成,则rabbitmq在内存中删除这个消息
  • 当autoask为true时:一旦rabbitmq将消息分发给消费者后,就会在内存中删除这个消息

消息持久化

boolean durable = false;
channel.queueDeclare(QUEUE_NAME, durable, false, false, null);
  • 当durable为true时,表示数据将被持久化到磁盘
    *当durable为true时,表示数据不会被持久化到磁盘上
  • 已创建的队列,不可修改持久化的值,需重新定义一个队列

订阅模式

一个生产者对多个消费者,且一个消息可被多个消费者处理

模型
graph LR
生产者-->交换机
交换机-->消息队列1
交换机-->消息队列2
消息队列1-->消费者1
消息队列2-->消费者2

RabbitMQ学习_第1张图片

解释
  1. 一个生产者拥有多个消费者
  2. 一个消费者拥有自己的队列
  3. 生产者不是直接把消息发给消费者,而是把消息发送给交换机
  4. 每个队列都要绑定到交换机上
  5. 生产者发送的消息,经过交换机,就能是实现将一个消息发送给多个消费者
注意
  1. 在RabbitMQ中,只有队列具有存储的功能,交换机没有存储的功能
  2. 生产者产生100个消息,在控制界面的Total字段显示有200条消息,是因为消息会被送到两个不同的队列中
生产者
/**
 * @author lhc
 * @Title: PsSend
 * @ProjectName springinaction
 * @Description: TODO
 * @date 2018/12/11下午 9:09
 */
public class PsSend {
    private static final String EXCHANGE_DECLARE = "text_chage_channel";

    public static void main(String[] args) {
        Connection connection = GetConnection.getConn();
        try {
            Channel channel = connection.createChannel();
            channel.exchangeDeclare(EXCHANGE_DECLARE, "fanout");
            for (int i = 0; i < 10000; i++) {

                channel.basicPublish(EXCHANGE_DECLARE, "", null, String.valueOf(i).getBytes());
                System.out.println("[*] Send " + i);
            }
            channel.close();
            connection.close();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        }
    }
}
消费者1
/**
 * @author lhc
 * @Title: PSRece1
 * @ProjectName springinaction
 * @Description: TODO
 * @date 2018/12/11下午 9:24
 */
public class PSRece1 {
    private static final String QUUE_NAME = "test_exchange_queue1";
    private static final String EXCHANGE_DECLARE = "text_chage_channel";

    public static void main(String[] args) {

        Connection connection = GetConnection.getConn();
        try {
            Channel channel = connection.createChannel();
            channel.queueDeclare(QUUE_NAME, false, false, false, null);
            channel.queueBind(QUUE_NAME, EXCHANGE_DECLARE, "");
            channel.basicQos(1);
            Consumer consumer = new DefaultConsumer(channel) {
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                    String str = new String(body, "utf-8");
                    System.out.println("[*] Rece1: " + str);
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        channel.basicAck(envelope.getDeliveryTag(),false);
                    }
                }
            };
            channel.basicConsume(QUUE_NAME,false,consumer);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
消费者2
/**
 * @author lhc
 * @Title: PSRece2
 * @ProjectName springinaction
 * @Description: TODO
 * @date 2018/12/11下午 9:31
 */
public class PSRece2 {

    private static final String QUUE_NAME = "test_exchange_queue2";
    private static final String EXCHANGE_DECLARE = "text_chage_channel";
    public static void main(String[] args) {
        Connection connection = GetConnection.getConn();
        try {
            Channel channel  = connection.createChannel();
            channel.queueDeclare(QUUE_NAME,false,false,false,null);
            channel.queueBind(QUUE_NAME,EXCHANGE_DECLARE,"");

            Consumer consumer = new DefaultConsumer(channel) {
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                    String str = new String(body,"utf-8");
                    System.out.println("[2] Rece2: "+str);
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        channel.basicAck(envelope.getDeliveryTag(),false);
                    }
                }
            };
            channel.basicConsume(QUUE_NAME,false,consumer);
            channel.basicQos(1);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

Routing路由模式

Exchange(交换机)

匿名转发

Fanout(不处理路由键) 只要数据经过了该路由模式,则所有的消息会到达与该交换机绑定的队列中

Direct(路由模式) 首先消息到达交换机,交换机将routingKey值与消息队列进行对比,如果消息的key值与消息队列的key值相同,则将消息进入该队列

模型

RabbitMQ学习_第2张图片

routekey值相同的,放到消息队列中

你可能感兴趣的:(消息队列,MQ,rabbitmq,java后端,消息中间件)