2.4 如何做架构设计

对于如何做架构设计,笔者总结了以下几点:

●以终为始,不忘初心。

●使用PMC(Production Material Control,生产及物料控制)框架。

●从多视角、多层次看架构。

●满足利益相关者的需要。

●聚焦SLA(Service-Level Agreement,服务等级协议)。

●抽象、协作、扩展、复用。

●分析全息视图。

前4点本质上都是从客户价值出发的,第5点和第6点针对的是多层次视图问题,第7点谈的是探寻架构的基本方法。下面具体谈一谈。

2.4.1 以终为始,不忘初心

笔者在如何做架构设计这部分旗帜鲜明地提出:以终为始,不忘初心。初心是什么,就是用户的本质需求。如图2.7所示,不论系统如何组织,用户最关心的是响应时间(Response Time,RT),以及展示的金额是否准确。所以,在一开始设计时就要考虑如何获取到用户关注的两个要素——体验和准确性。是否要考虑链路级的监控,以及链路级的关键数据校验;如果规则分散到多个系统,如何检查整体组合的命中等情况;链路上每个系统的容量和响应时间是多少;等等。以上这些都在考虑范围内。

图2.7

2.4.2 PMC框架

什么是PMC框架呢?P代指平台端(Platform),M代指商户端(Merchant),而C代指客户端(Customer)。平台型的产品都有这三端的考量。在构建一个具体软件的时候,一定要搞明白它在服务谁?是客户还是商户,还是兼而有之。以滴滴/Uber、天猫和某餐饮平台的App来进行比较,如图2.8所示,其中C端为客户端,B端为商户端。

滴滴或Uber解决出行的问题,天猫解决网上购买商品的问题。平台设计的本质是符合平台端、商户端、客户端的三端诉求。比如,电商的 IM(Instant Messaging,即时通信)解决信息沟通问题,客服解决售后问题,还有其他方式解决可能的作弊、交易、安全及评价体系等一系列的问题。Uber也是基于交易的平台,乘客需要付款给司机,客户的需求则是更快更省地叫到车,司机的信用评价沿用了星级评价、投诉等保障措施,但对于 Uber来说,基于地理位置的撮合(用户的地点,以及在一定范围内寻找可以承载他的车,包括允许共享座位的车)是其核心,也是其孵化出dispatch模块的原因。

图2.8

有意思的事情发生了,Uber的老板一觉醒来发现他创造的不仅仅是一家出行公司,如图2.9所示。

司机和车只是平台的资源,供应方(Supply)可以是一家蛋糕店、鲜花店或者是西湖的休闲船只管理中心,那么面向不同的供应方,该平台架构的哪些能力是可以复用的呢?如下:

●调度能力。

●用户呼叫的界面、策略(大部分)。

●地理位置处理。

●交易事后处理等。

图2.9

大部分变化在于运送的物品(属性)及一些附属需求,而这些都是可以通过良好的平台设计及可扩展的新市场产品特性来完成的。

有一位互联网圈资深产品经理曾提及要识别刚需和伪需求。刚需比如衣食住行,而我理解的伪需求或许是小众需求,只针对特定客户体现其价值,并可能仅仅是一点点演绎,而非本质需求。做软件架构设计,识别本质需求比较重要。比如客户让你把对账单导出成Excel,可能他还需要导入手工账单,以便和自己记账的数据进行核对,那么如何为用户提供对账功能就值得再考虑了。

2.4.3 从多视角、多层次看架构

由于不同角色对领域或者概念的理解存在差异,因此很容易导致频繁的争吵。前面提到了统一概念定义,讨论了远景、目标和价值,但是在更具体的讨论中,往往有可能陷入第二个层次的喋喋不休,而研发管理者往往乐见这些争吵。模拟一下管理者的心理:

“嗨,比尔,你看,架构师讨论得多专注,多投入啊!一个概念问题竟然讨论了大半天,对自己要求太严格了!这是工匠精神呢!”

另外一个场景就是,小V在说红包是一个支付工具,而小B说它明明是一个营销工具。有时候还上升到哲学层面,比如门是因为有把手才成为门,还是因为它本来是门,而把手只是它的组成部分。笔者也乐见架构师们不断探究的精神,但是有以下两点建议:

