聊聊 Docker Swarm 部署 gRPC 服务的坑

gRPC 是一个高性能、开源和通用的 RPC 框架,面向移动和 HTTP/2 设计,也是目前流行的微服务架构中比较突出的跨语言 RPC 框架。

一直以来,我们的微服务都是基于 gRPC 来开发,使用的语言有 .NETJAVANode.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

640?wx_fmt=jpeg


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/313217.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

动手造轮子:实现一个简单的依赖注入(零)

动手造轮子&#xff1a;实现一个简单的依赖注入(零)Intro依赖注入为我们写程序带来了诸多好处&#xff0c;在微软的 .net core 出来的同时也发布了微软开发的依赖注入框架 Microsoft.Extensions.DependencyInjection&#xff0c;大改传统 asp.net 的开发模式&#xff0c;asp.ne…

【WPF on .NET Core 3.0】 Stylet演示项目 - 简易图书管理系统(2)

上一章《回忆一下我们的登录逻辑,主要有以下4点:当"用户名"或"密码"为空时, 是不允许登录的("登录"按钮处于禁用状态).用户名或密码不正确时, 显示"用户名或密码不正确"的消息框.用户名输入"waku", 并且密码输入"123&q…

gRPC 流式调用

gRPC 使用 Protocol buffers 作为接口定义语言&#xff08;IDL&#xff09;来描述服务接口和输入输出消息的结构&#xff0c;目前支持 4 种定义服务方法类型&#xff1a;类型说明简单 RPC客户端传入一个请求对象&#xff0c;服务端返回一个结果对象客户端流式 RPC客户端传入多个…

模型压缩案例-SSDYou only look once

http://write.blog.csdn.NET/postedit 在上一篇文章中&#xff0c;介绍了以regionproposal来检测的框架&#xff0c;这一系列速度和精度不断提高&#xff0c;但是还是无法达到实时。存在的主要问题为&#xff1a;速度不够快&#xff0c;主要原因是proposal比较多&#xff0c;特…

.NET如何将字符串分隔为字符

前言如果这是一道面试题&#xff0c;答案也许非常简单&#xff1a;.ToCharArray()&#xff0c;这基本正确……我们以“AB吉??????”作为输入参数&#xff0c;首先如果按照“正常”处理的思路&#xff0c;用 .ToCharArray()&#xff0c;然后转换为 JSON&#xff08;以便方…

Orleans 知多少 | 3. Hello Orleans

1. 引言是的&#xff0c;Orleans v3.0.0 已经发布了&#xff0c;并已经完全支持 .NET Core 3.0。所以&#xff0c;Orleans 系列是时候继续了&#xff0c;抱歉&#xff0c;让大家久等了。万丈高楼平地起&#xff0c;这一节我们就先来了解下Orleans的基本使用。2. 模板项目讲解在…

.NET Core 3.0之深入源码理解ObjectPool(二)

写在前面前文主要介绍了ObjectPool的一些理论基础&#xff0c;本文主要从源码角度理解Microsoft.Extensions.ObjectPool是如何实现的。下图为其三大核心组件图&#xff1a;核心组件ObjectPoolObjectPool是一个泛型抽象类&#xff0c;里面只有两个抽象方法&#xff0c;Get和Retu…

VC维学习

http://www.flickering.cn/machine_learning/2015/04/vc维的来龙去脉/ 说说历史Hoeffding不等式Connection to Learning学习可行的两个核心条件Effective Number of HypothesesGrowth FunctionBreak Point与ShatterVC BoundVC dimension深度学习与VC维小结参考文献 VC维在机器学…

.NET Core 3.0 一个 jwt 的轻量角色/用户、单个API控制的授权认证库

作者&#xff1a;痴者工良&#xff08;朋友合作原创&#xff09;来源&#xff1a;https://www.cnblogs.com/whuanle/p/11743406.html目录说明一、定义角色、API、用户二、添加自定义事件三、注入授权服务和中间件三、如何设置API的授权四、添加登录颁发 Token五、部分说明六、验…

