RabbitMq的原理及四种常用方式(direct,fanout,topic)

1.RabbitMQ简介

在介绍RabbitMQ之前实现要介绍一下MQ,MQ是什么?

        MQ全称是Message Queue,可以理解为消息队列的意思,简单来说就是消息以管道的方式进行传递。
        RabbitMQ是一个实现了AMQP(Advanced Message Queuing Protocol)高级消息队列协议的消息队列服务,用Erlang语言的。

使用场景

        在我们秒杀抢购商品的时候,系统会提醒我们稍等排队中,而不是像几年前一样页面卡死或报错给用户。
像这种排队结算就用到了消息队列机制,放入通道里面一个一个结算处理,而不是某个时间断突然涌入大批量的查询新增把数据库给搞宕机,所以RabbitMQ本质上起到的作用就是削峰填谷,为业务保驾护航。

2.为什么选择RabbitMQ

         现在的市面上有很多MQ可以选择,比如ActiveMQ、ZeroMQ、Appche Qpid,那问题来了为什么要选择RabbitMQ?

1.除了Qpid,RabbitMQ是唯一一个实现了AMQP标准的消息服务器;
2.可靠性,RabbitMQ的持久化支持,保证了消息的稳定性;
3.高并发,RabbitMQ使用了Erlang开发语言,Erlang是为电话交换机开发的语言,天生自带高并发光环,和高可用特性;
4.集群部署简单,正是应为Erlang使得RabbitMQ集群部署变的超级简单;
5.社区活跃度高,根据网上资料来看,RabbitMQ也是首选;

3.工作机制

生产者、消费者和代理
在了解消息通讯之前首先要了解3个概念:生产者、消费者和代理
生产者:消息的创建者,负责创建和推送数据到消息服务器;
消费者:消息的接收方,用于处理数据和确认消息;
代理:就是RabbitMQ本身,用于扮演“快递”的角色,本身不生产消息,只是扮演“快递”的角色。
消息发送原理
        首先你必须连接到Rabbit才能发布和消费消息,那怎么连接和发送消息的呢?
        你的应用程序和Rabbit Server之间会创建一个TCP连接,一旦TCP打开,并通过了认证,认证就是你试图连接Rabbit之前发送的Rabbit服务器连接信息和用户名和密码,有点像程序连接数据库,使用Java有两种连接认证的方式,后面代码会详细介绍,一旦认证通过你的应用程序和Rabbit就创建了一条AMQP信道(Channel)。
        信道是创建在“真实”TCP上的虚拟连接,AMQP命令都是通过信道发送出去的,每个信道都会有一个唯一的ID,不论是发布消息,订阅队列或者介绍消息都是通过信道完成的。
为什么不通过TCP直接发送命令?
        对于操作系统来说创建和销毁TCP会话是非常昂贵的开销,假设高峰期每秒有成千上万条连接,每个连接都要创建一条TCP会话,这就造成了TCP连接的巨大浪费,而且操作系统每秒能创建的TCP也是有限的,因此很快就会遇到系统瓶颈。
如果我们每个请求都使用一条信道连接,既满足了性能的需要,又能确保每个连接的私密性,这就是引入信道概念的原因。
你必须知道的Rabbit
想要真正的了解Rabbit有些名词是你必须知道的。
包括:ConnectionFactory(连接管理器)、Channel(信道)、Exchange(交换器)、Queue(队列)、RoutingKey(路由键)、BindingKey(绑定键)。
ConnectionFactory(连接管理器):应用程序与Rabbit之间建立连接的管理器,程序代码中使用;
Channel(信道):消息推送使用的通道;
Exchange(交换器):用于接受、分配消息;
Queue(队列):用于存储生产者的消息;
RoutingKey(路由键):用于把生成者的数据分配到交换器上;
BindingKey(绑定键):用于把交换器的消息绑定到队列上

看到上面的解释,最难理解的路由键和绑定键了,那么他们具体怎么发挥作用的,请看下图
在这里插入图片描述

4.消息持久化

Rabbit队列和交换器有一个不可告人的秘密,就是默认情况下重启服务器会导致消息丢失,那么怎么保证Rabbit在重启的时候不丢失呢?答案就是消息持久化。

