高并发原则
无状态
如果设计的应用是无状态的,那么应用比较容易进行水平扩展。实际生产环境可能是这样的:应用无状态,配置文件有状态。比如,不同的机房需要读取不同的数据源,此时,就需要通过配置文件或配置中心指定。
拆分
做一个大而全的系统还是按功能模块拆分系统,这个需要根据环境进行权衡。
京东秒杀系统,访问量是非常大的,而且投入的资源还是蛮充足的,在这种情况下,就可以考虑按功能拆分系统。
拆分主要有如下几种情况:
- 系统维度
- 读写维度
- AOP维度
- 模块维度
消息队列
消息队列是用来解耦一些不需要同步调用的服务或者订阅一些自己系统关心的变化。
使用消息队列可以实现服务解耦(一对多消费)、异步处理、流量削峰/缓冲等。
大流量缓冲
电商搞大促时,系统流量会高于正常流量的几倍甚至几十倍,此时就要进行一些特殊的设计来保证系统平稳度过这段时期。
扣减库存
交易订单系统
数据异构
订单分库分表一般按照订单ID进行分,如果要查询某个用户的订单列表,则需要聚合多个表的数据后才能返回,这样会导致订单表的读性能很低。此时需要对订单表进行异构,异构一套用户订单表,按照用户ID进行分库分表。另外,还需要考虑对历史订单数据进行归档处理,以提升服务的性能和稳定性。
数据来源太多,影响服务稳定性的因素就非常多了。因此,最好的办法是把使用到的数据进行异构存储,形成数据闭环,基本步骤如下:
- 数据异构:通过如MQ机制接收数据变更,然后原子化存储到合适的存储引擎,如Redis或持久化KV存储。
- 数据聚合:这步是可选的,数据异构的目的是把数据从多个数据源拿过来,数据聚合的目的是把这些数据做个聚合,这样前端就可以一个调用拿到所有数据,此步骤一般存储到KV存储中。
- 前端展示:前端通过一次或少量几次调用拿到所需要的数据。
缓存银弹
并发化
如果串行获取,那么需要60ms。
如果并发化获取,则需要30ms,能提升一倍的性能。
高可用原则
降级
高可用服务,很重要的一个设计就是降级开关,在设计降级开关时,主要依据如下思路:
- 开关集中化管理:通过推送机制把开关推送到各个应用
- 可降级的多级读服务:比如服务调用降级为只读本地缓存、只读分布式缓存、只读默认降级数据
- 开关前置化:如架构是Nginx→Tomcat,可以将开关前置到Nginx接入层
- 业务降级
当高并发流量来袭,在电商系统大促设计时保障用户能下单、能支付是核心要求,并保障数据最终一致性即可。这样就可以把一些同步调用改成异步调用,优先处理高优先级数据或特殊特征的数据,合理分配进入系统的流量,以保障系统可用
限流
限流的目的是防止恶意请求流量、恶意攻击,或者防止流量超出系统峰值。
可以考虑如下思路:
- 恶意请求流量只访问到cache。
- 对于穿透到后端应用的流量可以考虑使用Nginx的limit模块处理。
- 对于恶意IP可以使用nginx deny进行屏蔽。
原则是限制流量穿透到后端薄弱的应用层。
切流量
切流量是非常重要的,比如多机房环境下某个机房挂了,或者某个机架挂了,或者某台服务器挂了,都需要切流量。
- DNS:切换机房入口。
- HttpDNS:主要APP场景下,在客户端分配好流量入口,绕过运营商LocalDNS并实现更精准流量调度。
- LVS/HaProxy:切换故障的Nginx接入层。
- Nginx:切换故障的应用层。
可回滚
版本化的目的是实现可审计可追溯,并且可回滚。当程序或数据出错时,如果有版本化机制,那么就可以通过回滚恢复到最近一个正确的版本,比如事务回滚、代码库回滚、部署版本回滚、数据版本回滚、静态资源版本回滚等。通过回滚机制可保证系统某些场景下的高可用。
业务设计原则
防重设计
结算页需要考虑重复提交,还有如下单扣减库存时需要防止重复扣减库存。解决方案可以考虑防重key、防重表。而有些场景如重复支付,是因为有的电商网站同时支持微信支付、京东支付,渠道不一样是无法防止重复支付的。但是,在系统设计时,需要将支付的每笔情况记录下来。
幂等设计
在交易系统中,经常会用到消息,而现有消息中间件基本不保证不发生重复消息的消费。因此,需要业务系统在重复消息消费时进行幂等处理。还有在使用第三方支付时,第三方支付会进行异步回调,也要考虑做好回调的幂等处理。
流程可定义
如果接触过保险业务,就会发现不同保险的理赔服务是不一样的。我们在系统设计时就设计了一套理赔流程服务。而承保流程和理赔流程是分离的,在需要时进行关联,从而可以复用一些理赔流程,并提供个性化的理赔流程。
状态与状态机
在设计交易订单系统时,会存在正向状态(待付款、待发货、已发货、完成)和逆向状态(取消、退款)等,正向状态和逆向状态应该根据系统的特征来决定要不要分离存储。状态设计时应有状态轨迹,方便用户跟踪当前订单的轨迹并记录相关日志,万一出问题时可回溯问题。
另外,还有订单状态的变迁,比如待支付、已支付待发货、待收货、完成的迁移。要考虑要不要使用状态机来驱动状态的变更和后续流程节点操作,尤其当状态很多的时候使用状态机能更好地控制状态迁移。
后台系统操作可反馈
很多场景都需要反馈,比如,修改了某些内容后想预览看看最终效果,即想得到一些反馈;
还有一些是在规则系统中,希望看到这些规则在系统数据下的反馈。
因此,在设计后台系统时,需考虑效果的可预览、可反馈。
后台系统审批化
对于有些重要的后台功能需要设计审批流,比如调整价格,并对操作进行日志记录,从而保证操作可追溯、可审计。
文档和注释
一些系统是完全没有文档、代码没有注释的,完全是人传人。这将导致后来人接手很痛苦,而且对有些代码是完全不敢改动的,比如,有些代码完全是因为业务的一些特殊情况而写的,可以说没有注释是完全不懂为什么那么做的。
因此,在一个系统发展的一开始就应该有文档库(设计架构、设计思想、数据字典/业务流程、现有问题),业务代码和特殊需求都要有注释。
可以使用TOGAF企业架构设计。
备份
包括代码和人员。
代码主要提交到代码仓库进行管理和备份,代码仓库应该至少具备多版本的功能。
人员备份指的是一个系统至少应该有两名开发人员是了解的,即使其中一名离职了也不会出现新人接手之后手忙脚乱事故频发的状况。
还有一些是“核心人员”,写着系统的核心代码,被认为是“不可替代的”,这种情况也是尽可能地让他带一名兄弟一起开发核心代码(业务系统),即使离职也还是可以努力一下克服困难。