分布式基础概念 - 消息中间件 [RabbitMQ]
消息队列的优缺点,使用场景
优点:
解耦,降低系统之间的依赖
异步处理,不需要同步等待
削峰填谷,将流量从高峰期引到低谷期进行处理
缺点:
增加了系统的复杂度,幂等、重复消费、消息丢失等问题的带入
系统可用性降低,mq 的故障会影响系统可用
一致性,消费端可能失败
场景:
日志采集、发布订阅等
如何保证消息不被重复消费
幂等:一个数据或者一个请求,重复来多次,确保对应的数据是不会改变的,不能出错。
思路:
如果是写 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,备注- 技术交流 。或关注微信公众号【码上遇见你】。
版权声明: 本文为 InfoQ 作者【派大星】的原创文章。
原文链接:【http://xie.infoq.cn/article/868a837857e795310c2ecc893】。
本文遵守【CC BY-NC-ND】协议,转载请保留原文出处及本版权声明。
评论