《微服务设计》读书笔记(下)

第七章:测试

1. 测试类型

  • 面向业务
  • 面向技术
  • 评估产品
  • 支持团队

在这里插入图片描述

  趋势:放弃大规模的手工测试,尽可能多地使用自动化是近年来业界的一种趋势。

2. 测试范围

  “测试金字塔”模型:将自动化测试划分为单元测试、服务测试和用户界面测试三层。

在这里插入图片描述

2.1 单元测试

  单元测试通常只测试一个函数和方法调用。

  • TDD:测试驱动开发
      通过TDD (Test-Driven Design,测试驱动开发)写的测试就属于这一类,由基于属性的测试技术所产生的测试也属于这一类。在单元测试中,我们不会启动服务,并且对外部文件和网络连接的使用也很有限。通常情况下你需要大量的单元测试。如果做得合理,它们运行起来会非常非常快,在现代硬件环境上运行上千个这种测试,可能连一分钟都不需要。

  • 单元测试目的:
      能够对于功能是否正常快速给出反馈。单元测试对于代码重构非常重要,如果不小心犯了错误,这些小范围的测试能很快做出提醒,这样我们就可以放心地随时调整代码。

2.2 服务测试

  服务测试是绕开用户界面、直接针对服务的测试。

服务测试范围:
  在独立应用程序中,服务测试可能只测试为用户界面提供服务的一些类。对于包含多个服务的系统,一个服务测试只测试其中一个单独服务的功能。

服务测试目的:
  只测试一个单独的服务可以提高测试的隔离性,这样我们就可以更快地定位并解决问题。

2.3 端到端测试

  端到端测试会覆盖整个系统。

测试方法:
  这类测试通常需要打开一个浏览器来操作图形用户界面(GUI),也很容易模仿类似上传文件这样的用户交互。

2.4 权衡

  金字塔模型,靠近金字塔的顶端测试特点:

  1. 测试覆盖的范围越大,我们对被测试后的功能也越有信心
  2. 反馈周期会变长;
  3. 难以定位失败原因;

金字塔模型,靠近金字塔的底部测试特点:

  1. 测试快,测试周期短;
  2. 容易定位失败原因;
  3. 持续集成的构建时间短;
  4. 测试通过并不能代表整个服务就能正常运行;
2.5 比例

  一个好的经验法则是,顺着金字塔向下,下面一层的测试数量要比上面一层多一个数量级。如果当前的权衡确实给你带来了问题,那可以尝试调整不同类型自动化测试的比例。

3. 实现服务测试

  单元测试比较简单,服务测试和端到端测试的实现则要复杂得多。
  服务测试只想要测试一个单独服务的功能,为了隔离其他的相关服务,需要一种方法给所有的外部合作者打桩。
  对于每一位下游合作者,我们都需要一个打桩服务,然后在运行服务测试的时候启动它们(或者确保它们正常运行)。我们还需要配置被测服务,在测试过程中连接这些打桩服务。接着,为了模仿真实的服务,我们需要配置打桩服务为被测服务的请求发回响应。

4. 端到端测试缺点

4.1 脆弱的测试

  随着测试范围的扩大,测试中的服务数量越多,测试流程自身失败可能性增大,测试就会越脆弱,不确定性也就越强。

4.2 谁来写测试
4.2.1 方法一:所有团队成员都可以在无须对测试套件质量有任何理解的情况下随意添加测试。

存在的问题:

  1. 测试用例爆炸;
  2. 因为测试没有真正的拥有者,所以它们的结果会被忽略;
  3. 大家不在乎测试是否通过;
4.2.2 方法二:专门的团队来写这些测试。

存在的问题:

  1. 开发软件的人渐渐远离测试代码,反馈周期时间会变长;
  2. 编写测试的团队,难以了解系统运行细节;
4.2.3 解决方法
  1. 共享端到端测试套件的代码权,但同时对测试套件联合负责。
  2. 团队可以随意提交测试到这个套件,但实现服务的团队必须全都负责维护套件的健康。
4.3 测试多长时间

  测试用例规模大,运行缓慢和脆弱性,造成测试时间延长。
  并行运行测试可以改善缓慢的问题,但不能代替去真正了解什么需要被测试,以及哪些不必要的测试可以被删掉。
  删除测试,风险难以理解。
  现象:很少能见到有人能够精细地对大范围、高负担的测试进行管理和维护。

