服务端开发:技术、方法与实用解决方案
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

1.3 服务端开发核心流程

软件开发一般是以项目为单位进行的。从技术的视角看,一个软件项目从提出需求到落地,通常要经历需求评审、系统设计、开发、联调、测试、验收、上线等诸多环节,如图1-13所示。

图1-13 软件项目流程

服务端开发是核心岗位,仅仅具备“开发运行于服务器端的程序”的能力是远远不足以胜任的。在头部互联网企业对该岗位的职责定义中,普遍要求服务端开发工程师需要具备业务分析、产品设计、架构设计、技术攻关、团队协作、文档编写、系统维护等综合能力。读者可能会认为如此高要求完全是“内卷”的结果,但从笔者的工作经验来看,这些能力其实是服务端开发的不同阶段所需的,具体而言,服务端开发可分为需求分析、抽象建模、系统设计、数据设计、非功能性设计、编码实现及发布运维等阶段,每个阶段都需要相应的能力作为支撑,核心流程如图1-14所示。

图1-14 服务端开发核心流程

1.3.1 需求分析

在实践中,根据业务复杂度的不同,有时候可能只需要画个简单的流程图便可梳理清楚系统,但是复杂系统的设计则困难得多。以12306为例,2020年春运期间高峰日点击量达1495亿次/天,约170万次/秒,单从点击量数据看,项目的设计、实现就不容易,在此,读者不妨设想一下,如果你是12306项目的设计师,你准备如何设计这个系统呢?你的设计方法是怎样的?

在笔者看来,不论采用何种设计方案,首先需要明确一个问题:你了解铁路票务吗?如果根本不了解铁路票务,那又怎么可能设计出满足铁路票务业务需求的系统呢?在实践中,设计并开发一个软件系统并不单纯是技术层面的问题,首要任务是深入了解其业务。而要了解业务,则必须要经过需求分析。

(1)需求分析的定义

需求分析也称为软件需求分析、系统需求分析或需求分析工程,是软件工程师经过深入细致的调研和分析,准确理解业务方和产品经理的具体要求,将非形式化的需求表述转化为完整的需求定义,从而确定软件系统必须做什么的过程。

需求分析是软件计划阶段的重要活动,也是软件生存周期中的一个重要环节。该阶段是分析系统在功能上需要实现什么,而不是考虑如何去实现。需求分析的目标是把业务方和产品经理对待开发软件提出的“要求”或“需要”进行分析与整理,确认后形成描述完整、清晰、规范的文档,确定软件需要实现哪些功能、完成哪些工作。

(2)需求和业务的关系

很多工程师认为理解需求就是理解业务,这是一种误解。需求其实是业务经过产品经理消化后的产物,通常已经经过产品经理的演绎和拆解,因此并不是业务本身。当然,了解的需求越多,对业务的全貌就越清楚。那么,什么是业务呢?业界对“业务”有多种定义,但其主要思想基本一致:业务是指商业(或非商业)组织及其运作的活动流程。直白点说,业务就是一系列人通过一系列活动完成某一任务的过程,业务可大、可小、可拆分,比如支付宝的支付业务,往下可拆分为花呗、余额宝、银行卡等。

(3)为什么要进行需求分析

在互联网行业,一个完整的需求通常参与者众多,包括业务方(如运营)、产品经理、服务端开发、客户端开发、测试、交互、视觉、项目管理等人员。正如上文所述,需求的本质是业务方原始诉求经过产品经理演绎、拆解的产物,一些问题由此而生:需求文档所述与业务方的核心诉求可能不一致;产品设计难免存在遗漏和缺陷。因此,作为工程师,拿到需求后并不是立即投入研发,而是进一步分析需求。

(4)需求分析阶段需要厘清的内容

❑业务背景:即业务当前状况及对业务发展、变化起重要作用的客观因素。例如,某虚拟电商平台用户数量达2亿,客单价约140元。

