技术

并发的成本 基础设施优化 hashicorp raft源码学习 docker 架构 mosn细节 与微服务框架整合 Java动态代理 编程范式 并发通信模型 《网络是怎样连接的》笔记 go细节 codereview mat使用 jvm 线程实现 go打包机制 go interface及反射 如何学习Kubernetes 《编译原理之美》笔记——后端部分 《编译原理之美》笔记——前端部分 Pilot MCP协议分析 go gc 内存管理玩法汇总 软件机制 istio流量管理 Pilot源码分析 golang io 学习Spring mosn源码浅析 MOSN简介 《datacenter as a computer》笔记 学习JVM Tomcat源码分析 Linux可观测性 MVCC 学习存储 学计算 Gotty源码分析 kubernetes operator kaggle泰坦尼克问题实践 kubernetes自动扩容缩容 神经网络模型优化 直觉上理解机器学习 knative入门 如何学习机器学习 神经网络系列笔记 TIDB源码分析 《阿里巴巴云原生实践15讲》笔记 Alibaba Java诊断工具Arthas TIDB存储——TIKV 《Apache Kafka源码分析》——简介 netty中的线程池 guava cache 源码分析 Springboot 启动过程分析 Spring 创建Bean的年代变迁 Linux内存管理 自定义CNI IPAM 扩展Kubernetes 副本一致性 spring redis 源码分析 kafka实践 spring kafka 源码分析 Linux进程调度 让kafka支持优先级队列 Codis源码分析 Redis源码分析 C语言学习 《趣谈Linux操作系统》笔记 docker和k8s安全机制 jvm crash分析 Prometheus 学习 Kubernetes监控 Kubernetes 控制器模型 容器日志采集 容器狂占cpu怎么办? 容器狂打日志怎么办? Kubernetes资源调度——scheduler 时序性数据库介绍及对比 influxdb入门 maven的基本概念 《Apache Kafka源码分析》——server Kubernetes objects之编排对象 源码分析体会 自动化mock AIOps说的啥 《数据结构与算法之美》——算法新解 Kubernetes源码分析——controller mananger Kubernetes源码分析——apiserver Kubernetes源码分析——kubelet Kubernetes介绍 ansible学习 Kubernetes源码分析——从kubectl开始 jib源码分析之Step实现 kubernetes实践 jib源码分析之细节 线程排队 跨主机容器通信 jib源码分析及应用 为容器选择一个合适的entrypoint kubernetes yaml配置 marathon-client 源码分析 《持续交付36讲》笔记 mybatis学习 程序猿应该知道的 无锁数据结构和算法 CNI 为什么很多业务程序猿觉得数据结构和算法没用? 串一串一致性协议 当我在说PaaS时,我在说什么 《数据结构与算法之美》——数据结构笔记 swagger PouchContainer技术分享体会 harbor学习 用groovy 来动态化你的代码 《深入剖析kubernetes》笔记 精简代码的利器——lombok 学习 编程语言的动态性 rxjava3——背压 rxjava2——线程切换 spring cloud 初识 《深入拆解java 虚拟机》笔记 《how tomcat works》笔记 hystrix 学习 rxjava1——概念 Redis 学习 TIDB 学习 分布式计算系统的那些套路 Storm 学习 AQS1——论文学习 Unsafe Spark Stream 学习 linux vfs轮廓 mysql 批量操作优化 《自己动手写docker》笔记 java8 实践 中本聪比特币白皮书 细读 区块链泛谈 比特币 大杂烩 总纲——如何学习分布式系统 hbase 泛谈 forkjoin 泛谈 看不见摸不着的cdn是啥 《jdk8 in action》笔记 程序猿视角看网络 bgp初识 mesos 的一些tips mesos 集成 calico calico学习 AQS2——粗略的代码分析 我们能用反射做什么 web 跨域问题 《clean code》笔记 硬件对软件设计的影响 《Elasticsearch权威指南》笔记 mockito简介及源码分析 2017软件开发小结—— 从做功能到做系统 《Apache Kafka源码分析》——clients dns隐藏的一个坑 《mysql技术内幕》笔记2 《mysql技术内幕》笔记1 log4j学习 为什么netty比较难懂? 回溯法 apollo client源码分析及看待面向对象设计 学习并发 从一个marathon的问题开始的 docker 环境(主要运行java项目)常见问题 Scala的一些梗 OpenTSDB 入门 spring事务小结 事务一致性 javascript应用在哪里 《netty in action》读书笔记 netty对http2协议的解析 ssl证书是什么东西 http那些事 苹果APNs推送框架pushy apple 推送那些事儿 编写java框架的几大利器 java内存模型 java exception Linux IO学习 network channel network byte buffer 测试环境docker化实践 netty(七)netty在框架中的使用套路 Nginx简单使用 《Linux内核设计的艺术》小结 Go并发机制及语言层工具 mesos深入 Macvlan Linux网络源代码学习——数据包的发送与接收 《docker源码分析》小结 docker中涉及到的一些linux知识 hystrix学习 Linux网络源代码学习——整体介绍 zookeeper三重奏 数据库的一些知识 Spark 泛谈 链式处理的那些套路 netty(六)netty回顾 Thrift基本原理与实践(二) Thrift基本原理与实践(一) 回调 异步执行抽象——Executor与Future Docker0.1.0源码分析 java gc Jedis源码分析 Redis概述 机器学习泛谈 Linux网络命令操作 JTA与TCC 换个角度看待设计模式 Scala初识 向Hadoop学习NIO的使用 以新的角度看数据结构 并发控制相关的硬件与内核支持 systemd 简介 那些有用的sql语句 异构数据库表在线同步 quartz 源码分析 基于docker搭建测试环境(二) spring aop 实现原理简述 自己动手写spring(八) 支持AOP 自己动手写spring(七) 类结构设计调整 分析log日志 自己动手写spring(六) 支持FactoryBean 自己动手写spring(九) 总结 自己动手写spring(五) bean的生命周期管理 自己动手写spring(四) 整合xml与注解方式 自己动手写spring(三) 支持注解方式 自己动手写spring(二) 创建一个bean工厂 自己动手写spring(一) 使用digester varnish 简单使用 关于docker image的那点事儿 基于docker搭建测试环境 分布式配置系统 JVM内存与执行 git spring rmi和thrift maven/ant/gradle使用 再看tcp mesos简介 缓存系统 java nio的多线程扩展 《Concurrency Models》笔记 回头看Spring IOC IntelliJ IDEA使用 Java泛型 vagrant 使用 Go常用的一些库 Python初学 Goroutine 调度模型 虚拟网络 《程序员的自我修养》小结 VPN(Virtual Private Network) Kubernetes存储 Kubernetes 其它特性 访问Kubernetes上的Service Kubernetes副本管理 Kubernetes pod 组件 使用etcd + confd + nginx做动态负载均衡 如何通过fleet unit files 来构建灵活的服务 CoreOS 安装 CoreOS 使用 Go学习 JVM类加载 硬币和扑克牌问题 LRU实现 virtualbox 使用 ThreadLocal小结 docker快速入门

