为什么使用MQ?为什么使用rocketMQ

为什么在你们系统架构中要引入消息中间件?

回答这个问题,其实就是让你先说说消息中间件的常见使用场景。然后结合你们自身系统对应的使用场景,说一下在你们系统中引入消息中间件解决了什么问题。

系统解耦

看这么个场景。A 系统发送数据到 BCD 三个系统,通过接口调用发送。如果 E 系统也要这个数据呢?那如果 C 系统现在不需要了呢?A 系统负责人几乎崩溃......

在这个场景中,A 系统跟其它各种乱七八糟的系统严重耦合,A 系统产生一条比较关键的数据,很多系统都需要 A 系统将这个数据发送过来。A 系统要时时刻刻考虑 BCDE 四个系统如果挂了该咋办?要不要重发,要不要把消息存起来?头发都白了啊!

如果使用 MQ,A 系统产生一条数据,发送到 MQ 里面去,哪个系统需要数据自己去 MQ 里面消费。如果新系统需要数据,直接从 MQ 里消费即可;如果某个系统不需要这条数据了,就取消对 MQ 消息的消费即可。这样下来,A 系统压根儿不需要去考虑要给谁发送数据,不需要维护这个代码,也不需要考虑人家是否调用成功、失败超时等情况。

总结:通过一个 MQ,Pub/Sub 发布订阅消息这么一个模型,A 系统就跟其它系统彻底解耦了。
 

大家可别以为这是开玩笑,一个大规模系统,往往会拆分为几十个甚至上百个子系统,每个子系统又对应 N 多个服务,这些系统与系统之间有着错综复杂的关系网络。

如果某个系统产出一份核心数据,可能下游无数的其他系统都需要这份数据来实现各种业务逻辑。

此时如果你要是采取上面那种模式来设计系统架构,那么绝对你负责系统 A 的同学要被烦死了。

先是来一个人找他要求发送数据给一个新的系统 H,系统 A 的同学要修改代码,然后在那个代码里加入调用新系统 H 的流程。

一会那个系统 B 是个陈旧老系统要下线了,告诉系统 A 的同学:别给我发送数据了,接着系统 A 再次修改代码不再给这个系统 B。

然后如果要是某个下游系统突然宕机了呢?系统 A 的调用代码里是不是会抛异常?

那系统A的同学会收到报警说异常了,结果他还要去 care 是下游哪个系统宕机了。

所以在实际的系统架构设计中,如果全部采取这种系统耦合的方式,在某些场景下绝对是不合适的,系统耦合度太严重。

并且互相耦合起来并不是核心链路的调用,而是一些非核心的场景(比如上述的数据消费)导致了系统耦合,这样会严重的影响上下游系统的开发和维护效率。

因此在上述系统架构中,就可以采用 MQ 中间件来实现系统解耦。系统 A 就把自己的一份核心数据发到 MQ 里,下游哪个系统感兴趣自己去消费即可,不需要了就取消数据的消费,如下图所示:

异步调用

再来看一个场景,A 系统接收一个请求,需要在自己本地写库,还需要在 BCD 三个系统写库,自己本地写库要 3ms,BCD 三个系统分别写库要 300ms、450ms、200ms。最终请求总延时是 3 + 300 + 450 + 200 = 953ms,接近 1s,用户感觉搞个什么东西,慢死了慢死了。用户通过浏览器发起请求,等待个 1s,这几乎是不可接受的。

一般互联网类的企业,对于用户直接的操作,一般要求是每个请求都必须在 200 ms 以内完成,对用户几乎是无感知的。

如果使用 MQ,那么 A 系统连续发送 3 条消息到 MQ 队列中,假如耗时 5ms,A 系统从接受一个请求到返回响应给用户,总时长是 3 + 5 = 8ms,对于用户而言,其实感觉上就是点个按钮,8ms 以后就直接返回了,爽!网站做得真好,真快!

流量削峰

假设你有一个系统,平时正常的时候每秒可能就几百个请求,系统部署在 8 核 16G 的机器的上,正常处理都是 ok 的,每秒几百请求是可以轻松抗住的。

在高峰期一下子来了每秒钟几千请求,瞬时出现了流量高峰,此时你的选择是要搞 10 台机器,抗住每秒几千请求的瞬时高峰吗?

 

