PC(Remote Procedure Call)远程过程调用,主要解决远程通讯间的问题,不须要了解底层网络的通讯机制。前端
RPC框架spring
知名度较高的有Thrift(FB的)、dubbo(阿里的)。数据库
RPC的通常须要经历4个步骤:缓存
一、创建通讯服务器
首先要解决通信的问题:即A机器想要调用B机器,首先得创建起通讯链接,主要是经过在客户端和服务器之间创建TCP链接。网络
二、服务寻址架构
要解决寻址的问题,A服务器上如何链接到B服务器(如主机或IP地址)以及特定的端口,方法的名称是什么。并发
三、网络传输负载均衡
1)序列化框架
当A服务器上的应用发起一个RPC调用时,调用方法和参数数据都须要先进行序列化。
2)反序列化
当B服务器接收到A服务器的请求以后,又须要对接收到的参数等信息进行反序列化操做。
四、服务调用
B服务器进行本地调用(经过代理Proxy)以后获得了返回值,此时还须要再把返回值发送回A服务器,一样也须要通过序列化操做,而后再通过网络传输将二进制数据发送回A服务器。
一般,一次完整的PRC调用须要经历如上4个步骤。
MQ(消息队列)
消息队列(MQ)是一种能实现生产者到消费者单向通讯的通讯模型,通常来讲是指实现这个模型的中间件。
典型的MQ中间件:
RabbitMQ、ActiveMQ、Kafka等
典型的特色:
一、解耦
二、可靠投递
三、广播
四、最终一致性
五、流量削峰
六、消息投递保证
七、异步通讯(支持同步)
八、提升系统吞吐、健壮性
典型的使用场景:秒杀业务中利用MQ来实现流量削峰,以及应用解耦使用。
RPC和MQ的区别和关联
1.在架构上,RPC和MQ的差别点是,Message有一个中间结点Message Queue,能够把消息存储。
2.同步调用:对于要当即等待返回处理结果的场景,RPC是首选。
3.MQ 的使用,一方面是基于性能的考虑,好比服务端不能快速的响应客户端(或客户端也不要求实时响应),须要在队列里缓存。
另一方面,它更侧重数据的传输,所以方式更加多样化,除了点对点外,还有订阅发布等功能。
4.并且随着业务增加,有的处理端处理量会成为瓶颈,会进行同步调用改造为异步调用,这个时候能够考虑使用MQ。
那么,这些详细的MQ消息队列的选型咱们该如何选择比较呢?
主流的消息队列MQ比较,详解MQ的4类应用场景
消息队列已经逐渐成为企业IT系统内部通讯的核心手段。它具备低耦合、可靠投递、广播、流量控制、最终一致性等一系列功能,成为异步RPC的主要手段之一。
当今市面上有不少主流的消息中间件,如老牌的ActiveMQ、RabbitMQ,煊赫一时的Kafka,阿里巴巴自主开发的Notify、MetaQ、RocketMQ等。
如今主要探讨主流的消息队列MQ比较,特征,以及典型使用场景。
目前主流的MQ产品
1.ZeroMQ
号称最快的消息队列系统,尤为针对大吞吐量的需求场景。
扩展性好,开发比较灵活,采用C语言实现,实际上只是一个socket库的从新封装,若是作为消息队列使用,须要开发大量的代码。ZeroMQ仅提供非持久性的队列,也就是说若是down机,数据将会丢失。其中,Twitter的Storm中使用ZeroMQ做为数据流的传输。
2.RabbitMQ
结合erlang语言自己的并发优点,支持不少的协议:AMQP,XMPP, SMTP, STOMP,也正是如此,使的它变的很是重量级,更适合于企业级的开发。
性能较好,可是不利于作二次开发和维护。
3.ActiveMQ
历史悠久的开源项目,是Apache下的一个子项目。已经在不少产品中获得应用,实现了JMS1.1规范,能够和spring-jms轻松融合,实现了多种协议,不够轻巧(源代码比RocketMQ多),支持持久化到数据库,对队列数较多的状况支持很差。
4.Redis
作为一个基于内存的K-V数据库,其提供了消息订阅的服务,能够看成MQ来使用,目前应用案例较少,且不方便扩展。对于RabbitMQ和Redis的入队和出队操做,各执行100万次,每10万次记录一次执行时间。
测试数据分为 128Bytes、512Bytes、1K和10K四个不一样大小的数据。
实验代表:入队时,当数据比较小时Redis的性能要高于RabbitMQ,而如 果数据大小超过了10K,Redis则慢的没法忍受;出队时,不管数据大小,Redis都表现出很是好的性能,而RabbitMQ的出队性能则远低于 Redis。
5.Kafka/Jafka
Kafka是Apache下的一个子项目,是一个高性能跨语言分布式发布/订阅消息队列系统,而Jafka是在Kafka之上孵化而来的,即Kafka的一个升级版。
具备如下特性:
快速持久化,能够在O(1)的系统开销下进行消息持久化;
高吞吐,在一台普通的服务器上既能够达到10W/s的吞吐速率;彻底的分布式系统,Broker、Producer、Consumer都原生自动支持分布式,自动实现负载均衡;
支持Hadoop数据并行加载,对于像Hadoop的同样的日志数据和离线分析系统,但又要求实时处理的限制,这是一个可行的解决方案。
Kafka经过Hadoop的并行加载机制统一了在线和离线的消息处理。Apache Kafka相对于ActiveMQ是一个很是轻量级的消息系统,除了性能很是好以外,仍是一个工做良好的分布式系统。
时须要消息队列
当你须要使用消息队列时,首先须要考虑它的必要性。
可使用mq的场景有不少,最经常使用的几种:
作业务解耦
最终一致性
广播
错峰流控等
反之,若是须要强一致性,关注业务逻辑的处理结果,则RPC显得更为合适。
消息队列使用场景
1.解耦
解耦是消息队列要解决的最本质问题。所谓解耦,简单点讲就是一个事务,只关心核心的流程。而须要依赖其余系统但不那么重要的事情,有通知便可,无需等待结果。换句话说,基于消息的模型,关心的是“通知”,而非“处理”。
举一个例子,关于订单系统,订单最终支付成功以后可能须要给用户发送短信积分什么的,但其实这已经不是咱们系统的核心流程了。
若是外部系统速度偏慢(好比短信网关速度很差),那么主流程的时间会加长不少,用户确定不但愿点击支付过好几分钟才看到结果。那么咱们只须要通知短信系统“咱们支付成功了”,不必定非要等待它当即处理完成。
2.最终一致性
最终一致性指的是两个系统的状态保持一致,要么都成功,要么都失败。
固然有个时间限制,理论上越快越好,但实际上在各类异常的状况下,可能会有必定延迟达到最终一致状态,但最后两个系统的状态是同样的。
业界有一些为“最终一致性”而生的消息队列,如:
Notify(阿里)
QMQ(去哪儿)等
其设计初衷,就是为了交易系统中的高可靠通知。
以一个银行的转帐过程来理解最终一致性,转帐的需求很简单,若是A系统扣钱成功,则B系统加钱必定成功。反之则一块儿回滚,像什么都没发生同样。
然而,这个过程当中存在不少可能的意外:
A扣钱成功,调用B加钱接口失败。
A扣钱成功,调用B加钱接口虽然成功,但获取最终结果时网络异常引发超时。
A扣钱成功,B加钱失败,A想回滚扣的钱,但A机器down机。
可见,想把这件看似简单的事真正作成,真的不那么容易。
全部跨VM的一致性问题,从技术的角度讲通用的解决方案是:
强一致性,分布式事务,但落地太难且成本过高,后文会具体提到。
最终一致性,主要是用“记录”和“补偿”的方式。在作全部的不肯定的事情以前,先把事情记录下来,而后去作不肯定的事情,结果多是:成功、失败或是不肯定,“不肯定”(例如超时等)能够等价为失败。成功就能够把记录的东西清理掉了,对于失败和不肯定,能够依靠定时任务等方式把全部失败的事情从新搞一遍,直到成功为止。
回到刚才的例子,系统在A扣钱成功的状况下,把要给B“通知”这件事记录在库里(为了保证最高的可靠性能够把通知B系统加钱和扣钱成功这两件事维护在一个本地事务里),通知成功则删除这条记录,通知失败或不肯定则依靠定时任务补偿性地通知咱们,直到咱们把状态更新成正确的为止。
整个这个模型依然能够基于RPC来作,但能够抽象成一个统一的模型,基于消息队列来作一个“企业总线”。
具体来讲,本地事务维护业务变化和通知消息,一块儿落地(失败则一块儿回滚),而后RPC到达broker,在broker成功落地后,RPC返回成功,本地消息能够删除。不然本地消息一直靠定时任务轮询不断重发,这样就保证了消息可靠落地broker。
broker往consumer发送消息的过程相似,一直发送消息,直到consumer发送消费成功确认。
咱们先不理会重复消息的问题,经过两次消息落地加补偿,下游是必定能够收到消息的。而后依赖状态机版本号等方式作判重,更新本身的业务,就实现了最终一致性。
最终一致性不是消息队列的必备特性,但确实能够依靠消息队列来作最终一致性的事情。
另外,全部不保证100%不丢消息的消息队列,理论上没法实现最终一致性。好吧,应该说理论上的100%,排除系统严重故障和bug。
像Kafka一类的设计,在设计层面上就有丢消息的可能(好比定时刷盘,若是掉电就会丢消息)。哪怕只丢千分之一的消息,业务也必须用其余的手段来保证结果正确。
2.广播
消息队列的基本功能之一是进行广播。
若是没有消息队列,每当一个新的业务方接入,咱们都要联调一次新接口。有了消息队列,咱们只须要关心消息是否送达了队列,至于谁但愿订阅,是下游的事情,无疑极大地减小了开发和联调的工做量。
好比本文开始提到的产品中心发布产品变动的消息,以及景点库不少去重更新的消息,可能“关心”方有不少个,但产品中心和景点库只须要发布变动消息便可,谁关心谁接入。
3.错峰与流控
试想上下游对于事情的处理能力是不一样的。
好比,Web前端每秒承受上千万的请求,并非什么神奇的事情,只须要加多一点机器,再搭建一些LVS负载均衡设备和Nginx等便可。
但数据库的处理能力却十分有限,即便使用SSD加分库分表,单机的处理能力仍然在万级。因为成本的考虑,咱们不能奢求数据库的机器数量追上前端。
这种问题一样存在于系统和系统之间,如短信系统可能因为短板效应,速度卡在网关上(每秒几百次请求),跟前端的并发量不是一个数量级。
但用户晚上个半分钟左右收到短信,通常是不会有太大问题的。若是没有消息队列,两个系统之间经过协商、滑动窗口等复杂的方案也不是说不能实现。
但系统复杂性指数级增加,势必在上游或者下游作存储,而且要处理定时、拥塞等一系列问题。并且每当有处理能力有差距的时候,都须要单独开发一套逻辑来维护这套逻辑。因此,利用中间系统转储两个系统的通讯内容,并在下游系统有能力处理这些消息的时候,再处理这些消息,是一套相对较通用的方式。
消息队列使用总结
1.消息队列不是万能的,对于须要强事务保证并且延迟敏感的,RPC是优于消息队列的。
2.对于一些无关痛痒,或者对于别人很是重要可是对于本身不是那么关心的事情,能够利用消息队列去作。
3.支持最终一致性的消息队列,可以用来处理延迟不那么敏感的“分布式事务”场景,并且相对于笨重的分布式事务,多是更优的处理方式。
4.当上下游系统处理能力存在差距的时候,利用消息队列作一个通用的“漏斗”,在下游有能力处理的时候,再进行分发。
5.若是下游有不少系统关心你的系统发出的通知的时候,果断地使用消息队列吧。