
文章来源 | 阿里巴巴淘系技术团队
系统设计与架构理论这个问题,回答起来非常宽泛,基本所有的技术理论都可以涵盖。作为一个撸代码快 10 年的后端技术人员,简单发表一下我的看法。
系统设计与架构,与系统的业务类型关联还是很大的,比如传统的业务系统主要关注的是领域建模设计,高并发、高可用、数据一致性等系统,在设计的时候会与业务系统有较大的差别,所以这里针对不同类型的系统,来简单介绍一下设计的时候面临的一些难点与解决方案。
背景常规业务系统设计关键——领域模型
业务系统设计的关键是在于如何定义系统的模型以及模型之间的关系,其中主要是领域模型的定义,当我们在模型确定之后,模型之间的关系也会随之明确。
模型设计可以参考领域模型的经典书籍《Domain-Driven Design》一书,通过这个基本可以对领域定义、防腐层、贫血模型等概念有一个较为清晰的认识了。
单个应用内的领域模型系统也需要注意领域分层,作为开发大家是不是见过、重构过很多Controller-Service-DAO 样式的代码分层设计?往往在在做重构的时候会令人吐血。
设计较好的领域设计这里给一个分层建议:
接口层 Interface
主要负责与外部系统进行交互&通信,比如一些 dubbo服务、Restful API、RMI等,这一层主要包括 Facade、DTO还有一些Assembler。
应用层 Application
这一层包含的主要组件就是 Service 服务,但是要特别注意,这一层的Service不是简单的DAO层的包装,在领域驱动设计的架构里面,Service层只是一层很“薄”的一层,它内部并不实现任何逻辑,只是负责协调和转发、委派业务动作给更下层的领域层。
领域层 Domain
Domain 层是领域模型系统的核心,负责维护面向对象的领域模型,几乎全部的业务逻辑都会在这一层实现。内部主要包含Entity(实体)、ValueObject(值对象)、Domain Event(领域事件)和 Repository(仓储)等多种重要的领域组件。
基础设施层 Infrastructure
它主要为 Interfaces、Application 和 Domain 三层提供支撑。所有与具体平台、框架相关的实现会在 Infrastructure 中提供,避免三层特别是 Domain 层掺杂进这些实现,从而“污染”领域模型。Infrastructure 中最常见的一类设施是对象持久化的具体实现。
高并发系统设计
在面试中是不是经常被问到一个问题:如果你系统的流量增加 N 倍你要怎么重新设计你的系统?这个高并发的问题可以从各个层面去解,比如
代码层面
-
锁优化(采用无锁数据结构),主要是 concurrent 包下面的关于 AQS 锁的一些内容
-
数据库缓存设计(降低数据库并发争抢压力),这里又会有缓存、DB 数据不一致的问题,在实际使用中,高并发系统和数据一致性系统采用的策略会截然相反。
-
数据更新时采用合并更新,可以在应用层去做更新合并,同一个 Container 在同一时间只会有一个 DB 更新请求。
-
其他的比如基于 BloomFilter 的空间换时间、通过异步化降低处理时间、通过多线程并发执行等等。
架构设计层面
-
分布式系统为服务化
-
无状态化支持水平弹性扩缩容
-
业务逻辑层面 failfast 快速失败
-
调用链路热点数据前置
-
多级缓存设计
-
提前容量规划等等
高可用系统设计
-
狭义上的负载均衡按照类型可以分为这几种:
-
硬件负载:比如F5等
-
软件负载:比如 LVS、Ngnix、HaProxy、DNS等。
-
当然,还有代码算法上的负载均衡,比如Random、RoundRobin、ConsistentHash、加权轮训等等算法
-
广义上的负载均衡可以理解为负载均衡的能力,比如一个负载均衡系统需要如下4个能力:
-
故障机器自动发现
-
故障服务自动摘除(服务熔断)
-
请求自动重试
-
服务恢复自动发现
幂等设计问题
-
请求层面
由于请求会重试所以必须做幂等,需要保证请求重复执行和执行一次的结果完全相同。请求层面的幂等设计需要在数据修改的层做幂等,也就是数据访问层读请求天然幂等,写请求需要做幂等。读请求一般是天然幂等的,无论查询多少次返回的结果都是一致。这其中的本质实际上是分布式事务问题,这里下面再详细介绍。
-
业务层面
不幂等会造成诸如奖励多发、重复下单等非常严重的问题。业务层面的幂等本质上是分布式锁的问题,后面会介绍。如何保证不重复下单?这里比如token机制等等。如何保证商品不超卖?比如乐观锁等。MQ消费方如何保证幂等等都是面试的常见题。
分布缩式
-
zookeeper
CP/ZAB/N+1可用:基于临时节点实现和Watch机制。
-
ETCD
CP or AP/Raft/N+1可用:基于 restful API;KV存储,强一致性,高可用,数据可靠:持久化;Client TTL 模式,需要心跳CAS 唯一凭证 uuid。
服务的熔断
服务降级
-
服务层降级,主要手段
-
拒绝部分请求(限流),比如缓存请求队列,拒绝部分等待时间长的请求;根据Head,来拒绝非核心请求;还有其他通用算法上的限流比如令牌桶、漏桶算法等等。
-
关闭部分服务:比如双11大促0点会关闭逆向退款服务等等。
-
分级降级:比如自治式服务降级,从网关到业务到DB根据拦截、业务规则逐渐降低下游请求量,体现上是从上到下的处理能力逐渐下降。
-
数据层降级
-
柔性可用策略
发布方式引发的可用性问题
数据一致性系统设计
事务一致性问题
-
TCC 模式/或者叫2阶段模式
在 try 阶段预扣除资源(但是不锁定资源,提升可用性),在Confirm 或者 Cancel 阶段进行数据提交或者回滚。一般需要引入协调者,或者叫事务管理器。
-
SAGA 模式
业务流程中每个参与者都提交本地事务,当出现某一个参与者失败则补偿前面已经成功的参与者,支持向前或者向后补偿。
-
MQ 的事务消息
就是先发 halfMsg,在处理完之后,再发送 commit 或者 rollback Msg,然后 MQ 会定期询问 producer ,halfMsg 能不能 commit 或者 rollback,最终实现事务的最终一致性。实际上是把补偿的动作委托给了 RocketMQ。
-
分段事物(异步确保)
基于可靠消息+本地事务消息表 + 消息队列重试机制。目前这也是一些大厂的主流方案,内部一般称为分段事物。
最后
关于 JavaScript 未来的猜想