标签


《从0开始学架构》笔记

2018年05月06日

简介

内容来自 李运华 在 极客时间 《从0开始学架构》 的 分享

优秀的人总是会做一件事,对于一个领域,发现一些tip,提炼一个方法论,试错和完善这个方法论。

tips

架构设计的 关键思维是 判断和 取舍,程序设计 的关键思维 是逻辑和实现。

架构是什么?

  1. 首先 梳理几个概念:系统与子系统、模块与组件、框架与架构
  2. 系统的定义,维基百科:系统泛指由一群有关联的个体组成,根据某种规则运作,能完成个别元件不能单独完成的工作的群体。包含多个有关联的个体,个体按定义好的规则运行。
  3. 逻辑角度拆分,划分模块的主要目的是职责分离。物理角度拆分,划分组件的主要目的是 单元复用。比如做一个学生信息管理系统,从逻辑角度拆分可以分为登陆注册、个人信息、个人成绩等,从物理角度来拆分,可以分为Nginx、Web服务器、MySQL
  4. 框架 通常指的是为了实现 某个业界标准或完成特定基本 任务的软件组件规范,提供规范所要求之基础功能。软件架构:软件系统的顶层结构。

优秀程序猿和架构师之间一个明显的鸿沟是:不确定性。对于程序猿 编程来说,程序是确定的(有语法约束等),执行结果是确定的。而对架构设计来说,A和B公司可能架构完全不同,但都运转的挺好。面对不确定性,就要选择,就要取舍。

  1. 业界很多领先的方案,都是逼出来的。
  2. 演化优于一步到位,经常拿软件架构和建筑架构举例,但建筑架构一旦完成就不再改变,软件却需要根据业务不断地变化。在迭代中保留优秀的设计,修复有缺陷的设计,改正错误的设计,去掉无用的设计

