写点什么

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

作者:派大星
  • 2023-12-12
    辽宁
  • 本文字数:1895 字

    阅读完需:约 6 分钟

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

优点:


  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


简述 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 数据,然后判断里面是否有所设置的键值对,如果发现匹配成功,才将消息分发到队列中;这种交换器类型在性能上相对来说较差,在实际工作中很少会用到


如有问题,欢迎加微信交流:w714771310,备注- 技术交流  。或关注微信公众号【码上遇见你】。




发布于: 刚刚阅读数: 5
用户头像

派大星

关注

微信搜索【码上遇见你】,获取更多精彩内容 2021-12-13 加入

微信搜索【码上遇见你】,获取更多精彩内容

评论

发布
暂无评论
分布式基础概念-消息中间件[RabbitMQ]_Java 面试题_派大星_InfoQ写作社区