java基础之----RabbitMQ
概述
MQ也即是Message Queue,翻译就是消息队列,那何为消息队列就是存放消息的队列,为什么需要一个队列来存放这些消息,为什么不直接发送呢?举个例子,如果一个服务正常可以支持tps是1000,现在突然请求量达到5000,怎么办,有可能直接把服务器访问崩溃了,这时如果使用消息队列,就可以先把消息放到队列中,然后服务器慢慢处理,这叫做错峰处理,在我们实际的工作经验中,mq更多的是用在服务端需要长时间处理的场景中,因为http请求要求的时效性太高,如果服务端处理时间过长,就会导致很多的请求超时,而使用mq就可以解决这个问题。
消息队列的作用
上面概述中已经叙述了消息队列的部分功能,下面列举一下消息队列的详细作用。
- 非实时性:当不需要立即获得结果,但是并发量又需要进行控制的时候,差不多就是需要使用消息队列的时候。主要解决了应用耦合、异步处理、流量削锋等问题。
- 应用耦合:多应用间通过消息队列对同一消息进行处理,避免调用接口失败导致整个过程失败;(如:订单->库存)
- 异步处理:多应用对消息队列中同一消息进行处理,应用间并发处理消息,相比串行处理,减少处理时间;(点对多场景,广播场景(注册发短信,发邮件)等等)
- 限流削峰:应用于秒杀或抢购活动中,避免流量过大导致应用系统挂掉的情况;(根据服务承受度设置队列大小,超过了就返回活动结束了,咱们经常各大商城秒杀,心里还没有点B数吗)减少压力,避免服务挂掉。
- 消息驱动的系统:系统分为消息队列、消息生产者、消息消费者,生产者负责产生消息,消费者(可能有多个)负责对消息进行处理;(分工处理(各自对应相应的队列),灵活应用(收到就处理/定时处理))
消息队列的缺点
- 系统的复杂性提高
- 系统可用性降低,一旦mq当机宕机,整个系统就无法使用了
RabbitMQ如何保证高可用
rabbitmq有三种模式:
- 单机模式,这个不会在成产上使用
- 简单集群模式,这种集群模式还是只有一个队列,但是消费者可以连接到不同的节点去消费,因为别的节点会保存一份这个队列的元数据信息,然后rabbitmq内部,把消息从这个队列先同步到别的节点,然后在发给消费者,如果存放消息的那个节点宕机了,那就玩完了,消费者无法在获取消息
- 镜像集群模式:这种模式是rabbitmq实现的真正的高可用模式,这种模式是mq会把同一个队列复制多份,然后生产这发送的消息会同步到每个队列,也就是说多个节点,每个节点保留一份完整的消息。这样当某个节点挂机了,不影响其他节点继续提供服务。
具体参考,这个是石杉老师讲的:https://www.jianshu.com/p/d372ee0646f3
实现消息队列的两种模式
点对点:每个消息只有一个消费者,不可重复消费,而且消费完消息就从队列中清除。
发布\订阅:每条消息有多个消费者,比如微信的订阅功能,点击订阅之后就可以不定期的给你推送,这个还分pull模式和push模式。所谓的pull模式就是消费者主动去队列消费消息,而push模式是生产者将消息发到mq之后,mq服务端马上将这条消息投递给 Consumer,优劣很容易看出,消费者按照自己的能力去拿消息处理的方式肯定更好,这也是为什么消息队列基本都使用pull模式的原因。
常用的MQ对比
我使用过的消息队列有RabbitMQ,kafka,其他的消息队列,比如ActiveMQ,RocketMQ,这两个不太熟悉,不过大同小异。对每种消息队列的性能对比感兴趣读这篇文章:中间件对比----Kafka、ActiveMQ、RabbitMQ及RocketMQ性能对比
MQ技术选型
activemq:这个mq的优点是功能完备,单机的吞吐量是万级,延迟是毫秒级。缺点就是现在社区越来越不活跃,而且存在偶尔会丢消息的情况,目前在国内用的越来越少。
rabbitmq: 这个优点也是功能完备,有一个强大的后台管理界面,很多配置管理都可以通过这个完成,单机吞吐量也是万级,社区活跃度高,延迟是微秒级别。 缺点就是使用erlang语言写的,不容易看懂源码,很难定制化开发。
rocketmq: 这个优点是单机吞吐量达到10万级别,分布式,易于扩展,经过阿里大数据量消息吞吐的检验,质量有保证,适合高吞吐量的场景。缺点就是阿里的项目很可能就黄了,那到时候只能自己维护,需要有实力才可以使用。
kafka:这个优点是单机10万级别的吞吐量,分布式,易于扩展,高可用,在实时计算,日志处理等大数据场景非常合适。是大数据场景的规范。
MQ如何保证消息的幂等性
mq是不保证消息重复消费的,需要从业务上去保证,有两种方法,
第一种是把消息先生成一个key存到redis,然后每次消费消息的时候去redis检查一下
第二种是使用数据库的唯一索引保证
MQ消息丢失怎么处理
rabbitmq消息丢失有三种场景:
1.生产者发送消息到rabbitmq中,消息还没有存储到mq,在网络传输中消息就丢失了
解决方法:解决方法也有两种。
- 具体使用事务的方式,在发送消息的时候开启事务,如果发送失败,会抛出异常,然后事务回滚就可以了。这个的缺点就是开启事务之后就会处于等待状态,无法发送下一条消息,变成同步发送了。api是一般是调用channal.txSelect.
- 使用confirm机制,这个机制分为3中第一种是每发送一条消息就confirm一次,这种方式也是处于等待的状态,效率低下,api为:channel.waitForConfirms()。第二种方式是批量confirm,就是发送一批数据出去,如果mq返回说这一批都收到了,那只需要confirm一次,效率相对来说提高了,但是如果一批消息中有一个消息丢失,这批消息就需要全部重新发送一遍,api为:channel.waitForConfirmsOrDie()。第三种方式是真正的异步发送,这种方式通过回调函数实现的,就是说发送端需要维护一个发送序号列表,服务端每次回调的确认的时候需要知道确认的是那一条消息,所以就给每条消息设置了一个编号,即使1,2,3,4这样的编号,发送端收到编号之后,需要知道这些编号对应的是哪条消息,如果有异常才知道重发那一条消息,api为:channel.confirmSelect();。
具体参考:(10)RabbitMQ生产者确认:事务与confirm机制
2.rabbitmq自己挂掉了,保存到内存中的消息丢失了
解决方法:设置消息的持久化,就是将内存中的消息写到磁盘中,但是这样只是把队列的元数据写入到磁盘中,还需要发送消息的时候将deliveryMode设置为2才可以真正将消息持久化,但是这行仍然不能保证消息不丢失,比如,内存中消息还没有完全来得及写入磁盘,mq的那个节点挂了,那消息就丢失了。
3.消费者消费消息设置了自动确认,当消费了消息之后,消息还没有处理成功,就自动通知mq说已经处理完了,这个时候如何消费者挂了,消息就丢了。
解决方法:去掉自动确认机制,通过手动确认,即使等消息完全处理完之后在提交ack
RabbitMQ消息大规模积压怎么处理
这个首先第一点要保证生产环境mq不要设置消息的过期时间,如果设置了,那没办法,只能手动处理了,想办法把丢掉的消息找回来再写到mq,如果没有设置过期时间,而且积压了很多怎么处理呢?这种可以把原来mq的消费者代码修改一下,把原来队列中的消息写入多个新申请的队列中去,然后呢在多搞一点消费者从新队列中把消息处理掉。如果生产环境实在耽搁不起mq积压时间过长,那就没办法,只能直接先把积压的消息丢掉,先让mq回复正常,之后在把丢失的消息找回来。
RabbitMQ介绍
组件介绍
Broker:也就是服务器实体
Publisher:生产者
Virtual Host:虚拟主机,表示一批交换器、消息队列和相关对象。虚拟主机是共享相同的身份认证和加密环境的独立服务器域,这里需要注意一下,Exchage,Queue是和Virtual Host绑定的,一个broker中可以有多个Virtual Host
Queue:存储消息的队列
Channel:信道,多路复用连接中的一条独立的双向数据流通道。信道是建立在真实的 TCP 连接内地虚拟连接,AMQP 命令都是通过信道发出去的,不管是发布消息、订阅队列还是接收消息,这些动作都是通过信道完成。因为对于操作系统来说建立和销毁 TCP 都是非常昂贵的开销,所以引入了信道的概念,以复用一条 TCP 连接
Consumer:消费者
Exchange:交换机,用来接收生产者发送的消息并将这些消息路由给服务器中的队列。Exchange分为4中类型,direct,fanout,topic,headers,其中headers和direct交换器完全一致,但是性能比direct模式差很多,基本不用,所以直接看另外三种类型。
direct交换器
消息中的路由键(routing key)如果和 Binding 中的 binding key 一致,换器就将消息发到对应的队列中。它是完全匹配、单播的模式。
fanout模式
这种模式叫广播模式,就是一条消息路由到多个队列,很像子网广播。这种模式是不区分路由键的。
topic模式
topic 交换器通过模式匹配分配消息的路由键属性,其实这种模式和direct模式是一样的,不过direct模式是精确匹配,这个是模糊匹配,精确匹配那就只有一个队列了,模糊匹配就有可能匹配到多个队列。
根据工作经验来看,使用direct和fanout模式比较多一点,第三种方式使用的不太多。
总结
总的来说rabbitmq还是挺简单的,很稳定,在中小型的服务中推荐使用,springboot中也集成了rabbitmq,使用起来更加简单。
参考文章:https://juejin.im/post/5b32044ef265da59654c3027