❑业务问题:即结合业务当前状况和客观因素,分析与业务预期之间的差距。接续上面的例子,用户规模、客单价都是当前的状况,而经过调研发现,竞争对手的客单价已达200元。为什么差距这么大呢?业务分析认为,关键因素是自身平台营销活动数量少于竞争对手,这就是业务面临的问题。

❑业务诉求:即业务想到达到的目标。接续上面的例子,诉求是丰富营销活动模式,增加活动频次,比如每月开展一次“满减活动”(如“每消费满50元减5元”),促使用户凑单,从而提升客单价。

❑业务价值:即预期可以产生的收益。接续上面的例子,营销活动促进客单价提升,由GMV(Gross Merchandise Volume)=成交用户数×客单价可知,客单价提升,GMV增长,进而实现利润增加。

❑产品方案:即基于业务诉求形成的产品方案。接续上面的例子,支持如“满50减5”的营销策略,需要一个运营后台,支持运营灵活地配置营销活动(满减门槛、优惠额度、预算控制等),同时须支持将活动定向投放给指定的人群(精细化运营,针对不同的人群投放不同的活动)。

❑评估指标:即评估产品落地后实际效果的客观指标。接续上面的例子,评估指标如GMV、客单价、满减活动参与用户数等。

❑技术现状:结合上述内容,站在技术角度初步评估当前技术架构、系统容量、风控能力等是否可支持业务诉求落地和未来发展。

需求分析要义:作为工程师,不要急于给出“解决方案”,而应带着问题分析需求,大胆假设,小心求证。例如,需求所述的问题是问题吗?对于问题根因,业务方或产品经理能分析准确吗?产品方案能解决问题吗?产品方案落地成本可接受吗?解决问题的效果可评估吗?

1.3.2 抽象建模

这一阶段需要基于需求分析阶段的知识储备,进一步提炼、建立可以刻画业务本质特征的模型。抽象建模实际上包含抽象和建模两个部分,由于两者通常同步进行,因此归纳为抽象建模。

1.抽象

抽象在中文里可作为动词,也可作为名词。作为动词的抽象是指一种行为,这种行为的结果,就是作为名词的抽象。百度百科上是这么定义抽象的:人们在实践的基础上,对于丰富的感性材料通过去粗取精、去伪存真、由此及彼、由表及里地加工制作,形成概念、判断、推理等思维形式,以反映事物的本质和规律的方法。如图1-15所示,对现实中的公牛进行抽象,用简单的线条勾勒出公牛的本质特征(如牛尾、牛角、牛鞭等),抽象牛具备更好的泛化能力,不再局限于具体品种的公牛,而是可以实例化为几乎所有品种的公牛。

图1-15 抽象示意图

事实上,日常生活中抽象无处不在。例如数字,人类初期并没有数字这一概念,原始人或许能够理解3个苹果和3只鸭子,但他们的脑海里不存在数字“3”这个概念,在他们的意识里,3个苹果和3只鸭子是没有任何联系的。人类进化到一定阶段后发现了这两者之间存在的一种共性,即是数字“3”,于是数字这个概念就逐渐形成了。此后,人们就开始用数字对各类事物进行计数。

软件开发本身就是一个不断抽象的过程。软件工程师把业务需求抽象成数据模型、模块、服务和系统,面向对象开发时抽象出类和对象,面向过程开发时抽象出方法和函数。换言之,上面提到的模型、模块、服务、系统、类、对象、方法、函数等,都是一种抽象。由此可见,抽象对软件开发非常重要。

2.建模

业务需求大都是以具象的现实世界事物概念来描述的,依附于自然语言体系,距离软件工程非常“远”。为了将需求落地,工程师需要开展一系列的工作,其中建模尤为重要。建模的过程实际上就是从业务领域里找出反映业务本质的事物、规则和结构,并将其抽象化,进而描述业务运行的基本原理、交互机制及用户的首要利益。从某种意义上说,建模的过程就是系统地实施抽象的过程。

目前,服务端开发常用的建模方法主要有3种,即用例建模法、服务建模法和事件建模法,在实践中需要根据业务场景的特点和复杂度选型。关于建模方法详见第3章。

1.3.3 系统设计

