RabbitMq

rabbitmq基础

目标
  • RabbitMq是什么?
  • RabbitMq能解决什么问题?
  • RabbitMq怎么用的?
  • RabbitMq模式
  • 安装RabbitMq
  • 掌握springboot整合RabbitMq
什么是MQ

MQ(Message Quene) : 翻译为 消息队列,通过典型的 生产者消费者模型,生产者不断向消息队列中生产消息,消费者不断的从队列中获取消息。因为消息的生产和消费都是异步的,而且只关心消息的发送和接收,没有业务逻辑的侵入,轻松的实现系统间解耦。别名为 消息中间件 通过利用高效可靠的消息传递机制进行平台无关的数据交流,并基于数据通信来进行分布式系统的集成。

MQ有哪些

当今市面上有很多主流的消息中间件,如老牌的ActiveMQRabbitMQ,炙手可热的Kafka,阿里巴巴自主开发RocketMQ等。

AMQP和JMS

MQ是消息通信的模型,并不是具体实现。现在实现MQ的有两种主流方式:AMQP、JMS。

两者间的区别和联系:

  • JMS是定义了统一的接口,来对消息操作进行统一;AMQP是通过规定协议来统一数据交互的格式
  • JMS限定了必须使用Java语言;AMQP只是协议,不规定实现方式,因此是跨语言的。
  • JMS规定了两种消息模型;而AMQP的消息模型更加丰富

JMS是一种规范,只限定java语言,从API层进行限定,类比于JDBC

AMQP是一种协议,不限定语言,它兼容JMS,类比于http协议

MQ的作用

1、解耦
场景说明:用户下单后,订单系统需要通知库存系统

  • 传统做法

    • 传统的做法是,订单系统调用库存系统的接口。如下图:
      在这里插入图片描述
    • 传统模式的缺点:假如库存系统无法访问,则订单减库存将失败,从而导致订单失败,订单系统与库存系统耦合
    • 如何解决以上问题呢?
  • 使用消息队列

    • 引入应用消息队列后的方案,如下图:
      在这里插入图片描述
    • 订单系统:用户下单后,订单系统完成持久化处理,将消息写入消息队列,返回用户订单下单成功
    • 库存系统:订阅下单的消息,采用拉/推的方式,获取下单信息,库存系统根据下单信息,进行库存操作
    • 在下单时库存系统不能正常使用。也不影响正常下单,因为下单后,订单系统写入消息队列就不再关心其他的后续操作了。实现订单系统与库存系统的应用解耦

2、异步
场景说明:用户注册后,需要发注册邮件和注册短信

  • 传统做法

    • a.串行:将注册信息写入数据库成功后,发送注册邮件,再发送注册短信
      在这里插入图片描述

    • b.并行:将注册信息写入数据库成功后,发送注册邮件的同时,发送注册短信
      在这里插入图片描述

  • 使用消息队列

    • 将不是必须的业务逻辑,异步处理。改造后的架构如下:
      在这里插入图片描述

3、流量削峰
场景说明:商品秒杀业务,一般会因为流量过大,导致流量暴增,应用挂掉

  • 传统做法
    • 限制用户数量
  • 使用消息队列
    • 用户的请求,服务器接收后,首先写入消息队列,秒杀业务根据消息队列中的请求信息,再做后续处理
      在这里插入图片描述

    • 假如消息队列长度超过最大数量,则直接抛弃用户请求或跳转到错误页面。

4、日志处理

日志处理是指将消息队列用在日志处理中,比如Kafka的应用,解决大量日志传输的问题

  • 使用消息队列完成日志处理
    • 日志采集客户端,负责日志数据采集,定时写受写入Kafka队列
    • Kafka消息队列,负责日志数据的接收,存储和转发
    • 日志处理应用:订阅并消费kafka队列中的日志数据
      在这里插入图片描述
MQ的具体产品
`1、RabbitMq`
	+ ERLANG语言 用起来环境要求高
	+ 吞吐量 5W级别
	+ 消息的延迟级别 WS
	+ 支持很多协议
    + 消息会丢失(少)
