gRPC(Java) keepAlive机制研究

news/2024/10/11 17:48:11/

文章目录

    • 结论
    • Client端KeepAlive
      • 使用入口
      • 简要时序列表
    • Server端KeepAlive
      • 使用入口
      • 简要时序列表
      • KeepAliveEnforcer

基于java gRPC 1.24.2 分析

结论

  1. gRPC keepAlive是grpc框架在应用层面连接保活的一种措施。即当grpc连接上没有业务数据时,是否发送pingpong,以保持连接活跃性,不因长时间空闲而被Server或操作系统关闭
  2. gRPC keepAlive在client与server都有,client端默认关闭(keepAliveTime为Long.MAX_VALUE), server端默认打开,keepAliveTime为2小时,即每2小时向client发送一次ping
// io.grpc.internal.GrpcUtil
public static final long DEFAULT_SERVER_KEEPALIVE_TIME_NANOS = TimeUnit.HOURS.toNanos(2L);
  1. KeepAlive的管理使用类io.grpc.internal.KeepAliveManager, 用于管理KeepAlive状态,ping任务调度与执行.

Client端KeepAlive

使用入口

  1. 我们在使用io.grpc框架创建grpc连接的时候,可以设置keeplive, 例如下面:
NettyChannelBuilder builder = NettyChannelBuilder.forTarget(String.format("grpc://%s", provider)) //.usePlaintext() //.defaultLoadBalancingPolicy(props.getBalancePolicy()) //.maxInboundMessageSize(props.getMaxInboundMessageSize()) //.keepAliveTime(1,TimeUnit.MINUTES).keepAliveWithoutCalls(true).keepAliveTimeout(10,TimeUnit.SECONDS).intercept(channelManager.getInterceptors()); //
  1. 其中与keepAlive相关的参数有三个,keepAliveTime,keepAliveTimeout,keepAliveWithoutCalls。这三个变量有什么作用呢?
  • keepAliveTime: 表示当grpc连接没有数据传递时,多久之后开始向server发送ping packet
  • keepAliveTimeout: 表示当发送完ping packet后多久没收到server回应算超时
  • keepAliveTimeoutCalls: 表示如果grpc连接没有数据传递时,是否keepAlive,默认为false

简要时序列表

Create & Start

NettyChannelBuilder-----> NettyTransportFactory---------> NettyClientTransport-------------> KeepAliveManager & NettyClientHandler

响应各种事件
当Active、Idle、DataReceived、Started、Termination事件发生时,更改KeepAlive状态,调度发送ping任务。

Server端KeepAlive

使用入口

// 只截取关键代码,详细代码请看`NettyServerBuilder`
ServerImpl server = new ServerImpl(this,buildTransportServers(getTracerFactories()),Context.ROOT);
for (InternalNotifyOnServerBuild notifyTarget : notifyOnBuildList) {notifyTarget.notifyOnBuild(server);
}
return server;// 在buildTransportServers方法中创建NettyServer
List<NettyServer> transportServers = new ArrayList<>(listenAddresses.size());
for (SocketAddress listenAddress : listenAddresses) {NettyServer transportServer = new NettyServer(listenAddress, resolvedChannelType, channelOptions, bossEventLoopGroupPool,workerEventLoopGroupPool, negotiator, streamTracerFactories,getTransportTracerFactory(), maxConcurrentCallsPerConnection, flowControlWindow,maxMessageSize, maxHeaderListSize, keepAliveTimeInNanos, keepAliveTimeoutInNanos,maxConnectionIdleInNanos, maxConnectionAgeInNanos, maxConnectionAgeGraceInNanos,permitKeepAliveWithoutCalls, permitKeepAliveTimeInNanos, getChannelz());transportServers.add(transportServer);
}

简要时序列表

Create & Start

NettyServerBuilder---> NettyServer---------> NettyServerTransport-------------> NettyServerHandler-----------------> KeepAliveEnforcer

