博客 | NGINX

MRA,第 6 部分: 使用 NGINX Plus 实现断路器模式

NGINX-F5-horiz-black-type-RGB 的一部分
Chris Stetson 缩略图
克里斯·斯特森
2016 年 11 月 10 日发布

作者注本篇博文是系列文章的第六篇:

  1. NGINX 微服务参考架构简介
  2. MRA,第 2 部分: 代理模型
  3. MRA,第 3 部分: 路由器网格模型
  4. MRA,第 4 部分: 织物模型
  5. MRA,第 5 部分: 调整十二要素应用以适应微服务
  6. MRA,第 6 部分: 使用 NGINX Plus 实现断路器模式(本篇文章)

所有六个博客,加上一个关于微服务应用的 Web 前端的博客 <.htmla>,都已收集到一本免费电子书中。

另请查看有关微服务的其他 NGINX 资源:

 

微服务应用设计彻底改变了应用的运行方式。 在微服务架构中,“应用”现在是相互依赖来执行任务和提供功能的服务的集合。 在复杂的应用中,服务图可能非常深,并且各个服务之间存在多个相互依赖关系。

例如,用户服务可能是依赖于该服务所提供数据的许多其他服务的组成部分。 在这种情况下,用户服务的故障可能会导致整个应用的一系列故障。

断路器模式(由 Martin Fowler 推广的一个术语)作为避免级联服务故障的应用设计模式,在微服务架构师中越来越流行。 断路器模式的理念是监视您的应用服务及其之间的流量,以防止故障 - 并且,当发生故障时,尽量减少这些故障对您的应用的影响。

对于微服务,断路器模式尤其有价值,可以提供自下而上的弹性。 如果正确实施,即使在服务不可用时它也可以通过提供服务的连续性来帮助避免级联故障。 断路器模式最为著名地被 Netflix 所采用,成为其应用设计理念中的一个关键组件。

不要逃避失败,要拥抱失败

现代应用设计的一个关键原则就是失败是必然的。 现代应用所依赖的分层结构——从云托管的虚拟机到容器到应用程序库到动态网络——意味着任何应用中的移动部件都是众多的。 您需要假设应用的一个或多个部分在某些时候会以某种方式失败。 预期失败并建立机制来减轻其影响对于提高应用的弹性大有裨益。

断路器模式最关键的目标之一是首先尝试防止故障。 对于某些类型的错误情况,例如内存不足,可以认识到故障即将发生并采取措施防止故障。 这通常是通过服务发出其不健康信号来实现的,然后断路器通过限制请求数量或完全重新路由来为服务提供恢复的机会。 一旦服务恢复,断路器也会谨慎地缓慢增加对该服务的请求,以免立即压垮服务并冒着再次变得不健康的风险。

NGINX 微服务参考架构中,我们有一个名为resizer 的服务。 当一张大照片上传到系统时,调整器会对其进行解压缩、校正其旋转、缩小,然后再次缩小,并将校正后的原始图像和两张调整大小后的图像保存到对象存储中。 这些过程的性质使调整器成为应用中处理器最密集和内存最密集的部分。

当同时调整许多图像的大小时,调整器可能会耗尽内存,并且在某些情况下会完全失败。 为了避免出现问题,我们在调整大小服务实例和向其提供图像的上传服务实例之间放置了一个断路器。 上传器定期查询调整器实例以了解其健康状况。 该查询会触发调整器评估其是否已使用超过 80% 的可用内存,以及其他健康检查,并将其健康状态反馈给上传器。

如果调整器实例表明其不健康,则上传器会将请求路由到其他实例(如图 1 所示),但会继续检查该调整器实例是否已恢复。 当调整器实例指示其再次恢复健康时,它将被放回负载平衡池中,并且上传器会缓慢地将流量提升至实例的全部容量。 这种设计可以防止调整器实例完全失败、防止工作开始但未完成、防止用户过度等待原本会失败的进程,并帮助系统最有效地处理发送给它的请求流。

断路器模式会切断流向不健康实例的流量。 断路器和 NGINX 可以很好地协同工作。
图 1. 主动健康检查可防止调用不健康的微服务实例

断路器模式提高一致性

在 NGINX 级别实现断路器的好处之一是,它创建了一个通用、一致且高度灵活的层,用于管理整个微服务应用中的断路器。 这种通用性和一致性意味着您不必管理和构建每种语言的断路器库的细微差别和不一致性。

通过将大多数断路器功能保留在每个服务的代码之外,并在 NGINX Plus 中实现它,您可以获得许多优势:

  • 例如,用 Java 编写的服务的断路器与用 PHP 编写的服务的断路器相同 - 并且断路器本身可以根据需要用另一种语言编写
  • 你无需在每个服务所使用的多种语言和支持库之间重新实现断路器功能
  • 每个不需要包含断路器代码的服务都得到了简化;它运行得更快,并且更易于编写、调试、运行和维护
  • 每个服务的支持代码都得到简化;所使用的库和系统组合仅能反映服务的核心功能
  • 断路器代码得到简化;它只存在于一个地方,可以精简到只剩下基本内容,而不需要适应当地情况
  • 断路器代码可以利用 NGINX Plus 的缓存等功能,使其功能更加强大
  • 您可以微调 NGINX Plus 级断路器代码,然后在其他应用和部署平台(例如本地、不同的云平台和混合环境)中重复使用它