笔者说一个自己的体会,笔者负责一个项目,与一般的业务处理不同,其更偏向于数据处理,笔者经过调研发现,很像apache 的commons-pipeline. 于是尝试用commons-pipeline 来改写项目,但改动起来发现非常的别扭

  1. commons-pipeline 与 spring 整合起来不是很顺。不是很顺的原因是, commons-pipeline的构建是自成一体的(也就是new 出来的)。同时,一般业务也离不开 对数据库的、redis等第三方数据源。
  2. 笔者对项目的抽象 也未能完全摸清,对项目得理解 通常也无法一步到位

此时,以重构的方法 来慢慢 演化项目,向commons-pipeline 上靠,改一点上线一点,逐步验证,无疑更稳重的多。

架构设计的历史背景

  1. 如果要深入理解一个事物的本质,最好的方式就是去追寻这个事物出现的历史背景和推动因素
  2. 随着软件 系统规模的增加,计算相关 的算法 和 数据结构不再构成 主要的设计问题,当系统由许多部分组成时,整个系统的组织,也就是所说的“软件架构”,导致了一系列 新的设计问题。 笔者想起昨天 学习的paxos 算法,虽说是算法,但paxos 明显不再关注 各个节点如何存储、检索本地存储的通信数据 这类事儿,而是提出proposer、acceptor 等角色,规定了它们的 交流规则,这才是难点。

不好的架构有什么特点:不容易写,不容易读/懂,不容易改/debug

架构设计 的主要目的是 为了解决 软件系统复杂度带来的问题(复杂度 变大 带来了第一次和第二次软件危机)。

复杂度来源

  1. 高性能
  2. 高可用
  3. 可扩展性
  4. 低成本、安全和规模。

    • 低成本通常不是首要目标,往往只有创新才能达到低成本目标。
    • 规模变大后,量变可能会引起质变,如大数据就独立成为一门热门技术。

高性能

  • 硬件性能的提升。通常不会带来复杂度的提升,比如存储从纸带,磁带,磁盘,ssd,替换就完事了
  • 软件系统性能的提升,通常会带来复杂度的提升

软件这块

1 单机内部为提高性能带来的复杂度。单进程 ==> 多进程/多进程通信方案 ==> 多进程/多线程/多线程“通信”方案
2 多台计算机为了高性能带来的复杂度

* 任务分配,调度算法、负载均衡、负载监控报警、动态扩容缩容
* 任务分解

高可用

系统无中断的执行其功能的能力,主要通过冗余来实现高可用。

  1. 计算高可用
  2. 存储高可用,存储高可用的难点不在于如何备份儿数据,而在于如何减少或者规避数据不一致对业务造成的影响(因为网络延迟、中断等不可避免)
  3. 高可用状态决策

可扩展性

