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