Map 1
微服务设计
  Ch01: 微服务
    什么是微服务
      很小，专注好做一件事
        构建服务时，内聚性很重要
        单一职责原则：把因相同原因而变化的东西聚合到一起，把因不同原因而变化的东西分离开来
        服务越小，独立性的好处越多，但管理大量服务也会越复杂
      自治性
        能独立部署
        通过网络通信
        能独立修改（不对调用方产生影响）
    主要好处
      技术异构性
      弹性
        舱壁，一个组件失效不会影响其它部分
      扩展
        粒度更细
      简化部署
        局部更新（C端常用）
        快速迭代，容易回滚
      与组织结构相匹配
      可组合性
        利于重用已有功能
      对可替代性的优化
        （可以重写小服务）
    面向服务的架构
      Service-Oriented Architecture(SOA)
      可以认为微服务架构是 SOA 的一种特定方法
    其它分解技术
      微服务两个优势
        较小的粒度
        解决问题方法上的更多选择
      共享库
        无法选择异构的技术
        无法保证弹性
        个人观点：共享库通常业务信息太浓
      模块
        OSGI：语言对生命周期管理支持不足，模块作者工作很大，进程内模块间耦合太重
        Erlang 在语言层面内支持模块，很强大。但还是无法采用异构的技术，局限性
        模块在实际中会迅速耦合（强烈同意）
      没有银弹
  Ch02: 演化式架构师
    不准确的比较
      Architect 这个词是有问题的
      建筑领域内，是详细的计划和按部就班的实施
      但在软件领域内，这是不可能的
    架构师的演化视角
      大量的需求变更
      使用的工具、技术多样
      发布之后依然在变化
      类比城市规划师，应当聚焦于规划，保证系统适合开发人员在其上工作
    分区
      服务边界（对比于城市里的区域）
      应该考虑服务间如何交互，较少关心服务内
    一个原则性方法
      战略目标
        通常我们不需要去定义战略目标
      原则
        为了与目标保持一致，制定一些规则，称为原则
        一般不要超过 10 个
        区分“原则”和“约束”
      实践
        通过实践来保证原则能得到实施
        通常实践是技术相关的、偏底层的
        实践应该巩固原则
      将原则和实践结合
        有时原则和实际会混淆
        重要的是需要有原则指导系统的演化
      Real World Example
    要求的标准
      监控
        能够清楚地描绘出跨服务系统的健康状态非常关键
        必须在系统级别而非单个服务级别进行考虑
        建议所有服务使用同样的方法报告健康状态
        Subtopic 4
      接口
        选用几种少数明确的接口技术有助于新消费者集成
        使用一种或两种接口技术作为标准，不要多
      架构安全性
        必须保证每个服务都可以应对下游服务的错误
        返回码也应该遵守一定规则
        处理不同的请求
          正常且正确处理
          错误请求且被系统识别
          被访问的服务宕机，无法判断请求是否正常
    代码治理
      如何保证共识？
      范例
        理想情况下，范例应该来自真实项目
      裁剪服务代码模板
        即新服务的核心属性最好是一致、现成的
        对标：maven 的 architecture
        注意：服务模板不是中心化的职责，也不是指导
        重用代码有危险，可能会引入服务间的耦合
    技术债务
      有时可能会因为紧急发布某些特性而忽略一些约束，但最终是需要还债的
      架构师需要从更高的层次出发，理解如何做权衡
      理解债务的层次及对系统的影响
    例外管理
      多次对原则的偏离可能意味着需要修改原则
    集中治理和领导
      治理通过评估干系人的需求、当前情况及下一步的可能性来确保企业目标的达成，通过排优先级和做决策来设定方向。对于已经达成一致的方向和目标进行监督
      架构师不应该独自做这些事
      有时架构师不认同小组的决定，最好尊重他们
    建设团队
      重要的是帮助你的队友成长
    小结：架构师的职责
      愿景：确保系统有一个充分沟通的技术愿景，可能帮助满足客户和组织的需求
      同理心：理解你的决定对客户和同事的影响
      合作：和尽量多的同事沟通，更好地对愿景进行定义、修订和执行。
      适应性：确保在你的客户和组织需求的时候调整技术愿景
      自治性：在标准化和团队自治之间寻找一个正确的平衡点
      治理：确保系统按照技术愿景实现
  Ch03: 如何建模服务
    什么样的服务是好服务
      低耦合+高内聚
      低耦合
        修改一个服务不需要修改另一个服务
        尽可能少地知道其它服务的信息
      高内聚
        把相关的行为聚在一起，不相关的放在别处
        最好能只在一个地方进行修改，就可以尽快发布
    限界上下文
      一个由显式边界限定的特定职责，类比“细胞”
      共享的隐藏模型
        同一个名字在不同的上下文中有完全不同的含义
        应当共享特定模型，不应该共享内部表示
      模块和服务
        一旦发现领域内的限界上下文，一定要用模块对其进行建模，同时共享和隐藏模型
        一般来说，服务应该和限界上下文保持一致
      过早划分
        很多时候，将一个已有代码库划分成微服务，比重头构建要简单得多
    业务功能
      限界上下文应从业务功能出发，而非共享数据
    逐步划分上下文
      嵌套上下文 vs 完全分离？
      也许应该根据组织结构决定
      嵌套的另一个好处是方便成块测试
    关于业务概念的沟通
    技术边界
      边界划分时要考虑组织结构（如地理位置）
  Ch04: 集成
    寻找理想的集成技术
      SOAP? XML-RPC? REST? Protocol Buffers?
      避免破坏性修改
        比如在响应中添加一个字段，已有的消费方不应该受到影响
      保证 API 的技术无关性
        用以兼容未来的技术
      隐藏内部的实现细节
    共享数据库
      最快的集成方式
      暴露了内部实现，后续修改困难
      消费方与特定技术绑定
        如果今后要使用 NoSQL?
      行为上，如果隐藏修改的 API？
        修改的逻辑可能散落在各个地方，破坏内聚性
    同步与异步
      同步一般基于请求/响应
      异步一般基于事件
    编排与协同
      如何处理跨服务业务流程的逻辑
      编排(orchestration)：中心化管理
        相当于有一个统一的协调者
        比如规则引擎（商业流程建模软件）
        缺点：编排的任务过于重大
      协同(chereography)：各自为政
        只触发事件，各方作出各自的响应
        缺点：缺少明确的流程视图
        需要额外工作来监控流程是否正确执行
        总评：能降耦合，但需要跨服务监控，微服务下优先考虑
    远程过程调用(RPC)
      技术的耦合
        耦合的技术方便使用，解耦的技术方便扩展
        如 Java RMI 只能在 Java 中使用，方便但限定技术栈
        如 Thrift, protocol buffers 能对接各种语言，但使用过程比较繁琐
      本地调用与远程调用并不相同
        RPC 的核心是隐藏远程调用的复杂性
        封装得太好会导致人们忽略了性能、可靠性
      脆弱性
        如 Java RMI，添加接口的新方法需要修改所有客户端
        如 Java RMI，仅在服务端删除某个字段会导致序列化和反序列化逻辑不一致，从而导致服务失败
      RPC 很糟糕吗？
        如果决定使用 RPC，注意：不要对远程调用过度抽象
    REST
      REST 和 HTTP
        它们在一起会更强
      Hypermedia As The Engine Of Application State(HATEOAS)
        避免客户端和服务端耦合的一个原则
        如 Amazon，用户点击购物车，这是一种隐式约定，即使购物车的真实地址变化，用户还是能找到
        对电子用户而言，我们希望它总是从“商品”找到购买链接，尽管链接会变化，商品本身不变
        缺点是：通信次数比较多
        实践中，可以先让用户自行遍历，有必要时再优化
      JSON、XML 及其它
      留心过多的约定
        不要直接暴露内部的对象（转成 JSON）
      基于 HTTP 的 REST 的缺点
        易用性：无法生成客户端的 stub 代码
        有些 Web 框架无法很好地支持所有的 HTTP 动词
        性能上：性能不如二进制协议（如 Thrift），不适用于低延迟的通信
    实现基于事件的异步协作方式
      技术选择
        发布事件和接收事件的机制
        如 RabbitMQ 的消费代理
          能够处理发布和接收的问题
          同时还能对消息进行管理追踪
          代价是增加开发复杂度，因为需要额外中间件
          尽量保持中间件简单，把业务逻辑放在自己的服务里
        通过 HTTP 传播事件
          例如 ATOM 协议（feed)
          有事件时发布到该聚合上，消费者会轮询
      异步架构的复杂性
        复杂性，不仅来源于发布、订阅操作
        银行系统的示例
          如何处理消费者故障？
          如何处理失败消息？最大重试次数？死信队列？
          简评：异步架构看似各自为政，但会隐藏一些全局的需求。这些需求恰恰是复杂性的来源
    服务即状态机
      将领域的生命周期显示建模
      不仅是对 CRUD，也对状态转换封装一些行为
    响应式扩展(Reactive Extensions, Rx)
      组装一个或多个异步调用，简化代码
      简评：异步是坑，Think Twice
    微服务中的 DRY 和代码重用的危险
      个人认为 DRY 抽取重复代码是增加内聚性的过程，不需要修改多个地方
      共享代码会导致微服务间的耦合，一处修改会影响多个服务
      经验法则：微服务内 DRY，跨服务适当违反
    按引用访问
      领域实体的生命周期应当只在某个服务内管理
      如果保存本地副本，应该保留原始实体的引用，方便后续查询，更新
      需要知道事件是否发生，还需要知道发生了什么
      具体是否引用要看场景，是否数据切片能满足要求
    版本管理
      尽可能推迟
        采用一些耦合小的技术（如 REST 而非数据库集成）
        鼓励客户端的正确行为
          宽进严出（对自己发送的东西要严格，对接收的东西要宽容）
      及早发现破坏性修改
      使用语义化的版本管理
        MAJOR.MINOR.PATCH
        个人觉得并没有什么用
      不同的接口共存
        平滑过渡
      同时使用多个版本的服务
        坏处：老版本永远不会升级
    用户界面
      是否轻界面，重后端
      走向数字化
        API 的粒度，细 vs 粗
      约束
        如移动端的带宽、电量等的影响
      API 组合（让 UI 直接访问 API）
        不同的设备可能需要不同的数据，一个解决方案是允许客户选择字段（如 GraphQL?）
        谁来创建 UI？如果是不同团队，会回到分层合并的模式，则细微的修改都需要多个团队参与
      UI 版本的组合：服务直接暴露部分 UI，上层应用对其进行组合
      为前端服务的后端：即增加一层代理，将细粒度 API 拼接成粗粒度，提供给前端
        问题是最终代理层会变得巨大无比
      混合方式
    与第三方软件集成
      缺乏控制
      定制化
        一般购买的定制化成本很大
      在自己可控的平台上进行定制化
        把三方软件当成服务，以此为基础搭建自己的系统
      绞杀者模式
        拦截对三方系统的应用，路由部分到新系统
  Ch05: 分解单块系统
    关键是接缝
      从接缝处抽取相对独立的代码，对其修改不影响其它部分
      尝试找到服务的边界
    分解单块系统的原因
      改变的速度
        抽成服务，加速后期开发
      团队结构
      安全
      技术
        可以用其它的技术栈
    杂乱的依赖
      经常会发现：数据库是所有杂乱依赖的源头
    找到问题的关键
      即找到服务对数据库的交叉使用
    例子：打破外键关系
      把外键变成服务调用
    例子：共享静态数据
      例如：国家代码等数据
      方法一：为每个包复制一份
      方法二：静态数据放入代码（属性文件、枚举）
      方法三：静态数据作为单独服务
    例子：共享（可变）数据
      原因：领域概念在数据库中隐式建模
      示例中可以将这个对数据的交叉抽离成新的服务
    例子：共享表
      如：Catalog 中的条目与电子记录的条目存放在通用条目表
      方法：拆成两个表
    重构数据库
      推荐在分离服务前先分离数据库结构
      逐步进行：单块服务+单表 -> 单块服务 + 分离表 -> 分离服务+分离表
    事务边界
      Retry Later
        最终一致性
      Abort
        补偿事务抵消之前的操作
        多个服务下如何处理？
      分布式事务
        2PC
      太复杂怎么办？
        业务是否需要一致？ double check
        能否从业务上做处理？而不是技术
    分离数据库后如何做报告
      报告生成需要跨数据库/表访问
      定期同步到报告数据库
        表结构修改怎么办
      通过服务调用获取数据
        只适用于少量数据，长时间同期的数据量太大
        批量 API 将结果写入文件
      推送数据到报告系统（ELK）
        直接对数据库操作
      导出事件数据
        低耦合
        比较难应对大数据量
      数据导出的备份
        持久化数据备份+Hadoop
    修改的代价
      修改的量越大风险越大
      在影响最小的地方犯错误（白板）
  Ch06: 部署
    持续集成简介
      是否每天合并代码到主线？
      是否有测试来验证修改？
      构建失败后，修复 CI 是否是团队的头等大事？
    CI 应用到微服务
      单仓库、单构建
        问题是一次性部署多个服务
        粒度太粗，影响 CI 周期，不易定位发生修改的服务
      单仓库、多构建
        缺点：代码粒度太粗，一个项目小修改 CI 的失败会卡其它项目的 CI
      多仓库、多构建
    pipeline 与 CI
      评：pipeline 可以将测试粒度变细，fail fast
      例外：团队刚开始的时候应该先用单仓库、单构建
        开始时不容易分辨边界
        开始时容易有跨服务的修改
        但只能是过渡
    Artifact
      Platform specific Artifact
        Ruby gem, Java jar/war, Python egg
        构建物可能不够，还需要中间件和其它配置
        考虑用 puppet, chef, ansible 管理
      OS specific artifact
        rpm, deb, MSI
        好处多多，写构建脚本比较困难，且平台支持不一
      定制化镜像
        构建镜像会花费大量时间
        镜像可能很大
      将镜像作为 artifact
      Immutable Server
        配置漂移：如果有人登录服务器修改了配置呢？
    环境
      耗时测试、UAT、性能测试、生产
      环境要保持一致，否则会有预期之外的情况
    服务配置
      尽量各环境的配置保持一致
    服务与主机的映射
      “每台机器应该有多少服务”？
      单主机、多服务
        主机管理工作量小
        管理工作量不随服务增长而增长
        监控困难
        服务部署也会更复杂，难以保证服务不相互影响
        不利于团队自治性
        虚拟技术的发展，不太应该继续这样
      应用程序容器(tomcat, glassfish,...)
      每个主机一个服务
      PaaS
        如果出错比较难排查
        一些特定的要求比较难满足
    自动化
      自动化管理是微服务的必经之路
    从物理机到虚拟机
      传统的虚拟化技术
        一般不做，虚拟化会有额外的开销
      Vagrant
        开发机上资源消耗大
      Linux 容器
        如 LXC
        更轻量
        并不是真正的隔离
      Docker
        构建于容器之上的平台
    一个部署接口
      最佳实践是有一个统一的接口来部署服务
      服务的名字、版本、哪个环境
  Ch07: 测试
    测试类型
      单元测试（是否正确实现功能）
      非功能性测试（响应、扩展、性能、安全）
      验收测试（是否实现了正确的功能）
      探索性测试（如何破坏系统）
    测试范围（金字塔）
      用户界面
        范围最大、信心最足、最难定位
      服务测试
        绕开界面，直接对服务测试
        测试单独的服务可以提高测试的隔离性
        为了达到要求，需要 stub
      单元测试
        TDD
        不会启动服务
        外部文件、网络连接访问有限
        对重构非常重要
      比例
        经验：下层比上层多一个数量级
    实现服务测试
      Mock vs Stub?
        这两个概念可能跟我们平时的用法不一样
        这里 stub 无副作用，mock 有
        stub 用得多
    端到端测试
      微秒的端到端测试
        界面依赖多个服务，当前服务依赖的其它服务也有新版本，测试时要用什么组合？
        不同服务的测试要重合，要重复测试吗？
        一种方法是让每个服务“扇入”到端到端测试
      脆弱的测试
        存在一些非功能错误无法识别（如底层服务未响应）
        谁来写测试？
        测试多长时间？
        测试量大大，可能产生大量堆积
        元版本
          会产生为所有服务给一个统一的版本号
          这样就回到了单块架构
      测试场景、而不是故事
        核心端到端，其余在服务测试中覆盖
      消费者驱动的测试
        端到端测试解决问题：修改不破坏消费者
        另一种方式：CDC(Consumer-Driven Contract 消费者驱动的契约)
        即定义消费者的期望，并转化成测试代码，进入 CI pipeline
        Pact: 一个 CDC 测试工具
        CDC 与 story 一样有助于沟通
      还要做端到端测试吗？
        评：在业务还在强烈变化时，个人认为还是需要的，优化点在于有多少可以转移成 CDC
    部署后再测试
      区分部署和上线
        部署后不一定立马切流量，切流量才是上线
      Canary Releasing
        小流量测试
      平均修复时间 胜于 平均故障时间
        平均故障间隔时间 (Mean Time Between Failures, MTBF)
        平均修复时间 (Mean Time To Prepare, MTTP)
        MTBF 是指两次故障之间正常工作时间的均值，需要靠更多的测试，而 MTTP 指出现故障平均花多长时间修复。这两者是需要平衡的
    非功能性需求（性能、用户、稳定……）
      频率可能更小，但一定要做，不能拖到上生产
  Ch08: 监控
    单一服务、单一服务器
      主机本身：CPU、内存等主机数据
        如 Nagios 工具或 New Relic 托管服务
      服务器本身的日志
        rogrotate 移除旧日志
    单一服务、多服务器
      应用场景：负载均衡
      除了查看所有主机的数据，也需要查看单个主机的数据
      如果只有几个主机，可以使用 ssh-multiplexers
      响应时间可以看负载均衡器中的聚合数据
    多个服务、多个服务器
      是系统问题？服务问题？是哪个服务？
      回答：集中收集和聚合尽可能多的数据
    日志、日志、更多的日志
      新需求：日志收集子系统
      例如 logstash、Kibana
    多个服务的指标跟踪
      有了日志后，最好能生成指标方便追踪
      Graphite 系统
        接收指标并展示
        通过有效配置，聚合数据减少存储容量
        跨样本做聚合
    服务指标
      collectd 生成操作系统的大量指标
      Nginx, Varnish 支撑系统也会暴露有用的信息
      自己服务的指标呢？
        Metrics 库
    综合监控
      Naive 的监控：设定一个标准值，超过报警
      示例：定期插入假事件，并判断是否被处理，更贴近最终的监控需求
      合成事务执行语义监控，比使用低层指标更能表明问题
      在生产系统上执行端到端测试（保证无副作用）
    关联标识
      用 GUID 给请求打标识，并传递给后续所有系统
      Zipkin
      使用包装的客户端确保信息不会丢失
    级联
      上游需要确认下游服务的健康状态并打日志
      添加断路器更加优雅地处理
    标准化
      以标准的方式暴露监控接口、落日志
  Ch09: 安全
    身份验证和授权
      人或事，抽象为“主体”(Principle)
      常见的单点登录（SSO）实现
        SAML, OpenID Connect
        OAuth 2.0
      单点登录网关
        网关接收请求，如果已授权，则将主体信息放在 HTTP Header 信息中，如果未授权，则由网关进行授权
        孤立地在微服务中定位问题会更难（包括生产环境的搭建）
        虚假的安全感：如果网关服务故障了……
        小心网关变成耦合点
      细粒度的授权
        细粒度的 Authorization 应该留给具体服务
    服务间的身份验证和授权
      在边界内允许一切
        风险：当有人入侵网络后，系统对中间人攻击没有任何防备
        许多组织使用，但其实有许多风险，更糟糕的是，很多时候人们没有意识到风险
      HTTP(s) Basic Authentication
        在 HEADER 中指定用户名密码
        HTTP 会被中间人截取明文
        HTTPS 的证书管理复杂（尤其是多台机器）
        流量无法被反向代理缓存，但可以在反向代理时转成 HTTP 流量
        如何和现有 SSO 集成？
      SAML 或 OpenID Connect
        同一个网关来路由内部流量
        微服务的客户端有一组凭证，用于验证自身，服务获取所需信息，用于细粒度验证
        需要为每个微服务创建自己的凭证，可撤销
        缺点：需要客户端安全地存储凭证
      客户端证书
        每个客户端存储一个 X.509 证书，与服务端建立通信
        缺点：管理证书的工作很繁重
      HTTP 上的 HMAC
        防止密码泄露
        即传输的是哈希后的内容，服务端独立算哈希
        好处 1 ：如果哈希不匹配，则得知中间人篡改过
        好处 2: 密钥不泄露
        好处 2: 开销低于 HTTPS
        缺点 1: 需要共享密钥，不好撤销
        缺点 2: 它是一个模式，不是标准，实现不一
        缺点 3: 它只保证密码不泄露，其它信息依旧可能被嗅探
      API 密钥 (token)
        具体如何使用取决于使用的具体技术
        常见方法：使用公钥私钥对，并集中管理
        该方法关注了程序的易用性
      代理问题
        服务提供方是允许代理方访问服务？
          混淆代理人问题：欺诈代理人去访问本不该是自己能访问的服务。服务提供方是否
        一种方法是让代理/路由做验证
        另一种方法：请求时传递主体凭证
        没有简单的答案
    静态数据的安全
      防止网络被攻破，静态数据被窃取
      使用众所周知的加密方法
        AES
        密码哈希加盐
      一切皆与密钥相关
        密码不应和密文存储在一起
        方案一：单独的安全设备来加密和解密
        方案二：单独的密钥库
      选择你的目标
        考虑哪些数据可以被放入日志，帮助识别哪些数据需要加密
      按需解密
      加密备份
        备份加密数据
        确保备份也被加密
    深度防御
      防火墙
      日志
        可以检测异常、之后恢复
        要小心在日志中存储敏感信息
      入侵检测（和预防）系统
      网络隔离
      操作系统
    保持节俭
      有些信息是不需要存储的
    黄金法则
      不要自己实现加密算法
    内建安全
      相应的安全意识、流程
      自动化工具探测系统漏洞
    外部验证
      开发人员离问题太近导致可能发现不了问题
  Ch10: 康威定律和系统设计
    康威定律
      任何组织在设计一套系统（广义）时，所交付的设计方案在结构上都与该组织的沟通结构保持一致
      “如果你有四个小组开发一个编译器，那你会得到一个四步编译器”
    证据
      松耦合组织和紧耦合组织
        紧耦合组织：商业产品公司，所有员工一起工作
        松耦合组织：开源社区
        组织的耦合度越低，模块化越好，耦合度越低
      Windows Vista
        组织结构相关的指标与软件质量的相关度最高
      Netflix 和 Amazon
        “两个比萨团队”，没有团队应该大到两个比萨不够吃
    适应沟通途径
      当协调成本增加后，人们要么设法降低成本，要么停止更改数据
    服务所有权
      所有权程度的增加会增加自治和交付速度
    共享服务的原因
      难以分割
        拆分成本太高
      特性团队
        小团队负责一系列我需要的所有功能，即使功能跨组件（甚至服务）
        大范围采用特性团队后，所有服务都是共享的
        微服务根据业务领域划分而非技术，更容易进行以特性为导向的开发
      交付瓶颈
        如果某个服务突然出现大量的变量需求？
    内部开源
      守护者角色
        核心团队+不受信任的提交者
      成熟
        往往 1.0 后才接受外部提交
      工具
        版本控制、CI、CD
    限界上下文和团队结构
      可能的话，团队结构和限界上下文保持一致
    孤儿服务
      长时间不需要更改的服务
    反向的康威定律
      系统设计能改变组织结构吗？
    人
      “不管一开始看起来是什么样，它永远是人的问题”
  Ch11: 规模化微服务
    故障无处不在
      大规模后的故障是必然发生的
      即使买最好的工具、最昂贵的硬件也必然发生
      少花些精力在阻止故障发生，而想办法从故障中恢复
    多少是太多
      系统的非功能性需求取决于用户
        如内部系统的自动扩容可能无意义
      理解以下需求
        响应时间/延迟
          如：200 并发下，90%的响应在 2s 内
        可用性
          能接受服务出现故障吗？
        数据持久性
          多大比较的数据丢失是可以接受的
          数据应该保存多久
    功能降级
      依赖多个服务时，不能因为一个系统宕机导致整个调用链都失败（否则可用性和单块系统没区别）
      具体的决策依赖业务
        有些服务是致命的，有些则是可以绕过的
    架构性安全措施
      目标：确保如果真的出错，不会引起严重的级联影响
      处理慢下游比处理宕机下游要难得多
    反脆弱的组织
      Netflix 通过引发故障来确保系统的容错
      Chaos Monkey
        随机停掉服务器或机器（生产环境）
      拥抱故障
      如何应对？
        超时
          给所有跨进程的调用都加上超时
        断路器
          被动 & 手动
          被动断路器依赖于业务定义
        舱壁
          必要情况下可以“自闭”
          例如为每个下游服务的连接使用不同连接池
          例如将功能分离成独立的微服务
          可以把断路器作为密封舱壁的自动机制
        隔离
          在实现上游时，允许下游服务离线
    幂等
      多次调用的效果与一次调用相同
      实际要完全实现幂等挺困难
        如需要保存已经处理的事件
        多个 worker 可能在窗口内会处理多个事件
    扩展(scale?)
      防止单点故障，负载加强性能
      更强大的主机（垂直扩展）
      拆分负载
        之前在单机上的微服务拆分到多机上
      分散风险
        “两地三机房”
      负载均衡
        避免单点故障
        SSL 终止
      基于 worker 的系统
        即任务提交到队列，由 worker 运行
        woker 本身不需要很高的可靠性，但任务队列需求
      重新设计
        最初的架构可能无法应对很大负载
    扩展数据库
      （单点）瓶颈到了数据库上
      区分“可用性”和“数据的持久性”
        例如对数据库备份，数据不丢，但服务不可用
      扩展读取
        只读复本（如 MySQL 主从）
        最终一致性
      扩展写操作
        分片
          分片如何处理跨分片查询？
          如何添加新节点？
          写入分片可能会扩展写容量，但不会提高弹性
          一致性哈希
      共享数据库基础设施
      CQRS
        读写分离
        Event Sourcing
    缓存
      HTTP 协议本身允许缓存(GET)
      客户端、代理、服务器缓存
      HTTP 缓存(cache)
        Cache-control + Expires
          一般适用于静态资源
        ETag
      写缓存(buffer)
        writebehind 缓存
          类比操作系统写入磁盘前缓存在内存
      为弹性使用缓存(buffer)
        下游失效时先缓存请求
      隐藏源服务
        缓存雪崩，缓存服务宕机，导致大量请求打到源服务
        一种方法：第一时间不要对源服务发起请求
        源服务定期填写缓存，客户端在 miss 时发布通知
      保持简单
        不需要不要用
      缓存可能中毒
    自动伸缩
      响应型伸缩
      预测型伸缩
      事实上比起响应负载，更多应用于响应故障
    CAP
      CP 还是 AP？P 是一定需要的
      对于具体的服务，要求可能不同
    服务发现
      DNS
        标准
        更新很痛苦（TTL）
        DNS 指向负载均衡
      动态服务注册
        ZK
        Consul
        Eureka
      自己构建
      别忘了人的需求（查看、监控）
    文档服务
      API 文档
      Swagger
      HAL 和 HAL 浏览器
      自描述系统（UDDI)
  Ch12: 总结
    微服务原则
      围绕业务概念建模
      接受自动化文化
      隐藏内部实现细节
      一切去中心化
      可独立部署（不影响当前服务）
      隔离失败
        不要像使用本地调用那样处理远程调用
        反脆弱信条
      高度可观察
        日志、监控
    什么时候不使用
      不了解领域，无法很好做拆分
      基础设施不完善前
感觉是对许多选择做了枚举，分析一些优劣，但这些优劣其实也并不是绝对的，总感觉对现在的我还说还不太适合
总体感觉是点到为止
如原则：开发团队应该对软件的开发全流程有控制权
则对应实践：所有的服务部署在不同的 AWS 帐户中，从而提供资源的自助管理和其它团队的资源隔离
如 HTTP/REST 是实践还是原则？
在考虑取舍时，要考虑：系统允许多少可变性？
如何在优化单个服务自治性的同时，兼顾全局？
一种方法是：清楚地定义出一个好服务应用的属性
换句话说，有什么是系统的所有服务要统一遵守的？
JPA 也是类似，相比于 Mybatis 封装性更好，更易用，但是也容易过度使用导致性能问题
服务怎么拆？拆了怎么办？
源头是业务，找到上下文的边界。
拆的过程中很可能最终发现数据库层面的耦合。
拆成服务后可能会遇到分布式事务的要求
还有一些跨数据库（报告）的需求和方法。
这个概念和之前的理解有出入
小结：要做监控，要标准化做法
手段：
传递唯一标识
最低限度：服务的响应时间
最你限度：下游的健康状态
在现场交付时，需要先量化这些指标