`kafka`  apache
	+ 吞吐量特别高 可以到100W/S
	+ 高堆积能力(亿级)
	+ 会丢失消息数据
	+ 消息的延迟级别  MS
	+ 在日志领域里使用比较多
`rocketmq` apache alibaba 
	+ JAVA  
	+ 吞吐量很高(50w/s)
	+ 高堆积能力(亿级)
	+ 在线扩容能力很强
    + 理论上不丢失
    + 支持事务消息
    + 可以实现消费消息的顺序性
    + 消息的延迟级别 MS

`activemq `apache
	+性能差
	+消息丢失很严重 
	+ spring
RabbitMq的组件介绍

Server:Broker、RabbitMQ Server,实现 AMOP 实体服务,接受客户端的连接

Conneciton:链接,应用程序与 Server 的网络连接

Channel:网络信道,进行消息读写的通道,客户端可以建立多个 Channel,每个 Channel 就是一个会话

Message:消息,服务器和应用程序之间传输的数据,由 Properties 和 Body 组成。Properties 用于修饰消息,比如消息优先级、延迟等,Body 是消息体

Virtual host:虚拟地址,用于逻辑隔离,是最上层的路由。一个虚拟地址中可以有多个 Exchange 和 Queue,但不允许同名

Exchange:交换机,用于接收生产者的消息,根据 Routing key 转发到 Queue

Queue:Message queue,消息队列,保存消息并转发给消费者,消费者监听这个队列达到接收消息的目的

Bingding:Exchange 和 Queue 之间的虚拟连接,可以包含多个 Routing key
在这里插入图片描述

在这里插入图片描述

端口:

5672: rabbitMq的编程语言客户端连接端口

15672:rabbitMq管理界面端口

25672:rabbitMq集群的端口

RabbitMq的消息模式

在这里插入图片描述
在这里插入图片描述

1、简单模式
2、工作模式 work
3、发布订阅模式
+ 广播模式 fanout
+ 路由模式 direct
+ 通配符模式 route

简单模式

在这里插入图片描述
在上图的模型中,有以下概念:

  • P:生产者,也就是要发送消息的程序
  • C:消费者:消息的接受者,会一直等待消息到来。
  • queue:消息队列,图中红色部分。类似一个邮箱,可以缓存消息;生产者向其中投递消息,消费者从其中取出消息。

pom

<dependency>
    <groupId>com.rabbitmq</groupId>
    <artifactId>amqp-client</artifactId>
    <version>5.7.2</version>
</dependency>

开发生产者

public class provider {
   
    //消息生产者
    public static void main(String[] args) throws IOException, TimeoutException {
   
        //创建连接工厂对象
        ConnectionFactory connectionFactory = new ConnectionFactory();
        //设置RabbitMq服务主机地址默认localhost
        connectionFactory.setHost("localhost");
        //设置RabbitMq服务端口,默认5672
        connectionFactory.setPort(5672);
        //设置虚拟主机名称,默认/
        connectionFactory.setVirtualHost("/");
        //设置用户连接名,默认guest
        connectionFactory.setUsername("guest");
        //设置连接密码,默认guest
        connectionFactory.setPassword("guest");
        //创建连接
        Connection connection = connectionFactory.newConnection();
        //创建频道
        Channel channel = connection.createChannel();
        /**
         * 声明队列
         * 参数1:队列名称
         * 参数2:是否定义持久化队列
         * 参数3:是否独占本次连接
         * 参数4:是否在不使用的时候自动删除队列
         * 参数5:队列其它参数
         * **/
        channel.queueDeclare("simple_queue", true, false, false, null);
        //创建消息
        String message = "hello!welcome to itheima";
        /**
         * 消息发送
         * 参数1:交换机名称,如果没有指定则使用默认Default Exchage
         * 参数2:路由key,简单模式可以传递队列名称
         * 参数3:消息其它属性
         * 参数4:消息内容
         */
        channel.basicPublish("", "simple_queue", null, message.getBytes());
        //关闭资源
        channel.close();
        connection.close();
    }
}

