您可以捐助,支持我们的公益事业。

1元 10元 50元





认证码:  验证码,看不清楚?请点击刷新验证码 必填



  求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Model Center   Code  
会员   
   
 
     
   
 
 订阅
架构权衡评估方法(ATAM):如何评估一个系统的质量
 
作者: Java前线
  1987  次浏览      16 次
 2023-2-2
 
编辑推荐:
本文主要介绍了如何评估一个系统的质量。希望对你的学习有帮助。
本文来自于知乎,由火龙果软件Linda编辑、推荐。

1 质量属性

在系统设计和开发过程中,我们比较容易关注系统的功能维度,例如有没有实现预期功能,输入参数和输出参数是否匹配等等,这是比较容易测试和衡量的。

但是我们不能就此止步,因为满足功能是系统的基本要求,还需要关注系统的非功能维度,例如系统性能是否优秀,代码可扩展性如何,出现异常是否可以自动降级等等,这些指标决定了系统能否提供高质量的服务。

在之前文章《结构化思维如何指导技术系统优化》提到了三个质量属性:高性能、高可用、高扩展。本文我们充实为六个质量属性:性能、可用性、可修改性、可靠性、安全性、易用性:

1.1 性能

1.1.1 如何定义

性能有两个定义维度,第一个维度是单位时间内可以做多少事情,第二个维度是做完单位数量的事情需要多长时间,常用以下参数进行量化:

QPS:每秒处理请求数

TPS:每秒处理事务数

并发数:同一时刻处理请求数/事务数

响应时间:系统对请求做出响应的时间

并发数 = QPS x RT

1.1.2 如何提升

提升性能可以从两个维度思考,第一个维度是时间维度,从事前、事中、事后三个时间节点进行优化。事前是指在访问最开始就拒绝无效流量。事中可以使用提高并发度(并发编程),增加资源(服务器、分布式缓存、读写分离,分库分表),减少交互(批量请求)等方案。事后是指数据分析需求可以放到离线数据中心进行,不要放在主应用和主数据库进行。

第二个维度是层次维度,每一层都可以进行优化。系统架构一般分为数据层、缓存层、服务层、网关层、客户端、代理层,每一层都可以按照事前、事中、事后进行优化,而不是一提到优化就是加缓存,应该更加全面地思考。

1.2 可用性

1.2.1 如何定义

可用性是指系统正常运行时间占总运行时间比例,业界常用X个9指标进行量化,例如可用性达到5个9,那么全年系统不可用时间只有5分钟:

1.2.2 如何提升

(1) 非线性

我们从另一个概念理解可用性:非线性,这个概念在生活中无处不在。

假设要赶早上8点钟的火车,如果6:30出发可以在7:00到达车站,所以得到一个结论:只要30分钟就可以到达车站。

如果早上睡晚一点7:15出发,那么按照预期7:45可以到达车站。但是最可能的结果是错过这趟火车。因为正好遇上早高峰导致至少需要1个小时才能到达车站。

我们再分析一个互联网秒杀场景。假设秒杀系统当每秒30个请求时,响应时间是10毫秒。如果按照线性思维可以做出如下设计:

每秒30个访问量响应时间10毫秒

每秒300个访问量响应时间100毫秒

每秒3000个访问量响应时间1000毫秒

如果按照这个思路做系统设计可能会发生重大的错误。因为当每秒3000个访问量发生时,响应时间可能不是1000毫秒,而是可能直接导致系统崩溃。

这就是非线性,事物不是简单线性叠加关系,当达到某个临界值时会造成一种截然不同的结果。

(2) 提升策略

冗余 + 自动故障转移

最基本冗余策略就是主从模式。原理是准备两台机器,部署了同一份代码,在功能层面是相同的,都可以对外提供相同的服务。

一台机器启动提供服务,这就是主服务器。另一台机器启动在一旁待命,不提供服务,随时监听主服务器的状态,这就是从服务器。当发现主服务器出现故障时,从服务器立刻替换主服务器,继续为用户提供服务。

自动故障转移策略是指当主系统发生异常时,应该可以自动探测到异常,并自动切换为备用系统。不应该只依靠人工去切换成,否则故障处理时间会显著增加。

降级策略

当系统遇到无法承受的压力时,选择暂时关闭一些非关键的功能,或者延时提供一些功能,把此刻所有的资源都提供给现在最关键的服务。

