设计系统时思考墨菲定律
1. 任何事都没有表面看起来那么简单
2. 所有事都会比预计的时间长
3. 可能出错的事总会出错
4. 如果某个事可能发生,那么一定会发生
系统划分时思考康威定律
1. 系统架构是公司组织架构的反映
2. 应该按照业务闭环进行系统拆分/组织架构划分,实现闭环/高内聚/低耦合,减少沟通成本
3. 如果沟通出现问题,那么应该考虑进行系统和组织架构的调整
4. 在合适的时机进行系统拆分,不要一开始就把系统/服务拆得很细,虽然闭环,但每个人维护的系统多,维护成本高
系统设计是一个不断迭代的过程,设计原则应该在系统迭代过程中,根据现有问题或者特征匹配使用。如果刚开始时遇到的不是核心问题,不要复杂化系统设计。
1.1 高并发原则
1.1.1 无状态
如果应用是无状态的,那么容易进行水平扩展。在实际生产环境中,应用无状态,配置文件有状态,部署到不同服务器上,根据配置文件定义差异。
1.1.2 拆分
根据开发资源评判是否需要拆分,如果开发资源紧张,排期紧,则没必要进行系统拆分。
拆分角度有如下几种
系统维度:按照业务拆分,比如订单系统、结算系统、购物车系统、商品系统
功能维度:按照功能拆分,属于更细维度,比如优惠券系统拆分成领券系统、用券系统、券创建系统
读写维度:按照读写拆分。读服务和写服务,读服务可以用缓存读从库,写服务使用分库分表。
AOP维度:将一些公有服务拆分出去,比如CDN,页面渲染系统
模块维度:按照基础模块或代码维度进行拆分。基础模块比如数据库拆分即分库分表、代码维度比如Web,Service和DAO拆分
1.1.3 服务化
进程内服务——>单机远程服务——>集群手动注册服务——>自动注册和发现服务——>服务的分组/隔离/路由——>服务治理如限流/黑白名单
1.1.4 消息队列
1.削峰 2.订阅状态变化 3.数据分发 4.重试 5.异步处理
需要处理重复消费和消息丢失的情况
1.1.5 数据异构
以方便查询为目的将数据转存一份,同时源系统出问题不会影响自己系统的查询。一般使用MQ实现数据分发。
1.1.6 缓存银弹
浏览器缓存:实时性要求不高的数据,比如评价,广告词
APP客户端缓存:将静态页面素材缓存
CDN缓存:将数据推送到离用户最近的CDN节点,注意url中不能有随机数
接入层缓存:使用Nginx搭一层接入层
应用层缓存:使用Tomcat时,用堆内/堆外缓存。堆内缓存重启会丢失,堆外缓存还可以使用local redis cache实现,在应用所在服务器部署一组Redis,直接读取本机Redis减去网络传输开销
分布式缓存:单台服务器容量优先,使用分片机制将流量分散到多台。或使用分布式缓存
1.1.7 并发化
不相关的请求并发执行
1.2 高可用
1.2.1 降级
开关集中化:通过推送机制将开关推送到各个应用
可降级的多级读服务:降级为只读本地缓存,只读分布式缓存,只读默认降级数据
开关前置化:业务降级,保证核心业务的处理优先级,将非核心业务异步处理,保障最终一致性即可。
1.2.2 限流
防止恶意请求流量、恶意攻击,或防止流量超出系统峰值。
恶意请求只访问cache
恶意IP使用nginx deny屏蔽
穿透到后端的流量使用Nginx的limit模块处理(漏桶等)
1.2.3 切流量
对发生故障的服务进行流量切换,防止客户请求一直异常
1.2.4 可回滚
当程序/数据出错,通过版本化机制回滚恢复到最近一个正确的版本。包括事务回滚、代码库回滚、部署版本回滚、数据版本回滚、静态资源版本回滚等等
高可用总结:
通过负载均衡和反向代理实现分流
通过限流避免服务雪崩
通过降级实现部分可用
通过隔离实现故障隔离
通过超时和重试避免请求堆积造成雪崩
通过回滚快速修复错误版本
1.3 业务设计原则
1.3.1 防重设计,譬如防止不同渠道的重复支付
1.3.2 幂等设计,针对消息重试/异步通知的幂等
1.3.3 状态和状态机,状态设计时应该有状态变化轨迹的记录,方便出问题时回溯。使用状态机控制状态的迁移
1.3.4 后台系统操作可反馈,考虑用户操作之后的效果的可预览可反馈
1.3.5 后台系统审批化,重要的操作做记录,保证可追溯可审计
1.3.6 文档和注释
1.3.7 备份,人员备份和代码备份,一个系统至少有两名开发人员是了解的。