最近在做交易系统的重构, 整体学习了下 微服务的相关理念. 看了一本书, 微服务设计-豆瓣链接 .
作者相关信息: Sam Newman . 个人网站地址: sam newman ThoughtWorks技术专家.
以下是具体的笔记内容:
1章 微服务
微服务定义
Martin的 单一职责原则
把因相同原因而变化的东西聚合到一起,而把因不同原因而变化的东西分离开来
-
根据业务的边界来确定服务的边界.
- 经验: 一个微服务应该两周可以完全重写.
- 足够小即可, 不要过小.
自治性
服务之间均通过网络调用进行通信,从而加强了服务之间的隔离性,避免紧耦合。
微服务的好处
- 技术异构性
- 弹性
- 扩展性.
- 简化部署和发布
- 两次发布之间的差异越大. 出错的可能性越大
- 与组织架构相匹配
- 可组合性
- 对可替代性的优化
2章 演化架构师
软件系统是演化出来的
- 对于我们创造的大多数产品来说,交付到客户手里之后,还是要响应客户的变更需求,而不是简单地交给客户一个一成不变的软件包
- 软件架构师更像城市规划师. 而不是建筑师.
- 架构师应该像城市规划师那样专注在大方向上,只在很有限的情况下参与到非 常具体的细节实现中来
- 担心服务之间的交互,而不需要过于关注各个服务内部发生的事
一个原则性的方法
- Heroku的12原则o: https://www.12factor.net/
实践和原则的对应关系
- 实践, 原则. 原则最好不要超过10个.
- 原则基本是很少变的. 而实践随着时间迁移, 会不断进行改变.
创建代码服务模板
当开发人员想要实现一个新服务时,所有实现核心属性的那些代码都应该是现 成的。 一些推荐的代码服务模板:
技术债
- 往往因为一些业务的紧急需要而快速上线一些特性, 而忽略了技术的规则和原则. 积累的这些债务, 总一天是是要偿还的.
集中治理和领导
- 架构师的部分职责是治理
- 治理通过评估干系人的需求、当前情况及下一步的可能性来确保企业目标的达成,通过排优先级和做决策来设定方向。对于已经达成一致的方向和目标进行监督
通过微服务系统建设团队 - 全生命周期
微服务架 构中存在多个自治的代码库,每个代码库都有着自己独立的生命周期,这就给更多人提供了对单个服务负责的机会,而当这些人在单个服务上面得到足够锻炼之后,就可以给他们 更多的责任,从而帮助他们逐步达成自己的职业目标.
建设团队
- 对于技术领导人来说,更重要的事情是 帮助你的队友成长 ,帮助他们 理解这个愿景 , 并保证他们可以积极地参与到愿景的实现和调整中来
- 单独的服务, 全生命周期负责. 更加的锻炼成员.
3章 如何构建微服务
核心原则:松耦合 高内聚
- 能够独立修改及部署单个服务而不需要修改系统的其他部分
- 如果你要 改变某个行为的话,最好能够只在一个地方进行修改,然后就可以尽快地发布
服务边界
避免过早划分
尽量根据业务领域划分服务, 而不是技术层面.
- 洋葱架构. 将一个系统按照技术层面, 划分为多层. 会导致多个系统联动进行频繁的更改.
5章 集成
共享数据的问题
- 外部系统可以看到内部实现细节
- 消费方与特定的技术绑定到了一起
编排VS协同
- 编排: 有一个中心节点控制了所有的任务
- 协同: 分布式的. 每一个模块通过订阅来独自处理任务.
- 用异步方式有利于协同方案的实施,从而大大减少服务间 的耦合,这恰恰就是我们为了能独立发布服务而追求的特性
RPC的一些技术评价
RMI. thrift 通过生成客户端代码虽然带来了一些编写的快速性. 但是会有很多潜在的问题:
- 技术耦合
- 本地调用和远程调用并不相同.
异步协作模式
- 优点: 能很好的进行解耦.
- 缺点: 带来了一定的复杂度.
响应式扩展
提供了RxJava的响应式能力.
DRY原则与微服务
在微服务内部不要违反 DRY,但在跨服务的情况下可以适当违反 DRY。
版本管理
尽可能的延迟接口的改变. 如何尽可能的延迟版本管理问题.
鲁棒性原则
客户端尽可能灵活地消费服务响应这一点符合 Postel 法则(也叫作鲁棒性原则,https:// tools.ietf.org/html/rfc761)。该法则认为,系统中的每个模块都应该“宽进严出”,即对自己 发送的东西要严格,对接收的东西则要宽容。
不同版本共存
通过在接口Url中增加版本号, 可以提供多个版本号共存的方法.
单独的API接口层
BFF(Backends For Frontends,为前端服务的后端)。它允许团队在专 注于给定 UI 的同时,也会处理与之相关的服务端组件。
为不同的客户端聚合API接口进行单独的API聚合层.
如何决定是否使用商业软件还是自己开发
对于一般规模的组织来说,如果某个软件非常特殊,并且它是你的战略性资产的话,那就 自己构建;如果不是这么特别的话,那就购买
小结
- 无论如何避免数据库集成
- 理解REST和RPC之间的取舍,但总是使用REST作为请求/响应模式的起点 • 相比编排,优先选择协同
- 避免破坏性修改、理解Postel法则、使用容错性读取器
- 将用户界面视为一个组合层
5章 切分单个服务
关键是接缝
在《修改代码的艺术》这本书中,Michael Feathers定义了接缝的概念,从接缝处可以抽取出相对独立的一部分代码,对这部分代码进行修改不会影响系统的其他部分。识别出接缝,不仅仅能够清理代码库,更重要的是,这些被识别出的接缝可以成为服务的边界。
分解单块系统
服务拆分之后, 可能会导致单个表的事物变为如何确保多个系统的多个事物一致性, 解决办法?
- 再试一次 . 最终一致性**。相对于使用事务来保证系统处于一致的状态,最 终一致性可以接受系统在未来的某个时间达到一致.
- 补偿事物. 定位问题困难.
- 分布式事物. 利用比较复杂的技术, 来确保不同进程的事物一致性.
报表服务
周期性的同步事物数据库到相应的NoSQL中, 提供相应的报表服务.
修改的代价
做小的、增量修改的各种原因,但其中一个关键的好处 是,能够理解做出的那些改变会造成什么影响. 切分成微小的服务. 然后处理相应的逻辑.
根本原因
- 第一件需要理解的事情是,服务一定会慢慢变大,直至大到需要拆分.系统的架构随着时间的推移增量地进行变化。 关键是要在拆分这件事情变得太过昂贵之前 , 意识到你需要做这个拆分。(注释: 如果直到发生了重大事故才明白需要拆分, 太晚了!)
- 软件的一个衡量因素-复杂度. 如果一个系统不进行可以的维护和refactor. 其复杂度, 必然是无限上升的.
6章 部署
CI(持续集成)
Fabric
https://www.cnblogs.com/holbrook/archive/2012/03/05/2380398.html
7章 测试
测试分类
测试微服务架构的系统跟测试独立系统的区别,很大程度上在于各种类型的自动化 测试。因此,我们将集中精力在自动化测试上面
测试金字塔模型

越往上的端到端测试需要花费的时间越多. 端到端的测试更多的是业务测试, QA关注较多. 单元测试, Unit Test 更多的是RD自己应该关注的, 提高系统的稳定性.
单元测试
单元测试更多是帮助RD来降低错误率的, 自动化的单元测试. 谁的代码, 谁来写单元测试.
打桩,是指为被测服务的请求创建一些有着预设响应的打桩服务.
脆弱的测试
- 在测试中的服务数量越多,测试就会越脆弱,不确定性也就越强. 可能后期就会导致, 我们放弃Unit Test. 这在我们的工程项目中, 一次又一次的发生了.
- 单元测试容易成为, Diane Vaughn 所说的异常正常化(the normalization of deviance)的受害者.
- 如何解决? Martin Fowler的建议: “Eradicating Non-Determinism in Tests”( https://martinfowler.com/articles/nonDeterminism.html ). 发现脆弱的测试时应该立刻记录下来,当不能立即修复时,需要把它们从测试套件中移除,然后就可以不受打扰地安心修复它们
部署后再测试
- 蓝绿部署.
- 你需要能够切换生产流量到不同的主机(或主机 集群)上。切换可以通过改变 DNS 条目,或更改负载均衡的配置
- 使用蓝 / 绿部署可以降低风险,也让你有能力在遇到问题时尽快恢复
蓝绿部署示意图:

- 金丝雀发布
- 金丝雀发布是指通过将部分生产流量引流到新部署的系统,来验证系统是否按预期执
- 金丝雀发布与蓝 / 绿发布的不同之处在于,新旧版本共存的时间更长,而且经常会调整流量
平均修复时间胜过平均故障间隔时间. MTBF VS MTTR
- 有一个问题需要注意: 不论测试是多么全面, 线上的bug还是不可避免发生 . 关键是, 能否在故障的快速的时候 , 尽快恢复.
- 平均故障间隔时间(Mean Time Between Failures,MTBF) 和平均修复时间(Mean Time To Repair,MTTR)之间的权衡优化。
- 减少修复时间的技术可以简单到尽快回滚加上良好的监控(在第 8 章我们将讨论),类似 蓝 / 绿部署。
- MTTR: https://en.wikipedia.org/wiki/Mean_time_to_repair
- https://en.wikipedia.org/wiki/Fault_tolerance
性能测试
- 将系统拆分为较小的微服务 后,跨网络边界调用的次数明显增加了 . 需要定期的进行系统的压力测试, 不断发现系统的bottlenect
- 性能测试需要与系统性能的监控同时进行.
推荐 《敏捷软件测试》, 关于测试有更多详细的介绍.
8章 监控
日志, 更多的日志
通过ELK日志聚合系统, 聚合更多的日志, 到一起.
多指标跟踪
使用Graphite, 进行简单的指标聚合. 美团内部, 提供了太多的数据指标聚合系统, 背后的核心都是 打点上报 到时间序列数据库.
关联表示 UUID
使用相应的关联方法. 多个上下游系统, 使用UUID. 然后相互携带. 统一聚合到 日志中心. 底层依赖ELK技术栈. 然后统一收集日志.
另外, 监控级联的风险, 使用 hystrix组件.
总结
- 最低限度要跟踪请求响应时间。做好之后,可以开始跟踪错误率及应用程序级的指标。
- 最低限度要跟踪所有下游服务的健康状态,包括下游调用的响应时间,最好能够跟踪错误率。一些像 Hystrix 这样的库,可以在这方面提供帮助。
- 标准化如何收集指标以及存储指标。
- 如果可能的话,以标准的格式将日志记录到一个标准的位置。如果每个服务各自使用不同的方式,聚合会非常痛苦!
- 聚合CPU之类的主机指标.
- 查看单台主机的情况.
- 通过单个查询工具对日志进行聚合和存储.
- 聚合关联UUID.
10章 康威定律和系统设计
康威定律
任何组织在设计一套系统(广义概念上的系统)时,所交付的设计方案在结构上 都与该组织的沟通结构保持一致
- 松耦合组织 VS 紧耦合组织
- 商业组织的系统耦合非常紧密.
- amazon 的两个披萨团队. 一个团队应该达到两个披萨不够吃.
相关链接:
服务所有权
所有 权延伸到服务的方方面面,从应用程序的需求、构建、部署到运维。它把决定权交给最合适的人,赋予团队更多的权力和自治,也使 其对工作更负责. Eat your dog food!
- 成员轮换
成员时不时地在这些团队之间轮换,但往往长时间留在这个业务线,以确保团队成员可以更好地建立业务线的领域知识 - 如何构建高并发.安全.稳定 的交易系统.
人
“不管一开始看起来什么样,它永远是人的问题。” ——杰拉尔德 • 温伯格,咨询第二定律
11章 规模化微服务
故障无处不在
https://en.wikipedia.org/wiki/Fallacies_of_distributed_computing . 分布式计算的谬论:
- 网络是可靠的.
- 延时为0
- 带宽是无限制的
- 网络是安全的
- 拓扑是不会变的
- 只有一个管理员
- 传输成本为0
- 网络是是均匀的.
针对谬误, 软件设计和开发过程中, 需要做一些降级去处理这些问题, 拥抱这些故障.
系统指标
- 响应时间
- 可用性
- 数据持久性
功能降级
从业务角度来思考, 我们该如何对不同的系统优雅的降级使用.
架构性安全措施
有一些模式,组合起来被称为架构性安全措施,它们可以确保如果事情真的出错了,不会引起严重的 级联影响 .
如何来解决下游服务的不可用, 导致一个依赖服务拖垮我们整个服务, 耗尽对外的http线程池?
反脆弱性在netflix中的使用
- Netflix通过引发故障来确保其系统的容错性
- 谷歌通过 DiRt. 灾难恢复测试来测试系统.
-
Netflix chaos monkey. 随机停掉服务器. 制造混乱
- Simian Army
- Latency Monkey
- chaos monkey
-
正确设置超时时间.
- 设置默认超时时间, 防止超长时间等待.
- 实现仓壁隔离不同线程池
- 实现不同业务的线程池隔离. 推荐 netflix的hystrix解决方案.
- 实现断路器
- 当对下游资源的请求发生一定数量的失败后,断路器会打开
- 断路器模式 断路器
- 隔离
- 一个服务越依赖于另一个,另一个服务的健康将越能影响其正常工作的能力.
幂等
- 多次执行操作和一次执行操作幂等. 退款就应该是这样. 重复支付失败. - 如何进行隔离? 如何隔离?
负载均衡
- 是把所有的微服务实例都 放在一个独立的 VLAN 里。VLAN 是一个虚拟局域网,所有的外部请求只能通过一个路由 器访问内部。在这个例子中,这个路由器也就是 SSL 终端负载均衡器。VLAN 外部跟微服 务通信的唯一方式是通过 HTTPS,而内部的所有通信都是通过 HTTP. 内部的负载均衡. nginx只用来进行SSL加密通信.
重新设计
你的设计应该“考虑 10 倍容量的增长,但超过 100 倍容量时就要重写 了”。在某些时刻,你需要做一些相当激进的事情,以支持负载容量增加到下一个级别。 参考: Challenges in Building Large-Scale Information Retrieval Systems
扩展数据库
- 服务可用性 VS 数据持久性. 不同的概念.
- 扩展读取.
- 读从库. 可能会有数据一致性问题. CAP原理
- 读缓存.
- 扩展写
- Hash -> 分片. mysql. 分库分表. 缓解压力
- 问题. 如何进行跨分片的查询. 同步到NOSQL进行查询.
- 内存计算.聚合返回 / 替代的读数据库包含所有数据. ES.
- mongdb . cassandra. 替换的选择. NOSQL数据库.
- CQRS. (command-query responsibility segregation).
- 将查询和修改状态的请求命令进行隔离.
- CQRS的说明和解释 Martin Fowler: CQRS
使用缓存
-
客户端缓存
- 可以减少客户端调用次数
- 无法使全部消费者同时发生变化
-
服务端缓存
- 一切对客户端都是不透明的,它们不需要关心任何事情。缓存在服务 器外围或服务器限界内时,很容易了解一些类似数据是否失效这样的事情,还可以跟踪 和优化缓存命中率.
-
代理服务器缓存
- 不需要每次查询.
-
HTTP缓存
- cache-control. 控制是否需要缓存该时间.
- expires头部, 失效时间.
- Etag. 可以发送缓存内容的最新版本号.
-
为写试用缓存
- 后写式缓存(writebehind) - 批量处理操作.
- 如果你使用后写式(writebehind)缓存,可以先写入本地缓存中,并在之后的 某个时刻将缓存中的数据写入下游的、可能更规范化的数据源中
- write-back
- cache写机制
- 后写式缓存(writebehind) - 批量处理操作.
-
防止缓存突然全部失效
保护源服务 - 后台重建缓存 -
保持简单
缓存越多,就越难评估任何数据的新鲜程度
CAP原理
在分布式系统中有三方面需要彼 此权衡:一致性(consistency)、可用性(availability)和分区容忍性(partition tolerance)。 具体地说,这个定理告诉我们最多只能保证三个中的两个。
- 需要根据你的业务来具体判断, 你的系统需求属性. 比如: 我们的系统作为一个整体,不需要全部是 AP 或 CP 的.
服务发现
-
动态服务机制
-
zookeeper. 树形的结构, 可以增加新的节点. 复杂的选举机制,来确保可用性.Zookeeper 的核心是提供了一个用于存储信息的分层命名空间
-
Consul. Consul 提供的杀手级特性之一是,它实际上提供了现成的 DNS 服务器. 网址: http://www.consul.io/ . 项目github地址: https://github.com/hashicorp/consul
-
Eureka. netflix提供的服务发现功能.Netflix 的开源系统 Eureka( https://github.com/Netflix/eureka )
-
文档服务
API文档服务解决方案: Swagger. 用动态的接口来展示文档.
12章 - 总结
坚持反脆弱性信条
心中持有反脆弱的信条,预期在任何地方都会发生故障,这说明我们正走在正确 的路上.
- 设置 超时
- 使用 仓壁和断路器
实现微服务体系化
- 围绕业务概念建模
- 接收自动化文化
- 接受自动化测试工作
- 隐藏内部实现细节
- 隐藏数据库
- 一切都去中心化
- 可独立部署
- 蓝绿部署. 灰度部署, 经历一个完整的高峰期.
- 金丝雀部署技术. 区分部署和发布.
- 隔离失败
- 断路器
- 仓壁模式
- 高度可观察
- 通过语义监控
- 聚合日志和数据
ChangeLog
12/19/17 完成11章节和12章节笔记.
12/27/17 完成全部章节笔记
注:本文内容来自互联网,旨在为开发者提供分享、交流的平台。如有涉及文章版权等事宜,请你联系站长进行处理。