1. 微服务

一些系统工作的小而自治的服务。

专注于做好一件事 自治性,解耦,正确地剑魔服务和API

好处:技术异构性,弹性(不可用和功能降级问题),扩展,简化部属,与组织结构相匹配,可组合性,对可替代性的优化,

1.3 面相服务的架构SOA

2. 演化式架构师

职责:愿景,同理心,合作,适应性,自治性,治理

3. 如何建模服务

什么样的服务是好服务: 低耦合:修改一个服务无需修改另一个服务。限制两个服务之间的不同调用形式的数量 高内聚:相关的行为聚集一起

3.3限界上下文 共享的隐藏模型(领域驱动设计) 模块和服务:服务边界和领域的限界上下文保持一致 过早划分:

3.4 业务功能 建模服务时,应该将这些功能作为关键操作提供给其他协作者(其他服务)

3.5 逐步划分上下文

3.6 关于业务概念的沟通 修改系统的目的是为了满足业务需求 技术边界:

4.集成

4.1寻找理想的集成技术 避免破坏性修改 保证API的技术无关性 使你的服务易于消费方式用 隐藏内部实现细节 为用户创建接口 共享数据库

4.4同步异步 4.5编排与协同 编排: 依赖于某个中心大脑指导并且驱动整个流程 协同:告知系统中各个部分的职责,细节留给他们自己。发布订阅。降低耦合度,但是需要对业务流程做跨服务监控。RPC 和 REST

4.6远程过程调用 缺点: 技术的耦合;本地调用和远程调用并不相同;脆弱性

4.7 REST

4.8实现基于事件的异步协作方式 微服务发布事件机制和消费者接受事件机制 尽量让消息中间件保持简单,把业务逻辑放在自己的服务中 关联ID对跨进程间的请求进行追踪

4.9 服务即状态机 4.10响应式扩展

4.11微服务世界中的DRY 和代码重用的危险: 跨服务可以适度违反DRY

语义化版本控制 MAJOR.MINOR.PATCH。通过语义化版本控制可以直接看出来是否有不兼容修改,调用方是否需要修改调用点代码。

5.分解单块系统

通过限界上下文来确定业务拆分边界。

SchemaSpy 工具生成数据库之间的关系图

拆除外键:通过API 来提供访问。(这样做会增加请求减慢访问速度,需要做个权衡) 作者这里推荐先做数据库拆分,之后去拆分业务代码。

数据库重构: 拆分过程可以根据业务边界重新设计db

拆分之后如何处理事务不一致问题:

  • 再试一次,比如一个操作成功了,一个失败了。但是要保证重试可以修复这个问题(最终一致性)
  • 终止整个操作。发起一个补偿事务抵消之前的操作,但是补偿也可能失败,不好处理,可能需要一个后台
  • 分布式事务:两阶段提交.尽量用现有的技术方案

把设计画在白板上,在你认为的服务边界上运行用例,看看会发生什么。

CRC(class-responsibility-collaboration): 类、职责、交互卡片

6 部署

CI (continuous intergration) 持续集成

  • 是否每天签入代码到主线
  • 是否有一组测试验证修改
  • 构建失败后,是否把修复 CI 当做第一优先级事情来做

CD(continuous delivery) 持续交付

7 测试

stub vs mock

stub: 打桩是指为被测服务的请求创建一些有预设相应的打桩服务。 mock 还会进一步验证请求本身是否被正确调用。

《测试驱动的面向对象软件开发》

8 监控

监控小的服务,然后聚合起来看整体。

日志: ELK elastic-search + logstash + kibana

多个服务的指标跟踪:graphite

zipkin 跨多个系统边界跟踪调用

9 安全

身份验证和授权。分布式系统下,我们希望有一个单一的标识且只需要一次验证。

单点登录SSO(single signon);使用网关实现单点登录。

服务间身份验证和授权:在边界内允许一切;https 基本身份验证

9.3 静态数据的安全

使用众所周知的加密算法

9.4 深度防御

防火墙(iptables);日志(踢出敏感信息);入侵检测和防御系统;网络隔离

9.7 个人因素

心怀不满的雇员损害你的系统

10 康威定律和系统设计

任何组织在设计一套系统(广义概念的系统)时,所交付的设计方案在结构上都与该组织的沟通结构保持一致。

组织的耦合度越低,创建的系统模块化就越好,否则越差。

11 规模化微服务