但值得注意的是,断路器不能单独在 NGINX Plus 中实现。 真正的断路器需要服务在指定的 URI(通常是/health )处提供内省、主动的健康检查。 健康检查必须适合特定服务的需求。

在开发健康检查时,您需要了解服务的故障情况以及可能导致故障的情况类型,例如数据库连接失败、内存不足、磁盘空间不足或 CPU 过载。 这些情况在健康检查过程中进行评估,然后提供健康或不健康的二元状态。

断路器模式提供灵活性

当你在 NGINX 级别实现断路器模式时(如此处所述),当服务实例传达其不健康的情况时,由 NGINX Plus 来处理。 有很多种选择。

第一个选项是将请求重定向到其他健康的实例,并继续查询不健康的实例以查看它是否恢复。 第二种选择是向请求服务的客户端提供缓存响应,即使服务不可用也能保持稳定性。 该解决方案适用于面向读取的服务,例如内容服务。

另一种选择是提供替代数据源。 例如,我们的一个客户有一个个性化的广告服务器,它使用个人资料数据为其用户提供有针对性的广告。 如果个性化广告服务器出现故障,用户请求将被重定向到备份服务器,该备份服务器提供适合每个人的通用广告。 这种替代数据源方法非常有效。

最后,如果您对服务的故障情况有非常清楚的了解,则可以通过向断路器添加速率限制来减轻故障。 仅允许请求以其可以处理的速率通过服务。 这会在断路器内创建一个缓冲区,以便它可以吸收流量高峰。

速率限制在路由器网格模型等集中式负载均衡场景中尤其有效,其中应用流量通过有限数量的负载均衡器路由,这些负载均衡器可以很好地了解整个站点的总流量使用情况。

在 NGINX Plus 中实现断路器模式

正如我们上面所描述的,断路器模式可以通过减少不健康服务的流量或将请求路由出去,从而防止故障发生。 这需要对每个服务进行主动健康检查,并连接到内省健康监视器。 不幸的是,被动的健康检查并不能起到作用,因为它只能检查故障——此时,采取预防措施已经太晚了。 正是由于这个原因,NGINX Open Source 无法实现断路器模式——它仅支持被动健康检查。

然而,NGINX Plus 拥有强大的主动健康检查系统,提供多种检查和应对健康问题的选项。 查看微服务参考架构的某些服务类型的实现,为实现断路器的选项和用例提供了很好的例子。

让我们从连接到调整器大小的上传器服务开始。 上传器将图像放入对象存储,然后告诉调整器打开图像、进行校正并调整其大小。 这是一个计算密集型和内存密集型的操作。上传器需要监控调整器的健康情况并避免使其过载,因为调整器实际上可能会杀死正在运行它的主机。

首先要做的是专门为调整器健康检查创建一个位置块。 此块是内部位置,这意味着无法通过对服务器的标准 URL ( http://example.com/health-check-resizer ) 的请求来访问它。 相反,它充当健康检查信息的占位符。 health_check指令每三秒向/health URI 发送一次健康检查,并使用match块中定义的称为条件的测试来检查服务实例的健康状况。 当服务实例错过一项检查时,它会被标记为不健康。 proxy_*指令使用 HTTP 1.1 上的 TLS 1.2 将健康检查发送到调整器上游组,并将指示的 HTTP 标头设置为空。

位置 /health-check-resizer { 内部;
health_check uri=/health match=conditions 失败=1 间隔=3s;

proxy_pass https://resizer;
proxy_ssl_session_reuse on;
proxy_ssl_protocols TLSv1.2;
proxy_http_version 1.1;
proxy_set_header 连接“”;
proxy_set_header 接受编码“”;
}

下一步是创建条件匹配块来指定代表健康和不健康状况的响应。 第一个检查是响应状态代码:如果它在以下范围内:200通过399,测试继续进行下一个评估语句。 第二次检查是Content-Type是否为应用/json 。 最后,第三次检查是针对死锁磁盘内存指标的值进行正则表达式匹配。 如果全部健康,则确定该服务是健康的。

匹配条件 { 状态 200-399;
标头 Content-Type ~ “应用/json”;
身体〜'{
“死锁”:{“健康”:true},
“磁盘”:{“健康”:true},
“记忆”:{“健康”:true}
}';
}

NGINX Plus 断路器/健康检查系统还具有慢启动功能。上游块中调整器服务的服务器指令的slow_start参数告诉 NGINX Plus 在调整器实例首次从不健康状态返回时调节流量。 恢复服务的流量并不是简单地向健康服务发送相同数量的请求,而是在slow_start参数指示的时间段内(在本例中为 30 秒)缓慢增加到正常速率。 慢启动增加了服务恢复到满负荷状态的机会,同时减少了若未恢复负荷状态所产生的影响。