从文章的材料看,文中的可扩展更多指的是 设计的可扩展,而不是性能的可扩展,比如加个机器以提高服务能力之类。

  1. 面向对象思想的提出
  2. 设计模式
  3. 两个基本条件:正确预测变化,完美封装变化
  4. 如何避免扩展时改动范围太大,是软件架构可扩展性设计的主要思考点。基本思想就是拆。常见的拆分思路(不是非此即彼,通常可组合使用)

      对应的架构 例子 备注
    面向流程拆分 分层 controller-service-dao/tcp协议栈 分层不一定都是自顶向下依赖,比如mvc 就是两两依赖
    面向服务拆分 soa、微服务    
    面向功能拆分 微内核    
  5. 拆分方式决定了 系统的扩展方式
  6. 某些扩展,改A也行,改B也行,但团队中总有菜鸟程序猿,改A还是改B完全取决于他觉得哪里容易改。面向容易实现/修改编程
  7. 分层

    • 我们设计一个系统,一般会先分析 系统之间有什么组件/对象/成员,然后就是分析对象之间的交互,最快的情况是所有对象之间两两交互。分层使得 在系统——成员+成员的交互 之间 多了一个“层” 这个层次,有助于从所有对象两两交互的最复杂情况 削减不必要的依赖。
    • 分层,本质在于隔离关注点,层与层之间要有清晰的边界、层与层的依赖是稳定的。
  8. 微服务

    • 微服务并不轻量级, serviceA 代码 调用 serviceB,可比rpc 调用简单多了。
    • 一个微服务由3个人负责开发。3个人的技术小组既能够形成有效地讨论,又能够快速达成一致意见。1个人容易有思维盲区,2个人容易各执己见,4个人容易应付事儿。

完美封装变化

  1. 系统需要拆分出变化层和稳定层
  2. 需要设计变化层和稳定层之间的接口

如何判断技术演进的方向

  1. 大多数时候,是业务驱动了技术
  2. 判断业务当前和接下来的一段时间的主要复杂度,如何判断?基于业务的发展阶段。 ==> 架构师必须具备理解业务的能力,包括但不限于:业务是什么,当下的最重要问题是什么,可预见的下一阶段的问题是什么。

业务发展阶段 主要有两块

  1. 复杂性
  2. 用户规模,用户规模的扩大会带来性能和可用性的挑战

复杂性的发展阶段

  1. 初创期,对技术的要求就是,快速实现业务
  2. 发展期,对技术的要求就是,快速实现业务的同时,兼顾用户数量的增长
    1. 堆功能期
    2. 优化期,加缓存、换oracle等
    3. 架构期
  3. 竞争期,越来越多的系统对技术的要求就是

    1. 平台化,解决重复造轮子的问题
    2. 服务化,解决系统交互问题,交互逃不过同步(服务治理框架)和异步(消息队列)
  4. 成熟期

普适的架构模板

从宏观角度看,无论bat还是创业公司,其技术架构基本是一样的,只是在具体技术的实现上稍有不同(当然也可能完全不同)。

本章节类似于 秒杀,推送,广告,推荐,计数-互联网非典型业务系统架构设计 介绍各种典型的互联网业务系统,相比来说《从0开始学架构》组织性更强一些。

网络层

  1. 负载均衡

    • dns,一般用来实现地理级别的负载均衡,这也是为什么dig 域名会返回多个ip。若是做机器级别的负载均衡,则太耗费ip 资源了。
    • http-dns
    • nginx,同一个地点内机器级别的负载均衡
    • cdn
  2. 多机房,多机房的主要目标是灾备,当业务故障时,可以快速的将业务切换到另一个机房。
  3. 多中心,要求每个中心都可以对外提供服务,且业务能够自动在多中心间切换。

业务层

互联网的业务千差万别,所以业务层没有办法提炼一些公共的系统或组件。抛开业务上的差异, 各个互联网业务发展到最终面临的问题都是类似的:业务复杂度越来越高。

  1. 系统越来越庞大,业务越来越多 ==> 拆
  2. 子系统太多 ==> 将职责关联比较强的子系统合成一个虚拟业务域,然后通过网关统一对外呈现(类似于facade模式、另一种角度看是将网关进一步下沉)。

心法

  1. 是架构重构还是系统优化,一个简单的判断方法:假设我们现在需要从0开始设计当前系统,新架构和老架构是否类似?如果差异不大,说明采取系统优化即可;如果差异很大,那可能就要进行系统重构了。
  2. 重构的实施,分阶段实施。将要解决的问题根据优先级、重要性、实施难度等划分为不同的阶段,每个阶段聚焦于一个整体的目标,集中资源和精力解决一类问题。

    • 每个阶段都有一个明确的目标,做完之后,效果明显,团队信心足,后续推进更加容易
    • 每个阶段工作量不会太大,可以和业务并行
    • 每个阶段改动不会太大, 降低了总体风险

    先易后难

    • 一开始就做最难的部分,会发现要解决这个最难的问题,得先解决其他容易的问题
    • 最难的问题耗时长,占用资源多,影响士气
    • 刚开始的分析不一定全面, 所以一开始最难的或者最关键的事项的判断可能会出错。如果同时负责多个项目,需要先通过实现简单的内容,对项目有一定的“沉浸时间”

