分布式基础概念-消息中间件[RabbitMQ]

2023-11-30 17:01:34 浏览数 (2)

消息队列的优缺点,使用场景

优点:

  1. 解耦,降低系统之间的依赖
  2. 异步处理,不需要同步等待
  3. 削峰填谷,将流量从高峰期引到低谷期进行处理

缺点:

  1. 增加了系统的复杂度,幂等、重复消费、消息丢失等问题的带入
  2. 系统可用性降低,mq的故障会影响系统可用
  3. 一致性,消费端可能失败

场景:

  • 日志采集、发布订阅等

如何保证消息不被重复消费

幂等:一个数据或者一个请求,重复来多次,确保对应的数据是不会改变的,不能出错。

思路

  • 如果是写redis,就没问题,反正每次都是set,天然幂等性
  • 生产者发送消息的时候带上一个全局唯一的id,消费者拿到消息后,先根据这个id去redis里查一下,之前有没消费过,没有消费过就处理,并且写入这个id到redis,如果消费过了,则不处理。
  • 基于数据库的唯一键

Kafka、ActiveMQ、RabbitMQ、RocketMQ对比

ActiveMQ:JMS规范,支持事务、支持XA协议,没有生产大规模支撑场景、官方维护越来越少

RabbitMQ:erlang语言开发、性能好、高并发,支持多种语言,社区、文档方面有优势,erlang语言不利于java程序员二次开发,依赖开源社区的维护和升级,需要学习AMQP协议、学习成本相对较高

以上吞吐量单机都在万级

Kafka:高性能,高可用,生产环境有大规模使用场景,单机容量有限(超过64个分区响应明显变 长)、社区更新慢。吞吐量单机百万

RocketMQ:Java实现,方便二次开发、设计参考了Kafka,高可用、高可靠,社区活跃度一般、支持语言较少。吞吐量单机十万

详细对比可参考文章:直击灵魂的面试之MQ七连问

RabbitMQ架构设计

Broker:RabbitMQ的服务节点

Queue:队列,是RabbitMQ的内部对象,用于存储消息。RabbitMQ中消息只能存储在队列中。生产者投递消息到队列,消费者从队列中获取消息并消费。多个消费者可以订阅同一个队列,这时队列中的消息会被平均分摊(轮询)给多个消费者进行消费,而不是每个消费者都收到所有的消息进行消费。(注意:RabbitMQ不支持队列层面的广播消费,如果需要广播消费,可以采用一个交换器通过路由Key绑定多个队列,由多个消费者来订阅这些队列的方式。

Exchange:交换器。生产者将消息发送到Exchange,由交换器将消息路由到一个或多个队列中。如果路由不到,或返回给生产者,或直接丢弃,或做其它处理。

RoutingKey:路由Key。生产者将消息发送给交换器的时候,一般会指定一个RoutingKey,用来指定这个消息的路由规则。这个路由Key需要与交换器类型和绑定键(BindingKey)联合使用才能最终生效。在交换器类型和绑定键固定的情况下,生产者可以在发送消息给交换器时通过指定RoutingKey来决定消息流向哪里。

bindingKey:通过绑定将交换器和队列关联起来,在绑定的时候一般会指定一个绑定键,这样RabbitMQ就可以指定如何正确的路由到队列了。

交换器和队列实际上是多对多关系。就像关系数据库中的两张表。他们通过BindingKey做关联(多对多关系表)。在投递消息时,可以通过Exchange和RoutingKey(对应BindingKey)就可以找到相对应的队列。

信道:信道是建立在Connection之上的虚拟连接。当应用程序与Rabbit Broker建立TCP连接的时候,客户端紧接着可以创建一个AMQP信道(Channel),每个信道都会被指派一个唯一的D。RabbitMQ处理的每条AMQP指令都是通过信道完成的。信道就像电缆里的光纤束。一条电缆内含有许多光纤束,允许所有的连接通过多条光线束进行传输和接收。

vhost:虚拟主机,每一个应用可以指定不同的vhost,此时对于应用来说、vhost就是broker

image.png

简述RabbitMQ的交换机类型

交换器分发会先找出绑定的队列,然后再判断routekey,来决定是否将消息分发到某一个队列中。

Channel channel=connection.createChannel();//在RabbitMQ中创建一个信 channel.exchangeDeclare("exchangeName","direct");//创建一个type为direct的交换器 channel.queueDeclare("queueName");//创建一个队列 channel.queueBind("queueName","exchangeName","zhangsna");//绑定并设置路由键 channel.queueBind("queueName","exchangeName","lisi");//绑定并设置路由键 channel.queueBind("queueName","exchangeName","wangwu");//绑定并设置路由键

fanout:扇形交换机,不再判断routekey,直接将消息分发到所有绑定的队列

direct:判断routekey的规则是完全匹配模式,即发送消息时指定的routekey要等于绑定的routekey

topic:判断routekey的规则是模糊匹配模式

header:绑定队列与交换器的时候指定一个键值对,当交换器在分发消息的时候会先解开消息体里的headers数据,然后判断里面是否有所设置的键值对,如果发现匹配成功,才将消息分发到队列中;这种交换器类型在性能上相对来说较差,在实际工作中很少会用到

0 人点赞