在秒杀场景中下订单就是最核心最关键的功能。当系统压力将要到达临界值时,可以暂时先关闭一些非核心功能如查询功能。

还有一种降级策略,当系统依赖的下游服务出现错误,甚至已经完全不可用了,那么此时就不能再调用这个下游服务了,否则可能导致雪崩。所以直接返回兜底方案,把下游服务直接降级。

延时策略

用户下订单成功后就需要进行支付。假设秒杀系统下订单每秒访问量是3000,有没有必要将每秒3000次访问量压力传递给支付服务器?

答案是没有必要。因为用户秒杀成功后可以稍晚付款,例如可以跳转到一个支付页面,提示用户只要在10分钟内支付完成即可。

这样流量就被分摊至几分钟,有效保护了系统。技术架构还可以使用消息队列做缓冲,让下游系统根据处理能力拉取消息。

隔离策略

物理隔离:应用分别部署在不同物理机、不同机房,资源之间不会互相影响。

线程隔离:不同类型的请求进行分类,交给不同的线程池处理,当一类请求出现高耗时和异常,不影响另一类请求访问。

1.3 可修改性

1.3.1 如何定义

可修改性是指是否能够以较高的性价比对系统进行变更的能力,可以分为以下四种类型:

可扩展性:系统扩展新构件时对其它构件的影响程度

可维护性:系统修改旧构件时对其它构件的影响程度

结构重组:重新组织构件关系的难易程度

可移植性:在不同硬件平台、编程语言、操作系统间移植的难易程度

1.3.2 如何提升

可修改性最终在解决牵一发而动全身的复杂性问题,复杂业务之所以复杂,一个重要原因是涉及角色或者类型较多,如果平铺直叙地进行设计会出现if-else代码块,可读性和可修改性都很低。

我们分析一个下单场景。当前有ABC三种订单类型:A订单价格9折,物流最大重量不能超过9公斤,不支持退款。B订单价格8折,物流最大重量不能超过8公斤,支持退款。C订单价格7折,物流最大重量不能超过7公斤,支持退款。按照需求字面含义平铺直叙地写代码也并不难:

public class OrderServiceImpl implements
OrderService { @Resource private OrderMapper orderMapper; @Override public void createOrder(OrderBO orderBO) { if (null == orderBO) { throw new RuntimeException("参数异常"); } if (OrderTypeEnum.isNotValid(orderBO
.getType())) { throw new RuntimeException("参数异常"); } // A类型订单 if (OrderTypeEnum.A_TYPE.getCode().
equals
(orderBO.getType())) { orderBO.setPrice(orderBO.getPrice
() * 0.9); if (orderBO.getWeight() > 9) { throw new RuntimeException
("超过物流最大重量"); } orderBO.setRefundSupport
(Boolean.FALSE); } // B类型订单 else if (OrderTypeEnum.B_TYPE.
getCode
().equals(orderBO.getType())) { orderBO.setPrice(orderBO.
getPrice
() * 0.8); if (orderBO.getWeight() > 8) { throw new RuntimeException
("超过物流最大重量"); } orderBO.setRefundSupport
(Boolean.TRUE); } // C类型订单 else if (OrderTypeEnum.C_TYPE.getCode
().equals(orderBO.getType())) { orderBO.setPrice(orderBO.getPrice
() * 0.7); if (orderBO.getWeight() > 7) { throw new RuntimeException
(
"超过物流最大重量"); } orderBO.setRefundSupport(
Boolean
.TRUE); } // 保存数据 OrderDO orderDO = new OrderDO(); BeanUtils.copyProperties(
orderBO
, orderDO); orderMapper.insert(orderDO); } }

 

上述代码从功能上完全可以实现业务需求,但是程序员不仅要满足功能,还需要思考代码的可维护性。如果新增一种订单类型,或者新增一个订单属性处理逻辑,那么我们就要在上述逻辑中新增代码,如果处理不慎就会影响原有逻辑。

为了避免牵一发而动全身这种情况,设计模式中的开闭原则要求我们面向新增开放,面向修改关闭,我认为这是设计模式中最重要的一条原则。

需求变化通过扩展,而不是通过修改已有代码实现,这样就保证代码稳定性。扩展也不是随意扩展,因为事先定义了算法,扩展也是根据算法扩展,用抽象构建框架,用实现扩展细节。标准意义的二十三种设计模式说到底最终都是在遵循开闭原则。

