nginx grpc streaming负载均衡的排坑和思考

前言:

我们知道nginx在1.13版本之后就可以支持grpc的负载均衡了。官方给出的使用也很简单,类似proxy_pass的语法。但在使用的过程中遇到短连接的问题。

该文章后续仍在不断的更新修改中, 请移步到原文地址 http://xiaorui.cc/?p=5970

大量的timewait短连接:

我们知道grpc是基于http2的,http2的设计就是长连接的设计,在连接上可以跑多个stream来规避http 1.1 中的队头阻塞(Head of line blocking),简单说http1协议导致单个连接不能多路复用。

以前在cdn公司的基础架构组干过,所以对nginx算是熟悉了。我知道在使用nginx proxy_pass upstream的时候,需要配置keepalive,不然nginx做负载均衡转发一律会按照短连接处理。 没想到grpc upstream也要配置keepalive。

😅 没有在nginx upstream keepalive连接池配置的时候,timewait的连接会到4w左右。需要注意的是 keepalive是单个worker的连接池,毕竟nginx是多进程的,在nginx的架构模型下是不能连接共享的。

nginx grpc timewait
// xiaorui.cc
server {
    listen       6666 http2;
    server_name  localhost;

    #charset koi8-r;
    access_log  /var/log/nginx/host.access.log  main;

    location / {
        grpc_pass grpc://grpcservers;
    }
}

upstream grpcservers {
    server 10.161.11.181:8090;
    server 10.161.11.180:8090;
    server 10.161.11.179:8090;
    server 10.161.11.178:8090;
    server 10.161.11.177:8090;
    keepalive 2000;
}

有朋友问,使用nginx做grpc的负载均衡是否存在异常? 我使用client -> nginx -> 5个grpc server实例来测试,负载均衡没问题。

思考:

grpc unaryRpc可以负载均衡,这个好理解,一个请求一个返回,最后单个连接client的请求会均衡到nginx upstream下游的主机上。经过我的测试,server/client streaming和bidi streaming模式,nginx grpc也完美支持。

只要client跟nginx后面的服务建立了streaming的请求,那么就一直占用这个连接了,同一时间nginx到服务下游的连接不能复用。就是说如果有10000个client streaming长请求,那么nginx就要创建10000个连接对接下游服务。而unary rpc请求是可以复用连接池的。

nginx grpc streaming

grpc的streaming请求可以理解为有状态的请求,nginx是怎么区分的请求?看了下nginx 1.13关于grpc的代码,没怎么看明白 😅。通过tcpdump抓包来分析grpc streaming bidi/unary,没有发现标明grpc请求类型的标识。

我先前开发过一个grpc网关,是通过protobuf描述符、动态注册、各种反射来实现的。通过fullMethod和protobuf描述符里的descriptor可以分析该请求是否有streaming。有兴趣的可以看看我封装的库。 https://github.com/rfyiamcool/grpcall

总结:

grpc的负载均衡除了借助外部的gateway之外,还可以使用内置的grpc balancer接口实现。

我司的k8s ingress网关采用envoy做负载均衡,个人觉得envoy的强大值得拥有。这里不是说envoy比nginx强大,我可以负责的说envoy性能比nginx差不少。envoy的优势在微服务体系下有更灵活的配置和适配,比如对接istio,自定义xds接口,rest api -> grpc转换。


对Golang感兴趣的朋友可以加群: 278517979 !!!
另外如果大家觉得文章对你有些作用! 如果想赏钱,可以用微信扫描下面的二维码, 感谢!
另外再次标注博客原地址  xiaorui.cc