前言
架构设计是一个技术活,它不仅考验着我们对整体的抽象整合能力,也检验了我们对业务的变化应对能力。一个好的架构,能让系统的依赖关系清晰明了,也能让后续的维护成本降到最低。
那到底什么才是好的架构呢?个人觉得,站在使用者的角度,好的架构应该是易于理解的、高度自治的、可衡量的;站在实施效果的角度,应该是高可用、高并发、可扩展的。
易于理解
我们知道,之所以要进行架构设计,其主要目的在于降低系统的复杂度。在软件行业里,改需求加功能是家常便饭的事,几乎每隔半个月就有一次小的迭代。随着变动越来越多,系统将会越来越庞大。或许过了几个月后,连我们自己也捋不清整体流程了。
所以,我们应该尽可能的让架构设计能被更多的人接受,并且易于理解。当我们整理出简洁的稳定和详细的设计描述后,最好是能和周围的同事过一遍或者进行一次技术评审,征求他人的意见,确保其他人能快速的接受。
设计易于理解的系统并不是那么容易的,因为这涉及到了人与人之间的沟通确认,我们需要做的就是在前期不断的暴露自己,不断的修正自己,才能让系统更加人性化。
高度自治
当我们要进行一项新的开发工作时,很少出现一个人包揽全部工作的情况,更多的是团队合作,同时进行,这就涉及到了职责划分和模块联结问题。
最理想的状态就是各自负责自己的功能,在跟其他模块交互时,直接以外部系统这种形式进行对接。但这种交互太独立了,不利于各个组件的复用。好的架构设计应该是高度自治和扁平化的,能够由团队来协同进行。
高度自治在逻辑层是深度解耦的。通过业务域的划分,各自负责一部分功能模块。在开发之前,把需要的上下文接口定义好,把数据源的输入输出定义好。这样在进行开发、测试、上线时能够将彼此的影响降到最低。
随着业务的不断增加,之前高度自治的模块总会不知不觉的增加很多特性功能,从原来的小模块变成了小系统。我们总会面临拆与不拆的选择。
个人觉得,可以从 2 方面去考虑拆分动作。一个是业务的复杂度,如果当前的模块对外提供的接口已经达到了上百个,那毫无疑问,肯定是需要细分成小模块的。另一个是性能问题,当遇到了性能瓶颈时,就不得不进行服务的细化。
所以,高度的自治系统往往跟持续的演化是有关联的,在不断的拆分和聚合中,我们为每一个模块功能划分了明确的界限,让系统联动更加的轻柔方便。
可衡量程度
如果说一个架构设计出来了,在经过重重困难开发后,你告诉其他人我也不知道可不可行,那你觉得别人敢用吗?又或者说,没有可衡量指标的系统,你能确定系统是否还在正常运行吗?
从大的方向来讲,我们最起码要能清楚的知道系统能处理多少请求;如果要细分的话,则是请求的性能是怎么样的,也就是平常我们所看到那些监控指标,比如 cpu、内存使用率等。
可衡量指标在项目的初期我们可以先定义个大概的,比如各个模块系统能承受的压力是多少,是否符合当前的业务承受能力。例如产品给出的访问量是每天万级别的,那我们就得估算为 2~3 倍,然后根据这个指标进行链路压力测试,保证系统的正常运行。
当项目进行到后期了,要有一些图形化的关键指标,能够细粒度的把控系统的运行情况,比如请求的响应时间、资源的使用率、API 的调用次数等,关键时刻还能进行报警通知。
高可用
在系统的可用性这一块,相信大家都听说过最常见的 N 个 9,例如:
N 个 9 | 占比 | 每年最长停机时间 |
---|---|---|
1 个 9 | 90% | 36.5 天 |
2 个 9 | 99% | 3.65 天 |
3 个 9 | 99.9% | 8.76 小时 |
4 个 9 | 99.99% | 52.56 分 |
当然,想要达到越高级别的可用性,那么付出的代价是越大的,像腾讯、支付宝的同城多机房、两地三中心等方案都需要消耗大量的人力、物力、财力。所以,在实现高可用性之前,可用先看看当前的公司可以支撑到哪个阶段。
高可用方案需要我们在尽可能短的时间内将故障进行修复并恢复运行。一般我们会进行硬件、软件方面的冗余,最大限度的减少或避免停机处理。而这需要持续的维护、监控、演练,以确保方案的可行性。
高可用方案涉及的点大概有以下几点:
- 冗余:通过在关键节点或组件上进行备份来避免单点故障,通常会进行 N + 1 或 N + 2 的冗余。
- 数据备份:要有完善的数据保护和灾难恢复措施来保证数据安全,通常需要进行数据备份或同步。
- 故障转移:当系统发送故障时能将请求重定向到备份系统,减少人为的维护参与。
- 负载均衡:将请求打散到多个服务器或组件上,提高整体的运行性能和可靠性。
另外,高可用方案在运维这一块有比较成熟的应用,像 docker、k8s 等,有实力的公司可以尝试一下。
高并发
高并发更多时候是一种优化策略,在用户访问时尽可能的往快、多、稳
靠拢。常常涉及的技术点有:
- 应用服务与数据分离:将服务无状态化,专人干专活。
- 服务集群:通过横向扩展,避免单点瓶颈。
- 缓存:热点数据冗余到高速设备,降低数据库的访问压力。
- 消息队列:同步操作转异步,降低服务耦合。
- 数据库优化:读写分离,主从热备,提高数据库负载能力。
- NoSQL 和搜索引擎:解决复杂的检索需求,允许多数据源筛选。
- CDN 和反向代理:让用户的请求就近访问,减轻服务器负载压力。
上面这些手段很多时候会作为系统的基础组件存在,为各个模块功能服务。
不过高并发这个概念比较广,从架构设计到模块功能再到代码编程都有所涉及。从广义上来讲,无论再怎么高,也是会有个上限的,像前面提到过的平常访问量的 2~3 倍等。
所以在给高并发方案时,先给自己设个上限,一旦超过了此上限,那么就采用其他策略,比如限流或直接拒绝访问,这也是一种兜底操作。 Y(_)Y
可扩展
首先,可拓展性是什么?对于软件行业来讲,它天生就支持了这种扩展特性,因为它的可擦除成本太低了。但关键就在于,怎么将系统的影响降到最低,总不可能每次要加点东西或改点需求便要重构吧!所以,可扩展性其实就是尽可能的以最低成本去改动系统,提高效率。
可拓展性好的关键在于识别变化点,比如当前的系统要是超负荷了,那是要横向扩展加机器还是水平扩展拆分服务呢?又或者应用服务如果携带数据状态,要进行多节点部署,是否要抽离出来呢?所以,把一些常见的影响比较大的变化罗列出来,将它们隔离开来,那就可以构建一个可扩展性好的系统了。
将变化点隔离开来常见的做法便是增加一层处理,像我们消息队列、缓存组件这些都可以很好的应对变化点。当然,这种也有点拆分的意思在里面,通过将变化点拆分出去,使得改动范围变小,影响范围也就变小了。
总结
架构设计要考虑的因素很复杂,需要我们不断的去抽象,不断的去规划。而一个好的架构肯定是合适、简单、演化的。只要我们把控好关键点,相信好的架构设计也就不远了。
感兴趣的朋友可以搜一搜公众号「 阅新技术 」,关注更多的推送文章。
可以的话,就顺便点个赞、留个言、分享下,感谢各位支持!
阅新技术,阅读更多的新知识。