如何改变平铺直叙的思考方式?这就要为问题分析加上纵向和横向两个维度,我选择使用分析矩阵方法,其中纵向表示策略,横向表示场景:

(1) 纵向做隔离

纵向维度表示策略,不同策略在逻辑上和业务上应该是隔离的,本实例包括优惠策略、物流策略和退款策略,策略作为抽象,不同订单类型去扩展这个抽象,策略模式非常适合这种场景。本文详细分析优惠策略,物流策略和退款策略同理。

// 优惠策略
public interface DiscountStrategy {
    public void discount(OrderBO orderBO);
}

// A类型优惠策略
@Component
public class TypeADiscountStrategy
implements DiscountStrategy { @Override public void discount(
OrderBO
orderBO) { orderBO.setPrice(orderBO.
getPrice
() * 0.9); } } // B类型优惠策略 @Component public class TypeBDiscountStrategy
implements DiscountStrategy { @Override public void discount(OrderBO orderBO
) { orderBO.setPrice(orderBO.getPrice
() * 0.8); } } // C类型优惠策略 @Component public class TypeCDiscountStrategy
implements DiscountStrategy { @Override public void discount(OrderBO
orderBO) { orderBO.setPrice(orderBO
.getPrice() * 0.7); } } // 优惠策略工厂 @Component public class DiscountStrategyFactory
implements InitializingBean { private Map<String, DiscountStrategy
> strategyMap = new HashMap<>(); @Resource private TypeADiscountStrategy
typeADiscountStrategy; @Resource private TypeBDiscountStrategy
typeBDiscountStrategy; @Resource private TypeCDiscountStrategy
typeCDiscountStrategy; public DiscountStrategy getStrategy
(
String type) { return strategyMap.get(type); } @Override public void afterPropertiesSet
() throws Exception { strategyMap.put
(
OrderTypeEnum.A_TYPE.getCode(),
typeADiscountStrategy); strategyMap.put
(
OrderTypeEnum.B_TYPE.getCode(),
typeBDiscountStrategy); strategyMap.put
(
OrderTypeEnum.C_TYPE.getCode(),
typeCDiscountStrategy); } } // 优惠策略执行 @Component public class DiscountStrategyExecutor { private DiscountStrategyFactory
discountStrategyFactory; public void discount(OrderBO
orderBO) { DiscountStrategy discountStrategy
=
discountStrategyFactory.getStrategy
(orderBO.getType()); if (null == discountStrategy) { throw new RuntimeException
("无优惠策略"); } discountStrategy.discount(orderBO); } }

(2) 横向做编排

横向维度表示场景,一种订单类型在广义上可以认为是一种业务场景,在场景中将独立的策略进行串联,模板方法设计模式适用于这种场景。

模板方法模式一般使用抽象类定义算法骨架,同时定义一些抽象方法,这些抽象方法延迟到子类实现,这样子类不仅遵守了算法骨架约定,也实现了自己的算法。既保证了规约也兼顾灵活性,这就是用抽象构建框架,用实现扩展细节。

// 创建订单服务
public interface CreateOrderService {
    public void createOrder(OrderBO
orderBO); } // 抽象创建订单流程 public abstract class AbstractCreateOrderFlow { @Resource private OrderMapper orderMapper; public void createOrder(OrderBO orderBO) { // 参数校验 if (null == orderBO) { throw new RuntimeException
(
"参数异常"); } if (OrderTypeEnum.
isNotValid(orderBO.getType())) { throw new RuntimeException
(
"参数异常"); } // 计算优惠 discount(orderBO); // 计算重量 weighing(orderBO); // 退款支持 supportRefund(orderBO); // 保存数据 OrderDO orderDO = new OrderDO(); BeanUtils.copyProperties
(orderBO, orderDO); orderMapper.insert(orderDO); } public abstract void discount
(OrderBO orderBO); public abstract void weighing
(OrderBO orderBO); public abstract void supportRefund
(OrderBO orderBO); } // 实现创建订单流程 @Service public class CreateOrderFlow extends
AbstractCreateOrderFlow { @Resource private DiscountStrategyExecutor
discountStrategyExecutor
; @Resource private ExpressStrategyExecutor
expressStrategyExecutor
; @Resource private RefundStrategyExecutor
refundStrategyExecutor
; @Override public void
discount(OrderBO orderBO) { discountStrategyExecutor.
discount(orderBO); } @Override public void
weighing(OrderBO orderBO) { expressStrategyExecutor.
weighing(orderBO); } @Override public void supportRefund
(OrderBO orderBO) { refundStrategyExecutor.supportRefund
(orderBO); } }

 