当你把消息发送到Rabbit服务器的时候,你需要选择你是否要进行持久化,但这并不能保证Rabbit能从崩溃中恢复,想要Rabbit消息能恢复必须满足3个条件:
投递消息的时候durable设置为true,消息持久化,代码:channel.queueDeclare(x, true, false, false, null),参数2设置为true持久化;
设置投递模式deliveryMode设置为2(持久),代码:channel.basicPublish(x, x, MessageProperties.PERSISTENT_TEXT_PLAIN,x),参数3设置为存储纯文本到磁盘;
消息已经到达持久化交换器上;
消息已经到达持久化的队列;
持久化工作原理
Rabbit会将你的持久化消息写入磁盘上的持久化日志文件,等消息被消费之后,Rabbit会把这条消息标识为等待垃圾回收。
持久化的缺点
消息持久化的优点显而易见,但缺点也很明显,那就是性能,因为要写入硬盘要比写入内存性能较低很多,从而降低了服务器的吞吐量,尽管使用SSD硬盘可以使事情得到缓解,但他仍然吸干了Rabbit的性能,当消息成千上万条要写入磁盘的时候,性能是很低的。
所以使用者要根据自己的情况,选择适合自己的方式。
虚拟主机
每个Rabbit都能创建很多vhost,我们称之为虚拟主机,每个虚拟主机其实都是mini版的RabbitMQ,拥有自己的队列,交换器和绑定,拥有自己的权限机制。
vhost特性
RabbitMQ默认的vhost是“/”开箱即用;
多个vhost是隔离的,多个vhost无法通讯,并且不用担心命名冲突(队列和交换器和绑定),实现了多层分离;

5.rabbitmq常用(direct,fanout,topic)

举例用到的三个类:MqConfig,MqSender,MqReceiver

1.direct模式

direct原理图
在这里插入图片描述
我们可以看到direct模式下,无需路由键和绑定键,生产者生产消息通过交换器到了队列,然后由消费者消费
direct实现
MqConfig

    /**
     * direct的队列
     */
    public static final String DIRECT_QUEUE = "direct.queue";

    @Bean
    public Queue directQueue() {
        return new Queue(DIRECT_QUEUE, true);
    }

MqSender

   /**
     * direct 模式
     *
     * @param messages 发的消息
     */
    public void sendDirectMessage(Object messages) {
        String message = RedisService.beanToString(messages);
        log.info("rabbitmq send direct message:" + message);
        amqpTemplate.convertAndSend(MqConfig.DIRECT_QUEUE, message);
    }

MqReceiver

  @RabbitListener(queues = MqConfig.DIRECT_QUEUE)
    public void receiveDirectMessage(String message) {
        log.info("rabbitmq receive direct message:" + message);
    }

测试结果:
在这里插入图片描述

2.topic模式

topic原理图
在这里插入图片描述
我们可以看到topic模式下,路由键和绑定键,生产者生产消息通过交换器到了队列,然后由消费者消费,绑定键中的#,#代表匹配一个或者多个单词,*代表不多于1个单词,就是出现0或者1个
实现
MqConfig

/**
     * 第二种方式:topic方式
     *
     * @return
     */

    /**
     * topic的队列
     */
    public static final String TOPIC_QUEUE1 = "topic.queue1";
    public static final String TOPIC_QUEUE2 = "topic.queue2";
    public static final String TOPIC_QUEUE3 = "topic.queue3";
    public static final String TOPIC_EXCHANGE = "topic.exchange";

    @Bean
    public Queue topicQueue1() {
        return new Queue(TOPIC_QUEUE1, true);
    }

    @Bean
    public Queue topicQueue2() {
        return new Queue(TOPIC_QUEUE2, true);
    }
    @Bean
    public Queue topicQueue3() {
        return new Queue(TOPIC_QUEUE3, true);
    }

    @Bean
    public TopicExchange topicExchange() {
        return new TopicExchange(TOPIC_EXCHANGE);
    }

    @Bean
    public Binding bindingTopicExchange1() {
        return BindingBuilder.bind(topicQueue1()).to(topicExchange()).with("topic.#");
    }

    @Bean
    public Binding bindingTopicExchange2() {
        return BindingBuilder.bind(topicQueue2()).to(topicExchange()).with("topic.keys1");
    }
    @Bean
    public Binding bindingTopicExchange3() {
        return BindingBuilder.bind(topicQueue3()).to(topicExchange()).with("topic.*");
    }

MqSender

  /**
     * topic 模式
     *
     * @param messages 发的消息
     */
    public void sendTopicMessage(Object messages) {
        String message = RedisService.beanToString(messages);
        log.info("rabbitmq send topic message:" + message);
                amqpTemplate.convertAndSend(MqConfig.TOPIC_EXCHANGE, "topic.keys2", "topic.keys2:" + message);
        amqpTemplate.convertAndSend(MqConfig.TOPIC_EXCHANGE, "topic.keys1.time", "topic.keys1.time:" + message);
    }

