RabbitMQ初级

本文详细介绍了RabbitMQ中涉及的消息队列协议,如AMQP、MQTT、OpenMessage和Kafka,强调了持久化和消息分发策略。通过入门案例展示了简单模式的生产者和消费者实现。RabbitMQ常用于解耦、消峰和异步处理,提高系统性能。此外,还讨论了工作模式中的轮询和公平分发策略。
摘要由CSDN通过智能技术生成


前言

消息中间件负责数据的传递,存储,分发,三部分,数据存储和分发的过程中肯定要遵循某种约定成的规范,而这些规范就是协议


一、消息队列协议

1. AMQP协议

AMQP:(全称: Advanced Message Queuing Protocol)是高级消息队列协议。由摩根大通集团联合其他公司共同设计(与 Spring是一家公司)。是一个提供统一消息服务的应用层标准高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。基于此协议的客户端与消息中间件可传递消息,并不受客户端/中间件不同产品,不同的开发语言等条件的限制。Erlang中的实现有RabbitMQ等。

特性:

  1. 分布式事务支持。
  2. 消息的持久化支持。
  3. 高性能和高可靠的消息处理优势。

2.MQTT协议

MQT协议: (Message Queueing Telemetry Transport)消息队列是IBM开放的一个即时通讯协议,物联网系统架构中的重要组成部分。

特点:

  1. 轻量
  2. 结构简单
  3. 传输快,不支持事考
  4. 没有持久化设计

应用场景

  1. 适用于计算能力有限
  2. 低带宽网络不稳定的场景

3.OpenMessage协议

是近几年由阿里、雅虎和滴滴出行、Sltrermalio等公司共同参与创立的分布式消息中间件、流处理等领域的应用开发标准。

特点:

  1. 结构简单
  2. 解析速度快
  3. 支持事务和持久化设计

4.kafka协议

Kafka协议是基于TCP/IP的二进制协议。消息内部足通过长度来分割,由一些基本数据类型组成。

特点:

  1. 结构简单
  2. 解析速度快
  3. 无事务支持
  4. 有持久化设计

二、消息队列持久化

1.常见的持久化方式

持久化方式ActiveMQRabbitMQkafkaRocketMQ
文件存储支持支持支持支持
数据库支持///

2.消息分发测略

MQ消息队列有如下几个角色

  1. 生产者
  2. 存储消息
  3. 消费者

那么生产者生成消息以后,MQ进行存,消费者是如何获取消息的呢?
一般获取数据的方式无外乎推(push)或者拉(pull)两种方式,典型的git就有推拉机制,我们发送的Ihttp请求就是一种典型的拉取数据库数据返回的过程。而消息队列MQ是一种推送的过程,而这些推机制会适用到很多的业务场景也有很多对应推机制策略。


三、入门案例

1.simple 简单模式

package com.rabbitmq.simple;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeoutException;

/**
 * @author chuyao
 * @version 1.0
 * @date 2021/7/26 10:34
 * @description: TODO 生产者简单模式 simple
 */
public class Producer {

    public static void main(String[] args) {
        //1.创建连接工程
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("47.108.167.33");
        connectionFactory.setPort(5672);
        connectionFactory.setUsername("admin");
        connectionFactory.setPassword("admin");
        connectionFactory.setVirtualHost("/");

        Connection connection = null;
        Channel channel = null;
        try {
            //2.创建 connection
            connection = connectionFactory.newConnection();
            //3.通过连接获取通道
            channel = connection.createChannel();
            //4.创建交换机,声明队列
            String name = "queue";
            /**
             * @params1 队列名称
             * @params2 是持久化,不管true/false持久化都会存盘,false的时候持久化存盘会随着服务器的重启而丢失。
             * @params3 排他性,是否是独占独立
             * @params4 是否自动删除,随着最后一个消费者消费完毕后是否把队列删除
             * @params5 附带数据
             */
            channel.queueDeclare(name,false,false,false,null);
            //5.发送消息给队列
            channel.basicPublish("",name,null,"hello rabbitMQ".getBytes(StandardCharsets.UTF_8));
            System.out.println("发送成功");
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        }finally {
            try {
                //6.关闭连接
                if (connection != null && connection.isOpen())
                    connection.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

            try {
                //7.关闭通道
                if (channel != null && channel.isOpen())
                    channel.close();
            } catch (IOException e) {
            } catch (TimeoutException e) {
                e.printStackTrace();
            }

        }
    }
}

package com.rabbitmq.simple;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 1. @author chuyao
 2. @version 1.0
 3. @date 2021/7/26 10:35
 4. @description: TODO 消费者简单模式 simple
 */
public class Consumer {
    public static void main(String[] args) {
        //1.创建连接工程
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("47.108.167.33");
        connectionFactory.setPort(5672);
        connectionFactory.setUsername("admin");
        connectionFactory.setPassword("admin");
        connectionFactory.setVirtualHost("/");

        Connection connection = null;
        Channel channel = null;
        try {
            //2.创建 connection
            connection = connectionFactory.newConnection();
            //3.通过连接获取通道
            channel = connection.createChannel();
            //4.创建交换机,声明队列
            String name = "queue";
            channel.basicConsume(name, true, new DeliverCallback() {
                @Override
                public void handle(String s, Delivery delivery) throws IOException {
                    System.out.println("接受消息" + new String(delivery.getBody(), "UTF-8"));
                }
            }, new CancelCallback() {
                @Override
                public void handle(String s) throws IOException {
                    System.out.println("接受消息失败");
                }
        });
            System.in.read();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        }finally {
            try {
                //6.关闭连接
                if (connection != null && connection.isOpen())
                    connection.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

            try {
                //7.关闭通道
                if (channel != null && channel.isOpen())
                    channel.close();
            } catch (IOException e) {
            } catch (TimeoutException e) {
                e.printStackTrace();
            }

        }
    }
}

常见面试题