1.4 可靠性

1.4.1 如何定义

可靠性包括容错性和健壮性,系统面对错误输入仍能保证正确输出的能力,可以分为两种类型:系统可靠性和业务可靠性。

系统可靠性是指面对出现基本错误的输入,系统能够识别和拦截,而不是任由其在构件中传递,造成错误数据或者引发系统异常。例如空值引发的空指针异常,不应该出现在系统中。

业务可靠性是指输入参数在基本校验通过的情况下,系统能够进行业务校验,不会引发超出业务预期的输出结果。例如电商系统中的超卖现象,重复创建订单现象都是业务可靠性较低的表现。

1.4.2 如何提升

(1) 拦截

提升可靠性的关键是应该尽早在上层识别并拦截异常数据,阻止其在构件中流动,避免产生系统异常和错误数据,尤其当产生错误数据后,数据修复难度大。

提升系统可靠性可以在服务入口增加判空校验、参数类型校验、范围校验、合法枚举值校验等基本校验,一旦发现异常直接拒绝。

提升业务可靠性可以增强业务校验,例如库存预扣减,活动有效期校验,参与活动次数校验,扣减库存校验,分布式锁控制并发等方案,如果校验规则复杂可以引入规则引擎进行条件组合,不满足业务条件直接拒绝请求。

(2) 告警

如果第一阶段没有将异常输入拦截成功,那么就要在发生异常时及时感知,异常分为系统异常和业务异常。

系统异常是不允许出现的异常,例如空指针,操作数据库失败等异常,一旦出现就要立即告警。

业务异常可以分为以下类型:

业务告警:单位时间出现X次需要告警

延时告警:某指标单位时间内是否变化

数据告警:单位时间数据指标是否正常

1.5 安全性与易用性

安全性是指系统防止非法用户访问的能力,易用性是指系统使用的难易程度,本文不展开论述,下一个章节会通过实例提到。

2 架构评估方法

2.1 三种评估方法

因为涉及到众多变量和场景,所以评估一个复杂技术系统的质量并不是一件容易的事情。业界有以下三种评估方法:

第一是基于问卷的方式,通过问卷调查对系统比较熟悉的相关人员,这种方式主观性很强。

第二是基于度量的方式,对系统指标完全量化,基于量化指标评价系统,这种方式需要评估者对系统非常熟悉。

第三种是基于场景的方式,筛选出系统的关键场景,根据系统在不同场景中的表现进行评估,这种方式具有一定的主观性,需要评估者对系统比较熟悉,这也是目前较为流行的架构评估方法。

架构权衡评估方法(ATAM)的英文全称是:Architecture Tradeoff Analysis Method,由卡梅隆大学软件工程协会提出,是一种基于场景的架构评估方法,核心是结合质量属性效用树对系统进行评价,确定风险点、敏感点、权衡点,并对系统架构做出决策和折中。

ATAM分为以下步骤,其中1、2、3为描述和介绍阶段,4、5、6为调查和分析阶段,7、8为测试阶段,9为报告阶段。

2.2 ATAM

本章节以之前文章《结合DDD讲清楚编写技术方案的七大维度》足球运动员信息管理系统为例看一看ATAM如何实际应用。

第一阶段是描述和介绍阶段,首先由架构师向大家介绍什么是ATAM方法,其次由产品经理介绍开发足球运动员信息管理系统商业动机,最后由架构师介绍系统整体架构,例如怎样划分领域,系统分为持久层、缓存层、中间件、业务中台、服务层、网关层、客户端和代理层等等。

第二阶段是调查和分析阶段,不同需求方均提出了相关需求,所涉及质量场景如下:

(1) 系统在100毫秒内响应用户请求

(2) 当主数据库发生故障后,10秒内自动切换至从数据库

(3) 当主机房发生故障后,5分钟内请求重定向至灾备机房

(4) 新增球员比赛和训练指标,开发工作在5人日内完成