5. 提交堆积

  端到端测试的反馈周期过长,失败的修复周期也都会变长,减少了端到端测试通过的次数,导致大量的提交堆积。
  解决这个问题的个方法是:端到端测试失败后禁止提交代码,但考虑到测试套件的运行时间过长,这个要求通常是不切实际的。
  部署的变更内容越多,发布的风险就会越高,我们就越有可能破坏一些功能。保障频繁发布软件的关键是基于这样的一个想法:尽可能频繁地发布小范围的改变。

6. 场景测试

  随着服务增多,测试套件会变得非常廉肿,测试场景甚至可能会出现笛卡儿积式的爆炸。
  解决这个问题的最佳方法是,把测试整个系统的重心放到少量核心的场景上来。
  通过专注于少量的测试,我们可以缓解端到端测试的缺点,但并不能避免所有的缺点。

7. 消费者驱动测试

  使用之前所提到的端到端测试,我们试图解决的关键问题是什么?是试图确保部署新的服务到生产环境后,变更不会破坏新服务的消费者。有一种不需要使用真正的消费者也能达到同样目的的方式,它就是CDC (Consumer-Driven Contract,消费者驱动的契约)。

特点:

  1. 定义服务(或生产者)的消费者的期望,这些期望最终会变成对生产者运行的测试代码。
  2. 使用得当,这些CDC应该成为生产者CI流水线的一部分;
  3. 如果这些契约被破坏了的话,生产者就无法部署;
  4. 测试更快,也更可靠。
    消费者契约的测试集成到测试金字塔:

在这里插入图片描述

7.1 测试工具 Pact
  1. 消费者使用Ruby DSL来定义生产者的期望;
  2. 启动一个本地mock服务器,并对其运行期望来生成Pact规范文件;

8. 部署后再测试

  需要部署后再测试原因:仅仅依靠部署之前进行的测试,不能把缺陷率降低为零。

8.1 金丝雀发布

  金丝雀发布是指:将部分生产流量引流到新部署的系统,来验证系统是否按预期执行。

9. 跨功能的测试

  非功能性需求,是对系统展现的一些特性的一个总括的术语,这些特性不能像普通的特性那样简单实现。

10. 性能测试

10.1 需要性能测试原因:
  1. 追踪延迟根源,系统拆分为较小的微服务后,跨网络边界调用的次数明显增加,这些调用都可能减缓系统操作的速度;
  2. 同步调用链时,链的任何部分变得缓慢,整个链都会受影响,最终会对整体速度有明显的影响;
10.2 警惕:

  通常性能测试被推迟的原因是,最初没有足够的系统资源用于测试。我理解这个原因,但通常性能测试会一直拖延,如果不是直到上线都没有发生的话,也通常只在上线前才会发生!不要掉入这个拖延的陷阱。

10.3 测试方法
  1. 模拟客户逐渐增多,然后在给定的客户场景一起运行;
  2. 调用延迟随着负荷的增加如何变化,这意味着性能测试需要运行一段时间;
  3. 需要一个类似生产的数据量,并需要更多的机器来匹配基础设施;
  4. 每天运行一个子集,每周运行一个更大的集合。要确保尽可能频繁地运行;
  5. 性能测试需要与系统性能的监控同时进行,理想情况下,应该在性能测试环境下使用与生产环境中相同的可视化工具,这样我们更容易对两者进行比较。

第八章:监控

  微服务会将系统拆分成更小的、细粒度的微服务会带来很多好处。然而,它也增加了生产系统的监控复杂性。细粒度的系统给系统监控和定位问题带来新的挑战。解决方法:监控小的服务,然后聚合起来看整体。

1. 单一服务,单一服务器

  一台主机运行一个服务,只需要自己本身,需要监控的数据:CPU、内存、服务器日志。单机服务监控相对简单。随着时间推移,负载增加,系统就需要扩容。

2. 单服务,多个服务器

  当CPU占用率高时,如果这个问题发生在所有的主机上,那么可能是服务的问题;
但如果只发生在一台主机上,那么可能是主机本身的问题;
除了要查看所有主机的数据,还要查看单个主机自己的数据。

3. 多个服务,多个服务器

  多个服务合作为我们的用户提供功能,这些服务运行在多个物理的或虚拟的主机上。
  如何在多个主机上的、成千上万行的日志中定位错误的原因?
  如何确定是一个服务器异常,还是一个系统性的问题?
  如何在多个主机间跟踪一个错误的调用链,找出引起这个错误的原因?
  答案是,从日志到应用程序指标,集中收集和聚合尽可能多的数据到我们的手上。

4. 多个服务的指标跟踪

需要的功能:

  1. 能够方便地从新的主机收集指标;
  2. 能够看到整个系统聚合后的指标;
  3. 能够获取到给定的服务实例聚合后的指标。
5. 服务指标

