消息队列-RabbitMQ

一、Docker安装Rabbitmq

1.1Docker环境搭建

1、下载docker离线安装包
https://download.docker.com/linux/static/stable/x86_64/docker-20.10.6.tgz
选择版本
https://download.docker.com/linux/static/stable/
2、离线安装工具
https://github.com/Jrohy/docker-install/
3、将下载的安装文件放在一个目录中
4、把目录上传虚拟机上
5、修改docker-install执行权限
6、执行安装

# 进入 docker-install 文件夹
cd docker-install
# 为 docker-install 添加执行权限
chmod +x install.sh
# 安装
./install.sh -f docker-20.10.6.tgz

7、设置镜像加速
修改配置文件 /etc/docker/daemon.json

cat <<EOF > /etc/docker/daemon.json
{
  "registry-mirrors": [
    "https://docker.mirrors.ustc.edu.cn",
    "http://hub-mirror.c.163.com"
  ],
  "max-concurrent-downloads": 10,
  "log-driver": "json-file",
  "log-level": "warn",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
    },
  "data-root": "/var/lib/docker"
}
EOF

然后dockers就安装好了,可以愉快的使用docker搞事情了

1.2 Rabbitmq 搭建

1、配置管理员用户名密码

mkdir /etc/rabbitmq
vim /etc/rabbitmq/rabbitmq.conf
# 添加两行配置:
default_user = admin
default_pass = admin

2、启动Rabbitmq

docker run -d --name rabbit \
-p 5672:5672 \
-p 15672:15672 \
-v /etc/rabbitmq/rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf \
-e RABBITMQ_CONFIG_FILE=/etc/rabbitmq/rabbitmq.conf \
--restart=always \
rabbitmq:management

访问管理控制台 http://192.168.64.140:15672
在这里插入图片描述
主要端口介绍
4369 – erlang发现口
5672 – client端通信口
15672 – 管理界面ui端口
25672 – server间内部通信口

二、RabbitMq使用场景

2.1 服务解耦

背景:传统的架构,上游的A服务调用下游的服务,业务之间的耦合性过于紧密。代码维护困难。
解决方法:A服务只需要向消息服务器发送消息,而不用考虑谁需要这些数据;下游服务如果需要数据,自行从消息服务器订阅消息,不再需要数据时则取消订阅即可

在这里插入图片描述

2.2 流量削峰

背景:当特殊时间突发的访问压力,后台服务器可能顶不住访问压力而崩溃。
解决方案:使用RabbitMQ来进行流量削峰,高峰情况下,瞬间出现的大量请求数据,先发送到消息队列服务器,排队等待被处理,而我们的应用,可以慢慢的从消息队列接收请求数据进行处理,这样把数据处理时间拉长,以减轻瞬时压力
在这里插入图片描述

2.3 异步调用

同步调用存在的问题:系统响应时间长,每个服务执行完成,调用链才算完成。但是由于网络问题,整个执行链都无法完成。
异步调用优势:消息生产者把生产的消息放入消息队列,消费者订阅消息队列,消费消息。系统响应时间短,

三、RabbitMq六种工作模式

3.1 简单模式

3.1.1 消息生产者

1、创建连接工程
2、配置连接参数
3、创建队列
4、创建通讯通道
5、向服务器发送消息

