大家好,我是王有志,欢迎和我聊技术,聊漂泊在外的生活。快来加入我们的Java提桶跑路群:共同富裕的Java人。
业务背景
先做个简单的介绍,目前我供职于某保险公司,负责核心业务契约链路。契约简单点解释就是用户购买保险与保司签订保险合同的过程。
本文是站在保险业务系统的角度思考我司现有系统的架构设计,因此不会涉及到诸如索引优化,代码优化等技术细节。
宏观背景
过往保险业务的特点是高额低频,实践中更加关注数据的准确和安全。但近年来,随着疫情对线下销售行为的冲击,各保司不断在互联网发力,加之依托于支付宝,微信的蚂蚁保和微保带来的流量,保司陈旧笨重的业务系统在海量流量的冲击中就显得脆弱不堪了。
为了适应互联网低额高频的特点,以及某安在线在互联网为各保司做出的榜样,我司也对核心系统,以及面向客户的应用提出了新的要求,第一个目标便是“全面提速”。
实际上是?
冠冕堂皇的理由说完了,说点接地气的,某天领导找到我说:保单详情页面响应也太慢了吧,将近5秒才能有结果。这样,你们明天出个优化方案,一天开发,一天测试和上线,没问题吧?
听完,我第一时间想到的是,今天的桶装不下我的手办,现在就跑路有点不赶趟了,再看了看花呗后,不得不咬牙应了下来。
发现问题
其实问题已经暴露了出来,但修改起来却很让人头疼。因为在整个保险业务中,看似简单的查询,可能需要很长的链路,比如,用户从APP开始查询保单详情,前后要涉及到6个服务。
确定了整体的链路,我们开始梳理服务间的调用关系(时序图是个不错的选择):
整体上并不算很离谱,但是我们注意到了两点:
- 查询服务调用保单管理得到的是单张保单的全部数据
- 保单管理需要下载电子保单后才能生成公网链接
初步断定是这两点拖慢了整体响应时间。为了验证这个想法,我们跑了一轮测试,统计了链路中各关键环节的响应时间(ms):
结果很明显了,1s的左右的保单数据查询,将近2.5秒的文件下载,占了响应时间的70%,它们两个正是罪魁祸首。
解决办法
看完上面的分析过程,解决办法也就浮出水面了,拆分查询服务中的数据查询和电子保单查询,能解决50%的问题。其余50%中,查询保单数据又占据了40%,可以继续拆分成按需提供数据的接口。
到这里我产生了疑问,为什么APP会调用这么”巨大“的保单查询接口?
正常来说,这个接口应该是Web和内部服务调用的。而且在保险业务中,这样的接口势必会有大量的关联查询,面向客户的APP不应该,也不能调用它。
在我咨询了其他小伙伴后,我终于悟了。
不同系统有不同查询要求,每次大家都在这个接口新增内容,才造就了这么“巨大”的查询。
实际上查询服务冗余了保单的部分数据,能够满足APP日常使用需求,只有客户在经过多次交互后,才需要查询保单管理的数据。
当下,我们制定了一个初步的解决方案:
- 新增查询接口,Ctrl+C和Ctrl+V后,再屏蔽掉部分查询内容;
- 新增电子保单查询接口,还是Ctrl+C和Ctrl+V即可。
好了,领导的要求算是达到了,打完收工。
就这样?
如果就这么结束了,我的职业成长也就止步于此了。毕竟上面的只能称为办法,都算不上是方案。
我们需要的是一个真正的方案,“根治”这些问题,以及应对未来可能出现的状况。在此之前,先来审视下我们现有的架构。
保险业务架构
以下就是我司的保险业务系统架构(简化版)。
大部分金融公司在早期阶段,采用外购业务系统以达到快速上线的目的。逐渐稳定后,开始思考重构业务系统,达到去外包,灵活响应,快速迭代的的目的。
重构阶段大家都会参考现有业务系统的架构,并借鉴友商在互联网金融的行业实践,因此从整体架构上看大家高度相似,况且各CTO们也都是几家互相跳。
架构上的问题
首先和大家解释下我司的电商系统并不像某宝那样,除了基础的交易功能(真的很基础),它更多承载的是为前端提供聚合数据和适配的功能。
正是因为它承载的这些,目前电商有些让人头疼的问题:
- 电商中强耦合了各条业务线的逻辑,后端稍有风吹草动,电商就山洪爆发
- 为不同前端提供不同的适配接口,大量重复工作
电商的小伙伴对此早就苦不堪言了,他们也想尽早解脱,可是该怎么解脱呢?
新的方案
在分析完问题的当天,我们紧接着就开始讨论“一劳永逸”的方案了。
康威定律中提到了一点:
大的系统组织总是比小系统更倾向于分解。
单从架构上看,核心业务能拆也拆得差不多了,下一步的规划应该是利用领域驱动设计,由边缘服务开始重构,逐步完成整个核心的重构(农村包围城市,YYDS),厘清每个服务的职责,达到拥有清晰边界,合理设计的目的。
那么谁能拆?电商系统!
电商系统承载了本不属于它的工作,为前端聚合数据,提供接口,要拆分的话非它莫属。但是该怎么拆呢?
引入BFF
讨论中我们想到引入BFF为前端进行聚合,适配数据,降低电商系统与各后端服务的耦合,为电商系统瘦身。
BFF(Backends For Frontends,服务于前端的后端)是Sam Newman在2015年的文章《Backends For Frontends 》中提出的概念,Sam Newman也是《微服务设计》的作者。
说句题外话,近些年较火的领域驱动设计,是2003年Eric Evans提出的,思想都是远远领先于应用的。
言归正传,BFF简单来说就是为每个前端提供独立的“后端”服务,以适配不同的业务场景,降低系统间的耦合。
为什么做BFF?
Sam Newman文中提到:
For organisations using a large number of services however they can be essential, as the need to aggregate multiple downstream calls to deliver user functionality increases drastically.
大致意思是,对于使用大量微服务的系统来说,BFF是必不可少的,因为聚合多个服务调用,会导致需求开发的工作量急剧增加。
微软也有一篇关于BFF的文章《Backends for Frontends pattern》,文章中提到了4种使用BFF的情况:
A shared or general purpose backend service must be maintained with significant development overhead.
You want to optimize the backend for the requirements of specific client interfaces.
Customizations are made to a general-purpose backend to accommodate multiple interfaces.
An alternative language is better suited for the backend of a different user interface.
浅翻一下:
- 当你们需要大量工作来维护一个通用服务的后端时
- 需要针对不同的客户端进行定制功能开发时
- 为了适用于不同的接口,对通用后端进行定制时
- 某个客户端更适合使用另一种编程语言开发后端时
我司的电商系统,恰好踩中了前3条,看起来引入BFF非常有搞头。
做几个BFF?
摆在眼前的一个问题是,要为每个前端都做BFF吗?
这样虽然解决了电商系统的问题,但是也增加了运维,开发的工作量,同时还需要申请更多的服务器,成本也是不得不考虑的一个方面。
首先我们想到的是,iOS和Android的功能几乎一致,支付宝程序,微信小程序的功能也和它们大致相同,它们是不是可以共用BFF?
想法也得到了理论支持,Sam Newman文章中提到了Stewart Gleadow的建议:
One guideline that I really like from Stewart Gleadow (who in turn credited Phil Calçado and Mustafa Sezgin) was ‘one experience, one BFF’. So if the iOS and Android experiences are very similar, then it is easier to justify having a single BFF. If however they diverge greatly, then having separate BFFs makes more sense.
Stewart Gleadow建议,如果iOS和Android的使用体验非常相似,它们使用共同的BFF是合理的。那么我们初步的规划是,将电商系统的功能拆分出3个BFF,并为它们添加独立的网关:
这么规划的理由是:
- APP(iOS和Android),支付宝小程序和微信小程序有着几乎一致的功能;
- Web和H5的功能较为简单,大部分都是独立的变更,避免各系统的需求耦合;
- 开放平台的功能较为特殊,需要独立的BFF。
至于添加独立网关,是因为对鉴权,路由,数据统计等功能有不同的要求。
BFF真的解决了我们的问题吗?
我们再来回顾最初的问题–系统太慢了,造成这些的问题的原因是什么?
- 查询单张保单全量的数据,就好比你只需要看文章的摘要却给了你整本书;
- 查询数据的同时,连带查询了电子保单,进行文件IO的操作。
往小了说就是开发偷懒了,为了方便将所有数据都融合到同一个接口中。查询保障信息?调用保单查询接口;查询被保人信息?调用保单查询接口;查询受益人信息?全都可以调用保单查询接口。
往大了说就是随着保险行业不断深入互联网,传统保险行业的架构已经尽显疲态。
而BFF的引入可以为我们解决以下几点问题:
- 降低电商系统强耦合后端服务带来的系统复杂度,降低了变更风险
- 更加纯粹的电商系统,便于后期探索更多的保险自营业务的可能性
- 后端服务提供单一功能通用接口,BFF按需使用,避免了大而全带来的性能问题
- BFF完成聚合,适配,屏蔽前后端变化,实现快速响应功能迭代
特别是,引入BFF后前后端互为黑盒,对于部分前端的交互改版,后端完全可以不参与。
那么,Sam Newman,代价是什么?
世界上没有完美的的架构设计方案,每次新架构设计的引入都会带来新的问题,那么BFF带来了什么问题?
显而易见的问题是,BFF的引入并没有解决业务的复杂度,只是转移到了BFF层。
其次,引入新的服务,会降低全链路的可靠性,同时也带来了更高的网络延迟风险。
最后就是,开发成本,运维成本和硬件成本的增加。
到底做不做BFF?
在这点上,我们并没有太多的疑虑,BFF是肯定要做的,因为对我们来说,收益远高于成本。
开发成本,运维成本以及硬件成本,说白了就是Money。可靠性的话,我们有一套较为成熟的异地多活和同城灾备方案,也无需过多担心。
最后就是复杂度的问题,实际上对我们来说,引入BFF确确实实降低了电商的复杂度。至于BFF的复杂度,我们会结合这些年的实践经验,尽可能避免之前由业务需求带来的复杂度。
结语
好了,到这里我们已经初步拟定了后续的优化方案,至于技术实现就是另外一件事了,里面还牵扯到前后端谁来做BFF的问题,不过这些是后话了。
最后就是写PPT汇报了,领导听完非常高兴,当即要求我们修改OKR,争取年底能上线。并说道,你们好好干,明年我换辆新车。
推荐阅读
- Sam Newman – Backends For Frontends
- Backends for Frontends pattern – Azure Architecture Center | Microsoft Learn
- 微服务设计 (豆瓣)
- 人月神话(40周年中文纪念版) (豆瓣)
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net
机房租用,北京机房租用,IDC机房托管, http://www.e1idc.net