【一】rabbitmq原理概述(exchange、queue、channel、connection、vhost)持久化、消息分发

一、架构图

网上找的

【一】rabbitmq原理概述(exchange、queue、channel、connection、vhost)持久化、消息分发_第1张图片

【一】rabbitmq原理概述(exchange、queue、channel、connection、vhost)持久化、消息分发_第2张图片

二、exchange(交换机)

 生产者发消息发往交换机,交换机会自己投递消息到绑定的queue队列

这里有几个点需要注意

1.exchange根据什么规则把消息投递到哪些queue中?

exchange有4种类型

direct

对routing_key进行严格匹配,当消息来到的时候,只有exchange与某queue绑定的routing_key完全匹配才将消息投递到该queue

【一】rabbitmq原理概述(exchange、queue、channel、connection、vhost)持久化、消息分发_第3张图片

【一】rabbitmq原理概述(exchange、queue、channel、connection、vhost)持久化、消息分发_第4张图片

【一】rabbitmq原理概述(exchange、queue、channel、connection、vhost)持久化、消息分发_第5张图片

topic

对routing_key进行通配符模糊匹配,满足条件的queue都能收到消息,这里的routing_key以"."分隔,*匹配一个单词,#匹配多个单词,如果同一个queue满足多个条件不会被投递多次

一下图为例,如果消息的routingkey是quick.orange.rabbit,那么Q1 Q2都会收到这条消息。

如果消息的routingkey是quick.orange.fox,那么Q1会收到这条消息

【一】rabbitmq原理概述(exchange、queue、channel、connection、vhost)持久化、消息分发_第6张图片

headers

根据消息体内的headers属性匹配,绑定的时候可以制定键值对。不依赖routing_key匹配。

没有图,大致逻辑与direct差不多,只不过不是用的routing_key来匹配

fanout

转发消息到所有绑定队列,不依赖routing_key匹配

在不需要路由的时候,一般是使用的这个类型的exchange。

发布订阅:两个queue绑定到同一个exchange上,那么同一个消息被发送到exchange后,exchange会把这个消息发给绑定的所有队列,两个消费者,一人消费一个队列,这就在这两个消费者之间达到了发布订阅的效果

【一】rabbitmq原理概述(exchange、queue、channel、connection、vhost)持久化、消息分发_第7张图片

 竞争消费:两个消费者消费同一个队列,这就达到了这两个消费者之间的竞争消费效果。注意,下图没有画exchange,实际上在写代码的时候不显示指定exchange的数据是发送到一个默认的exchange上的。

【一】rabbitmq原理概述(exchange、queue、channel、connection、vhost)持久化、消息分发_第8张图片

2.exchange持久化怎么搞?

如果不设置持久化,broker挂了,再重启,这个exchange就不存在了。

在客户端声明exchange的时候有个入参来控制是否持久化

Exchange.DeclareOk exchangeDeclare(String exchange, String type, boolean durable) throws IOException;
Exchange.DeclareOk exchangeDeclare(String exchange, String type, boolean durable, boolean autoDelete,
                                   Map arguments) throws IOException;
Exchange.DeclareOk exchangeDeclare(String exchange, String type) throws IOException;
Exchange.DeclareOk exchangeDeclare(String exchange,
                                          String type,
                                          boolean durable,
                                          boolean autoDelete,
                                          boolean internal,
                                          Map arguments) throws IOException;
void exchangeDeclareNoWait(String exchange,
                           String type,
                           boolean durable,
                           boolean autoDelete,
                           boolean internal,
                           Map arguments) throws IOException;
Exchange.DeclareOk exchangeDeclarePassive(String name) throws IOException;

上图中,durable即是是否持久化

而autoDelete则是,当没有queue绑定的时候是否自动删除这个exchange

3.生产者ACK机制?(事务或者confirm机制)

【二】rabbitmq生产者确保消息一定送达

4.投递方法(basicPublish)中的mandatory和immediate

mandatory
当mandatory标志位设置为true时,如果exchange根据自身类型和消息routeKey无法找到一个符合条件的queue,那么会调用basic.return方法将消息返回给生产者(Basic.Return + Content-Header + Content-Body);

当mandatory设置为false时,出现上述情形broker会直接将消息扔掉。

immediate
当immediate标志位设置为true时,如果exchange在将消息路由到queue(s)时发现对于的queue上没有消费者,那么这条消息不会放入队列中。当与消息routeKey关联的所有queue(一个或者多个)都没有消费者时,该消息会通过basic.return方法返还给生产者。

换句话说,无法找到一个消费者时,消息返还给生产者

三、queue队列

消息队列,先进先出,有缓存消息的能力。

1.有3种类型的队列:

1.可以设置成是持久化队列,这样消息会落盘,没有消费的消息重启后不会丢。

2.如果是临时队列,则没有持久化,堆积的数据在rabbitmq重启后会丢失。

3.如果设置为自动队列,当没有消费者消费这个队列的时候,队列会自动删除。

2.持久化队列怎么搞?

在channel声明队列的时候可以设置

    public com.rabbitmq.client.impl.AMQImpl.Queue.DeclareOk queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map arguments) throws IOException {
        validateQueueNameLength(queue);
        return (com.rabbitmq.client.impl.AMQImpl.Queue.DeclareOk)this.exnWrappingRpc((new com.rabbitmq.client.AMQP.Queue.Declare.Builder()).queue(queue).durable(durable).exclusive(exclusive).autoDelete(autoDelete).arguments(arguments).build()).getMethod();
    }