●应该给争论设置一个时间节点——由于有些概念未明晰,工作便无法开展,因此争论有时不可避免;而有些概念并不影响现阶段甚至下一步工作的开展,因此需要避免对这些概念的过度争论。不要忘了,争论是为了更好地构建软件系统,而软件是为业务服务的。

●争论的问题未必是“非黑即白”的选择题,事物往往具有复杂性,通过不同维度和层次的抽离,可以看见不同层次的本源。比如CTG-MBOSS功能架构就对电信业务领域做了客户及产品、服务、资源、企业管理的划分,又基于提供、保障、计费这个层次做了划分,如图2.10所示。

有的同学可能会问,多个视角有什么用呢?图2.10通过颜色区分了企业管理支撑、业务支撑、运营支撑等。不难理解,业务支撑直接面向用户前端,而运营支撑是支持业务支撑的,比如产品管理、网络资源管理。运营支撑能力直接影响到前端业务的效率、体验和SLA。

以一个第三方支付的应用为例。单就这样一个应用来说就有诸多的考量因素,比如风控、营销、签约(签约的顺畅度)等,如图2.11所示。

图2.10

图2.11

图2.11提及的维度已经很全面了,但毕竟是离散的,可以进一步进行分类管理,分类如下:

●C端业务主路径:支付。

●B端业务路径:签约、收单、核对。

●平台端业务:签约流程管理(商户、产品、接口、费率、优惠等),收单流程管理(收单、支付、风控、额度、渠道、优惠)。

根据企业运营业务的经验,可以抽象出一种典型的组织形式:业务执行、业务运营、企业内部运营和主数据,如图2.12所示。业务执行、业务运营等视角又可以加以组合,组合可以用来考量设计的要素(谁为谁服务)。比如营销是业务运营的板块,是为了更好地执行业务。Uber的打折优惠也好,免费拼车也好,都是不影响主流程的,是可以进一步刺激消费的设计;然而,有的产品把营销活动设计得极其生硬,极其难用,相当于把一件大人的衣服穿到了一个小朋友身上。或许可以有这样一种设计,可以对产品的流量进行分析,当检测到某项指标达到某一阈值时,便强烈预警产品应该优化,并能通过分析给出优化建议。可能一开始数据不够准确,但是通过不断训练和尝试,可以形成正循环。

百度的朋友曾经在某个会议上分享过通过快速构建 MVP 以及收集舆情信息不断优化产品迭代的案例,这就是一种蛮有意思的尝试,业务运营一定要为业务执行(产品)服务。

图2.12

2.4.4 满足利益相关者的需要

在写本部分内容时,我的好友,同时也是时任诺基亚创意经理、架构师的石涛声向我推荐了《软件系统架构:使用视点和视角与利益相关者合作》一书,说我的某些观点与此书有相通之处。什么是利益相关者?利益相关者最终会对软件产品或者系统的范围、功能、操作特性和结果做出决定并产生影响。没有利益相关者就没有必要创建架构,因为没有人来构建部署它或者为此付费。

好的架构描述能够将架构的关键内容向利益相关者进行有效的展示。

2.4.5 聚焦SLA

SLA的全称是Service-Level Agreement,意思是服务等级协议。聚焦SLA时,有如下两个明确的注意事项:

●避免关注枝节而遗忘全局。

●避免关注自身而忘却出发点。

前面阐述了从客户价值出发构建架构,同时考虑C(客户端)、P(平台端)、M(商户端)的不同诉求。那么如何衡量这些诉求是否实现了呢?答案是需要清晰明确地设定SLA。举个例子,12306这个众所周知的火车票购买网站由于经历了2012年的阻塞、间或的宕机及与黄牛(抢票软件等)斗智斗勇开启了“杀伤力巨大”的图片校验码而引发众多人“吐槽”。那么,12306给客户带来的价值是什么呢?首先是让尽量多的人买到票(票的数量受制于运输能力),另外是让人能够比较顺畅地买到票,再者是保障了公平性(开发抢票软件的浏览器是不是应该治理,还有那些倒卖的地下软件)。有专家说12306不应该把图片校验码弄得这么复杂,对抗黄牛不能主要依靠这种手段。我认为只说对了一半,防作弊的确是它要做的事情,这是为了客户价值而做的。但是,有报道称,对12306图片识别困难的回应是“能买到票就不错了”,这样的说法显然降低了对价值实现的认可。另外,虽然票最终是会卖完的,但是票以什么样的方式卖完,大部分人购买时是否顺畅,这些也是非常重要的(近些年已经没有出现宕机问题了)。