public class Producer {
    public static void main(String[] args) throws IOException, TimeoutException {
        //1、连接
        ConnectionFactory f = new ConnectionFactory();
        //5672
        f.setHost("192.168.64.128");
        f.setPort(5672);
        f.setUsername("admin");
        f.setPassword("admin");
        //创建连接
        //2、在服务器上创建队列
        Connection connection = f.newConnection();
        //创建通讯通道
        Channel c = connection.createChannel();
        /**     queue – the name of the queue
        1、是否为持久队列,服务器重启后还存在
        durable – true if we are declaring a durable queue (the queue will survive a server restart)
        2、是否为排他队列,多个消费者能不能从同一个队列消费消息。
        exclusive – true if we are declaring an exclusive queue (restricted to this connection)
        3、没有消费者时,该队列是否被删除。
        autoDelete – true if we are declaring an autodelete queue (server will delete it when no longer in use)
        4、队列的其他属性
        arguments – other properties (construction arguments) for the queue*/
        c.queueDeclare("hello-rabbitmq",false,false,false,null);
        //3、向队列发送消息
         /**Params:
         1、交换机:空串默认的交换机
         exchange – the exchange to publish the message to
         2、队列名
         routingKey – the routing key
         3、消息的其他参数属性
         props – other properties for the message - routing headers etc
         4、消息体,为字节数组
         body – the message body*/
        c.basicPublish("","hello-rabbitmq",null,"helloworld".getBytes());
    }
}

3.2.1 消息消费者

