日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

聊聊 Docker Swarm 部署 gRPC 服务的坑

發布時間:2023/12/4 编程问答 41 豆豆
生活随笔 收集整理的這篇文章主要介紹了 聊聊 Docker Swarm 部署 gRPC 服务的坑 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

gRPC?是一個高性能、開源和通用的 RPC 框架,面向移動和 HTTP/2 設計,也是目前流行的微服務架構中比較突出的跨語言 RPC 框架。

一直以來,我們的微服務都是基于 gRPC 來開發,使用的語言有?.NET、JAVA、Node.js,整體還比較穩定,當然整個過程中踩過的坑也不少,今天主要介紹 gRPC 服務使用 Docker Swarm 部署遇到的問題。

問題描述

服務端空閑(沒有接受到任何請求)一段時間后(不到 20 分鐘),客戶端?第一次?向服務端發請求會失敗,重新請求則成功,具體錯誤日志如下,提示 gRPC 服務端將連接重置:

1
2
3
4
5
6
7
Grpc.Core.RpcException: Status(StatusCode=Unavailable, Detail="Connection reset by peer")
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Grpc.Core.Internal.AsyncCall`2.UnaryCall(TRequest msg)
at Grpc.Core.DefaultCallInvoker.BlockingUnaryCall[TRequest,TResponse](Method`2 method, String host, CallOptions options, TRequest request)
at Grpc.Core.Interceptors.InterceptingCallInvoker.<BlockingUnaryCall>b__3_0[TRequest,TResponse](TRequest req, ClientInterceptorContext`2 ctx)
at Grpc.Core.ClientBase.ClientBaseConfiguration.ClientBaseConfigurationInterceptor.BlockingUnaryCall[TRequest,TResponse](TRequest request, ClientInterceptorContext`2 context, BlockingUnaryCallContinuation`2 continuation)

解決方案

方案1:重試機制

最初通過查看官方文檔對?StatusCode=Unavailable?的解釋,發現當前遇到的問題確實可以使用重試機制來處理,所以在客戶端對 gRPC 服務的調用全部添加了重試策略。

雖然當時確實解決了問題,但也一直懷疑我們在使用方式上肯定有問題,畢竟 gRPC 在很多開源項目中都被驗證過,理論上肯定不是這么處理問題的,所以并不推薦這么玩。

方案2:調整 TCP keepalive

在進行日志分析時,發現生產環境并沒有此類型錯誤日志,所以問題基本和代碼本身沒什么關系,猜測是環境上的原因,而本地開發環境和生產環境的最大區別是:開發環境的服務通過 Docker Swarm 進行部署,線上環境則是使用 k8s?。所以嘗試從 Docker Swarm 上進行問題定位,最終找到相關資料?gRPC streaming keepAlive doesn’t work with docker swarm?(雖然 issue 聊的是?grpc-go?,但其實和語言無關) 和?IPVS connection timeout issue?,問題和我們遇到的基本一致。

經過多次測試驗證確定出問題的原因是當通過 Docker Swarm 部署 (基于 overlay 網絡) gRPC 服務(基于 TCP),客戶端調用服務端會經過?IPVS?處理,IPVS?簡單來說就是傳輸級的負載均衡器,可以將基于 TCP 和 UDP 的服務請求轉發到真實服務。gRPC 服務啟動時,IPVS?中會將此 TCP 連接記錄到連接跟蹤表,但為了保持連接跟蹤表干凈,900s(默認的 timeout,不支持調整)內空閑的連接會被清理 ,IPVS?更多介紹

1
2
[root@node1]~# ipvsadm -l --timeout
Timeout (tcp tcpfin udp): 900 120 300

所以當客戶端發請求時,如果?IPVS?的連接跟蹤表中不存在對應連接,則會返回?Connection reset by peer?,重置后第二次請求就正常了。

所以解決方式就是使?IPVS?的連接跟蹤表一直有該服務的連接狀態,在 Linux 的內核參數中,有 TCP 的 keepalive 默認設置,時間是 7200s,我們只需要將其改成小于 900s,這樣不到 900s 就發起探測,使連接狀態一直保持。因為如果使用默認的 7200s 探測一次,IPVS?的連接跟蹤表中此服務可能在 900s 的時候就已經被清理,所以在 901s~7200s 這個區間內有客戶端請求進來就會出錯。

1
2
3
4
[root@node1]~# sysctl -a | grep keepalive
net.ipv4.tcp_keepalive_time = 7200 # 表示當 keepalive 啟用的時候,TCP 發送 keepalive 消息的頻度,缺省是2小時
net.ipv4.tcp_keepalive_probes = 9 # 如果對方不予應答,探測包的發送次數
net.ipv4.tcp_keepalive_intvl = 75 # keepalive 探測包的發送間隔

修改可通過編輯?/etc/sysctl.conf?文件,調整后需?重啟 gRPC 服務?:

1
2
3
net.ipv4.tcp_keepalive_time = 800 #800s 沒必要太小,其他兩個參數也可相應做些調整
net.ipv4.tcp_keepalive_probes = 3
net.ipv4.tcp_keepalive_intvl = 15


如果不希望修改內核參數,也可以在 gRPC 服務代碼中通過修改?grpc.keepalive_time_ms,參考:Keepalive User Guide for gRPC Core?和?Grpc_arg_keys,服務端默認?grpc.keepalive_time_ms?也是 7200s,和內核參數一樣,以下是 .NET 代碼例子(其他語言類似):

1
2
3
4
5
6
7
8
9
10
var server = new Server(new List<ChannelOption>
{
new ChannelOption("grpc.keepalive_time_ms", 800000), // 發送 keepalive 探測消息的頻度
new ChannelOption("grpc.keepalive_timeout_ms", 5000), // keepalive 探測應答超時時間
new ChannelOption("grpc.keepalive_permit_without_calls", 1) // 是否允許在沒有任何調用時發送 keepalive
})
{
Services = { ServiceA },
Ports = { new ServerPort(host, port, ServerCredentials.Insecure) },
};


再回頭看看為什么生產環境的 k8s 沒有這個問題,首先?kube-proxy?是支持?IPTABLES?和?IPVS?兩種模式的,但目前我們使用的是?IPTABLES,當然還有很多區別,不過涉及更多運維層面的介紹我就不吹逼了,畢竟不在掌握范圍內 。

參考鏈接

  • gRPC streaming keepAlive doesn’t work with docker swarm

  • IPVS

  • IPVS connection timeout issue

  • Keepalive User Guide for gRPC Core

  • Grpc_arg_keys


總結

以上是生活随笔為你收集整理的聊聊 Docker Swarm 部署 gRPC 服务的坑的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。