开发消费者

//简单模式接受消息
public class consumer {
   
    public static void main(String[] args) throws IOException, TimeoutException {
   
        //创建链接工厂对象
        ConnectionFactory connectionFactory = new ConnectionFactory();
        //设置RabbitMQ服务主机地址,默认localhost
        connectionFactory.setHost("localhost");
        //设置RabbitMQ服务端口,默认5672
        connectionFactory.setPort(5672);
        //设置虚拟主机名字,默认/
        connectionFactory.setVirtualHost("/");
        //设置用户连接名,默认guest
        connectionFactory.setUsername("guest");
        //设置链接密码,默认guest
        connectionFactory.setPassword("guest");
        //创建链接
        Connection connection = connectionFactory.newConnection();
        //创建频道
        Channel channel = connection.createChannel();
        //声明队列
        channel.queueDeclare("simple_queue",true,false,false,null);
        //创建消费者,并设置消息处理(实现业务逻辑 比如:订单(生产者)发送消息, 库存(消费者)扣减库存)
        DefaultConsumer defaultConsumer = new DefaultConsumer(channel){
   
            /**
             * @param consumerTag 消费者名称 如果之前没有指定 则自己生成一个
             * @param envelope 就是消息的一些额外参数 比如exchange routing key等数据
             * @param properties 另外的数据所封装的属性对象
             * @param body  消息本身
             * @throws IOException
             */
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
   
                //自己实现消费的业务逻辑
                //路由的key
                String routingKey = envelope.getRoutingKey();
                //获取交换机信息
                String exchange = envelope.getExchange();
                //获取消息ID
                long deliveryTag = envelope.getDeliveryTag();
                //获取消息信息
                String message = new String(body,"UTF-8");
                System.out.println("routingKey:"+routingKey+",exchange:"+exchange+",deliveryTag:"+deliveryTag+",message:"+message);
            }
        };
        //消息监听
        //参数1 指定要监听的队列名称
        //参数2 设置消息的应答模式 true 自动应答  false 手动应答(消费者手动ACK)
        channel.basicConsume("simple_queue",true,defaultConsumer);
        //关闭资源(不建议关闭,建议一直监听消息)
    }
}

参数说明

  channel.queueDeclare("simple_queue",true,false,false,null);
  '参数1':用来声明通道对应的队列
  '参数2':用来指定是否持久化队列
  '参数3':用来指定是否独占队列
  '参数4':用来指定是否自动删除队列
  '参数5':对队列的额外配置
第二种模型(work quene)

Work queues,也被称为(Task queues),任务模型。当消息处理比较耗时的时候,可能生产消息的速度会远远大于消息的消费速度。长此以往,消息就会堆积越来越多,无法及时处理。此时就可以使用work 模型:让多个消费者绑定到一个队列,共同消费队列中的消息。队列中的消息一旦消费,就会消失,因此任务是不会被重复执行的。

在这里插入图片描述

角色:

  • P:生产者:任务的发布者
  • C1:消费者-1,领取任务并且完成任务,假设完成速度较慢
  • C2:消费者-2:领取任务并完成任务,假设完成速度快

首先写一个工具类

public class ConnectionUtil {
   

    /***
     * 创建链接对象
     * @return
     * @throws IOException
     * @throws TimeoutException
     */
    public static Connection getConnection() throws IOException, TimeoutException {
   
        //创建链接工厂对象
        ConnectionFactory connectionFactory = new ConnectionFactory();

        //设置RabbitMQ服务主机地址,默认localhost
        connectionFactory.setHost("localhost");

        //设置RabbitMQ服务端口,默认5672
        connectionFactory.setPort(5672);

        //设置虚拟主机名字,默认/
        connectionFactory.setVirtualHost("/");

        //设置用户连接名,默认guest
        connectionFactory.setUsername("guest");

        //设置链接密码,默认guest
        connectionFactory.setPassword("guest");

        //创建链接
        Connection connection = connectionFactory.newConnection();
        return connection;
    }
}

生产者