public class ConSumer {
    public static void main(String[] args) throws IOException, TimeoutException {
        //1、连接
        ConnectionFactory f = new ConnectionFactory();
        //5672
        f.setHost("192.168.64.128");
        f.setPort(5672);
        f.setUsername("admin");
        f.setPassword("admin");
        //创建连接
        //2、在服务器上创建队列
        Connection connection = f.newConnection();
        //创建通讯通道
        //2、创建队列(谁先启动谁创建队列)
        Channel c = connection.createChannel();
        c.queueDeclare("hello-rabbitmq",false,false,false,null);
        //3、从队列 接收消息,把消息传递给回调对象处理
        //创建回调对象
        DeliverCallback deliverCallback = (consumerTag,message) -> {
            byte[] body = message.getBody();
            System.out.println(new String(body));
        };
        CancelCallback cancelCallback = consumerTag -> {};
        /**
         * Params:
         * queue – the name of the queue
         1、手动确认:可以防止消息没有被消费;手动消费:消费者宕机,可能存在消息丢失的情况。
         * autoAck – true if the server should consider messages acknowledged once delivered;
         * false if the server should expect explicit acknowledgements
         * deliverCallback – callback when a message is delivered
         * cancelCallback – callback when the consumer is cancelled*/
        c.basicConsume("hello-rabbitmq",true,deliverCallback,cancelCallback);

3.2 工作模式

工作模式:
一个生产者,多个消费者,每个消费者获取到的消息唯一。
1、 自动模式
消费者从消息队列获取消息后,服务端就认为该消息已经成功消费。
2、 手动模式
消费者从消息队列获取消息后,服务端并没有标记为成功消费
消费者成功消费后需要将状态返回到服务端
在这里插入图片描述

3.2.1 消息生产者

public class Producer {
    public static void main(String[] args) throws IOException, TimeoutException {
        //1、连接
        ConnectionFactory f = new ConnectionFactory();
        f.setHost("192.168.64.128");
        f.setPort(5672);
        f.setUsername("admin");
        f.setPassword("admin");
        //创建连接
        //2、在服务器上创建队列
        Connection connection = f.newConnection();
        //创建通讯通道
        Channel c = connection.createChannel();
        c.queueDeclare("hello-rabbitmq",false,false,false,null);
        //3、循环输入消息
        while (true){
            System.out.print("请输入消息");
            String msg = new Scanner(System.in).nextLine();
            c.basicPublish("","hello-rabbitmq", MessageProperties.PERSISTENT_BASIC,msg.getBytes());
        }
    }
}

3.2.2 消息消费者

public class Consumer {
    public static void main(String[] args) throws IOException, TimeoutException {
        ConnectionFactory f = new ConnectionFactory();
        f.setHost("192.168.64.128");
        f.setPort(5672);
        f.setUsername("admin");
        f.setPassword("admin");
        //创建连接
        //2、在服务器上创建队列
        Connection connection = f.newConnection();
        //创建通讯通道
        //2、创建队列(谁先启动谁创建队列)
        Channel c = connection.createChannel();
        //1、回调对象
        //2、接收消息
        DeliverCallback deliverCallback = (consumerTag, message) -> {
            String msg = new String(message.getBody(), "UTF-8");
            System.out.println("收到消息:" + msg);
            for (int i = 0; i < msg.length(); i++) {
                if (msg.charAt(i) == '.'){
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
            /**1、在message对象中有一个tag回执,发给服务器,第二个参数:是否同时确认过收到的所有消息*/
            c.basicAck(message.getEnvelope().getDeliveryTag(),false);
            System.out.println("处理完成");
        };
        CancelCallback cancelCallback = consumerTag -> {
        };
        /**每次只收一条,处理完之前不收下一条*/
        c.basicQos(1);
        /**qos=1不许再手动确认模式下生效*/
        c.basicConsume("hello-rabbitmq", false, deliverCallback, cancelCallback);
    }
}

3.3 发布订阅模式

在这里插入图片描述

3.4 路由模式

在这里插入图片描述
再消息上携带一个关键词,再

3.6 主题模式

四、RabbitMq六种工作模式(整合springboot)

4.1 依赖导入

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>

yml配置

spring:
  rabbitmq:
    host: 192.168.64.128
    port: 5672
    username: admin
    password: admin

4.2 简单模式(模式一)

@RabbitListener(queues = “helloworld”) 监听队列
@RabbitHandler //收消息,一个类中只能有一个@RabbitHandler,配合@RabbitListener使用

4.2.1 生产者

在这里插入图片描述

4.2.2 消费者

在这里插入图片描述

4.2.2 启动类配置队列参数

@SpringBootApplication
public class M1Application {
    public static void main(String[] args) {
        SpringApplication.run(M1Application.class,args);
    }

    /**设置队列参数:import org.springframework.amqp.core.Queue;
*         /**     queue – the name of the queue
*         1、是否为持久队列,服务器重启后还存在
*         durable – true if we are declaring a durable queue (the queue will survive a server restart)
*         2、是否为排他队列,多个消费者能不能从同一个队列消费消息。
*         exclusive – true if we are declaring an exclusive queue (restricted to this connection)
*         3、没有消费者时,该队列是否被删除。
*         autoDelete – true if we are declaring an autodelete queue (server will delete it when no longer in use)
*         4、队列的其他属性
*         arguments – other properties (construction arguments) for the queue*/
    @Bean
    public Queue helloworldQueue(){
        return new Queue("helloworld",false,false,false);
    }

    /**注入生成者*/
    @Autowired
    private Producer producer;

    /**Spring的主线程执行流程
     * 自动扫描创建实例--》完成依赖注入---》执行@PostConstruct--->后续步骤*/
    @PostConstruct
    public void test(){
        //新建一个线程,不不阻塞主线程
        new Thread(() -> {
            try {
                //等待消费者先启动
                Thread.sleep(3000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            producer.send();
        }).start();
    }

}

4.3 工作模式(模式二)

工作模式解决的问题:
合理分发:
1、自动确认设置为false
spring封装的rabbitmq默认手动保存
2、qos=1,与抓取消息数量

spring:
  rabbitmq:
    host: 192.168.64.128
    port: 5672
    username: admin
    password: admin
    listener:
      simple:
        prefetch: 1 #每次抓取一条,处理完之前不接收下一条,默认250

消息持久化:
1、队列持久化
在这里插入图片描述
2、消息数据的持久化
spring发送的消息默认就是持久消息

4.3.1 生产者

@Component
public class Producer {
    //发送消息的封装工具
    @Autowired
    private AmqpTemplate amqpTemplate;
    public void send(String s){
        //向队列中发送数据-自动转换为Bye数组
        //队列的参数在启动类中或则配置类中设置
        amqpTemplate.convertAndSend("tesk-queue",s);
    }
}

4.3.2 消费者

@Component
public class Consumer {

    @RabbitListener(queues = "tesk-queue")
    public void getmq1(String msg){
        System.out.println("消费者1收到消息:"+msg);
    }

    @RabbitListener(queues = "tesk-queue")
    public void getmq2(String msg){
        System.out.println("消费者2收到消息:"+msg);
    }
}

4.3.3 启动类

@SpringBootApplication
public class M2Application {
    public static void main(String[] args) {
        SpringApplication.run(M2Application.class, args);
    }

    @Bean
    public Queue helloworldQueue() {
        return new Queue("tesk-queue", false, false, false);
    }

    /**
     * 注入生成者
     */
    @Autowired
    private Producer producer;

    /**
     * Spring的主线程执行流程
     * 自动扫描创建实例--》完成依赖注入---》执行@PostConstruct--->后续步骤
     */
    @PostConstruct
    public void test() {
        //新建一个线程,不不阻塞主线程
        new Thread(() -> {
            while (true) {
                System.out.println("输入消息:");
                String s = new Scanner(System.in).nextLine();
                producer.send(s);
            }

        }).start();
    }
}

4.3 发布订阅模式(模式三)

生产者:指定交换机
消费者:创建队列,绑定交换机

4.3.1 生产者

生产者向指定的交换机 logs 发送数据.
不需要指定队列名或路由键, 即使指定也无效, 因为 fanout 交换机会向所有绑定的队列发送数据, 而不是有选择的发送.

@Component
public class Producer {
    //发送消息的封装工具
    @Autowired
    private AmqpTemplate amqpTemplate;
    public void send(String s){
        //向队列中发送数据-自动转换为Bye数组
        //队列的参数在启动类中或则配置类中设置
        amqpTemplate.convertAndSend("logs",s);
    }
}

4.3.2 消费者

消费者需要执行以下操作:
定义随机队列(随机命名,非持久,排他,自动删除)
定义交换机(可以省略, 已在主程序中定义)
将队列绑定到交换机
spring boot 通过注解完成以上操作:

@Component
public class Consumer {

    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(),//队列,随机命名,false,true
            //declare=false不创建交换机,而使用已存在的交换机
            exchange = @Exchange(value = "logs",declare = "false") //交换机

    ))
    public void getmq1(String msg){
        System.out.println("消费者1收到消息:"+msg);
    }

    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(),//队列,随机命名,false,true
            //declare=false不创建交换机,而使用已存在的交换机
            exchange = @Exchange(value = "logs",declare = "false") //交换机

    ))
    public void getmq2(String msg){
        System.out.println("消费者2收到消息:"+msg);
    }
}

4.3.3 主程序

@SpringBootApplication
public class M3Application {
    public static void main(String[] args) {
        SpringApplication.run(M3Application.class, args);
    }
    @Bean
    public FanoutExchange logs(){
        //非持久不自动删除
        return new FanoutExchange("logs",false,false);
    }
    
    @Autowired
    AmqpTemplate amqpTemplate;

    /**
     * Spring的主线程执行流程
     * 自动扫描创建实例--》完成依赖注入---》执行@PostConstruct--->后续步骤
     */
    @PostConstruct
    public void test() {
        //新建一个线程,不不阻塞主线程
        new Thread(() -> {
            while (true) {
                System.out.println("输入消息:");
                String s = new Scanner(System.in).nextLine();
                amqpTemplate.convertAndSend("logs","",s);
            }
        }).start();
    }
}

4.3 工作模式

4.3.1 生产者

4.3.2 消费者

4.3.3 主程序

4.3 工作模式

4.3.1 生产者

4.3.2 消费者

4.3.3 启动类

四、RabbitMQ服务配置刷新

主题模式:实现有选择的配置刷新

五、RabbitMQ订单流量削峰

提高系统高并发能力,削掉短时间的流量峰值,拉长订单处理时间,

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值