MqReceiver

      @RabbitListener(queues = MqConfig.TOPIC_QUEUE1)
    public void receiveTopicMessage1(String message) {
        log.info("rabbitmq receive topic message:" + message);
    }

    @RabbitListener(queues = MqConfig.TOPIC_QUEUE2)
    public void receiveTopicMessage2(String message) {
        log.info("rabbitmq receive topic message:" + message);
    }

    @RabbitListener(queues = MqConfig.TOPIC_QUEUE3)
    public void receiveTopicMessage3(String message) {
        log.info("rabbitmq receive topic message:" + message);
    }

测试结果:
绑定键topic.keys1

amqpTemplate.convertAndSend(MqConfig.TOPIC_EXCHANGE, "topic.keys1", "topic.keys1:" + message);

绑定键topic.keys1,满足条件的队列为1,2,3
在这里插入图片描述
绑定键topic.keys2

amqpTemplate.convertAndSend(MqConfig.TOPIC_EXCHANGE, "topic.keys2", "topic.keys2:" + message);

绑定键topic.keys2,满足条件的队列为1,3
在这里插入图片描述
绑定键topic.keys1.time

        amqpTemplate.convertAndSend(MqConfig.TOPIC_EXCHANGE, "topic.keys1.time", "topic.keys1.time:" + message);

绑定键topic.keys1.time,满足条件的队列为1
在这里插入图片描述

3.fanout模式

fanout理图
在这里插入图片描述
我们可以看到fanout模式下,有路由键,无绑定键,生产者生产消息通过交换器到了队列,然后由消费者消费
fanout实现
MqConfig

 /**
     * 第三种:fanout广播模式
     */

    /**
     * fanout
     */

    public static final String FANOUT_QUEUE1 = "fanout.queue1";
    public static final String FANOUT_QUEUE2 = "fanout.queue2";
    public static final String FANOUT_EXCHANGE = "fanout.exchange";
    @Bean
    public Queue fanoutQueue1() {
        return new Queue(FANOUT_QUEUE1,true);
    }
    @Bean
    public Queue fanoutQueue2() {
        return new Queue(FANOUT_QUEUE2,true);
    }
    @Bean
    public FanoutExchange fanoutExchange(){
        return new FanoutExchange(FANOUT_EXCHANGE);
    }
    @Bean
    public Binding bindingFanoutBinding1(){
        return BindingBuilder.bind(fanoutQueue1()).to(fanoutExchange());
    }
    @Bean
    public Binding bindingFanoutBinding2(){
        return BindingBuilder.bind(fanoutQueue2()).to(fanoutExchange());
    }

MqSender

/**
     * fanout 模式
     *
     * @param messages 发的消息
     */
    public void sendFanoutMessage(Object messages) {
        String message = RedisService.beanToString(messages);
        log.info("rabbitmq send fanout message:" + message);
        amqpTemplate.convertAndSend(MqConfig.FANOUT_EXCHANGE, "", message);
    }

MqReceiver

@RabbitListener(queues = MqConfig.FANOUT_QUEUE1)
    public void receiveFanoutMessage1(String message) {
        log.info("rabbitmq receive fanout message:" + message);
    }

    @RabbitListener(queues = MqConfig.FANOUT_QUEUE2)
    public void receiveFanoutMessage2(String message) {
        log.info("rabbitmq receive fanout message:" + message);
    }

测试结果:
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
RabbitMQ是一个开源的消息中间件,它实现了高效的消息传递机制。其原理基于AMQP(Advanced Message Queuing Protocol,高级消息队列协议),它定义了消息的格式和传输规范。 RabbitMQ的核心概念是生产者、消费者和队列。生产者负责发送消息到队列,消费者从队列中接收消息并进行处理。队列是消息的缓冲区,它保存了待处理的消息。 当生产者发送消息时,RabbitMQ会将消息存储在队列中。消费者可以通过订阅队列来接收消息。RabbitMQ会按照一定的策略将消息分发给消费者,例如轮询、优先级等。 RabbitMQ还支持交换机(Exchange)的概念。生产者将消息发送给交换机,交换机根据特定的规则将消息路由到一个或多个队列。这个规则可以通过绑定(Binding)来定义,绑定将交换机和队列关联起来。 RabbitMQ提供了多种交换机类型,包括直连交换机(Direct Exchange)、主题交换机(Topic Exchange)、广播交换机(Fanout Exchange)等。不同类型的交换机根据路由规则的不同方式来决定消息的路由。 另外,RabbitMQ还具备持久化、确认机制、消息过期等特性,以提供更可靠和灵活的消息传递。 总结来说,RabbitMQ通过生产者将消息发送到队列,消费者从队列中接收并处理消息,交换机负责将消息路由到队列。这种基于AMQP的消息传递机制使得应用程序能够实现解耦、异步通信和可靠的消息传递。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值