记录遇到的系统设计问题,如内部支撑系统、2B的数据服务系统以及通用的系统设计问题
系统设计问题记录
常规敏捷开发流程
- 产品Story需求确认,制定Epic里程碑
- 产品交互原型设计
- 后端API接口设计确认/后端数据模型设计
- Story任务分解
- 前后端各自编码实现
迭代上述过程
衡量项目质量的指标
功能
:功能目标是应用的基本要求,如果不能实现既定的功能逻辑,应用就失去了存在的意义,因此实现产品需求
是应用的基本的目标。性能
:在基本的功能之上,会有一些性能的要求,但是很少有产品经理或者用户能提前提出这样的要求,因此架构师要有丰富的经验去发现和解决(或者为未来提升性能做准备
)性能问题。
性能的主要衡量有:单次请求的相应时间,单实例请求并发数,服务最大并发量等。扩展性
:目前互联网应用的开发模式:提出需求,快速响应,迭代开发,尽快上线
架构设计的主要过程
- 确定问题域:根据需求定位关键问题,需根据性能和扩展性定义问题域;如何
平衡性能和扩展的关系
,是架构师设计的关键。扩展把握关键问题,优先满足关键问题的性能,确定最小功能集
;确定最小功能集的优势可以快速实现,快速验证需求的准确性,每次需求开发都完成最小和最关键的需求
。 - 数据建模:StarUML,ER
- 模块划分:单实例结构(应用>DB);集群结构;分布式结构(显示层>服务n>DB) ;混合结构;
- 关键流程描述:检查系统架构是否满足需求和指导开发的必要条件。使用
流程图
解决关键问题 - 技术选型
- 代码实现
- 验收测试
内部系统类
B2B的支付机制
- 流程:记账->账单->扣款;
- 记账:业务系统异步写入操作记录;
- 账单:根据计费逻辑(折扣/套餐/阶梯)以存储过程生成账单;
- 扣款:运营月结/日结+法务催收;
系统耦合问题
问题:业务系统与基础设施系统(Boss)强耦合,Boss缓存问题影响业务系统鉴权;
方案:Boss数据库单点,缓存多点实时同步;业务系统读取本地缓存进行鉴权;
数据服务类
渠道分流控制工具/自动分流工具
需求:渠道调用超时自动切换,渠道恢复正常自动切换;
分析:1个服务对应多个提供者,多个提供者可配置权重,要求多个提供者之间可根据超时时间down或者on
技巧:超时时间=接口平均调用时间*2
保证主流程用户体验,减少故障点
梳理主流程,区分立即一致和最终一致,最终一致的业务逻辑可以消息中间件异步执行,以减少主流程阻塞时间;
eg 在实名验证+人脸比对前,异步调用防骇客接口;在认证结束时,从缓存获取防骇客接口调用结果,若检测到活体攻击,进入人审;若无攻击,正常返回;
认证结果通知机制
认证结果通知:SDK端同步返回和服务端异步返回,SDK同步返回时需支持敏感字段掩码(开关);
实时性要求不高,提供异步通知接口;实时性要求高,提供认证结果查询接口;
1)异步通知双层设计:
第一层:实时通知,设置正常超时时间(3S)且不重试,不论通知成功与否,都写表记录通知结果标识;
第二层:扫描异步通知表,对未成功通知的记录进行重试通知(3次,重试时间递增)
2)异步通知:商户异步通知地址不能保证Https,数据需加密传输
3)认证结果查询接口:独立查询库
通用问题
异常处理
调用外部服务异常时打印所有日志;
自定义异常,在最外围捕捉抛出
网络超时问题
从三个关键点排查
- 对端:TCP握手问题
- 数据传输:
- 读取结果:readTimeout的实际含义
the read timeout is that it corresponds to the timeout on a socket read.
So it’s not the time allowed for the full response to arrive, but rather the time given to a single socket read.
So if there are 4 socket reads, each taking 9 seconds, your total read time is 9 * 4 = 36 seconds.
http://stackoverflow.com/questions/9873810/using-apache-httpclient-how-to-set-the-timeout-on-a-request-and-response
HttpClient超时类型
- connectionRequestTimeout:从连接池中获取连接的超时时间,超过该时间未拿到可用连接,会抛出org.apache.http.conn.ConnectionPoolTimeoutException: Timeout waiting for connection from pool
- connectTimeout:连接上服务器(握手成功)的时间,超出该时间抛出connect timeout
- socketTimeout:服务器返回数据(response)的时间,超过该时间抛出read timeout
线程池使用场景
- 节省时间,多个线程并行处理后返回,如双重比对
- 异步任务,不影响主流程
输入提示服务
高并发系统的设计,关键在合理的数据结构的设计,而不在架构的套用
缓存+哈希
把搜索的搜索提示词存在redis集群中,每次来了请求直接redis集群中查找key,然后返回相应的value值就行了,完美解决,虽然耗费点内存,但是空间换时间嘛;
trie树
这种搜索提示的功能一般用trie树来做,耗费的内存不多,查找速度为O(k),其中k为字符串的长度,虽然看上去没有哈希表的O(1)好,但是少了网络开销,节约了很多内存,并且实际查找时间还要不比缓存+哈希慢多少,
一种合适当前场景的核心数据结构才是高并发系统的关键,缓存+哈希如果也看成一种数据结构,但这种数据结构并不适用于所有的高并发场景。
LRU缓存
LRU是Least Recently Used 近期最少使用算法;
硬盘上有N条数据,并且有一个程序包,提供GET和SET方法,可以操作磁盘读写数据,但是速度太慢,请设计一个内存中的数据结构,也提供GET和SET方法,保存最近访问的前100条数据,这个数据结构就是一个LRU了,让面试者实现出来,如果觉得写代码麻烦,可以把数据结构设计出来描述一下就行了,就这样,还很多人不会,这怎么能说是对缓存技术有深入了解呢?就这样,怎么能说有过大型高并发系统的经验呢?这只是开源工具的使用经验罢了。
常用容错机制
常见容错机制:failover ,failsafe,failfase ,failback,forking,来源于阿里的定义
- Failover 失败自动切换
当出现失败,重试其它服务器,通常用于读操作(推荐使用)。 重试会带来更长延迟。 - Failfast 快速失败
只发起一次调用,失败立即报错,通常用于非幂等性的写操作。 如果有机器正在重启,可能会出现调用失败 。 - Failsafe 失败安全
出现异常时,直接忽略,通常用于写入审计日志等操作。 调用信息丢失 可用于生产环境 Monitor。 - Failback 失败自动恢复
后台记录失败请求,定时重发。通常用于消息通知操作 不可靠,重启丢失。 可用于生产环境 Registry。 - Forking 并行调用多个服务器
只要一个成功即返回,通常用于实时性要求较高的读操作。 需要浪费更多服务资源 。 - Broadcast
广播调用,所有提供逐个调用,任意一台报错则报错。通常用于更新提供方本地状态 速度慢,任意一台报错则报错 。