MQ后端
做为金融企业对公众提供服务必定要保证其可用性,尽可能作到更多个9(通常SLA中描述的高可用性99.99%,中的9越多表明整年服务可用时间越长服务更可靠,停机时间越短),随着软件系统的复杂度愈来愈高,故障是不可避免的。这就须要企业实现总体的弹性架构(Resilient Architecture),为应对故障而设计。缓存
由于是同步请求,常由于执行方失败、超时等因素而影响最终用户体验,并且不少故障是没法完全消除的。并且RPC和RMI调用须要服务的消费方和服务的提供方同时在线,而且双方须要经过某种机制确认彼此的调用关系,由于存在这些弊端,就致使了面向消息的中间件(MOM)的产生,经过在企业架构中引入消息中间件,确保在故障发生时,受此影响的系统部分在一个很小的范围内。消息中间件就是在分布式系统中间引入一个透明的中间层,隔离服务的提供方和消费方。服务器
2 为何要引入MQ网络
2.1何为MQ多线程
消息队列(MQ)是一种不一样应用程序之间(跨进程)的通讯方法,用于上下游应用程序之间传递消息。咱们拆分来看:架构
消息:应用程序经过写入和检索出入列队的数据(消息)来通讯。并发
队列:除去了接收和发送应用程序同时执行的要求。负载均衡
这样就实现了上游与下游之间的解耦,上游向MQ发送消息,下游从MQ接收消息,上游下游互不依赖,它们只依赖MQ。由于有队列的存在,MQ可在上下游之间进行缓冲,把上游信息先缓存起来,下游根据本身的能力从MQ中拉去信息,起到削峰的做用。异步
2.2 使用MQ带来的好处分布式
2.2.1 解耦
什么是耦合
高内聚低耦合,是软件工程中的概念,这里的低耦合是指各个组件之间,尽量相互独立。通俗一点的理解就是,增长模块间调用透明化,最高的透明度就是不用知道彼此的存在,所以减小接口的复杂性、规范调用的方式及传递的信息,下降产品各模块的依赖,提升重用程度。
如何解耦
在企业总体架构中解耦,主要设计两个方面:一是简化减小交互,二是增长一个中间层实现两方的隔离,MQ就是其中的中间层(以下图所示)。
引入MQ后生产者和消费者没必要知道彼此的存在也没必要同时在线,主要交互流程以下:
1) 生产者负责:生产消息并经过SDK或API调用发送给MQ(同步发送或者异步发送);
2) MQ负责:接收消息,并持久化消息到消息存储(同步或异步存储消息);
3) 生产者接收来自MQ的响应(消息发送状态或异常);
4) 消费者订阅消息后,接收来自MQ的消息;
5) 消费者执行消息对应的后续业务操做;
6) 对消费结果进行确认(消费成功、失败、异常等)。
2.2.2 削峰填谷
因为系统闲忙分布不均,QPS常相差几十倍甚至更高,特别是在遇到营销活动时,瞬间流量极可能超事后端系统的承载能力,这就要考虑经过消息中间件来缓冲,MQ客户端实例根据本身的处理能力从MQ服务器拉取消息,以此来减轻或消除后端系统的瓶颈。
2.2.3 异构集成
因为各类缘由,咱们在企业信息化建设过程当中,都会面临软件产品来自不一样的厂家只解决某特定领域的问题,这些产品由于封闭的架构没法对外提供服务或缺乏核心开发而没法作大的改造,这就形成了彼此之间很难集成。经过引入MQ能够部分解决该问题,只须要在某个环节生产一条消息,或者根据消息作出具体的响应,只需与MQ对接,没必要与其余系统作一对一的对接。
2.2.4异步隔离
为了提供金融服务的总体弹性,须要隔离内部、外部系统间的依赖。如支付通知分为两种,一种是同步通知,这时API调用会由于网络故障而超时,由于服务提供方处理能力限制而得不到及时响应……等多种因素影响,另外一种是异步通知,在必定时效范围内最终通知到便可,从而提供提升最终用户的体验和交易成功率,提升业务的总体生产率。
3.常见产品特性比较
4.MQ的两种模式:点对点、发布/订阅
点对点模式:Queue,不可重复消费
生产者发送一条消息到queue,一个queue能够有不少消费者,可是一个消息只能被一个消费者接受,当没有消费者可用时,这个消息会被保存在queue中直到有一个可用的消费者;消息被消费之后,queue中再也不有存储,因此消息消费者不可能消费到已经被消费的消息。因此Queue实现了一个可靠的负载均衡。
发布/订阅模式:Topic,能够重复消费
发布/订阅模式涉及三个角色:发布者、订阅者、消费者
订阅者:是成为消费者的前提,订阅是向发布者说明本身是个消费者的过程。简单来讲,发布者发送到topic的消息,只有订阅了topic的订阅者才能成为消费者,才会接收到消息。
Topic: 发布/订阅模式中的topic实现了发布和订阅,当发布一个消息,全部订阅这个topic的服务都能获得这个消息,因此从1到N个订阅者都能获得这个消息的拷贝。
整个实现上分三个步骤:
一、 初始化发布者、订阅者。
二、 订阅者注册到发布者
三、 发布者依次向订阅者发布消息。
点对点模式和发布/订阅模式的结合:
发布订阅模式下,当发布者的消息量很大时,单个订阅者的处理能力很难知足系统要求。在实际应用场景中,通常是由多个订阅者节点组成一个订阅组来消费topic信息,即分组订阅,每一个消息只会由一个订阅组中的某一个订阅者来消费, 相似于点对点模式。这种模式下很容易就能实现对消费能力的线性扩展。
4、MQ主要应用场景:
主要针对如下特征可采用MQ
一、解耦
以贷款扣款为例: 订单系统 –> MQ -> 结算系统
订单系统在生成用户订单后将消息抛送至MQ后当即返回,而后由结算系统去消费该MQ中的消息,进行用户帐户金额的扣减。这样就将订单生成系统和结算系统就好了解耦,订单系统只需关注建立订单,无需等待后续流程的执行结果,这样能够减少订单系统的压力,最大可能的提升系统响应速度,进而提高订单量,而结算系统则只需关心帐户金额扣减,保证帐户金额的一致性便可。
二、削峰填谷
在每逢大促时,订单系统就会迎来数量高峰,在短期内会生成大量的订单,这势必会给结算系统带来压力,在短期内升高服务器的利用率,甚至满载,从而达到系统瓶颈。而在订单低谷期订单量比较小,结算系统服务器又会长时间处于空闲状态。
此类问题能够经过MQ的方式来解决。订单系统将订单消息抛送至MQ队列后,结算系统会经过消费端拉取消息的方式来处理订单消息,拉取的速度可由结算系统根据当前自身服务器的状况进行控制,这样就能够避开因为大促带来的订单瞬时高峰,使结算流量趋于平稳,保证告终算系统的稳定性。
三、最终一致性
订单系统:建立成功订单、发送成功通知到MQ,两个操做能够当作一个事务。
当一次发送MQ失败以后,能够结合定时任务进行补偿,这样能够保证生成订单的结果能够落地到mq的存储中。一样结算系统消费端依靠MQ重试机制一直发送消息,直到消费端最终确认扣款业务成功处理完成。这样咱们经过MQ实现了最终一致性。
5、优缺点
MQ对应的优势是:
一、高并发
在高并发分布式环境下,因为来不及同步处理,请求每每发生堵塞,影响系统性能;而异步处理请求,能够缓解系统的压力。
二、松耦合
任何一个应用对MQ的调用不依赖于任何其它应用,没有任何依赖或者时序要求,应用依赖于MQ的能力保证消息传递。(应用发送消息到MQ以后并不关心消息如何或者何时被传递。一样的消息的接收者也不关心消息从哪里或者如何到来。在不一样的环境中这样作的好处是容许客户端使用不一样的语言编写甚至使用不一样的线路协议。MQ做为中间人存在,容许不一样环境的集成和异步交互。)
MQ的不足是:
一、多了一个MQ组件,使消息系统更加复杂 。
二、消息传递路径更长,延时会增长 。
三、消息可靠性和重复性互为矛盾,消息不丢不重难以同时保证。
四、上游没法知道下游的执行结果。
举个例子:用户登陆场景,登陆页面调用passport服务,passport服务的执行结果直接影响登陆结果,此处的“登陆页面”与“passport服务”就必须使用调用关系,而不能使用MQ通讯。
(不管如何,记住这个结论:调用方实时依赖执行结果的业务场景,请使用调用,而不是MQ。)
6.问题总结
1、消息乱序
有些业务场景,好比购买流程,下单和扣款两个操做是有前后顺序的。
Topic1: 队列1
队列2
队列3
……
Topic2: 队列1
队列2
……
生产者发出topic+消息体, 根据匹配topic能够判断出应该把消息丢到哪一个队列里。每一个队列对应惟一topic,同一topic可能对应多个队列。也就是同一种topic的消息可能进入到不一样队列里,即使在同一个队列里的消息一般也是多线程处理的,因此没法保证消息的顺序。
要实现严格的顺序消息,简单且可行的办法就是:保证生产者 - MQ - 消费者是一对一对一的关系。好比同一个topic下只有一个队列,且队列是单线程处理的。但存在问题:一、并行度不够,影响整个系统性能。二、发生异常时(如消费端出现问题),会致使整个处理流程阻塞,须要花费更多时间解决问题。
PS:目前市场上不少应用都是不关心消息是否乱序的。而针对大部分业务场景也是没必要要求消息顺序的。
针对必需要保证顺序的业务,能够设置业务id,同一个业务的一组消息设置同一个id,丢入同一个队列中,而且单线程处理。
2、消息重复
因为网络延迟等容易引入消息重复的问题。如今一般的处理方法是不解决。可是不解决可能存在消费端收到两条同样的消息。解决这个问题,一般两种方法:
A、幂等性
幂等性:某个函数或者某个接口使用相同参数调用一次或者无限次,其形成的后果是同样的。
好比在系统中,调用方A调用系统B的接口进行用户的扣费操做时,因为网络不稳定,A重试了N次该请求,那么无论B是否接收到多少次请求,都应该保证只会扣除该用户一次费用。
例如:对于订单扣费行为,系统会生成惟一全局的一个流水号用来标识同一个业务流程,多个相同业务流程的流水号不重复。服务器1处理了流水号1的扣费行为后,再有流水号1的扣款消息到达其余服务器时,不管执行几回扣款行为,最终结果都是只扣款1次。
B、日志表
在后台维护一张表从而达到去重的目的。好比每条消息设置惟一的消息id,若发送成功则记录在日志表中,新消息到达时,查看消息是否已经在日志表中,若存在则再也不处理。
PS:MQ实现去重逻辑,必定程度上会影响消息系统的性能,因此目前不少去重逻辑是由上游业务方来保证。(MQ+上游双重保证)
3、消息重试:生产者端重试、消费者端重试
重点说下消费者端重试,消费结果两种:一、成功;二、失败重试。消费者必须返回其中一种结果。
消费者端的失败,分为2种状况,一个是exception,一个是timeout。
Exception: 消息正常的到了消费者,结果消费者发生异常,处理失败了。
处理方法:mq把失败的消息丢到重试队列中,重试队列通常会有重试次数限制,若超过该次数仍未消费成功,通常会有两种处理:一、丢弃该消息。(经常使用)二、再也不重试,且存储该消息,并使用另一种方式消费。
Timeout:好比因为网络缘由致使消息没有从MQ到达消费者上。
处理方法:丢到重试队列中不断的尝试发送这条消息,直至发送成功为止!(一般咱们会设置重试上限)