那如果瞬时高峰每天就那么半个小时,接着直接就降低为了每秒就几百请求,如果你线上部署了很多台机器,那么每台机器就处理每秒几十个请求就可以了,这不是有点浪费机器资源吗?

大部分时候,每秒几百请求,一台机器就足够了,但是为了抗那每天瞬时的高峰,硬是部署了 10 台机器,每天就那半个小时有用,别的时候都是浪费资源的。

 

但是如果你就部署一台机器,那会导致瞬时高峰时,一下子压垮你的系统,因为绝对无法抗住每秒几千的请求高峰。

此时我们就可以用 MQ 中间件来进行流量削峰。所有机器前面部署一层 MQ,平时每秒几百请求大家都可以轻松接收消息。

一旦到了瞬时高峰期,一下涌入每秒几千的请求,就可以积压在 MQ 里面,然后那一台机器慢慢的处理和消费。

等高峰期过了,再消费一段时间,MQ 里积压的数据就消费完毕了。

 

这个就是很典型的一个 MQ 的用法,用有限的机器资源承载高并发请求。

如果业务场景允许异步削峰,高峰期积压一些请求在 MQ 里,然后高峰期过了,后台系统在一定时间内消费完毕不再积压的话,那就很适合用这种技术方案。

引入消息中间件之后会有什么好处以及坏处?

如果你在系统架构里引入了消息中间件之后,会有哪些缺点?

系统可用性降低

首先是你的系统整体可用性绝对会降低,给你举个例子,我们就拿之前的一幅图来说明。

比如说一个核心链路里面,系统 A→系统 B→系统 C,然后系统 C 是通过 MQ 异步调用系统 D 的。

 

看起来很好,你用这个 MQ 异步化的手段解决了一个核心链路执行性能过差的问题。

但是你有没有考虑另外一个问题,就是万一你依赖的那个 MQ 中间件突然挂掉了怎么办?

这个还真的不是异想天开,MQ、Redis、MySQL 这些组件都有可能会挂掉。一旦你的 MQ 挂了,就导致你的系统的核心业务流程中断了。

本来你要是不引入 MQ 中间件,那其实就是一些系统之间的调用,但是现在你引入了 MQ,就导致你多了一个依赖。一旦多了一个依赖,就会导致你的可用性降低。

因此,一旦引入了 MQ 中间件,你就必须去考虑这个 MQ 是如何部署的,如何保证高可用性。

甚至在复杂的高可用的场景下,你还要考虑如果 MQ 一旦挂了以后,你的系统有没有备用兜底的技术方案,可以保证系统继续运行下去。

系统稳定性降低

 

不知道大家有没有发现一个问题,这个链路除了 MQ 中间件挂掉这个可能存在的隐患之外,可能还有一些其他的技术问题。

比如说,莫名其妙的,系统 C 发了一个消息到 MQ,结果那个消息因为网络故障等问题,就丢失了。这就导致系统 D 没有收到那条消息。

这可就惨了,这样会导致系统 D 没完成自己该做的任务,此时可能整个系统会出现业务错乱,数据丢失,严重的 Bug,用户体验很差等各种问题。

这还只是其中之一,万一说系统 C 给 MQ 发送消息,不小心一抽风重复发了一条一模一样的,导致消息重复了,这个时候该怎么办?

可能会导致系统 D 一下子把一条数据插入了两次,导致数据错误,脏数据的产生,***一样会导致各种问题。

或者说如果系统 D 突然宕机了几个小时,导致无法消费消息,结果大量的消息在 MQ 中间件里积压了很久,这个时候怎么办?

即使系统 D 恢复了,也需要慢慢的消费数据来进行处理。所以这就是引入 MQ 中间件的第二个大问题,系统稳定性可能会下降,故障会增多,各种各样乱七八糟的问题都可能产生。

而且一旦产生了一个问题,就会导致系统整体出问题。你就需要为了解决各种 MQ 引发的技术问题,采取很多的技术方案。

关于这个,我们后面会用专门的文章聊聊 MQ 中间件的这些问题的解决方案,包括:

  • 消息高可靠传递(0 丢失)
  • 消息幂等性传递(绝对不重复)
  • 百万消息积压的线上故障处理

分布式一致性问题

引入消息中间件,还有分布式一致性的问题。举个例子,比如说系统 C 现在处理自己本地数据库成功了,然后发送了一个消息给 MQ,系统 D 也确实是消费到了。

