基于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 {
@VisibleForTesting
static final int MAX_PING_STRIKES = 2;
@VisibleForTesting
static 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);
} @VisibleForTesting
KeepAliveEnforcer(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. */
@CheckReturnValue
public 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 correctness
return time1 - time2;
} @VisibleForTesting
interface Ticker {
long nanoTime();
} @VisibleForTesting
static class SystemTicker implements Ticker {
public static final SystemTicker INSTANCE = new SystemTicker(); @Override
public 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,而且cient keepAliveTime需要>=minTimeNanos

gRPC(Java) keepAlive机制研究的更多相关文章

  1. 大牛带你学会java类加载机制,不要错过,值得收藏!

    很多人对java类加载机制都是非常抗拒的,因为这个太难理解了,但是我们作为一名优秀的java工程师,还是要把java类加载机制研究和学习明白的,因为这对于我们在以后的工作中有很大的帮助,因为它在jav ...

  2. Java反射机制深入研究

    ava 反射是Java语言的一个很重要的特征,它使得Java具体了“动态性”.   在Java运行时环境中,对于任意一个类,能否知道这个类有哪些属性和方法?对于任意一个对象,能否调用它的任意一个方法? ...

  3. Java反射机制学习与研究

    Java反射机制:可以获取正在运行时的Java对象. 1.判断运行时对象对象所属的类. 2.判断运行时对象所具有的成员变量和方法. 3.还可以调用到private方法,改变private变量的值. S ...

  4. Java反射机制的学习

    Java反射机制是Java语言被视为准动态语言的关键性质.Java反射机制的核心就是允许在运行时通过Java Reflection APIs来取得已知名字的class类的相关信息,动态地生成此类,并调 ...

  5. java 反射机制01

    // */ // ]]>   java反射机制01 Table of Contents 1 反射机制 2 反射成员 2.1 java.lang.Class 2.2 Constructor 2.3 ...

  6. java反射机制浅谈

    一.Java的反射机制浅谈 最近研究java研究得很给力,主要以看博文为学习方式.以下是我对java的反射机制所产生的一些感悟,希望各位童鞋看到失误之处不吝指出.受到各位指教之处,如若让小生好好感动, ...

  7. Java类加载机制深度分析

    转自:http://my.oschina.net/xianggao/blog/70826 参考:http://www.ibm.com/developerworks/cn/java/j-lo-class ...

  8. Java反射机制(转载)

    原文链接:http://www.blogjava.net/zh-weir/archive/2011/03/26/347063.html Java反射机制是Java语言被视为准动态语言的关键性质.Jav ...

  9. Java 反射机制浅析

    Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性:这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反 ...

  10. 全面理解java异常机制

    在理想状态下,程序会按照我们预想的步骤一步一步的执行,但是即使你是大V,你也不可避免出错,所以java为我们提供了异常机制.本文将会从以下几个方面介绍java中的异常机制: 异常机制的层次结构 异常的 ...

随机推荐

  1. Session认证机制与JWT认证机制

    一.什么是身份认证? 身份认证(Authentication)又称"身份验证"."鉴权",是指通过一定的手段,完成对用户身份的确认.日常生活中的身份认证随处可见 ...

  2. [双重 for 循环]打印一个倒三角形

    [双重 for 循环]打印一个倒三角形 核心算法 里层循环:j = i; j <= 10; j++ 当i=1时,j=1 , j<=10,j++,打印10个星星 当i=2时,j=2 , j& ...

  3. KingbaseES 参数 - ignore_char_null_check

    KingbaseES 基于PostgreSQL进行了大量的Oracle兼容性开发,为了能同时兼容Oracle 和 PG 的特性,增加参数进行控制.以下介绍 KingbaseES 下特有的参数 igno ...

  4. 阿里云CentOS7安装K8S

    1. 在阿里云山申请三台云服务器 1.1 环境准备 完成配置后的信息 服务器IP 操作系统 CPU 内存 硬盘 主机名 节点角色 172.18.119.145 centos7 2 4G 50G k8s ...

  5. 【Android 逆向】ARM switch 逆向

    #include <stdio.h> int switch1(int a, int b, int i){ switch (i){ case 1: return a + b; break; ...

  6. Git Bash(提交文件到GitHub进行托管)

    ​ Introduction 使用Git Bash命令,可以将一个项目上传到Github官网中,进行托管,避免重要文件被误删 1.Git工具下载 Git for Windows 2.在github中新 ...

  7. 多版本并发控制 MVCC

    介绍多版本并发控制 多版本并发控制技术(Multiversion Concurrency Control,MVCC) 技术是为了解决问题而生的,通过 MVCC 我们可以解决以下几个问题: 读写之间阻塞 ...

  8. 以软件定义物联网芯片,以技术融合推动LPWAN2.0泛在物联

    作为数字化产业重要的基础设施之一,物联网迎来了黄金发展期.物联网通信技术通过数据的采集.分析.输出,从浅层次的互联工具和产品深化,到成为重塑生产组织方式的基础设施和关键要素,正深刻地改变着传统产业形态 ...

  9. Java安全之Velocity模版注入

    Java安全之Velocity模版注入 Apache Velocity Apache Velocity是一个基于Java的模板引擎,它提供了一个模板语言去引用由Java代码定义的对象.它允许web 页 ...

  10. NSIS自定义目录选择页面制作之安装…

    在nsis制作自定义界面中,目录选择页面个人感觉最为繁琐,因为该界面不仅涉及到界面控件的创建,还要涉及到控件消息传递和状态改变时的回调函数通告. 迅雷界面为例: 其中安装目录中的8盘符,在本机中并不存 ...