  1. 消息队列不开启持久化数据会存盘吗?

    答案:是会的,首先开启持久化和不开启持久化数据都会存盘!只是当你不开始持久化时 数据会根据你服务器下一次的重启而丢失。

  2. RabbitMQ 为什么是基于 channel (信道)去处理而不是 connection (连接)?

    答案:因为 connection 是一个短连接,短连接就会经过三次握手四次挥手,是会很慢的,耗时很长,而且连接需要开启和关闭,这会造成很昂贵的性能开销。所以rabbitmq把连接处理变成一个长连接,而长连接里面会有很多信道 channel ,当我们在高并发的场景下信道的性能非常高。它可以创建多个信道,一个连接可以创建多个信道来处理消息。所以说它的性能是非常高的!

  3. 可以存在没有交换机的队列吗?
    答案:肯定是不可能的,就算你没有指定交换机,而它一定会存在一个默认的交换机(默认交换机是一种分发策略)!因为 rabbitmq底层语言采用的是Erlang语言,本身就是专门开发交换机的语言。交换机就负责消息的分发投递

2.work工作模式

工作模式有两种模式:

  1. 轮询模式的分发:—个消费者一条,按均分配;
  2. 公平分发:根据消费者的消费力进行公平分发,处理快的处理的多,处理慢的处理的少;按劳分配

RabbitMQ核心组成部分

在这里插入图片描述

RabbitMQ的操作流程

  1. 获取Conection
  2. 获取Channel
  3. 定义Exchange,Queue
  4. 使用一个RoutingKey将Queue Binding到一个Exchange上
  5. 通过指定一个Exchange和一个RoutingKey来将消息发送到对应的Queue上
  6. Consumer在接收时也是获取connection,接着获取channel,然后指定一个Queue,到Queue上取消息,它对Exchange,RoutingKey及如何Binding都不关心,到对应的Queue上去取消息就行了。

**注意:**一个PublisherClient发送消息,哪些ConsumerClient可以收到消息,在于Exchange,RoutingKey,Queue的关系上

RabbitMQ的运行流程图

在这里插入图片描述

四、RabbitMQ使用场景

1.解耦,消峰,异步

同步异步问题(串行)

串行方式:将订单信息写入数据库成功后,发送注册邮件,再发送注册短信。以上三个任务全部完成后,返回给客户端
在这里插入图片描述

并行方式,异步线程池
并行方式:将订单信息写入数据库成功后,发送注册邮件的同时,发送注册短信。以上三个任务完成后,返回给客户端。与串行的差别是,并行的方式可以提高处理的时间

在这里插入图片描述
异步线程池存在问题:

  1. 耦合度高
  2. 需要自己写线程池自己维护成本太高
  3. 出现了消息可能会丢失,需要你自己做消息补偿
  4. 如何保证消息的可靠性你自己写
  5. 如果服务器承载正了,你需要自己去写高可用

异步消息队列的方式

在这里插入图片描述
好处

  1. 完全解耦,用MQ建立桥接
  2. 有独立的线程池和运行模型
  3. 出现了消息可能会丢失,MQ有持久化功能
  4. 如何保证消息的可靠性,死信队列和消息转移的等
  5. 如果服务器承载不了,你需要自己去写高可用,HA镜像模型高可用。

按照以上约定,用户的响应时间相当于是订单信息写入数据库的时间,也就是50毫秒。注册邮件,发送短信写入消息队列后,直接返回,因此写入消息队列的速度很快,基本可以忽略,因此用户的响应时间可能是50毫秒。因此架构改变后,系统的吞吐量提高到每秒20
QPS。比串行提高了3倍,比并行提高了两倍

2.高内聚,低耦合

在这里插入图片描述
其他场景:

  1. 流量的削峰
  2. 分布式事务的可靠消费和可靠生产
  3. 索、缓存、静态化处理的数据同步
  4. 流量监控
  5. 日志监控(ELK)08、下单、订单分发、抢票
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值