.NET Core 3.0 构建和部署

Default Executables 默认可执行文件 在 dotnet build 或 dotnet publish 期间&#xff0c;将创建一个与你使用的 SDK 的环境和平台相匹配的可执行文件。 和其他本机可执行文件一样&#xff0c;可以使用这些可执行文件执行相同操作&#xff0c;例如&#xff1a; 可以双击可执行…

为什么我会了SOA,你们还要逼我学微服务?

菜菜哥&#xff0c;我最近需要做一个项目&#xff0c;老大让我用微服务的方式来做那挺好呀&#xff0c;微服务现在的确很流行我以前在别的公司都是以SOA的方式&#xff0c;SOA也是面向服务的方式呀的确&#xff0c;微服务和SOA有相同之处面向服务的架构&#xff08;SOA&#xf…

面对万物互联的智能世界,你是否也想分一杯羹

第六届世界互联网大会于10月20日至22日在浙江乌镇顺利举行。作为世界互联网大会“13”架构的重要组成部分&#xff0c;“互联网之光”博览会以“智能互联网、开放合作——携手共建网络空间命运共同体”为主题&#xff0c;集中展示了全球范围内的互联网新技术、新成果、新产品、…

你必须知道的容器监控 (2) cAdvisor

# 实验环境&#xff1a;阿里云ECS主机&#xff08;两台&#xff09;&#xff0c;CentOS 7.401—cAdvisor简介为了解决容器的监控问题&#xff0c;Google开发了一款容器监控工具cAdvisor&#xff08;Container Advisor&#xff09;&#xff0c;它为容器用户提供了对其运行容器的…

代码阅读

http://alanse7en.github.io/caffedai-ma-jie-xi-4/ 三. 从一个比较宏观的层面上去了解caffe怎么去完成一些初始化的工作和使用Solver的接口函数&#xff0c;本文将主要分为四部分的内容&#xff1a; Google Flags的使用Register Brew Function的宏的定义和使用train()函数的…

动手造轮子:实现一个简单的依赖注入(一)

动手造轮子&#xff1a;实现一个简单的依赖注入(一)Intro在上一篇文章中主要介绍了一下要做的依赖注入的整体设计和大概编程体验&#xff0c;这篇文章要开始写代码了&#xff0c;开始实现自己的依赖注入框架。类图首先来温习一下上次提到的类图服务生命周期服务生命周期定义&am…

Qt 调试Caffe

http://blog.csdn.net/xg123321123/article/details/52817658 1.下载并安装Qt Creator 下载页面&#xff0c;推荐使用4.x版本&#xff0c;比如&#xff1a; Qt Creator 4.1.0 for Linux 64-bit下载的是run包&#xff0c;安装方法&#xff1a; cd到下载目录 chmod x xxx.run …

.NET Core 3.0 本地工具

.NET Core从最早期的版本就开始支持全局工具了。如果仅仅需要在某个项目中或某个文件夹中使用特定的工具&#xff0c;那么.NET Core 3.0就允许您这样做。 使用.NET Core 3.0&#xff0c;您可以在特定的文件夹下安装“本地”工具&#xff0c;它的作用范围仅限于该文件夹及其子文…

Nsight 调试 Caffe

http://blog.csdn.net/u014114990/article/details/47723877 右键属性菜单的General->Code Analysis->Paths and Symbols下进行加入&#xff1a; Includes下加入程序需要用到的头文件的路径&#xff1a; Library Path下添加需要用到库文件的路径&#xff1a; 具体用到的…

张高兴的 .NET Core IoT 入门指南:(五)PWM 信号输出

什么是 PWM在解释 PWM 之前首先来了解一下电路中信号的概念&#xff0c;其中包括模拟信号和数字信号。模拟信号是一种连续的信号&#xff0c;与连续函数类似&#xff0c;在图形上表现为一条不间断的连续曲线。数字信号为只能取有限个数值的信号&#xff0c;比如计算机中的高电平…