欢迎大家关注公众号「JAVA前线」查看更多精彩分享文章,主要包括源码分析、实际应用、架构思维、职场分享、产品思考等等,同时欢迎大家加我个人微信「java_front」一起交流学习

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 {@Resourceprivate OrderMapper orderMapper;@Overridepublic 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 {@Overridepublic void discount(OrderBO orderBO) {orderBO.setPrice(orderBO.getPrice() * 0.9);}
}// B类型优惠策略
@Component
public class TypeBDiscountStrategy implements DiscountStrategy {@Overridepublic void discount(OrderBO orderBO) {orderBO.setPrice(orderBO.getPrice() * 0.8);}
}// C类型优惠策略
@Component
public class TypeCDiscountStrategy implements DiscountStrategy {@Overridepublic void discount(OrderBO orderBO) {orderBO.setPrice(orderBO.getPrice() * 0.7);}
}// 优惠策略工厂
@Component
public class DiscountStrategyFactory implements InitializingBean {private Map<String, DiscountStrategy> strategyMap = new HashMap<>();@Resourceprivate TypeADiscountStrategy typeADiscountStrategy;@Resourceprivate TypeBDiscountStrategy typeBDiscountStrategy;@Resourceprivate TypeCDiscountStrategy typeCDiscountStrategy;public DiscountStrategy getStrategy(String type) {return strategyMap.get(type);}@Overridepublic 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 {@Resourceprivate 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 {@Resourceprivate DiscountStrategyExecutor discountStrategyExecutor;@Resourceprivate ExpressStrategyExecutor expressStrategyExecutor;@Resourceprivate RefundStrategyExecutor refundStrategyExecutor;@Overridepublic void discount(OrderBO orderBO) {discountStrategyExecutor.discount(orderBO);}@Overridepublic void weighing(OrderBO orderBO) {expressStrategyExecutor.weighing(orderBO);}@Overridepublic 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分为以下步骤,其中123为描述和介绍阶段,456为调查和分析阶段,78为测试阶段,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以质量属性效用树为核心,帮助架构师识别项目风险点、敏感点、权衡点,指导架构师做出合理架构决策。

4 延伸阅读

《结构化思维如何指导技术系统优化》

《结合DDD讲清楚编写技术方案的七大维度》

欢迎大家关注公众号「JAVA前线」查看更多精彩分享文章,主要包括源码分析、实际应用、架构思维、职场分享、产品思考等等,同时欢迎大家加我个人微信「java_front」一起交流学习

架构权衡评估方法(ATAM):如何评估一个系统的质量相关推荐

  1. 【架构师之路】四、系统架构

    高并发,大流量 Google 日均 PV 数 35 亿,日均 IP 访问数 3 亿 微信在线用户数 10 亿 天猫双十一活动一天交易额 3000 亿 高可用 系统 7*24 小时不间断服务.大型互联网 ...

  2. 系统应用全方面评估维度,全面评测一个系统。

    主要涵盖以下层面,全方面评价一个系统的稳健性. 1.联机交易系统性能 2.批处理系统性能 3.容量估计 4.应用扩展能力 5.应用可用性 6.交易数据一致性 7.应用可靠性 部分指标如下表,全面评价的 ...

  3. 架构设计师论文-论软件架构评估

    摘要: 随着国家新政策.新标准的推行及优化营商环境的要求,我所在单位现有的登记系统已不满足新形势下的业务需求:2019年,单位获批开展全省不动产统一登记云平台升级改造项目,我作为单位信息化部门负责人, ...

  4. iRank: 基于互联网类脑架构的阿尔法鹰眼发展趋势评估

    前言:iRank是人工智能学家基于科学院研究团队"互联网类脑智能巨系统架构"和"AI 智商评估模型"等研究成果,对智能产业优秀企业.产品和技术进行评估.发掘和推 ...

  5. 从宜人贷系统架构看互联网高并发对金融系统架构的挑战

    一.简介 随着互联网金融的持续火热,越来越多的银行纷纷发布了各自的互联网金融产品.但是互联网产品"高并发.大数据量"的特点却对于银行传统的核心系统架构带来了新的挑战. 1.互联网的 ...

  6. 软件体系结构设计文档_一个java架构师是如何设计出一个好的架构的

    一.架构的定义 所谓一千个架构师中有一千种"最好的架构"模式. "架构"是我们行业中非常普遍的词,表示它也必须是经过长时间磨合后形成的词. 架构一词的含义是什么 ...

  7. 业务系统如何评估服务器,系统容量预估

    系统容量预估 业务系统往往会被问到一些资源的问题,需要多少机器,机器足不足以支撑当前的业务增长等,这些都是系统容量的一些估算问题. 服务器容量 容量设计需要考虑的维度:业务规划 + 架构复杂度 + 组 ...

  8. 【广告架构day1】爱奇艺广告系统的演进之路:实践中的一些经验

    本文来自爱奇艺的分享孙立伟.近年来爱奇艺快速发展,优质内容层出不穷,爱奇艺广告也随之发展和壮大,广告在线服务同时服务于品牌.中小.DSP 等不同客户,形成了可以满足不同需求类型的较为完善的商业广告变现 ...

  9. 业务中台系统架构:大中台+小前台电子商务系统搭建框架思维

    [数商云]在电子商务系统搭建行业有近十几年的服务经验,近年来的数据中台.业务中台等系统架构兴起,大多数企业在不清楚的中台背景的情况下就盲目追求,最后只会导致自身平台丢失原有的优势框架.在这里,我们来总 ...

最新文章

  1. 武汉网络推广浅析当网站停止收录时该检查哪些问题?
  2. linux编辑文本文件aa的命令,linux入门必须掌握的命令--文本文件编辑
  3. python同时发大量请求_python http服务器,多个同时请求
  4. LeetCode 最大正方形
  5. 在浏览器地址栏按回车、F5、Ctrl+F5刷新网页的区别
  6. 监督学习和无监督学习_一篇文章区分监督学习、无监督学习和强化学习
  7. X9C102PIZ数字电位器-中文
  8. Java小程序:单循环比赛,总分高者获胜的源代码
  9. IDM:从Google Drive快速直接下载大文件
  10. 基于android的超级记事本,超级记事本基于设计论文
  11. Mysql读写分离的四种方案
  12. 常用的十大塑料成型工艺(优缺点介绍)
  13. html版权信息c怎么写,网页设计添加版权的语句肿么写
  14. CBDB中国历代人物历史可视化系统
  15. 我在上海奋斗五年 从月薪3500到700万
  16. JAVA学习第一步-配置JAVA开发环境和学习资料
  17. 2020 ICPC 济南 A Matrix Equation (高斯消元)
  18. netlink使用简介
  19. Linux树莓派开发——配置树莓派内核源码,内核编译,更换树莓派Linux内核
  20. Java各种视频教学(转)

热门文章

  1. java jtable方法_Java中JTable如何使用?
  2. python链接hbase模块_利用python访问Hbase(Thrift模块安装与测试)
  3. 单片机脉冲微盘_单片机产生脉冲波
  4. cdel调用方式解析
  5. 百度地图——poi搜索
  6. SQLyog下载、安装和破解
  7. 图书信息管理系统(数据结构链表,c语言版)
  8. Caffe2 Tutorials Overview(一)
  9. 嵌入式Linux中摄像头使用简要整理
  10. pr字幕模板 炫酷科技蓝色流线条动画pr模板