简介
内容来自 李运华 在 极客时间 《从0开始学架构》 的 分享
优秀的人总是会做一件事,对于一个领域,发现一些tip,提炼一个方法论,试错和完善这个方法论。
tips
架构设计的 关键思维是 判断和 取舍,程序设计 的关键思维 是逻辑和实现。
架构是什么?
- 首先 梳理几个概念:系统与子系统、模块与组件、框架与架构
- 系统的定义,维基百科:系统泛指由一群有关联的个体组成,根据某种规则运作,能完成个别元件不能单独完成的工作的群体。包含多个有关联的个体,个体按定义好的规则运行。
- 逻辑角度拆分,划分模块的主要目的是职责分离。物理角度拆分,划分组件的主要目的是 单元复用。比如做一个学生信息管理系统,从逻辑角度拆分可以分为登陆注册、个人信息、个人成绩等,从物理角度来拆分,可以分为Nginx、Web服务器、MySQL
- 框架 通常指的是为了实现 某个业界标准或完成特定基本 任务的软件组件规范,提供规范所要求之基础功能。软件架构:软件系统的顶层结构。
优秀程序猿和架构师之间一个明显的鸿沟是:不确定性。对于程序猿 编程来说,程序是确定的(有语法约束等),执行结果是确定的。而对架构设计来说,A和B公司可能架构完全不同,但都运转的挺好。面对不确定性,就要选择,就要取舍。
- 业界很多领先的方案,都是逼出来的。
- 演化优于一步到位,经常拿软件架构和建筑架构举例,但建筑架构一旦完成就不再改变,软件却需要根据业务不断地变化。在迭代中保留优秀的设计,修复有缺陷的设计,改正错误的设计,去掉无用的设计
笔者说一个自己的体会,笔者负责一个项目,与一般的业务处理不同,其更偏向于数据处理,笔者经过调研发现,很像apache 的commons-pipeline. 于是尝试用commons-pipeline 来改写项目,但改动起来发现非常的别扭
- commons-pipeline 与 spring 整合起来不是很顺。不是很顺的原因是, commons-pipeline的构建是自成一体的(也就是new 出来的)。同时,一般业务也离不开 对数据库的、redis等第三方数据源。
- 笔者对项目的抽象 也未能完全摸清,对项目得理解 通常也无法一步到位
此时,以重构的方法 来慢慢 演化项目,向commons-pipeline 上靠,改一点上线一点,逐步验证,无疑更稳重的多。
架构设计的历史背景
- 如果要深入理解一个事物的本质,最好的方式就是去追寻这个事物出现的历史背景和推动因素
- 随着软件 系统规模的增加,计算相关 的算法 和 数据结构不再构成 主要的设计问题,当系统由许多部分组成时,整个系统的组织,也就是所说的“软件架构”,导致了一系列 新的设计问题。 笔者想起昨天 学习的paxos 算法,虽说是算法,但paxos 明显不再关注 各个节点如何存储、检索本地存储的通信数据 这类事儿,而是提出proposer、acceptor 等角色,规定了它们的 交流规则,这才是难点。
不好的架构有什么特点:不容易写,不容易读/懂,不容易改/debug
架构设计 的主要目的是 为了解决 软件系统复杂度带来的问题(复杂度 变大 带来了第一次和第二次软件危机)。
复杂度来源
- 高性能
- 高可用
- 可扩展性
-
低成本、安全和规模。
- 低成本通常不是首要目标,往往只有创新才能达到低成本目标。
- 规模变大后,量变可能会引起质变,如大数据就独立成为一门热门技术。
高性能
- 硬件性能的提升。通常不会带来复杂度的提升,比如存储从纸带,磁带,磁盘,ssd,替换就完事了
- 软件系统性能的提升,通常会带来复杂度的提升
软件这块
1 单机内部为提高性能带来的复杂度。单进程 ==> 多进程/多进程通信方案 ==> 多进程/多线程/多线程“通信”方案
2 多台计算机为了高性能带来的复杂度
* 任务分配,调度算法、负载均衡、负载监控报警、动态扩容缩容
* 任务分解
高可用
系统无中断的执行其功能的能力,主要通过冗余来实现高可用。
- 计算高可用
- 存储高可用,存储高可用的难点不在于如何备份儿数据,而在于如何减少或者规避数据不一致对业务造成的影响(因为网络延迟、中断等不可避免)
- 高可用状态决策
可扩展性
可扩展性本质是占位符思想,告诉你有什么,但没有确定到底是什么。
从文章的材料看,文中的可扩展更多指的是 设计的可扩展,而不是性能的可扩展,比如加个机器以提高服务能力之类。
- 面向对象思想的提出
- 设计模式
- 两个基本条件:正确预测变化,完美封装变化
-
如何避免扩展时改动范围太大,是软件架构可扩展性设计的主要思考点。基本思想就是拆。常见的拆分思路(不是非此即彼,通常可组合使用)
对应的架构 例子 备注 面向流程拆分 分层 controller-service-dao/tcp协议栈 分层不一定都是自顶向下依赖,比如mvc 就是两两依赖 面向服务拆分 soa、微服务 面向功能拆分 微内核 - 拆分方式决定了 系统的扩展方式
- 某些扩展,改A也行,改B也行,但团队中总有菜鸟程序猿,改A还是改B完全取决于他觉得哪里容易改。面向容易实现/修改编程
-
分层
- 我们设计一个系统,一般会先分析 系统之间有什么组件/对象/成员,然后就是分析对象之间的交互,最快的情况是所有对象之间两两交互。分层使得 在系统——成员+成员的交互 之间 多了一个“层” 这个层次,有助于从所有对象两两交互的最复杂情况 削减不必要的依赖。
- 分层,本质在于隔离关注点,层与层之间要有清晰的边界、层与层的依赖是稳定的。
-
微服务
- 微服务并不轻量级, serviceA 代码 调用 serviceB,可比rpc 调用简单多了。
- 一个微服务由3个人负责开发。3个人的技术小组既能够形成有效地讨论,又能够快速达成一致意见。1个人容易有思维盲区,2个人容易各执己见,4个人容易应付事儿。
完美封装变化
- 系统需要拆分出变化层和稳定层
- 需要设计变化层和稳定层之间的接口
如何判断技术演进的方向
- 大多数时候,是业务驱动了技术
- 判断业务当前和接下来的一段时间的主要复杂度,如何判断?基于业务的发展阶段。 ==> 架构师必须具备理解业务的能力,包括但不限于:业务是什么,当下的最重要问题是什么,可预见的下一阶段的问题是什么。
业务发展阶段 主要有两块
- 复杂性
- 用户规模,用户规模的扩大会带来性能和可用性的挑战
复杂性的发展阶段
- 初创期,对技术的要求就是,快速实现业务
- 发展期,对技术的要求就是,快速实现业务的同时,兼顾用户数量的增长
- 堆功能期
- 优化期,加缓存、换oracle等
- 架构期
-
竞争期,越来越多的系统对技术的要求就是
- 平台化,解决重复造轮子的问题
- 服务化,解决系统交互问题,交互逃不过同步(服务治理框架)和异步(消息队列)
- 成熟期
普适的架构模板
从宏观角度看,无论bat还是创业公司,其技术架构基本是一样的,只是在具体技术的实现上稍有不同(当然也可能完全不同)。
本章节类似于 秒杀,推送,广告,推荐,计数-互联网非典型业务系统架构设计 介绍各种典型的互联网业务系统,相比来说《从0开始学架构》组织性更强一些。
网络层
-
负载均衡
- dns,一般用来实现地理级别的负载均衡,这也是为什么
dig 域名
会返回多个ip。若是做机器级别的负载均衡,则太耗费ip 资源了。 - http-dns
- nginx,同一个地点内机器级别的负载均衡
- cdn
- dns,一般用来实现地理级别的负载均衡,这也是为什么
- 多机房,多机房的主要目标是灾备,当业务故障时,可以快速的将业务切换到另一个机房。
- 多中心,要求每个中心都可以对外提供服务,且业务能够自动在多中心间切换。
业务层
互联网的业务千差万别,所以业务层没有办法提炼一些公共的系统或组件。抛开业务上的差异, 各个互联网业务发展到最终面临的问题都是类似的:业务复杂度越来越高。
- 系统越来越庞大,业务越来越多 ==> 拆
- 子系统太多 ==> 将职责关联比较强的子系统合成一个虚拟业务域,然后通过网关统一对外呈现(类似于facade模式、另一种角度看是将网关进一步下沉)。
心法
- 是架构重构还是系统优化,一个简单的判断方法:假设我们现在需要从0开始设计当前系统,新架构和老架构是否类似?如果差异不大,说明采取系统优化即可;如果差异很大,那可能就要进行系统重构了。
-
重构的实施,分阶段实施。将要解决的问题根据优先级、重要性、实施难度等划分为不同的阶段,每个阶段聚焦于一个整体的目标,集中资源和精力解决一类问题。
- 每个阶段都有一个明确的目标,做完之后,效果明显,团队信心足,后续推进更加容易
- 每个阶段工作量不会太大,可以和业务并行
- 每个阶段改动不会太大, 降低了总体风险
先易后难
- 一开始就做最难的部分,会发现要解决这个最难的问题,得先解决其他容易的问题
- 最难的问题耗时长,占用资源多,影响士气
- 刚开始的分析不一定全面, 所以一开始最难的或者最关键的事项的判断可能会出错。如果同时负责多个项目,需要先通过实现简单的内容,对项目有一定的“沉浸时间”
坚持
- 坚持梦想
- 坚持学习,时间总可以挤出来的,你对工作必须有所认可,有激情和兴趣。
- 坚持输出,逼着你把问题想清楚,锻炼表达能力、临场反应能力等
App 架构
组件化、容器化,区别在于发布方式
- 组件化,独立开发测试,然后跟随app 的某个版本统一上线,静态发布。
- 容器化,容器可以动态加载组件,组件准备好了直接发布, 无需等待某个app 版本才能上线。
如何学习一个开源项目
-
不要一上来就看源码,而是要基本掌握了功能、原理、关键设计之后再去看源码。看源码的主要目的是学习其代码的写作方式,以及关键技术的实现。
- 具体的数据接收与算法,应有所了解,但无需深入
- 不建议通读源码
- 写demo 故意打断点 来查看调用栈
- 安装一遍很有意义。可以了解系统有哪些 组件,依赖哪些库,配置文件有哪些配置(可以说明一些问题)
-
原理研究
- 关键特性的基本实现原理
- 项目的设计文档、白皮书,了解一个系统有哪些基本点
- 阅读以后的分析文档
- demo 验证
其它材料
优秀架构师必须掌握的架构思维 要点如下:
- 架构的本质是管理复杂性,抽象、分层、分治和演化思维是我们工程师 /架构师应对和管理复杂性的四种最基本武器。
- 抽象能力的强弱,直接决定我们所能解决问题的复杂性和规模大小。
- 有经验的程序员写代码会保持抽象层次的一致性,代码读起来像讲故事,比较清晰易于理解;而没有经验的程序员会有明显的抽象层次跳跃问题,比如一个购买流程:更新库存、打折计算、支付校验、支付、送货。那么你在buy 方法里,突然蹦出来 某个银行的调用api,这就是抽象层次跳跃。《clean code》中也在强调避免这个问题。函数中混乱的抽象层次会让读者思考代码用途和实现方式时被迫进行思维跳跃。当前抽象层次的代码告诉我们代码在做什么,而下一层次的代码则是关于代码要如何实现的。
- 对于互联网系统,基本上可以说是三分设计,七分演化。架构师除了要利用自身的架构设计能力,同时也要学会借助用户反馈和进化的力量,推动架构的持续演进,这个就是演化式架构思维。从另一个角度说,微服务架构就是单体架构逐渐演化来的。
脑图
- 三高特性,都是从存储 + 计算 两个方面来谈的。
- 感觉这也是我学习这个文章 最大的价值,认知 程序开发的边界。
会随着阅读的深入,继续补充
个人
个人感觉,首先是分层,最上层是用户怎么操作。最底层 是你有什么东西。中间 是 最上层和最底层 的 适配,可能有多层。每一层 都是多个子模块,划分子模块的依据是
-
业务层面
- 具有明确的领域边界。比如用户管理、订单管理
- 复用。比如红包和打赏 都要用到支付系统
-
技术层面:
- 易变和不易变的分开
- 流量大和流量小的分开
-
还是实践
- 纠结一个方案的时候,拿高性能、高可用、可扩展套一套
- 大理论与小细节的把握。笔者曾碰到一个问题,查询数据耗时。有两种意见, 一种认为mysql 针对特定字段加索引,速度不会很慢的;一种是使用pika(redis的持久化实现) 存储。从指标上,前者几ms,后者可以做到1ms 以下,在对性能极端要求的场合,适合采用pika方案。这个问题里,如果你对 性能指标没有 什么认识,则极容易陷入模棱两可中。
实现架构的时候,做好目标管理。先实现主要的,再实现次要的,这句其实不是废话。为什么?因为主要功能 很多时候决定了架构设计,而在迭代的过程中,上层架构会做微调,一旦微调,细节相关的代码就有可能会作废。
《从0开始学架构》教程
笔者个人微信订阅号