//work工作模式
public class Provider {
   
    public static void main(String[] args) throws IOException, TimeoutException {
   
        Connection connection = ConnectionUtil.getConnection();

        //创建频道
        Channel channel = connection.createChannel();
        /**
         * 声明队列
         * 参数1:队列名称
         * 参数2:是否定义持久化队列
         * 参数3:是否独占本次连接
         * 参数4:是否在不使用的时候自动删除队列
         * 参数5:队列其它参数
         * **/
        channel.queueDeclare("work_queue", true, false, false, null);
        for (int i = 0; i < 20; i++) {
   
            //创建消息
            String message = "hello world work" + i;
            /**
             * 消息发送
             * 参数1:交换机名称,如果没有指定则使用默认Default Exchage
             * 参数2:路由key,简单模式可以传递队列名称
             * 参数3:消息其它属性
             * 参数4:消息内容
             */
            channel.basicPublish("", "work_queue", null, message.getBytes());
        }
        //关闭资源
        channel.close();
        connection.close();
    }
}

消费者1

//工作模式接受消息
public class consumer1 {
   
    public static void main(String[] args) throws IOException, TimeoutException {
   
        Connection connection = ConnectionUtil.getConnection();
        //创建频道
        Channel channel = connection.createChannel();
        //声明队列
        channel.queueDeclare("work_queue", true, false, false, null);
        //创建消费者,并设置消息处理(实现业务逻辑 比如:订单(生产者)发送消息, 库存(消费者)扣减库存)
        DefaultConsumer defaultConsumer = new DefaultConsumer(channel) {
   
            /**
             * @param consumerTag 消费者名称 如果之前没有指定 则自己生成一个
             * @param envelope 就是消息的一些额外参数 比如exchange routing key等数据
             * @param properties 另外的数据所封装的属性对象
             * @param body  消息本身
             * @throws IOException
             */
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
   
                //自己实现消费的业务逻辑
                System.out.println("11111111111");
                //路由的key
                String routingKey = envelope.getRoutingKey();
                //获取交换机信息
                String exchange = envelope.getExchange();
                //获取消息ID
                long deliveryTag = envelope.getDeliveryTag();
                //获取消息信息
                String message = new String(body, "UTF-8");
                System.out.println("routingKey:" + routingKey + ",exchange:" + exchange + ",deliveryTag:" + deliveryTag + ",message:" + message);
            }
        };
        //消息监听
        //参数1 指定要监听的队列名称
        //参数2 设置消息的应答模式 true 自动应答  false 手动应答(消费者手动ACK)
        channel.basicConsume("work_queue", true, defaultConsumer);
        //关闭资源(不建议关闭,建议一直监听消息)
    }
}

消费者2

//work模式接受消息
public class consumer2 {
   
    public static void main(String[] args) throws IOException, TimeoutException {
   
        Connection connection = ConnectionUtil.getConnection();
        //创建频道
        Channel channel = connection.createChannel();
        //声明队列
        channel.queueDeclare("work_queue",true,false,false,null);
        //创建消费者,并设置消息处理(实现业务逻辑 比如:订单(生产者)发送消息, 库存(消费者)扣减库存)
        DefaultConsumer defaultConsumer = new DefaultConsumer(channel){
   
            /**
             * @param consumerTag 消费者名称 如果之前没有指定 则自己生成一个
             * @param envelope 就是消息的一些额外参数 比如exchange routing key等数据
             * @param properties 另外的数据所封装的属性对象
             * @param body  消息本身
             * @throws IOException
             */
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
   
                //自己实现消费的业务逻辑
                System.out.println("22222222222222");
                //路由的key
                String routingKey = envelope.getRoutingKey();
                //获取交换机信息
                String exchange = envelope.getExchange();
                //获取消息ID
                long deliveryTag = envelope.getDeliveryTag();
                //获取消息信息
                String message = new String(body,"UTF-8");
                System.out.println("routingKey:"+routingKey+",exchange:"+exchange+",deliveryTag:"+deliveryTag+",message:"+message);
            }
        
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值