但是结果不幸的是,系统 D 操作自己本地数据库失败了,那这个时候咋办?

系统 C 成功了,系统 D 失败了,会导致系统整体数据不一致了啊。所以此时又需要使用可靠消息最终一致性的分布式事务方案来保障。

 

总结

最后,我们来做一个简单的小结。在面试中要答好这个问题,首先一定要熟悉 MQ 这个技术的优缺点。了解清楚把他引入系统是为了解决哪些问题的,但是他自身又会带来哪些问题。

此外,对于引入 MQ 以后,是否对他自身可能引发的问题有一些方案的设计,来保证你的系统高可用、高可靠的运行,保证数据的一致性。这个也要做好相应的准备。

 

为什么选择rocketMQ

 

 RocketMQ

阿里系下开源的一款分布式、队列模型的消息中间件,原名Metaq,3.0版本名称改为RocketMQ,是阿里参照kafka设计思想使用java实现的一套mq。同时将阿里系内部多款mq产品(Notify、metaq)进行整合,只维护核心功能,去除了所有其他运行时依赖,保证核心功能最简化,在此基础上配合阿里上述其他开源产品实现不同场景下mq的架构,目前主要多用于订单交易系统。

具有以下特点:

  • 能够保证严格的消息顺序
  • 提供针对消息的过滤功能
  • 提供丰富的消息拉取模式
  • 高效的订阅者水平扩展能力
  • 实时的消息订阅机制
  • 亿级消息堆积能力

 

       RabbitMQ

使用Erlang编写的一个开源的消息队列,本身支持很多的协议:AMQP,XMPP, SMTP,STOMP,也正是如此,使的它变的非常重量级,更适合于企业级的开发。同时实现了Broker架构,核心思想是生产者不会将消息直接发送给队列,消息在发送给客户端时先在中心队列排队。对路由(Routing),负载均衡(Load balance)、数据持久化都有很好的支持。多用于进行企业级的ESB整合。

       ActiveMQ

Apache下的一个子项目。使用Java完全支持JMS1.1和J2EE 1.4规范的 JMS Provider实现,少量代码就可以高效地实现高级应用场景。可插拔的传输协议支持,比如:in-VM, TCP, SSL, NIO, UDP, multicast, JGroups and JXTA transports。RabbitMQ、ZeroMQ、ActiveMQ均支持常用的多种语言客户端 C++、Java、.Net,、Python、 Php、 Ruby等。

     Redis

使用C语言开发的一个Key-Value的NoSQL数据库,开发维护很活跃,虽然它是一个Key-Value数据库存储系统,但它本身支持MQ功能,所以完全可以当做一个轻量级的队列服务来使用。对于RabbitMQ和Redis的入队和出队操作,各执行100万次,每10万次记录一次执行时间。测试数据分为128Bytes、512Bytes、1K和10K四个不同大小的数据。实验表明:入队时,当数据比较小时Redis的性能要高于RabbitMQ,而如果数据大小超过了10K,Redis则慢的无法忍受;出队时,无论数据大小,Redis都表现出非常好的性能,而RabbitMQ的出队性能则远低于Redis。

     Kafka

Apache下的一个子项目,使用scala实现的一个高性能分布式Publish/Subscribe消息队列系统,具有以下特性:

  • 快速持久化:通过磁盘顺序读写与零拷贝机制,可以在O(1)的系统开销下进行消息持久化;
  • 高吞吐:在一台普通的服务器上既可以达到10W/s的吞吐速率;
  • 高堆积:支持topic下消费者较长时间离线,消息堆积量大;
  • 完全的分布式系统:Broker、Producer、Consumer都原生自动支持分布式,依赖zookeeper自动实现复杂均衡;
  • 支持Hadoop数据并行加载:对于像Hadoop的一样的日志数据和离线分析系统,但又要求实时处理的限制,这是一个可行的解决方案。

      ZeroMQ

号称最快的消息队列系统,专门为高吞吐量/低延迟的场景开发,在金融界的应用中经常使用,偏重于实时数据通信场景。ZMQ能够实现RabbitMQ不擅长的高级/复杂的队列,但是开发人员需要自己组合多种技术框架,开发成本高。因此ZeroMQ具有一个独特的非中间件的模式,更像一个socket library,你不需要安装和运行一个消息服务器或中间件,因为你的应用程序本身就是使用ZeroMQ API完成逻辑服务的角色。但是ZeroMQ仅提供非持久性的队列,如果down机,数据将会丢失。如:Twitter的Storm中使用ZeroMQ作为数据流的传输。

