gRPC实战包含一系列文章,包括原创和翻译。最终会造成一个完整的系列,后续会不断完善,增长新的内容:算法
=============================================================
RPC许多刚刚接触gRPC用户或是刚刚把gRPC服务部署到kubernetes中感到惊讶的是,发现Kubernetes的默认负载平衡一般没法与gRPC一块儿使用。例如,当您使用一个简单的gRPC Node.js微服务应用程序并将其部署在Kubernetes上时,将发生如下状况:segmentfault
尽管此处显示的服务具备多个Pod,但从Kubernetes的CPU图形能够清楚地看到,只有一个Pod实际上正在执行全部工做-由于只有一个Pod正在接收任何流量。为何?后端
gRPC使用性能加强的HTTP/2协议。 HTTP/2具有更低的延迟的特色,实际上是经过利用单个长期存在的TCP链接并在其上多路复用请求/响应。这会给第4层(L4)负载均衡器带来问题,由于它们的级别过低,没法根据接收到的流量类型作出路由决策。这样,尝试对HTTP/2流量进行负载平衡的L4负载平衡器将打开一个TCP链接,并将全部连续的流量路由到该相同的长期链接,从而实际上取消了负载平衡。服务器
Kubernetes的kube-proxy本质上是一个L4负载平衡器,所以咱们不能依靠它来平衡微服务之间的gRPC调用。架构
实际上gRPC负载均衡有如下两种实现方式:app
在代理负载平衡与客户端负载平衡之间进行选择实际上是结构的选择。在代理负载平衡中,客户端将RPC发送给负载平衡器(LB)代理。 LB将RPC调用分发到实现了实际服务调用逻辑的可用后端服务器之一。 LB跟踪每一个后端的负载,并实现用于公平分配负载的算法。客户端自己不了解后端服务器。客户可能不受信任。此体系结构一般用于面向用户的服务,开放式Internet的客户端能够将其链接到数据中心中的服务器,以下图所示。在这种状况下,客户端向LB(#1)发出请求。 LB将请求传递给后端之一(#2),后端将报告加载到LB(#3)。负载均衡
在客户端负载平衡中,客户端知道多个后端服务器,并为每一个RPC选择一个。客户端从后端服务器获取负载报告,而且客户端实施负载平衡算法。在更简单的配置中,不考虑服务器负载,客户端只能在可用服务器之间进行轮询。以下图所示。如您所见,客户端向特定后端(#1)发出请求。后端一般在执行客户端RPC的同一链接上以负载信息(#2)进行响应。客户端而后更新其内部状态。less
下边是两种模式的对比分析:ide
Proxy | Client Side | |
---|---|---|
优点 | 客户端能够专心业务逻辑,无需感知后端,与不受信任的客户端一块儿使用 | 高性能,由于少了一层代理 |
劣势 | 延迟更高,LB吞吐量可能会限制可伸缩性 | 客户端变复杂,客户端跟踪服务器的负载和运行情况,客户端实现负载平衡算法,每一个语言的实现和维护负担,客户端须要被信任,或者信任边界须要由后备LB处理。 |
使用gRPC客户端负载平衡器,该负载平衡器被嵌入到gRPC客户端库中。这样,每一个客户端微服务均可以执行本身的负载平衡。可是,最终的客户很是脆弱,须要大量的自定义代码来提供任何形式的弹性,指标或日志记录,全部这些咱们都须要针对管道中使用的每种不一样语言重复屡次。模块化
并且在云原生时代,整个基础架构升级,表现为
选择代理的负责均衡模式,对于拥抱云原生的公司更加有意义和可拓展性。
咱们真正须要的是更智能的负载均衡器。
咱们须要第7层(L7)负载平衡器,由于它们在应用程序层运行,而且能够检查流量以作出路由决策。最重要的是,它们能够支持HTTP/2协议。
L7负载平衡器有不少不一样的选项,包括NGINX和HAProxy,但事实证实,大多数选项太过沉重,没法轻松加入咱们的微服务架构。咱们将选择权缩减为两个关键竞争者-Envoy和Linkerd。二者都是在考虑微服务架构的状况下开发的,而且都支持gRPC。
虽然两个代理都有许多理想的功能,但咱们最终的决定权仍是取决于代理的范围。为此,有一个明显的赢家。特使很小。它使用C ++ 11编写,没有基于Java的Linkerd附带的企业级功能。
肯定Envoy以后,咱们便开始深刻研究其功能集,而且有不少值得一看的地方。
Envoy由Lyft编写和开源,是多年来与一般在微服务体系结构中发生的复杂路由问题做斗争的直接结果。它实质上是为解决咱们的问题而设计的,而且具备:
并且新版本的envoy将会支持wasm,意味着将来的不少扩展能够直接使用你熟悉的语言来完成,并且具有原生的性能。
在您的架构中,若是暂时没有使用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负载均衡的问题,会带来更多的优点。