背景
基于每次调用的负载均衡
需要注意的是,gRPC的负载均衡发生在每次调用时,而不是每次连接时。换句话说,就算所有的请求来自于同一个客户,我们也希望可以将它们负载均衡到所有的服务器。
负载均衡的方法
在讨论gRPC所使用的负载均衡之前,我们讨论一下常规的负载均衡方式。
代理方式
使用代理作为可信任的客户端,向负载均衡系统发送请求。因为它们需要有RPC请求和回复的临时副本,代理通常需要更多的资源来进行这个操作。这个也会增加RPCs请求的延迟。代理模式在处理heavy services (像存储)一类的时,被认为是低效的。
客户端处理负载均衡
复杂的客户端可以有负载均衡的逻辑。例如,客户端可以有许多负载均衡的算法(轮询,随机等)来从一组服务器中选择一个来发送请求。这组服务器可以是直接在客户端进行静态配置,通过name resolution系统提供,或者通过一个外部的负载均衡器来提供。无论是哪种方式,客户端需要从一组服务器中选择一个来发送请求。
这种方式有一个缺点,你需要维护多个语言,甚至每个语言多个版本的负载均衡算法,这些算法可能很复杂。一些算法需要客户端和服务端进行通信,来获取服务器的健康状态和负载信息,这些都会使客户端的逻辑变得更复杂,而不只是发送客户的rpc请求。
外部负载均衡服务
这种方式下,客户端会有比较简单和可移植性的代码,会实现一些用于服务器选择的很常用的算法(例如轮询)。复杂的负载均衡算法由负载均衡器来实现,并提供给客户端会用。客户端依赖于负载均衡器来获取负载均衡的配置信息和一组可以用来处理客户rpc请求的服务器列表。负载均衡器可能会收集服务器的负载和健康状态信息,处理这些信息,然后均衡器通过更新服务器列表来均衡负载。在必要的时候,均衡器会把这些修改告知给客户端。
构架
概况
当前grpc负载均衡的主要机制是外部负载均衡,也就是说使用外部负载均衡服务来提供一组可用的服务器。
当前grpc的做法是,客户端自带很少的一些负载均衡策略,复杂的负载均衡策略依赖于外部负载均衡服务。
工作流
复杂均衡的策略通过如下方式集成到name resolution和连接服务端之间的客户端工作流中。下面是其工作方式:
- 开始时,grpc客户端使用服务器名发送一个name resolution的请求。这个名字将会解析成一个或者多个IP地址,每一个地址会指出这个是一个服务器地址还是一个负载均衡器地址,还会返回一个服务器配置,用来给出使用哪种客户端负载均衡策略(例如round_robin或者grpclb)。
- 客户端创建这个负载均衡的实例。
注意:如果resolver返回的任意一个地址是负载均衡器的地址,那么客户端将会使用grpclb的负载均衡策略,而不是关心服务器配置上给出的负载均衡策略, 否则客 户端会使用服务器配置上给出的负载均衡策略. 如果服务器配置上没有给出负载均衡策略,那么客户端将会使用默认策略,也就是选择第一个可用的服务器地址.
3. 负载均衡策略创建一个子管道到每一个服务器地址
(1) 对于除了grpclb之外的所有策略,这意味着根据resolver返回的每一个地址,创建一个子管道. 注意,这些策略会忽略resolver返回的负载均衡器的地址.
(2) 对于grpclb策略,工作流如下:
a. 客户端从resolver返回的负载均衡器地址中选择一个创建数据流. 客户端询问询问负载均衡器, 获取可以用于服务的服务器地址(针对于之前请求的服务器
名, 即最初传入name resolver那个服务器名).
注:在grpclb策略中,可以返回不是负载均衡器的地址,这个用来防止连不上负载均衡器.
b. 如果负载均衡器调度需要的话, 客户端连接的服务器可能会向负载均衡器发送负载相关的信息.
c. 负载均衡器向grpc客户端的grpclb模块返回服务器列表. grpclb模块然后和返回的服务器列表中的每个服务器创建一个子管道.
4. 对于每一个rpc请求, 负载均衡策略决定使用哪个子管道发送这个请求.
在grpclb策略中, 客户端会按照负载均衡器返回的服务器列表中的顺序发送请求. 如果服务器列表为空, 这个调用将会阻塞, 一直等到存在可用的服务器为止.