共计 3566 个字符,预计需要花费 9 分钟才能阅读完成。
导读:
微服务架构已成为了互联网的热门话题之一,而这也是互联网技术发展的必然阶段。然而,微服务概念的提出者 Martin Fowler 却强调:分布式调用的第一原则就是不要分布式。
纵观微服务实施过程中的弊端,可以推断出作者的意图,就是希望系统架构者能够谨慎地对待分布式调用,这是分布式系统自身存在的缺陷所致。但无论是 RPC 框架,还是 REST 框架,都因为驻留在不同进程空间的分布式组件,而引入了额外的复杂度。因而可能对系统的效率、可靠性、可预测性等诸多方面带来负面影响。
信用算力自 2016 年开始实施微服务改造,通过消息队列(Message Queue),后文简称 MQ,来规避微服务存在的缺陷,实现金融级数据服务。以下是一些使用场景和心得。
为什么需要 MQ
一、案例介绍
先来看一个当前的真实业务场景。
对于通过信息流获客的企业而言,当用户注册时,因业务需求会调用用户服务,然后执行一系列操作,注册 -> 初始化账户信息 -> 邀友奖励发放 -> 发放优惠券 -> … -> 信息流数据上报。
用户服务的开发人员压力非常大,因为需要调用非常多的服务,业务耦合严重。如果当时账户服务正在执行发版操作,那么初始化账户动作会失败。然而平台经过不断的迭代更新,后续又新增了一个签到业务,新注册用户默认签到一次。这就需要修改用户服务,增加调用签到服务的接口。每当遇到此种情况,开发用户服务的同学就非常不爽了,为什么总是我?新增签到业务和用户服务又有什么关系?
为解决此类重度依赖的问题,我们在架构层面引入了 MQ,用来规避微服务之间重度耦合调用的弊端。新架构如下图:
用户完成注册动作后,只需要往 MQ 发送一个用户注册的通知消息,下游业务如需要依赖注册相关的数据,订阅注册消息的 topic 即可,从而实现了业务的解耦。
看完上述真实的案例后,大家可能产生疑惑,到底什么是 MQ,使用 MQ 又有什么好处?适合使用 MQ 的场景和不适合使用 MQ 的场景有哪些不同?
二、什么是 MQ?
简单来说,MQ(MessageQueue)是一种跨进程的通信机制,用于上下游传递消息。
适合使用 MQ 的场景有:
1、上游不关心下游执行结果, 例如上述案例中用户注册后,我们并不关心账户是否初始化,是否上报了信息流等;
2、异步返回执行时间长:例如上述案例中,当邀友奖励发放,需要经历很多风控规则,执行时间比较长,但是用户并不关注奖励何时发放。
不适合使用 MQ 场景
调用方实时关注执行结果,例如用户发起注册动作后,需要立刻知道,注册结果是成功还是失败,这种需要实时知道最终执行结果的场景,就不适合使用 MQ。
三、使用 MQ 的好处:
1、解耦
2、可靠投递
3、广播
4、最终一致性
5、流量削峰
6、消息投递保证
7、异步通信(支持同步)
8、提高系统吞吐、健壮性
MQ 的技术选型
目前业内比较主流的 MQ 包括 RocketMQ、ActiveMQ、RabbitMQ、Kafka 等,关于性能、存储、社区活跃度等各方面的技术对比已经很多,本文不再重复。
但我们发现通过简单的选型对比,很难抉择到底选择哪款 MQ 产品。因为金融行业对于数据一致性以及服务可用性的要求非常高,所以任何关于技术的选项都显得尤为重要。
经调研,如微众银行、民生银行、平安银行等国内知名的互联网银行和直销银行代表,都在使用 RocketMQ,且 RocketMQ 出生在阿里系,经受过各种生产压力的考验,非常稳定。并且,目前此项技术已经捐增给 Apache 社区,社区活跃度非常高。另外 RocketMQ 开发语言是 Java,开发同学遇到解决不了的问题点,或者不清楚的概念,可以直接 Debug 源码。经过多方面的比较,我们选择 RocketMQ 作为规避微服务弊端的利器。
MQ 在微服务下的使用场景
MQ 是一种跨进程的通信机制,用于上下游传递消息,目前信用算力将 RocketMQ 应用于解耦、流量削峰、分布式事务的处理等几个场景。
一、解耦
通常解耦的做法是生产者发送消息到 MQ,下游订阅 MQ 的特定 topic,当下游接收到消息后开始处理业务逻辑。
那么,消息发送方到底应该是由谁来承担?是服务提供者在处理完 RPC 请求后,根据业务需求开始发送消息吗?但此刻开发人员就会抱怨为什么总是我?为什么处理完业务后需要发送 MQ?
为此,在解耦的过程中通过订阅数据库的 BinLog 日志,开发了一套 BinLog 日志解析模块,专门解析日志,然后生成 JSON 字符串后发送消息到 MQ,下游订阅 MQ 即可。流程如下:
目前所有需要依赖下游服务的业务线,其数据变动都采用此方案。
方案优缺点:
优点:
1、服务之间依赖完全解耦,任何基于注册行为的业务变更,都无需依赖上游,只需订阅 MQ 即可;
2、系统的稳定性和吞吐量增加了,用户注册的响应时间缩短了;
缺点:
1、引入 MQ 后系统复杂性增加,维护成本增加;
2、从注册开始到全部数据初始化结束的整体时间增加了;
二、流量削峰
每逢遇到会员日的时候,平台会发送大量的会员福利活动通知,以短信、站内信、PUSH 消息的方式通知注册用户。所有的消息会在很短的时间全部推送到消息中心,同时正常的业务通知任然有大量业务消息推送到消息中心。为保障平台的稳定性和可靠性,在消息中心前置了多种 topic,如短信、推送、站内提醒。消息中心接收到消息后会全部写入不同 topic 的 MQ,多个消费者来消费并把信息推送给终端用户。
三、分布式事务
用户在平台上支付他订购某种业务的时候,需要涉及到支付服务、账户服务、优惠券服务、积分服务,在单体模式下这种业务非常容易实现,通过事务即可完成,伪代码如下:
然而,在微服务的情况下,原本通过简单事务处理的却变得非常复杂,若引入两阶段提交(2PC)或者补偿事务(TCC)方案,则系统的复杂程度会增加。
信用算力的做法是通过本地事务 + MQ 消息的方式来解决, 虽然 RocketMQ 也支持事务消息,但是其他主流 MQ 并没有此项功能,所以综合考虑采用如下方案:
消息上游:需要额外建一个 tc_message 表,并记录消息发送状态。消息表和业务数据在同一个数据库里面,而且要在一个事务里提交。然后消息会经过 MQ 发送到消息的消费方。如果消息发送失败,会进行重试发送;
消息上游:开启定时任务扫描 tc_message 表,如果超过设置的时间内状态没有变更,会再次发送消息到 MQ, 如重试次数达到上限则发起告警操作;
消息下游:需要处理这个消息,并完成自己的业务逻辑。此时如果本地事务处理成功,表明已经处理完成了,需要发起业务回调通知业务方;
方案优缺点:
优点:
1、用最小的代价实现分布式事物,以达到数据最终一致性;
2、方案非常灵活,任何环节都可以人为控制;
缺点:
1、复杂性增加了,业务操作的时候需要写入 tc_message 表以及发送 MQ,同时还需要考虑状态超时未变更的补发机制以及告警处理机制;
2、用户看到的数据,存在有短暂不一致的情况;
心得体会
使用 RocketMQ 3 年多了,总体来说运行的非常稳定,基本上没有发生过生产事故,下面说说这几年使用下来的心得体会:
1、一个应用尽可能用一个 Topic,消息子类型用 tags 来标识。Topic 名称和 Tags 名称可以自行设置。Producer,Consumer 都需要规范,要做到见名知意。发送消息时候必须携带 Tags,消费方在订阅消息时,才可以利用 Tags 在 Broker 做消息过滤。
2、每条消息在业务层面有唯一标识码,方便在系统出现异常的情况,可以通过业务维度查询。举个栗子,当用户在平台注册成功后,会以 Topic 和 UserID 作为唯一标识码(topic_user_10011),服务器会为每个消息创建索引,该消息会持久化入库,以防将来定位消息丢失等问题。下游收到消息后会以 Topic+Key 方式来记录消费行为,包括消息日期、当前机器 IP 地址、处理结果等;也可以通过 Topic+Key 的方式来查询这条消息内容,包括消息被谁消费,以及这条 MQ 在每个环节的处理状态。
3、消息发送成功或者失败,都需要记录 log 日志,且必须打印 sendresult、MsgID、唯一标识码。
4、由于上游会做消息重试机制,所以下游消息必须要做幂等处理。
5、需要封装 MQ 的 API 在封装后,API 需屏蔽底层 MQ 的特性,开发人员无需关注到底是用的哪个 MQ 来支持本地分布式事物、MQ 消息自动入库、自动打印日志,减少开发人员操作成本。
总的来说,MQ 是一个互联网架构中常见的解耦利器,在这 3 年中,信用算力在微服务中一直使用 MQ 来为金融客户提供高质量的数据服务。虽然 MQ 不是唯一方案,但是从目前阶段来看,的确是一种非常不错的解决方案。
本文作者:潘志伟(信用算力技术总监,QCon 演讲嘉宾,十多年 Java 从业经验,精通微服务架构,精通大数据。拥有亿级用户平台架构经验,万级并发的 API 网关经验。)
本文作者:中间件小哥阅读原文
本文为云栖社区原创内容,未经允许不得转载。