系统设计又称为系统架构。在系统设计阶段,需要将抽象建模的成果有条不紊地映射到具体的技术实现中,要通盘考虑、权衡取舍。工程师须具备一定的技术深度、技术视野,同时充分理解业务。如果在抽象建模阶段做得足够好,建模方法选型与业务特点匹配,且抽象出的模型可以准确刻画业务的本质特征,那么系统设计将是一件相对轻松的事情。

1.设计和划分功能域

在互联网领域,一些业务的复杂度是非常高的。图1-16所示为阿里的电商业务摘要架构图。该架构自顶向下划分为前台、移动中台、业务中台、PaaS、IaaS,而业务中台又可细分为会员中心、商品中心、交易中心、支付中心、评价中心和订单中心,这其实就是一种高层次的功能域设计和划分。

对于复杂的业务,首先应设计和划分功能域,以降低复杂度。具体而言,在完成抽象建模后,可基于模型将系统拆分为不同的功能域,各个功能域相互协作,共同实现业务需求。需要特别注意的是,不同功能域之间必须有清晰的职责边界,同时单个功能域的复杂度不能过高。我们可以将业务中台的会员中心进一步拆解,划分为积分、权益、淘气值等功能域,如图1-17所示。

2.设计功能域之间的协作

经过多年的发展,互联网领域的“高增长”时代已经过去,正大步迈入“存量”时代。由于大多数业务已经相当成熟,通用基础能力亦趋于完善,因此业务需求的复杂度通常不会高到需要步骤1中的设计和功能域划分。

设计功能域之间的协作,可视为一种“粗粒度”的服务编排。具体而言,借助已有功能域提供的服务,目标功能域(新建或者在已有功能域的基础上扩展)可以快速实现新的功能,满足业务需要。在实践中,功能域之间协作的关键在于充分、合理地利用已有公共基础服务。如图1-18所示,会员中心的权益投放通常有个性化推荐(算法推荐)和运营定向投放(运营针对特定人群,如新用户,定向投放特殊权益)两种策略。这两种投放策略可以通过不同的功能域协作实现。

图1-16 阿里的电商业务摘要架构图

图1-17 会员中心功能域

图1-18 功能域协作示意图

3.确定功能域之间的数据边界

功能域之间的协作设计完成后,整个系统的上下游依赖也就清楚了,接下来我们需要进一步明确功能域之间的数据边界。以微服务架构为例,一个功能域可作为一个应用,不同功能域之间通过服务调用来协作,对于服务,通常用请求(服务调用方发起请求)和响应(服务提供方响应请求返回结果)的数据模型来描述边界。

如果有多名服务端工程师(甚至团队)参与产品需求的开发,在确定数据边界之后,不同功能域的职责也就进一步明确了,工程师可以专注于其所负责功能域的内部设计和开发。

4.功能域内部设计

通过步骤3,功能域的技术目标得以明确,接下来便是通过设计去实现这些技术目标。在功能域内部,为了降低问题的规模和复杂度,同时增强系统的可扩展性和可维护性,一般采用如图1-19所示的“分层架构”。

在分层的同时,很自然会产生一些模块。需要注意的是,在本步骤中虽然通过分层将功能域进一步细化到了“模块粒度”,但并不涉及模块实现细节,这是下一个阶段要做的事情。

图1-19 功能域内部分层设计示意图

5.详细设计

详细设计是系统设计的最后一个环节,对大多数服务端开发工程师而言,这个环节应该是最为熟悉和擅长的,毕竟在一名工程师的职业生涯中,大多数时间都在从事子系统/模块的设计和开发。子系统/模块的设计是一种详细设计,需要从细节层面考量问题,所做的设计用于直接指导开发。在这一步中,设计结果一般包括以下内容。

❑相关模型:如领域模型图、类图、实体关系图、数据表清单等。其中领域模型图一般在抽象建模阶段完成,而数据表清单则属于数据设计范畴。

❑上下游交互:大多数业务场景或多或少存在上下游交互,交互一般通过数据库、接口、消息等方式。如果选择通过接口交互,须写明类名、方法名、入参、出参、结果码等;如果选择通过消息交互,须写明消息Topic、Group、消息体结构等;如果选择通过数据库交互,须写明表名、索引、QPS等。