连接准备就绪
调用 io.netty.channel.ChannelHandler的handlerAdded方法,关于此方法的描述:

Gets called after the ChannelHandler was added to the actual context and it's ready to handle events.
NettyServerHandler(handlerAdded)---> 创建KeepAliveManager对象

响应各种事件
同Client

KeepAliveEnforcer

在上面Server端的简要时序图中,可以看见,server端有一个特有的io.grpc.netty.KeepAliveEnforcer
此类的作用是监控clinet ping的频率,以确保其在一个合理范围内。

package io.grpc.netty;import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import java.util.concurrent.TimeUnit;
import javax.annotation.CheckReturnValue;/** Monitors the client's PING usage to make sure the rate is permitted. */
class KeepAliveEnforcer {@VisibleForTestingstatic final int MAX_PING_STRIKES = 2;@VisibleForTestingstatic final long IMPLICIT_PERMIT_TIME_NANOS = TimeUnit.HOURS.toNanos(2);private final boolean permitWithoutCalls;private final long minTimeNanos;private final Ticker ticker;private final long epoch;private long lastValidPingTime;private boolean hasOutstandingCalls;private int pingStrikes;public KeepAliveEnforcer(boolean permitWithoutCalls, long minTime, TimeUnit unit) {this(permitWithoutCalls, minTime, unit, SystemTicker.INSTANCE);}@VisibleForTestingKeepAliveEnforcer(boolean permitWithoutCalls, long minTime, TimeUnit unit, Ticker ticker) {Preconditions.checkArgument(minTime >= 0, "minTime must be non-negative");this.permitWithoutCalls = permitWithoutCalls;this.minTimeNanos = Math.min(unit.toNanos(minTime), IMPLICIT_PERMIT_TIME_NANOS);this.ticker = ticker;this.epoch = ticker.nanoTime();lastValidPingTime = epoch;}/** Returns {@code false} when client is misbehaving and should be disconnected. */@CheckReturnValuepublic boolean pingAcceptable() {long now = ticker.nanoTime();boolean valid;if (!hasOutstandingCalls && !permitWithoutCalls) {valid = compareNanos(lastValidPingTime + IMPLICIT_PERMIT_TIME_NANOS, now) <= 0;} else {valid = compareNanos(lastValidPingTime + minTimeNanos, now) <= 0;}if (!valid) {pingStrikes++;return !(pingStrikes > MAX_PING_STRIKES);} else {lastValidPingTime = now;return true;}}/*** Reset any counters because PINGs are allowed in response to something sent. Typically called* when sending HEADERS and DATA frames.*/public void resetCounters() {lastValidPingTime = epoch;pingStrikes = 0;}/** There are outstanding RPCs on the transport. */public void onTransportActive() {hasOutstandingCalls = true;}/** There are no outstanding RPCs on the transport. */public void onTransportIdle() {hasOutstandingCalls = false;}/*** Positive when time1 is greater; negative when time2 is greater; 0 when equal. It is important* to use something like this instead of directly comparing nano times. See {@link* System#nanoTime}.*/private static long compareNanos(long time1, long time2) {// Possibility of overflow/underflow is on purpose and necessary for correctnessreturn time1 - time2;}@VisibleForTestinginterface Ticker {long nanoTime();}@VisibleForTestingstatic class SystemTicker implements Ticker {public static final SystemTicker INSTANCE = new SystemTicker();@Overridepublic long nanoTime() {return System.nanoTime();}}
}
  1. 先来看pingAcceptable方法,此方法是判断是否接受client ping。
  • lastValidPingTime是上次client valid ping的时间, 连接建立时此时间等于KeepAliveEnforcer对象创建的时间。当client ping有效时,其等于当时ping的时间
  • hasOutstandingCalls其初始值为false,当连接activie时,其值为true,当连接idle时,其值为false。如果grpc调用为阻塞时调用,则调用时连接变为active,调用完成,连接变为idle.
  • permitWithoutCalls其值是创建NettyServer时传入,默认为false.
  • IMPLICIT_PERMIT_TIME_NANOS其值为常量,2h
  • minTimeNanos其值是创建NettyServer时传入,默认为5min.
  • MAX_PING_STRIKES其值为常量2
  1. resetCounters方法是当grpc当中有数据时会被调用,即有grpc调用时lastValidPingTime和pingStrikes会被重置。
  2. 如果client要想使用keepAlive,permitWithoutCalls值需要设置为true,而且client keepAliveTime需要>=minTimeNanos

http://www.ppmy.cn/news/48239.html

相关文章

快手社招Java后端开发岗面试,被问麻了

社招面试是基于你的工作项目来展开问的&#xff0c;比如你项目用了 xxx 技术&#xff0c;那么面试就会追问你项目是怎么用 xxx 技术的&#xff0c;遇到什么难点和挑战&#xff0c;然后再考察一下这个 xxx 技术的原理。 今天就分享一位快手社招面经&#xff0c;岗位是后端开发&…

陪诊小程序开发|陪诊软件开发功能特色

为了提升就医的服务质量&#xff0c;人们对于医疗服务的需求也在不断提高。这几年随着生活水平和医疗水平的提升&#xff0c;陪诊服务越来越受到人们的重视和青睐&#xff0c;越来越多的人开始意识到&#xff0c;陪伴和关爱在疾病治疗过程中的重要性&#xff0c;为了更好的规划…

第四章 面向对象(OOP)

目录 一、编程思想 1.1. 面向对象 1.2. 面向过程 1.3.举例说明&#xff08;把大象装进冰箱&#xff09; 1.4.二者的联系与区别 1.5.面向对象的三个阶段 1.6.什么是类&#xff0c;什么是实例&#xff0c;二者的联系 二、面向对象三大特征 2.1 封装 2.2 继承 2.3 多态…

2023年全国最新二级建造师精选真题及答案56

百分百题库提供二级建造师考试试题、二建考试预测题、二级建造师考试真题、二建证考试题库等&#xff0c;提供在线做题刷题&#xff0c;在线模拟考试&#xff0c;助你考试轻松过关。 11.关于施工企业项目经理部的说法&#xff0c;正确的是&#xff08;&#xff09;。 A.项目经…

学习风`宇博客用户权限菜单模块

文章目录 用户-角色-菜单-资源 各表关系图菜单 和 路由菜单表及分析分析 /api/admin/user/menus接口MenuServiceImpl#listUserMenus接口返回示例及分析 前端代码分析menu.jsSideBar.vue 接口权限控制资源表 及 分析分析 WebSecurityConfig权限控制整体流程先说登录UserDetailsS…

MATLAB应用笔记

其他 1、NaN值 MATLAB判断数据是否为NaN可以直接使用函数&#xff1a;isnan() 三、数据分析 1、相关性 均值、方差、协方差、标准差、相关系数 mean() %均值 nanmean()%去除NAN值求均值 var() %方差 cov() %协方差 std() %标准差 corrcoef(B,b) %R 相关系数plot()…

node项目(一) koa脚手架的搭建

一、koa 安装 // 安装koa npm install -g koa-generator // 创建项目 koa2 项目名称 当出现这个框的时候安装完毕 之后就是进入目录文件&#xff0c;根据package.json执行即可 二、出现问题 汇总 问题一&#xff1a;koa-generator安装失败 没有出现koa-generator安装成功 …

【杂凑算法篇】密码杂凑算法的安全强度

【杂凑算法篇】密码杂凑算法的安全强度 杂凑&#xff08;哈希&#xff09;算法安全强度—【蘇小沐】 文章目录 【杂凑算法篇】密码杂凑算法的安全强度&#xff08;一&#xff09;安全强度&#xff08;Security Strength)&#xff08;二&#xff09;杂凑算法的安全强度与对比总…