ZeroMQ套接字是与传输层无关的:ZeroMQ套接字对所有传输层协议定义了统一的API接口。默认支持 进程内(inproc) ,进程间(IPC) ,多播,TCP协议,在不同的协议之间切换只要简单的改变连接字符串的前缀。可以在任何时候以最小的代价从进程间的本地通信切换到分布式下的TCP通信。ZeroMQ在背后处理连接建立,断开和重连逻辑。

特性:

  • 无锁的队列模型:对于跨线程间的交互(用户端和session)之间的数据交换通道pipe,采用无锁的队列算法CAS;在pipe的两端注册有异步事件,在读或者写消息到pipe的时,会自动触发读写事件。
  • 批量处理的算法:对于批量的消息,进行了适应性的优化,可以批量的接收和发送消息。
  • 多核下的线程绑定,无须CPU切换:区别于传统的多线程并发模式,信号量或者临界区,zeroMQ充分利用多核的优势,每个核绑定运行一个工作者线程,避免多线程之间的CPU切换开销。

 

RocketMQ,是阿里开源的,经过阿里的生产环境的超高并发、高吞吐的考验,性能卓越,同时还支持分布式事务等特殊场景。

而且 RocketMQ 是基于 Java 语言开发的,适合深入阅读源码,有需要可以站在源码层面解决线上生产问题,包括源码的二次开发和改造。

RocketMQ :

  1. 支持事务消息(能够解决分布式事务的问题)
  2. 支持顺序消息(底层已经使用内存队列实现)
  3. 支持consumer端tag过滤,减少不必要的网络传输
  1. RocketMQ是逻辑概念 相等于一个Broker多个不同的 Master组合
  2. RocketMQ是自己的nameServer实现去中心化注册

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
RocketMQ是一个开源的分布式消息中间件,可以实现高可靠、高性能、可伸缩的消息传递。在使用RocketMQ时,你可以通过Maven依赖引入相应的库文件,比如引用所示的依赖配置。而在RocketMQ中,通常采用生产者-消费者模式来进行消息的传递和处理。生产者负责发送消息,消费者负责接收和处理消息。 对于事务消息,生产者在发送消息时,会先将消息转化为半消息(half),并存入一个不对消费者可见的内部Topic(RMQ_SYS_TRANS_HALF_TOPIC)。当本地事务执行成功后,生产者会向MQ发送commit信号,MQ会将消息转化为原本的Topic。当本地事务执行失败后,生产者会向MQ发送rollback信号,MQ会丢弃对应的消息。如果本地事务没有执行完,可以向MQ发送unknown信号,MQ收到unknown信号后,会让对应的消息等待,并定期回查状态为unknown的消息。这样可以保证事务消息的可靠性和一致性。引用提供了更详细的事务消息的处理流程。 在使用RocketMQ的生产者时,你可以按照如下代码示例来发送消息: ```java for (int i = 0; i < 100; i++) { for(int j=0; j<5; j++) { int orderId = i; // 每一个订单用同一个orderId Message msg = new Message("TopicTest", "order_" + orderId, "KEY" + orderId, ("order_" + orderId + "step" + j).getBytes(RemotingHelper.DEFAULT_CHARSET)); SendResult sendResult = producer.send(msg, new MessageQueueSelector() { @Override public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) { Integer id = (Integer) arg; // 每个订单的message用同一个orderId,必然会选中同一个MessageQueue,自然会顺序存进去 int index = id % mqs.size(); return mqs.get(index); } }, orderId); System.out.printf("%s%n", sendResult); } } ``` 这段示例代码展示了如何发送一系列订单消息,并按照每个订单的orderId将消息发送到对应的MessageQueue中。通过指定一个回调函数,可以实现按照一定的策略将消息发送至特定的MessageQueue。引用提供了更详细的代码示例。 总之,RocketMQ是一个功能强大的分布式消息中间件,通过生产者-消费者模式实现消息的可靠传递和处理。事务消息可以保证消息的一致性和可靠性。在使用RocketMQ时,你可以通过Maven引入相应的依赖,编写生产者和消费者的代码来实现消息的发送和接收。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值