坚持

  1. 坚持梦想
  2. 坚持学习,时间总可以挤出来的,你对工作必须有所认可,有激情和兴趣。
  3. 坚持输出,逼着你把问题想清楚,锻炼表达能力、临场反应能力等

App 架构

组件化、容器化,区别在于发布方式

  1. 组件化,独立开发测试,然后跟随app 的某个版本统一上线,静态发布。
  2. 容器化,容器可以动态加载组件,组件准备好了直接发布, 无需等待某个app 版本才能上线。

如何学习一个开源项目

  1. 不要一上来就看源码,而是要基本掌握了功能、原理、关键设计之后再去看源码。看源码的主要目的是学习其代码的写作方式,以及关键技术的实现。

    • 具体的数据接收与算法,应有所了解,但无需深入
    • 不建议通读源码
    • 写demo 故意打断点 来查看调用栈
  2. 安装一遍很有意义。可以了解系统有哪些 组件,依赖哪些库,配置文件有哪些配置(可以说明一些问题)
  3. 原理研究

    • 关键特性的基本实现原理
    • 项目的设计文档、白皮书,了解一个系统有哪些基本点
    • 阅读以后的分析文档
    • demo 验证

其它材料

优秀架构师必须掌握的架构思维 要点如下:

  1. 架构的本质是管理复杂性,抽象、分层、分治和演化思维是我们工程师 /架构师应对和管理复杂性的四种最基本武器。
  2. 抽象能力的强弱,直接决定我们所能解决问题的复杂性和规模大小。
  3. 有经验的程序员写代码会保持抽象层次的一致性,代码读起来像讲故事,比较清晰易于理解;而没有经验的程序员会有明显的抽象层次跳跃问题,比如一个购买流程:更新库存、打折计算、支付校验、支付、送货。那么你在buy 方法里,突然蹦出来 某个银行的调用api,这就是抽象层次跳跃。《clean code》中也在强调避免这个问题。
  4. 对于互联网系统,基本上可以说是三分设计,七分演化。架构师除了要利用自身的架构设计能力,同时也要学会借助用户反馈和进化的力量,推动架构的持续演进,这个就是演化式架构思维。从另一个角度说,微服务架构就是单体架构逐渐演化来的。

脑图

  1. 三高特性,都是从存储 + 计算 两个方面来谈的。
  2. 感觉这也是我学习这个文章 最大的价值,认知 程序开发的边界。

会随着阅读的深入,继续补充

个人

个人感觉,首先是分层,最上层是用户怎么操作。最底层 是你有什么东西。中间 是 最上层和最底层 的 适配,可能有多层。每一层 都是多个子模块,划分子模块的依据是

  1. 业务层面

    • 具有明确的领域边界。比如用户管理、订单管理
    • 复用。比如红包和打赏 都要用到支付系统
  2. 技术层面:

    • 易变和不易变的分开
    • 流量大和流量小的分开
  3. 还是实践

    1. 纠结一个方案的时候,拿高性能、高可用、可扩展套一套
    2. 大理论与小细节的把握。笔者曾碰到一个问题,查询数据耗时。有两种意见, 一种认为mysql 针对特定字段加索引,速度不会很慢的;一种是使用pika(redis的持久化实现) 存储。从指标上,前者几ms,后者可以做到1ms 以下,在对性能极端要求的场合,适合采用pika方案。这个问题里,如果你对 性能指标没有 什么认识,则极容易陷入模棱两可中。

实现架构的时候,做好目标管理。先实现主要的,再实现次要的,这句其实不是废话。为什么?因为主要功能 很多时候决定了架构设计,而在迭代的过程中,上层架构会做微调,一旦微调,细节相关的代码就有可能会作废。

《从0开始学架构》教程

笔者个人微信订阅号