11.1 故障无处不在

从统计学看,规模化后故障成为必然事件。 假设一切都会失败,会让你从不同的角度去思考如何解决问题。

考虑是否扩展系统,首先尝试理解一下需求:

  • 响应时间/延迟
  • 可用性
  • 数据持久性

11.3 功能降级

如果这个微服务宕了会发生什么?

11.4 架构性安全措施

一个无法控制的下游服务,可以让整个系统宕掉。

11.5 反脆弱的组织

混乱猴子,故意在系统中做破坏

做什么来应对系统故障呢?

  • 超时。给所有的跨进程调用设置超时,并选择一个默认的超时时间。记录日志并响应调整
  • 断路器。对下游资源请求发生一定量的失败后,断路器会打开。接下来所有的请求在断路器打开的状态下,会快速地失败。 一段时间后,客户端发送一些请求查看服务是否恢复,如果正常响应了,重置断路器。 Hystrix 基于 JVM 的断路器,附带监控
  • 舱壁(bulkhead):关注点分离。每个下游服务使用不同的连接池
  • 隔离,允许下游服务离线
  • 幂等, 多次执行产生的影响和一次执行的影响相同

11.7 扩展

  • 更强大的主机(垂直扩展);
  • 拆分负载;
  • 分散风险;
  • 负载均衡。aws elbs(elastci load balancers)
  • 基于 worker 的系统。
  • 重新设计服务

11.8 扩展数据库

11.8.1 服务的可用性和数据的持久性

数据库副本

11.8.2 扩展读取

使用只读副本。最终一致性

11.8.3 扩展写操作

通过哈希写分片。

11.8.4 共享数据库基础设施

11.9 缓存

11.9.1 客户端、代理和服务器缓存

客户端:减少网络调用,减少下游负载。但是改变缓存方式和失效数据比较棘手

代理服务器:squid 或varnish。增加网络跳数(network hops),但是相比性能优化来说可忽略

服务器:适合多客户端。追踪和统计命中率

11.9.2 HTTP 缓存

cache-control: 告诉客户端是否缓存资源,缓存几秒。

Expires: 指定改日知和时间后被认为失效

ETag: 标识资源是否已改变

《Rest 实战》

11.9.3 为写使用缓存

使用 writebehind 方式使用缓存

11.9.4 为弹性使用缓存

可以使用缓存中的数据保证某些场景服务不会宕机(但是数据可能过时,总比挂了好)

11.9.5 隐藏源服务

让请求快速失败,确保不占用资源或增加延迟,给下游服务恢复机会

11.9.6 保持简单

先保持简单,使用缓存之前慎重考虑

11.11 CAP 定理

  • 一致性(consistency):访问多个节点能得到同样的值
  • 可用性(availability): 每个请求都能获得响应
  • 分区容忍性(partition tolerance): 集群中某些节点失联后,集群整体还能继续进行服务的能力

考虑使用双主数据库来进行数据库同步,加入两个库之间断开了:

  • 牺牲一致性。AP 系统。最终一致性,用户可能看到失效的数据
  • 牺牲可用性。CP系统。为了让数据一致,拒绝用户请求。保证多个节点一致性很难。
  • 牺牲分区容忍性。不存在,除非单独的进程不跨越网络

需要权衡使用 AP 还是 CP。构建 AP 比 CP 很容易一些。

11.12 服务发现

常见的服务发现解决方案:

  • DNS

11.13 动态服务注册

  • Zookeeper
  • Consul:支持配置管理和服务发现
  • Eureka(netflix):

11.14 文档服务

  • Swagger
  • HAL和 HAL 浏览器。hypertext application language

12 总结

12.1 微服务原则

微服务(自治的小服务)的原则:

  • 围绕业务概念建模: 使用限界界上下文
  • 自动化: 持续交付
  • 隐藏内部实现细节:隐藏数据库。使用技术无关的 api,比如 rest/rpc
  • 一切都去中心化: 团队保持对服务的所有权;内部开源模式;康威定律;内聚性
  • 可独立部署: 你的消费者应该自己决定何时更新
  • 隔离失败: 超时;断路;舱壁。CAP
  • 高度可观察: 聚合日志和数据

12.2 什么时候不该使用微服务?

先花时间了解系统做什么的,尝试识别清晰的模块边界。

首先构建单块系统,稳定之后再考虑拆分

花时间构建工具和实践,管理微服务。