(5) 使用包含SSL数字证书的HTTPS访问协议

(6) 球员信息管理界面要求简单易用

(7) 出现异常引导用户至错误页面,不能展示异常栈信息

(8) 对于球员信息配置功能的灵活度尚未达成共识,影响了系统可修改性

(9) 对于球员比赛指标实时收集响应时间的要求,影响了数据存储设计

(10) 主教练提出了训练指标新模式,影响了系统性能和可修改性

根据上述场景生成质量属性效用树,(1)属于性能,(2)(3)属性可用性,(4)属于可修改性,(5)属于安全性,(6)属于易用性,(7)属于可靠性:

再根据这些场景分析系统的风险点、敏感点、权衡点。风险点是指某些操作会给系统带来隐患和风险,(8)属于风险点。敏感点是指为了实现某个特定质量属性,一个或多个系统组件所具有的特性,(9)属于敏感点。权衡点是指某些操作会影响系统的多个质量属性,(10)属于权衡点。

第三个阶段是测试阶段,根据足球运动员信息管理系统特性,我们首先确定场景优先级,由高到低分别是:性能、可靠性、可用性、可修改性、安全性、易用性。

架构权衡分析方法所谓权衡在此得到了体现,质量属性每个都很重要,但是根据系统特点需要对质量属性有优先级排序,架构设计时需要所有权衡和折中。

确定了优先级之后,我们需要具体阐述针对每个质量属性采取了哪些方案,例如提升性能使用了缓存,提升可修改性使用了策略模式,提升可靠性使用了统一异常处理框架等等,具体方案可以参考本文第一章节。

第四个阶段是报告阶段,我们将评估过程和结果都汇总整理成文档,其中包括质量属性效用树、风险点、敏感点、权衡点、每次评估会议纪要以及最终架构决策。

3 文章总结

第一系统满足功能性需求是最基本的要求,作为架构师不能就此止步,不仅应该关注功能性需求,还应该关注非功能性需求,质量属性就是衡量非功能性需求的重要指标。

第二架构评估方法分为基于问卷、基于度量、基于场景三种方式,目前业内较为流行的是基于场景的评估方法,ATAM是一种优秀的基于场景评估方法。

第三ATAM以质量属性效用树为核心,帮助架构师识别项目风险点、敏感点、权衡点,指导架构师做出合理架构决策。

   
1987 次浏览       16
相关文章

中央计算的软件定义汽车架构设计
汽车电子控制系统中的软件开发过程
一文读懂汽车芯片-有线通信芯片
OTA在汽车上有哪些难点痛点?
相关文档

汽车设计-汽车的整体结构及动力系统
自动驾驶汽车软件计算框架
SysML在汽车领域的应用实践
电子电气架构-大陆汽车系统架构平台
相关课程

AutoSAR原理与实践
功能安全管理体系(基于ISO26262)
MBSE(基于模型的系统工程)
基于SOA的汽车电子架构设计与开发

最新活动计划
MBSE(基于模型的系统工程)4-18[北京]
自然语言处理(NLP) 4-25[北京]
基于 UML 和EA进行分析设计 4-29[北京]
以用户为中心的软件界面设计 5-16[北京]
DoDAF规范、模型与实例 5-23[北京]
信息架构建模(基于UML+EA)5-29[北京]
 
 
最新文章
在EA中内嵌文档- Artifact
EA中模型视图
EA中的实体关系图
使用EA进行风险建模
EA中的项目词汇表
EA的模型导出或导入csv文件
自定义表格(Custom Table)在EA中的使用
Gap Analysis Matrix(差距分析矩阵)
更多...   
MBSE工具
MBSE平台
建模工具 EA
模型库-Model Center
需求管理-ReqManager
自动建模-Modeler
多级仿真-Sys Simulator
代码工程-Code Engineer
文档生成器-DocGenerator
更多...   
成功案例
广汽研究院 SysML+EA+软件分析设计
高合汽车研发部门 建模工具EA、WebEA、学习视频
国汽智联 建模工具EA、模型库、WebEA和iSpace
亿咖通 MBSE工程体系与工具链咨询
中航无人机 MBSE工具链
吉利汽车 购买EA工具
华科汽车零部件 购买EA工具
东风岚图汽车 购买EA工具 以及EA定制开发
更多...