tikv grpc连接泄漏,grpc-keepalive-time未生效

一个好的问题描述有利于社区小伙伴更快帮你定位到问题,高效解决你的问题

【TiDB 使用环境】生产环境 /测试环境
【TiDB 版本】v8.5.1
【部署方式】机器部署

使用tikv作为juicefs的元数据服务。当客户端没有优雅退出的时候,即客户端退出但是没有umount,没有主动关闭tcp连接,可以用iptables在juicefs客户端主动丢弃所有和tikv之间的报文来模拟。

则此时,tikv服务端观察,发现它和客户端的连接会一直处于established装态,即便客户端已经退出。

tikv的grpc-keepalive-time和grpc-keepalive-timeout均为默认值10s和3s

奇怪的是,如果tiup reload一下tikv节点,reload后juicefs会自动重试建立新的连接,在这个基础上,如果juicefs非优雅退出,则这些连接在服务端是可以自动关闭的,而且关闭的时间点完全符合grpc-keepalive-time和grpc-keepalive-timeout的配置。其他场景的连接都会泄漏。

感觉grpc-keepalive-time和grpc-keepalive-timeout这两个配置在某些场景(tiup reload tikv之后第一波建立的连接)是可以生效的,但是在其他场景下没有生效。

复现步骤
大概复现步骤是这样的

  1. tikv的配置是
    grpc-keepalive-time=10s和grpc-keepalive-timeout=3s,是默认配置
  2. 在tikv的服务端机器上执行 netstat -poant | grep 没有看到连接,符合预期
  3. 在客户端挂载juicefs
  4. 在tikv服务端机器上执行netstat -poant | grep 看到客户端和tikv服务端有4个连接,且tcp定时器为off,符合预期
  5. 在客户端执行iptables命令,drop掉和tikv之间所有的报文,模拟客户端非优雅退出(不close连接,不umount)
  6. 在tikv服务端执行netstat -poant | grep 发现原先的4个连接一直不会断开,定时器一直off状态。不符合预期,因为tikv应该会回收grpc的连接,即便tcp层面的定时器是off的。

sysctl -a | grep tcp

问题没有看的太明白啊

实时监控 TiKV 与客户端的连接状态

watch -n 1 “netstat -an | grep :.*20160 | grep ESTABLISHED”

在操作系统下面看下连接状态

iptables丢包?

sysctl -a | grep tcp

net.ipv4.tcp_abort_on_overflow = 0

net.ipv4.tcp_adv_win_scale = 1

net.ipv4.tcp_allowed_congestion_control = reno cubic

net.ipv4.tcp_app_win = 31

net.ipv4.tcp_autocorking = 1

net.ipv4.tcp_available_congestion_control = reno cubic

net.ipv4.tcp_available_ulp = espintcp mptcp

net.ipv4.tcp_base_mss = 1024

net.ipv4.tcp_challenge_ack_limit = 1000

net.ipv4.tcp_comp_sack_delay_ns = 1000000

net.ipv4.tcp_comp_sack_nr = 44

net.ipv4.tcp_congestion_control = cubic

net.ipv4.tcp_dsack = 1

net.ipv4.tcp_early_demux = 1

net.ipv4.tcp_early_retrans = 3

net.ipv4.tcp_ecn = 2

net.ipv4.tcp_ecn_fallback = 1

net.ipv4.tcp_fack = 0

net.ipv4.tcp_fastopen = 1

net.ipv4.tcp_fastopen_blackhole_timeout_sec = 3600

net.ipv4.tcp_fastopen_key = 00000000-00000000-00000000-00000000

net.ipv4.tcp_fin_timeout = 60

net.ipv4.tcp_frto = 2

net.ipv4.tcp_fwmark_accept = 0

net.ipv4.tcp_invalid_ratelimit = 500

net.ipv4.tcp_keepalive_intvl = 75

net.ipv4.tcp_keepalive_probes = 9

net.ipv4.tcp_keepalive_time = 7200

net.ipv4.tcp_l3mdev_accept = 0

net.ipv4.tcp_limit_output_bytes = 262144

net.ipv4.tcp_low_latency = 0

net.ipv4.tcp_max_orphans = 262144

net.ipv4.tcp_max_reordering = 300

net.ipv4.tcp_max_syn_backlog = 2048

net.ipv4.tcp_max_tw_buckets = 4096

net.ipv4.tcp_mem = 13676310 18235082 27352620

net.ipv4.tcp_min_rtt_wlen = 300

net.ipv4.tcp_min_snd_mss = 48

net.ipv4.tcp_min_tso_segs = 2

net.ipv4.tcp_moderate_rcvbuf = 1

net.ipv4.tcp_mtu_probing = 0

net.ipv4.tcp_no_metrics_save = 0

net.ipv4.tcp_notsent_lowat = 4294967295

net.ipv4.tcp_orphan_retries = 0

net.ipv4.tcp_pacing_ca_ratio = 120

net.ipv4.tcp_pacing_ss_ratio = 200

net.ipv4.tcp_probe_interval = 600

net.ipv4.tcp_probe_threshold = 8

net.ipv4.tcp_recovery = 1

net.ipv4.tcp_reordering = 3

net.ipv4.tcp_retrans_collapse = 1

net.ipv4.tcp_retries1 = 3

net.ipv4.tcp_retries2 = 15

net.ipv4.tcp_rfc1337 = 0

net.ipv4.tcp_rmem = 4096 87380 6291456

net.ipv4.tcp_sack = 1

net.ipv4.tcp_slow_start_after_idle = 0

net.ipv4.tcp_stdurg = 0

net.ipv4.tcp_syn_retries = 6

net.ipv4.tcp_synack_retries = 5

net.ipv4.tcp_syncookies = 0

net.ipv4.tcp_thin_linear_timeouts = 0

net.ipv4.tcp_timestamps = 1

net.ipv4.tcp_tso_win_divisor = 3

net.ipv4.tcp_tw_reuse = 2

net.ipv4.tcp_tx_skb_cache = 0

net.ipv4.tcp_window_scaling = 1

net.ipv4.tcp_wmem = 4096 16384 4194304

net.ipv4.tcp_workaround_signed_windows = 0

net.mptcp.add_addr_timeout = 120

net.mptcp.enabled = 0

添加了一些复现的步骤~

是的,在客户端执行
iptables -D OUTPUT -d -p tcp -j DROP;iptables -D INPUT -s -p tcp -j DROP
丢掉和tikv节点之间所有的报文

使用netstat查看的。
服务端tcp连接一直存在,状态一直是ESTABLISHED,定时器off。
即便tikv的客户端已经用iptables drop了所有tcp报文,服务端看到的连接依然存在,且状态完全不变。但是从客户端看tcp已经没有了。

gRPC 默认在“无数据传输”时会静默停止心跳探测 。通过开启 grpc-keepalive-permit-without-calls = true , 强制 TiKV 保持对连接的活性检测,从而解决非优雅退出导致的连接泄漏问题。

tikv的服务端好像并没有暴露这个参数,没办法配置为true

这个是参数配置,启动 reload 就可以把/