❑方案描述:当业务较为复杂的时候,文字描述难以直观地反映设计方案的细节,也不便于后续评审和实施,因此,一般通过流程图、时序图来描述。

需要注意的是,详细设计环节通常是包含数据设计的,但考虑到数据设计的重要性和复杂性,笔者会进行详细介绍。

1.3.4 数据设计

谈及数据设计,大多数IT从业者的第一反应是数据库设计。这其实是片面的。事实上,数据设计的内涵非常丰富,数据库选型、表结构设计、字段设计、索引设计、缓存设计、数据核对、数据监控等都属于数据设计的范畴。

如图1-20所示,完整的数据设计一般包含3个环节:领域概念模型设计、逻辑数据模型设计和物理存储模型设计。不过,落实到具体的业务需求,这3个环节并不是必需的。例如在一些相对简单的业务场景中,根本不涉及领域概念模型,也无须领域概念模型设计。本节重点介绍一下数据库选型和存储方案设计,其他数据设计相关的内容在后文再展开详细介绍。

图1-20 数据设计主要环节

1.数据库选型

目前,常见的数据库类型如表1-6所示,数据库类型不同,其特性和适用的场景也存在较大差异。服务端开发工程师需要根据业务场景自身的特点,结合数据库的性能、存储成本、容量、一致性、读写偏好、稳定性等指标综合评估选型。

表1-6 常见的数据库类型

(续)

2.存储方案设计

数据库选型确定后,接下来需要根据数据库进一步设计存储方案。为了便于读者理解,笔者以MySQL和HBase为例介绍存储方案设计。如图1-21所示,对于MySQL数据库,存储方案的核心是字段设计和索引设计;而对于HBase,核心则是RowKey设计。

图1-21 存储方案设计示例

1.3.5 非功能性设计

业务方提出的需求和产品经理设计的产品方案大都聚焦于业务功能描述,在验收时,通常也只是验证要求实现的功能是否符合预期,极少考虑稳定性、兼容性、安全性、异常补偿等非功能性问题。然而,很多时候,非功能性问题往往事关项目成败,因此必须根据业务场景谨慎评估非功能性问题并设计相应的解决方案。

1.稳定性设计

在互联网领域,稳定性设计是最重要的非功能性设计。根据阿里官方公布的数据,在2020年的“双11”大促活动中,天猫平台订单创建峰值达58.3万笔/秒。如此巨大的流量,若没有稳定、可靠系统和服务,业务便是空中楼阁,随时有崩塌的可能。那么,在互联网企业,可以通过哪些具体的措施来保障稳定性呢?如图1-22所示,在稳定性保障的流程中,容量评估、压测验证、限流/预案等环节都是需要服务端工程师来保障的。

图1-22 保障稳定性的一般流程

2.可测试性设计

与客户端不同,服务端对用户来说是不可见的,测试工程师无法直接通过UI(User Interface)界面来验证服务端的复杂逻辑,因此,服务端开发工程师在进行非功能性设计时,需要充分考虑可测试性。

❑功能可测试:对于客户端不直接可见的功能(如异步处理、定时任务补偿等),服务端可采用在关键链路打印摘要日志等方式来帮助测试人员识别不可见逻辑是否正确执行。

❑支持压测:压测数据(如用户账号、场景等)通常为虚构数据,在服务端强校验逻辑下(如账号校验)无法直接跑通全链路。因此,服务端需要识别压测数据,并支持压测标识传递,对于不支持压测的特殊环节还需要支持约束跳跃。

❑灰度可测试:重大变更通常需经过充分灰度测试才能对外切流。在灰度环节,服务端需要支持多种流量控制策略,如白名单、百分比、万分比等。

3.其他非功能性设计

非功能性设计涉及面广,除了前面介绍的稳定性和可测试性,还有应用安全、异常处理、扩展性、兼容性等方面,如图1-23所示。

图1-23 部分非功能性设计内容