指标可以反映出系统的行为。通过指标了解用户使用我们服务情况,然后依靠指标系统对它们进行处理。

6. 关联标识

  关联标识用于跟踪系统中触发的调用。
  建议:尽早考虑使用它。

7. 级联

  级联故障特别危险,它会使相关的服务无法交互,导致网络连接瘫痪。
  监控系统之间的集成点非常关键。每个服务的实例都应该追踪和显示其下游服务的健康状态,从数据库到其他合作服务。
  应该将这些信息汇总,以得到一个整合的画面。从而了解下游服务调用的响应时间,并检测是否有错误。

第九章:安全

1. 身份验证和授权

  通过授权机制,可以把主体映射到他可以进行的操作中。当一个主体通过身份验证后,将获得关于他的信息,这些信息可以帮助我们决定其可以进行的操作。

1.1 常见的单点登录实现

  SSO (Single Sign-On,单点登录):一种常用的身份验证和授权;
  SAML 和OpenID Connect:在企业级领域中占据统治地位,也提供了身份验证和授权功能;

1.2 单点登录网关

网关的功能:

  1. 使用位于服务和外部世界之间的网关作为代理;
  2. 集中处理重定向用户的行为,并且只在一个地方执行握手;

存在的问题:

  1. 孤立地在微服务中定位问题就变得更难。
  2. 它会带来一种虚假的安全感。深度防御的理念是从网络边界,到子网,到防火墙,到主机,到操作系统,再到底层硬件,我们需要在所有这些方面都实现安全措施的能力,而不是依靠网关来处理每一步的安全措施。

2. 服务间的身份验证和授权

2.1 在边界内允许一切

  在边界内对服务的任何调用都是默认可信的。隐式信任模型存在的风险,对典型的中间人基本没有任何防备。

2.2 HTTP(S)基本身份验证

HTTP基本身份验证,允许客户端在标准的HTTP头中发送用户名和密码。服务端可以验证这些信息,并确认客户端是否有权访问服务。

优点:

  1. 这是一种非常容易理解且得到广泛支持的协议;

缺点:

  1. HTTP有很高的风险,用户名和密码并没有以安全的方式发送。HTTP基本身份验证通常应该通过HTTPS进行通信;
  2. SSL之上的流量不能被反向代理服务器(比如Varnish或Squid)所缓存;
  3. 如果使用现成的SSO方案,需要承担存在重复行为的风险。
2.3 使用SAML或OpenID Connect

  如果你已经在使用SAML 或OpenID Connect作为身份验证和授权方案,你可以在服务之间的交互中也使用它们。如果你正在使用一个网关,可以使用同一个网关来路由所有内网通信,但如果每个服务自己处理集成,那么系统应该就自然而然这么工作。

这样做的好处:

  1. 可以利用现有的基础设施,并把所有服务的访问控制集中在中央目录服务器。如果想要避免中间人的攻击,我们仍然需要通过HTTPS来路由通信。

缺点:

  1. 需要安全地存储凭证(户名和密码);
  2. 做身份验证需要写相当繁琐的代码。
2.4 客户端证书

  确认客户端身份的另一种方法是,使用TLS (Transport Layer Security,安全传输层协议),TLS是SSL在客户端证书方面的继任者。在这里,每个客户端都安装了一个X.509证书,用于客户端和服务器端之间建立通信链路。服务器可以验证客户端证书的真实性,为客户端的有效性提供强有力的保证。

特点:

  1. 证书管理的工作要比只使用服务器端证书更加繁重;
  2. 出现问题不容易诊断;
  3. 撤销和补发证书的难度;

建议:

  1. 特别关注所发数据的敏感性,或无法控制发送数据所使用的网络时,才考虑使用这种技术;
2.5 API密钥

  API密钥允许服务识别出是谁在进行调用,然后对他们能做的进行限制。限制通常不仅限于特定资源的访问,还可以扩展到类似于针对特定的调用者限速,以保护其他人服务调用的质量等。

3. 静态数据的安全

  在许多有名的安全漏洞中,都发生了静态数据被攻击者获取的情况,且其中的内容对攻击者来说是可读的。这要么是因为数据以未加密的形式存储,要么是因为保护数据的机制有根本性的缺陷。

####3.1 使用众所周知的加密算法
  对于静态数据的加密,除非你有一个很好的理由选择别的,否则选择你的开发平台上的AES-128 或AES-256的一个广为人知的实现。
  关于密码,可以使用一种叫作加盐密码哈希的技术。

3.2 一切皆与密码有关

密钥存储方法:

  • 一个解决方案是:使用单独的安全设备来加密和解密数据。
  • 另一个方案是:使用单独的密钥库,当你的服务需要密钥的时候可以访问它。