1.durable是否持久化队列。

2.exclusive是否为独占(排他)队列。

exclusive队列的特点是:

  1. 只对首次声明它的连接(Connection)可见,注意,是首次声明它的connection不是channel。
  2. 会在其连接断开的时候自动删除。

3.autoDelete是否自动删除,如果为是,当没有消费者消费这个队列的时候,这个队列会被自动删除。

注意,这里只是说持久化队列,持久化队列在rabbitmq重启后依旧存在,如果需要未消费的消息在重启后依旧存在,还需要持久化消息 

3.是否支持消息回溯?

不支持。rabbitmq不像kafka那样,并没有偏移量。rabbitmq即使是持久化队列+持久化消息,在被消费后该数据会被标记为删除,等待回收。

4.mirror queue镜像模式 (高可用)

另外一篇将

5.消费者ACK机制

【三】rabbitmq消费者ACK机制message acknowledgment

6.持久化消息怎么搞

如果只是持久化队列,没有持久化消息,那么重启后,队列存在,消息不存在了。

持久化消息的设置在channel.basicPublish方法的入参中

void basicPublish(String exchange, String routingKey, BasicProperties props, byte[] body) throws IOException;
void basicPublish(String exchange, String routingKey, boolean mandatory, BasicProperties props, byte[] body)
        throws IOException;
void basicPublish(String exchange, String routingKey, boolean mandatory, boolean immediate, BasicProperties props, byte[] body)
        throws IOException;

入参中的 BasicProperties props用于设置消息是否持久化

public BasicProperties(
            String contentType,//消息类型如:text/plain
            String contentEncoding,//编码
            Map headers,
            Integer deliveryMode,//消息是否持久化 1:nonpersistent 2:persistent
            Integer priority,//优先级
            String correlationId,
            String replyTo,//反馈队列
            String expiration,//expiration到期时间
            String messageId,
            Date timestamp,
            String type,
            String userId,
            String appId,
            String clusterId)

deliveryMode=1表示不需要持久化消息,deliveryMode=2表示需要持久化消息 

7.消息是怎么分发给消费者的?

Round-robin dispatching 循环分发(默认)

若存在多个consumer,每个consumer的负载可能不同,有些处理的快有些处理的慢,RabbitMQ并不管这些,只是简单的以round-robin的方式分配message,这可能造成某些consumer积压很多任务处理不完,而一些consumer长期处于饥饿状态

那么,Rabbit是如何处理这种问题呢? 

Fair dispatch 公平分发

通过basic.qos方法设置prefetch_count=1,这样RabbitMQ就会使得每个Consumer在同一个时间点最多处理一个Message,换句话说,在接收到该Consumer的ack前,它不会将新的Message分发给它

channel.basic_qos(prefetch_count=1) 

注意,这种方法可能会导致queue满。当然,这种情况下你可能需要添加更多的Consumer,或者创建更多的virtualHost来细化你的设计。 

四、channel信道

是建立在真实的TCP连接内的虚拟连接(是我们与RabbitMQ打交道的最重要的一个接口)。

仅仅创建了客户端到Broker之间的连接后,客户端还是不能发送消息的,需要为每一个Connection创建Channel,AMQP协议规定只有通过Channel才能执行AMQP的命令。

AMQP的命令都是通过信道发送出去的(我们大部分的业务操作是在Channel这个接口中完成的,包括定义Queue、定义Exchange、绑定Queue与Exchange、发布消息等。)。

每条信道都会被指派一个唯一ID。在客户端的每个连接里,可建立多个channel,每个channel代表一个会话任务,理论上无限制,减少TCP创建和销毁的开销,实现共用TCP的效果。

之所以需要Channel,是因为TCP连接的建立和释放都是十分昂贵的,如果一个客户端每一个线程都需要与Broker交互,如果每一个线程都建立一个TCP连接,暂且不考虑TCP连接是否浪费,就算操作系统也无法承受每秒建立如此多的TCP连接。

注1:一个生产者或一个消费者与MQ服务器之间只有一条TCP连接

注2:RabbitMQ建议客户端线程之间不要共用Channel,至少要保证共用Channel的线程发送消息必须是串行的,但是建议尽量共用Connection。

五、connection连接

是Publisher/Consumer和Broker之间的TCP连接。断开连接的操作只会在Publisher/Consumer端进行,Broker不会断开连接,除非出现网络故障或者Broker服务出现问题,Broker服务宕了。

六、virtual host虚拟主机

每一个vhost本质上是一个mini-rabbitmq server,分别管理各自的exchange,和bindings。

一个Broker里可以开有多个VirtualHost,它的作用是用作不同用户的权限分离。 
这个特性在做多租户的时候较方便

1.vhost特性

1.RabbitMQ默认的vhost是“/”开箱即用;

2.多个vhost是隔离的,多个vhost无法通讯,并且不用担心命名冲突(队列和交换器和绑定),实现了多层分离;

3.创建用户的时候必须指定vhost;

2.vhost操作

可以通过rabbitmqctl工具命令创建:

rabbitmqctl add_vhost[vhost_name]

删除vhost:

rabbitmqctl delete_vhost[vhost_name]

查看所有的vhost:

rabbitmqctl list_vhosts

你可能感兴趣的:(rabbitmq,exchange,queue,channel,connection,vhost)