说了这些,大家不妨梳理一下实现客户价值的方法,比如是否可以使用预约的方法,只有预约才有资格买票,且实行7×12小时分批次购买等;再比如运行程序时如何区分购买者是普通用户还是刷票软件,针对不同的渠道采取不同的策略,这里肯定有不少事情可以做,而不是一图难天下。

讲了12306,再来看一个例子。以图2.13所示的第三方支付为例(仅作示意,切莫细究图示模块内容的完备性)。

业务运行(支付业务)涉及用户下单、形成交易并支付等相关处理,而用户非常关注能不能快速、正确且安全地进行支付。为了求证付款正确性,他们也会习惯性地在第一时间查询消费记录。对于商户而言,他们也有查询收支明细的诉求。到这里,可能有人会问,SLA和具体的架构设计存在哪些关联呢?

图2.13[15]

仍以图2.13为例,可以通过一系列假设进行恰当的预设计,如图2.14所示。

图2.14

比如从系统运行角度看,如何度量用户SLA(体验顺畅度、支付成功率、从下单到完成支付花费的时间)以及更多行为数据(用户在哪一步流失的,什么原因)等。要做这些,就需要进行全链路监控,进而需要做统一的上下文、事件码来串联请求的链路(traceId 描述技术语义,业务语义则可能被userId所描述)。再举一个例子,在数据量达到百万级的情况下,可以以buyerId或者sellerId为查询条件分别满足按买家、商家不同维度的消费记录查询。当数据规模扩大之后,可以通过数据库表的拆分来做Sharding,此时,则涉及键(Key)的选取问题,该问题就是比较典型的多维度查询问题。

2.4.6 抽象、协作、扩展、复用

抽象、扩展人人都知道,但是如何识别本质而不做BDUF(Big Design Up Front)则并不容易。以 Uber 为例,Uber 的定位已经不仅仅是一家出行公司,而是一家可以利用其物流能力叫飞机、叫外卖,包括送花的公司,如图2.15所示。

要知道Uber也不是一天炼成的。有文章披露,旧系统是为专用客车运输所设计的,其中做了很多假设:每辆车一个乘客,不适用Uber Pool(拼车服务)。将运送人的想法深深嵌入数据模型和接口里,会限制将该架构扩展到新的市场和产品上,比如运送食物和箱子。

最初的版本是按城市来划分的。这样做对于可扩展性而言是好的,每个城市可以独自运营。但当越来越多的城市加入的时候,这就变得越来越难以管理了。城市有大有小,负载也不一样。

后来,他们重构了 dispatch 模块,让调度的接口不依赖具体的专用客车领域而具备可扩展性,并通过微服务来进行切分和管理。

图2.15

2.4.7 分析全息视图

笔者有一个观点,就是要从全息视图来看问题,拘泥于细节上的问题会出现“一叶障目不见泰山”的情况。在软件的设计模式里有一个拦截器模式,在软件思想里有面向切面的设计思想,比如著名开源软件Spring的AOP思想。面向切面也是架构设计的一个思路,比如可以分析稳定性及性能,包括对应系统特有的资金安全等。

如图2.16所示,一个支付系统外部连接商户系统,中间做支付处理,后台与银行或其他机构进行通信。那么在幂等性、接口的结果状态和错误码约定方面都要做相应的处理。

图2.16

仅以网关系统和银行机构的通信来进行说明,是因为如果二者的约定出了问题,则后果不堪设想。比如银行返回“未知”或“处理中”,而如果支付系统将其当成“支付失败”来处理,则最终银行处理成功时可能会导致用户资产损失。