3.3 按需解密

  只在需要时进行解密,并确保解密后的数据不会存储在任何地方。

3.4 加密备份

  需要确保备份也被加密。

4. 深度防御

4.1 防火墙

  只在特定端口限制特定的通信类型。

4.2 日志

  好的日志实践,特别是聚合多个系统的日志的能力,虽然不能起到预防的作用,但可以帮助检测出发生了不好的事情,以便之后进行恢复。
  日志可以让你事后看看是否有不好的事情发生过。但需要注意,那些存储在日志里的信息!敏感信息需要被剔除。

4.3 入侵检测(和预防)系统

  IDS (Intrusion Detection Systems,人侵检测系统)可以监控网络或主机,当发现可疑行为时报告问题。IPS (Intrusion Prevention Systems,入侵预防系统),也会监控可疑行为,并进一步阻止它的发生。
  不同于防火墙主要是对外阻止坏事进来,IDS和IPS是在可信范围内积极寻找可疑行为。

4.4 网络隔离

  在微服务系统中,可以把服务放进不同的网段,控制服务间的通信。

4.5 操作系统

  给操作系统的用户尽量少的权限,开始时也许只能运行服务,以确保即使这种账户被盗,造成的伤害也最小。

5. 黄金法则

  不要实现自己的加密算法。不要发明自己的安全协议

6. 内建安全

  帮助培养开发人员的安全意识很关键,提高每个人对安全问题的普遍意识,有助于从最开始减少这些问题。

7. 外部验证

  1. 通过外部方实施的类似渗透测试这样的实验模拟现实世界的意图;
  2. 如果公司有专门的信息安全团队可以让他们协助帮助你;
  3. 如果公司没有专门的信息安全团队,可以找一个外部方;
  4. 早点接触安全团队,了解他们是如何工作的,并向其学习做一个安全测试需要关注哪些内容。

第十一章:规模化服务

1. 故障无处不在

  1. 我们可以尽力尝试去限制引起故障的因素,但达到一定规模后,故障难以避免。
  2. 我们需要有能够拥抱故障的心态,才能更好的管理系统。
  3. 在试图阻止不可避免的故障上少花一点时间,而花更多时间去优雅地处理它。

2. 功能降级

  构建一个弹性系统,尤其是当功能分散在多个不同的、有可能宕掉的微服务上时,重要的是能够安全地降级功能。
  我们需要做的是理解每个故障的影响,并弄清楚如何恰当地降级功能。如果购物车服务不可用,我们可能会有很多麻烦,但仍然可以显示列表清单页面。也许可以仅仅隐藏掉购物车,将其替换成一个新的图标“马上回来!

3. 架构性安全措施

  有一些模式,组合起来被称为架构性安全措施,它们可以确保如果事情真的出错了,不会引起严重的级联影响。这些都是你需要理解的非常关键的点,我强烈建议在你的系统中把它们标准化,以确保不会因为一个服务的问题导致整个系统的崩塌。

4. 反脆弱的组织

  通过预演生产环境中出现的潜在或极端情况,系统是否能正常工作。

4.1 超时

  给所有的跨进程调用设置超时,并选择一个默认的超时时间。当超时发生后,记录到日志里看看发生了什么,并相应地调整它们。

4.2 断路器

  使用断路器时,当对下游资源的请求发生一定数量的失败后,断路器会打开。接下来,所有的请求在断路器打开的状态下,会快速地失败。一段时间后,客户端发送一些请求查看下游服务是否已经恢复,如果它得到了正常的响应,将重置断路器。

4.3 舱壁

  通过把功能分离成独立的微服务,减少了因为一个功能的宕机而影响另一个的可能性。

4.4 隔离

  一个服务越依赖于另一个,另一个服务的健康将越能影响其正常工作的能力。如果我们使用的集成技术允许下游服务器离线,上游服务便不太可能受到计划内或计划外宏机的影响。

5. 幕等

  对暴等操作来说,其多次执行所产生的影响,均与一次执行的影响相同。
  如果操作是幕等的,我们可以对其重复多次调用,而不必担心会有不利影响。当我们不确定操作是否被执行,想要重新处理消息,从而从错误中恢复时,幕等会非常有用。

6. 扩展

扩展系统的两个原因。

  1. 为了帮助处理失败。如果我们担心有些东西会失败,那么多一些这些东西会有帮助;
  2. 为性能扩展,无论是处理更多的负载、减少延迟或两者兼而有之。