上游调整器 { 服务器调整器 slow_start=30s;
区域后端 64k;
least_time last_byte;
keepalive 300;
}

请求限制管理和调节对服务的请求流。 如果您足够了解应用的故障情况,知道它在任何给定时间可以处理的请求数量,那么实施请求限制对于该过程来说将是一个真正的福音。 但是,只有当 NGINX Plus 完全了解传递到服务中的连接总数时,此功能才有效。 因此,最有用的是在具有服务本身的容器中运行的 NGINX Plus 实例上实现请求限制断路器(如在 Fabric 模型中),或者在负责管理集群中所有流量的集中式负载均衡器中。

以下配置代码片段定义了对应用于其容器中的调整器服务实例的请求的速率限制limit_req_zone指令定义速率限制为每秒 100 个请求。 $server_addr变量用作关键,这意味着进入调整器容器的所有请求都会计入限制。 该区域的名称为moderateReqs ,保存请求计数的时间范围为1分钟。 limit_req指令使 NGINX Plus 能够缓冲最多 150 个请求。 当超过该数量时,客户端会收到503limit_req_status指令指定的错误代码,表示服务不可用。

http { # 审核投递
limit_req_zone $server_addr zone=moderateReqs:1m rate=100r/s;
# ...
server {
# ...
limit_req zone=moderateReqs burst=150;
limit_req_status 503;
# ...
}
}

在 NGINX Plus 中运行断路器的另一个强大的好处是能够合并缓存并集中维护缓存数据,以供整个系统使用。 这对于内容服务器等读取导向服务尤其有价值,因为从后端读取的数据不会频繁变化。

proxy_cache_path /app/cache levels=1:2 keys_zone=oauth_cache:10m max_size=10m inactive=15s use_temp_path=off;
上游用户管理器 {
服务器用户管理器;
区域后端 64k;
least_time last_byte;
keepalive 300;
}

服务器 {
listen 443 ssl;
location /v1/users {
proxy_pass http://user-manager;
proxy_cache oauth_cache;
proxy_cache_valid 200 30s;
proxy_cache_use_stale 错误超时 invalid_header 更新
http_500 http_502 http_503 http_504;
}
}

如图 2 所示,缓存数据意味着许多客户数据请求永远不会到达微服务实例,从而释放了以前未收到的请求的容量。

NGINX Plus 作为微服务断路器也支持缓存。
图 2. 虽然缓存通常用于通过阻止对微服务实例的调用来提高性能,但它也可以在服务完全故障时提供服务的连续性

然而,对于数据可以改变的服务(例如用户管理服务),需要明智地管理缓存。 否则,您最终可能会遇到这样的情况:用户更改了其个人资料,但由于数据已被缓存,因此在某些情况下会看到旧数据。 合理的超时和接受最终一致性的高可用性原则可以解决这个难题。

NGINX 缓存的一个很好的特性是,即使服务完全不可用,它也可以继续提供缓存数据。在上面的代码片段中,如果服务使用四种最常见的响应之一进行响应500‑系列错误代码。

即使服务器宕机,缓存也不是响应客户端的唯一选择。 正如我们在“断路器模式提供灵活性”中提到的,我们的一位客户需要一个有弹性的解决方案,以防他们的个性化广告服务器出现故障,而缓存响应并不是一个好的解决方案。 相反,他们想要一个通用广告服务器来提供通用广告,直到个性化服务器重新上线。 使用服务器指令的备份参数可以轻松实现这一点。 以下代码片段指定,当为personal-ad-server域定义的所有服务器不可用时,将使用为generic-ad-server域定义的服务器。

上游个人广告服务器 { 服务器个人广告服务器;
服务器通用广告服务器备份;
区域后端 64k;
最少时间最后字节;
保持活动 300;
}

最后,可以让 NGINX评估来自服务的响应代码并单独处理这些代码。 在以下代码片段中,如果服务返回503错误,NGINX Plus 将请求发送到备用服务。 例如,如果调整器具有此功能,并且本地实例过载或停止运行,则请求将被发送到调整器的另一个实例。

位置 / { error_page 503 = @fallback;
}

位置 @fallback {
proxy_pass http://alternative-backend;
}

结论

断路器模式是一个强大的工具,可以在微服务应用中提供弹性和控制。 NGINX Plus 提供了许多功能和选项来将断路器实现到您的环境中。 实现断路器模式的关键是了解您要保护的服务的故障情况,然后尽可能选择最能防止故障的选项,并在故障发生时最好地减轻故障的影响。

要试用 NGINX Plus,请立即开始30 天免费试用联系我们讨论您的用例。


“这篇博文可能引用了不再可用和/或不再支持的产品。 有关 F5 NGINX 产品和解决方案的最新信息,请探索我们的NGINX 产品系列。 NGINX 现在是 F5 的一部分。 所有之前的 NGINX.com 链接都将重定向至 F5.com 上的类似 NGINX 内容。”