gRPC实战包含一系列文章,包括原创和翻译。最终会形成一个完整的系列,后续会不断完善,增加新的内容:
- gRPC简介:why,what,how?
- gRPC服务健康检查最佳实践
- Kubernetes中使用envoy负载均衡gRPC流量
- 用Golang编写通过gRPC进行通信的服务
- 如何在NodeJS中有效使用gRPC流
=============================================================
RPC许多刚刚接触gRPC用户或是刚刚把gRPC服务部署到kubernetes中感到惊讶的是,发现Kubernetes的默认负载平衡通常无法与gRPC一起使用。例如,当您使用一个简单的gRPC Node.js微服务应用程序并将其部署在Kubernetes上时,将发生以下情况:
尽管此处显示的服务具有多个Pod,但从Kubernetes的CPU图形可以清楚地看到,只有一个Pod实际上正在执行所有工作-因为只有一个Pod正在接收任何流量。为什么?
gRPC使用性能增强的HTTP/2协议。 HTTP/2具备更低的延迟的特点,其实是通过利用单个长期存在的TCP连接并在其上多路复用请求/响应。这会给第4层(L4)负载均衡器带来问题,因为它们的级别太低,无法根据接收到的流量类型做出路由决策。这样,尝试对HTTP/2流量进行负载平衡的L4负载平衡器将打开一个TCP连接,并将所有连续的流量路由到该相同的长期连接,从而实际上取消了负载平衡。
Kubernetes的kube-proxy本质上是一个L4负载平衡器,因此我们不能依靠它来平衡微服务之间的gRPC调用。
gRPC负载均衡方式
实际上gRPC负载均衡有以下两种实现方式:
- Proxy(代理负载平衡在某些文献中也称为服务器端负载平衡)
- Client side
在代理负载平衡与客户端负载平衡之间进行选择其实是结构的选择。在代理负载平衡中,客户端将RPC发送给负载平衡器(LB)代理。 LB将RPC调用分发到实现了实际服务调用逻辑的可用后端服务器之一。 LB跟踪每个后端的负载,并实现用于公平分配负载的算法。客户端本身不了解后端服务器。客户可能不受信任。此体系结构通常用于面向用户的服务,开放式Internet的客户端可以将其连接到数据中心中的服务器,如下图所示。在这种情况下,客户端向LB(#1)发出请求。 LB将请求传递给后端之一(#2),后端将报告加载到LB(#3)。
在客户端负载平衡中,客户端知道多个后端服务器,并为每个RPC选择一个。客户端从后端服务器获取负载报告,并且客户端实施负载平衡算法。在更简单的配置中,不考虑服务器负载,客户端只能在可用服务器之间进行轮询。如下图所示。如您所见,客户端向特定后端(#1)发出请求。后端通常在执行客户端RPC的同一连接上以负载信息(#2)进行响应。客户端然后更新其内部状态。
下边是两种模式的对比分析:
Proxy | Client Side | |
---|---|---|
优势 | 客户端可以专心业务逻辑,无需感知后端,与不受信任的客户端一起使用 | 高性能,因为少了一层代理 |
劣势 | 延迟更高,LB吞吐量可能会限制可伸缩性 | 客户端变复杂,客户端跟踪服务器的负载和运行状况,客户端实现负载平衡算法,每个语言的实现和维护负担,客户端需要被信任,或者信任边界需要由后备LB处理。 |
为什么不选择客户端负载均衡方式?
使用gRPC客户端负载平衡器,该负载平衡器被嵌入到gRPC客户端库中。这样,每个客户端微服务都可以执行自己的负载平衡。但是,最终的客户非常脆弱,需要大量的自定义代码来提供任何形式的弹性,指标或日志记录,所有这些我们都需要针对管道中使用的每种不同语言重复多次。
而且在云原生时代,整个基础架构升级,表现为
- 越来越多的公司采用了kubernetes
- 以istio为代表的service mesh服务治理方案成为一个趋势
- dapper的出现,代表着不断将基础架构的功能放到代理层执行,减小基础架构组件升级对业务代码的影响
选择代理的负责均衡模式,对于拥抱云原生的公司更加有意义和可拓展性。
我们真正需要的是更智能的负载均衡器。
选择更智能的复杂均衡代理
我们需要第7层(L7)负载平衡器,因为它们在应用程序层运行,并且可以检查流量以做出路由决策。最重要的是,它们可以支持HTTP/2协议。
L7负载平衡器有很多不同的选项,包括NGINX和HAProxy,但事实证明,大多数选项太过沉重,无法轻松加入我们的微服务架构。我们将选择权缩减为两个关键竞争者-Envoy和Linkerd。两者都是在考虑微服务架构的情况下开发的,并且都支持gRPC。
虽然两个代理都有许多理想的功能,但我们最终的决定权还是取决于代理的范围。为此,有一个明显的赢家。特使很小。它使用C ++ 11编写,没有基于Java的Linkerd附带的企业级功能。
确定Envoy之后,我们便开始深入研究其功能集,并且有很多值得一看的地方。
Envoy由Lyft编写和开源,是多年来与通常在微服务体系结构中发生的复杂路由问题作斗争的直接结果。它实质上是为解决我们的问题而设计的,并且具有:
- 双向均支持HTTP/2和SSL
- 高透明度和出色的性能
- 成熟的文档集
- 不依赖任何给定的语言
而且新版本的envoy将会支持wasm,意味着未来的很多扩展可以直接使用你熟悉的语言来完成,而且具备原生的性能。
使Envoy适应我们的基础架构
在您的架构中,如果暂时没有使用service mesh。可以采取下面的方案。
在Kubernetes中,一组一个或多个容器被称为Pod。可以复制Pod以提供扩展,并封装在称为服务的抽象中,这些抽象为访问基础Pod提供了稳定的IP地址。从Kubernetes 1.2开始,请求服务IP的默认行为是将返回一个随机的后端Pod。但是,您可以将服务重新配置为headless的,以便服务IP将返回可用的pod IP的整个列表,从而使您能够执行自己的服务发现。
Envoy被设计为作为Sidecar容器运行,并与客户端容器并排放置,以模块化的方式补充了其功能。在Kubernetes中,这转换为在同一容器中运行客户端容器和Envoy容器。我们将服务配置为headless的,以便为Envoy提供端点以用于服务发现。而且由于Envoy输出了大量指标,因此我们能够轻松观察到连续gRPC调用的循环负载平衡,以确认其按预期运行。
最终效果如下:
当然最终的方案是service mesh。除了可以解决gRPC负载均衡的问题,会带来更多的优势。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。