6.1 更强大的主机

  一个有着更快的CPU和更好的I/O的机器,通常可以改善延迟和吞吐量,允许你在更短的时间内处理更多的工作。这种形式的扩展通常被称为垂直扩展。

垂直扩展特点:

  1. 能快速见效的提升系统性能;
  2. 非常昂贵;
  3. 需要充分利用它的性能才能发挥它的价值;
  4. 这种形式的扩展无法改善我们服务器的弹性。
6.2 拆分负载

  因为要扩展需要把现有的微服务拆分成几个部分,以更好地处理负载

6.3 分散风险
  1. 不要把多个服务放到一台主机上,因为主机的启机会影响多个服务。
  2. 不要让所有的服务都运行在同一个数据中心的同一个机架上,而是分布在多个数据中心。
6.4 负载均衡

  要想让服务具有弹性时,要避免单点故障。要达到这个目的最简单的方法是,在一个负载均衡器后,放置多台主机运行你的微服务实例。
  负载均衡器功能是,当实例不再健康时移除它们,并当它们恢复健康后再添加进来。

7. 扩展数据库

  扩展无状态的微服务是相对简单的。但如果我们把数据存储在一个数据库,我们也需要知道如何扩展数据库。不同类型的数据库会提供不同形式的扩展,理解哪种形式最适合你的使用场景,将确保从一开始你就选择了正确的数据库技术。

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

  写入数据库的数据:我可以将一份副本存储到一个弹性文件系统。如果数据库出现故障,数据不会丢失,因为有一个副本,但数据库本身是不可用的,这会使我们的微服务也不可用。
  主数据库出现故障:数据是安全的,但如果没有一个机制让主数据库恢复或提升副本为主数据库,即使数据是安全的,数据库依然不可用。

8. 缓存

  缓存是性能优化常用的一种方法,通过存储之前操作的结果,以便后续请求可以使用这个存储的值,而不需花时间和资源重新计算该值。通常情况下,缓存可以消除不必要的到数据库或其他服务的往返通信,让结果返回得更快。如果使用得当,它可以带来巨大的性能好处。

9. 自动伸缩

  自动伸缩的基本条件:需要可以完全自动化地创建虚拟主机以及部署你的微服务实例。
优点:

  1. 可以节省开支;
  2. 可以响应式地进行负载调整。

10. CAP理论

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

这个定理告诉我们最多只能保证三个中的两个。

10.1 牺牲一致性

  AP系统:系统放弃一致性以保证分区容忍性和可用性的这种做法,被称为最终一致性;也就是说,我们希望在将来的某个时候,所有节点都能看到更新后的数据,但它不会马上发生,所以我们必须清楚用户将看到失效数据的可能性。

10.2 牺牲可用性

  CP系统:牺牲可用性,系统是一致和分区忍性的。

10.3 牺牲分区容忍性

  不存在。如果系统没有分区容忍性,就不能跨网络运行。换句话说,需要在本地运行一个单独的进程。所以,CA系统在分布式系统中根本是不存在的。

11. 服务发现

服务发现的分成两部分:

  1. 提供了一些机制,让一个实例注册并告诉所有服务它的基本信息;
  2. 一旦服务被注册就可以找到它。

12. 动态服务注册

12.1 Zookeeper

  Zookeeper被用于众多使用场景中,包括配置管理、服务间的数据同步,leader选举消息队列和命名服务。
  Zookeeper的核心是提供了一个用于存储信息的分层命名空间。客户端可以在此层次结构中,插入新的节点,更改或查询它们。
  Zookeeper本身所提供的特性是相当通用的,这就是它适用于这么多场景的原因。你可以认为它只是信息树的一个副本,当它发生更改时对你做出提醒。这意味着,你通常会在它上面构建一些功能,以适应你的特定场景。

12.2 Consul

  和Zookeeper一样, Consul 也支持配置管理和服务发现。但它比Zookeeper更进一步,为这些关键使用场景提供了更多的支持。

  Consul从注册服务、查询键/值存储到插人健康检查,都使用的是RESTful HTTP接口,这使集成不同技术栈变得非常简单。另一个让我非常喜欢的事情是,Consul背后的团队把底层集群管理拆分了出来。Consul底层的Serf可以处理集群中的节点监测、故障管理和报警。然后Consul在其之上添加了服务发现和配置管理。

13. 文档服务

  通过将系统分解为更细粒度的微服务,我们希望以API的形式暴露出很多接缝。通过API的文档告诉用户正确的进行服务发现。
  文档往往会过时。理想情况下,我们会确保文档总是和最新的微服务API同步,并当大家需要知道服务在哪里时,能够很容易地看到这个文档。两种不同的技术,Swagger和HAL,试图使这成为现实。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值