前言:
我們知道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轉換。