mirror of https://github.com/grpc/grpc-java.git
netty: Add config for server keepalive enforcement
Now that there is a config, the new defaults are now being enabled. Previously there were no default limits. Now keepalives may not be more frequent than every 5 minutes and only when there are outstanding RPCs.
This commit is contained in:
parent
ebd2f2d2f7
commit
4236027713
|
|
@ -77,13 +77,16 @@ class NettyServer implements InternalServer {
|
|||
private final int maxHeaderListSize;
|
||||
private final long keepAliveTimeInNanos;
|
||||
private final long keepAliveTimeoutInNanos;
|
||||
private final boolean permitKeepAliveWithoutCalls;
|
||||
private final long permitKeepAliveTimeInNanos;
|
||||
private final ReferenceCounted eventLoopReferenceCounter = new EventLoopReferenceCounter();
|
||||
|
||||
NettyServer(SocketAddress address, Class<? extends ServerChannel> channelType,
|
||||
@Nullable EventLoopGroup bossGroup, @Nullable EventLoopGroup workerGroup,
|
||||
ProtocolNegotiator protocolNegotiator, int maxStreamsPerConnection,
|
||||
int flowControlWindow, int maxMessageSize, int maxHeaderListSize,
|
||||
long keepAliveTimeInNanos, long keepAliveTimeoutInNanos) {
|
||||
long keepAliveTimeInNanos, long keepAliveTimeoutInNanos,
|
||||
boolean permitKeepAliveWithoutCalls, long permitKeepAliveTimeInNanos) {
|
||||
this.address = address;
|
||||
this.channelType = checkNotNull(channelType, "channelType");
|
||||
this.bossGroup = bossGroup;
|
||||
|
|
@ -97,6 +100,8 @@ class NettyServer implements InternalServer {
|
|||
this.maxHeaderListSize = maxHeaderListSize;
|
||||
this.keepAliveTimeInNanos = keepAliveTimeInNanos;
|
||||
this.keepAliveTimeoutInNanos = keepAliveTimeoutInNanos;
|
||||
this.permitKeepAliveWithoutCalls = permitKeepAliveWithoutCalls;
|
||||
this.permitKeepAliveTimeInNanos = permitKeepAliveTimeInNanos;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -130,7 +135,8 @@ class NettyServer implements InternalServer {
|
|||
public void initChannel(Channel ch) throws Exception {
|
||||
NettyServerTransport transport = new NettyServerTransport(ch, protocolNegotiator,
|
||||
maxStreamsPerConnection, flowControlWindow, maxMessageSize, maxHeaderListSize,
|
||||
keepAliveTimeInNanos, keepAliveTimeoutInNanos);
|
||||
keepAliveTimeInNanos, keepAliveTimeoutInNanos, permitKeepAliveWithoutCalls,
|
||||
permitKeepAliveTimeInNanos);
|
||||
ServerTransportListener transportListener;
|
||||
// This is to order callbacks on the listener, not to guard access to channel.
|
||||
synchronized (NettyServer.this) {
|
||||
|
|
|
|||
|
|
@ -81,6 +81,8 @@ public final class NettyServerBuilder extends AbstractServerImplBuilder<NettySer
|
|||
private int maxHeaderListSize = GrpcUtil.DEFAULT_MAX_HEADER_LIST_SIZE;
|
||||
private long keepAliveTimeInNanos = DEFAULT_SERVER_KEEPALIVE_TIME_NANOS;
|
||||
private long keepAliveTimeoutInNanos = DEFAULT_SERVER_KEEPALIVE_TIMEOUT_NANOS;
|
||||
private boolean permitKeepAliveWithoutCalls;
|
||||
private long permitKeepAliveTimeInNanos = TimeUnit.MINUTES.toNanos(5);
|
||||
|
||||
/**
|
||||
* Creates a server builder that will bind to the given port.
|
||||
|
|
@ -274,6 +276,54 @@ public final class NettyServerBuilder extends AbstractServerImplBuilder<NettySer
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the most aggressive keep-alive time clients are permitted to configure. The server will
|
||||
* try to detect clients exceeding this rate and when detected will forcefully close the
|
||||
* connection. The default is 5 minutes.
|
||||
*
|
||||
* <p>Even though a default is defined that allows some keep-alives, clients must not use
|
||||
* keep-alive without approval from the service owner. Otherwise, they may experience failures in
|
||||
* the future if the service becomes more restrictive. When unthrottled, keep-alives can cause a
|
||||
* significant amount of traffic and CPU usage, so clients and servers should be conservative in
|
||||
* what they use and accept.
|
||||
*
|
||||
* @see #denyKeepAliveWithoutCalls()
|
||||
* @see #permitKeepAliveWithoutCalls()
|
||||
* @since 1.3.0
|
||||
*/
|
||||
public NettyServerBuilder permitKeepAliveTime(long keepAliveTime, TimeUnit timeUnit) {
|
||||
checkArgument(keepAliveTime >= 0, "permit keepalive time must be non-negative");
|
||||
permitKeepAliveTimeInNanos = timeUnit.toNanos(keepAliveTime);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow clients to send keep-alive HTTP/2 PINGs even if there are no outstanding RPCs on the
|
||||
* connection.
|
||||
*
|
||||
* @see #denyKeepAliveWithoutCalls()
|
||||
* @see #permitKeepAliveTime(long, TimeUnit)
|
||||
* @since 1.3.0
|
||||
*/
|
||||
public NettyServerBuilder permitKeepAliveWithoutCalls() {
|
||||
permitKeepAliveWithoutCalls = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Only allow clients to send keep-alive HTTP/2 PINGs when there are outstanding RPCs on the
|
||||
* connection. This reduces the resources idle connections may consume, reducing the impact of
|
||||
* permitting keep-alive. This is the default.
|
||||
*
|
||||
* @see #permitKeepAliveWithoutCalls()
|
||||
* @see #permitKeepAliveTime(long, TimeUnit)
|
||||
* @since 1.3.0
|
||||
*/
|
||||
public NettyServerBuilder denyKeepAliveWithoutCalls() {
|
||||
permitKeepAliveWithoutCalls = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
@CheckReturnValue
|
||||
protected NettyServer buildTransportServer() {
|
||||
|
|
@ -284,7 +334,8 @@ public final class NettyServerBuilder extends AbstractServerImplBuilder<NettySer
|
|||
}
|
||||
return new NettyServer(address, channelType, bossEventLoopGroup, workerEventLoopGroup,
|
||||
negotiator, maxConcurrentCallsPerConnection, flowControlWindow, maxMessageSize,
|
||||
maxHeaderListSize, keepAliveTimeInNanos, keepAliveTimeoutInNanos);
|
||||
maxHeaderListSize, keepAliveTimeInNanos, keepAliveTimeoutInNanos,
|
||||
permitKeepAliveWithoutCalls, permitKeepAliveTimeInNanos);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -118,7 +118,9 @@ class NettyServerHandler extends AbstractNettyHandler {
|
|||
int maxHeaderListSize,
|
||||
int maxMessageSize,
|
||||
long keepAliveTimeInNanos,
|
||||
long keepAliveTimeoutInNanos) {
|
||||
long keepAliveTimeoutInNanos,
|
||||
boolean permitKeepAliveWithoutCalls,
|
||||
long permitKeepAliveTimeInNanos) {
|
||||
Preconditions.checkArgument(maxHeaderListSize > 0, "maxHeaderListSize must be positive");
|
||||
Http2FrameLogger frameLogger = new Http2FrameLogger(LogLevel.DEBUG, NettyServerHandler.class);
|
||||
Http2HeadersDecoder headersDecoder = new GrpcHttp2ServerHeadersDecoder(maxHeaderListSize);
|
||||
|
|
@ -127,7 +129,8 @@ class NettyServerHandler extends AbstractNettyHandler {
|
|||
Http2FrameWriter frameWriter =
|
||||
new Http2OutboundFrameLogger(new DefaultHttp2FrameWriter(), frameLogger);
|
||||
return newHandler(frameReader, frameWriter, transportListener, maxStreams, flowControlWindow,
|
||||
maxHeaderListSize, maxMessageSize, keepAliveTimeInNanos, keepAliveTimeoutInNanos, true, 0);
|
||||
maxHeaderListSize, maxMessageSize, keepAliveTimeInNanos, keepAliveTimeoutInNanos,
|
||||
permitKeepAliveWithoutCalls, permitKeepAliveTimeInNanos);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
|
|
|
|||
|
|
@ -60,10 +60,13 @@ class NettyServerTransport implements ServerTransport {
|
|||
private final int maxHeaderListSize;
|
||||
private final long keepAliveTimeInNanos;
|
||||
private final long keepAliveTimeoutInNanos;
|
||||
private final boolean permitKeepAliveWithoutCalls;
|
||||
private final long permitKeepAliveTimeInNanos;
|
||||
|
||||
NettyServerTransport(Channel channel, ProtocolNegotiator protocolNegotiator, int maxStreams,
|
||||
int flowControlWindow, int maxMessageSize, int maxHeaderListSize, long keepAliveTimeInNanos,
|
||||
long keepAliveTimeoutInNanos) {
|
||||
long keepAliveTimeoutInNanos, boolean permitKeepAliveWithoutCalls,
|
||||
long permitKeepAliveTimeInNanos) {
|
||||
this.channel = Preconditions.checkNotNull(channel, "channel");
|
||||
this.protocolNegotiator = Preconditions.checkNotNull(protocolNegotiator, "protocolNegotiator");
|
||||
this.maxStreams = maxStreams;
|
||||
|
|
@ -72,6 +75,8 @@ class NettyServerTransport implements ServerTransport {
|
|||
this.maxHeaderListSize = maxHeaderListSize;
|
||||
this.keepAliveTimeInNanos = keepAliveTimeInNanos;
|
||||
this.keepAliveTimeoutInNanos = keepAliveTimeoutInNanos;
|
||||
this.permitKeepAliveWithoutCalls = permitKeepAliveWithoutCalls;
|
||||
this.permitKeepAliveTimeInNanos = permitKeepAliveTimeInNanos;
|
||||
}
|
||||
|
||||
public void start(ServerTransportListener listener) {
|
||||
|
|
@ -135,6 +140,7 @@ class NettyServerTransport implements ServerTransport {
|
|||
*/
|
||||
private NettyServerHandler createHandler(ServerTransportListener transportListener) {
|
||||
return NettyServerHandler.newHandler(transportListener, maxStreams, flowControlWindow,
|
||||
maxHeaderListSize, maxMessageSize, keepAliveTimeInNanos, keepAliveTimeoutInNanos);
|
||||
maxHeaderListSize, maxMessageSize, keepAliveTimeInNanos, keepAliveTimeoutInNanos,
|
||||
permitKeepAliveWithoutCalls, permitKeepAliveTimeInNanos);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -481,7 +481,7 @@ public class NettyClientTransportTest {
|
|||
server = new NettyServer(TestUtils.testServerAddress(0),
|
||||
NioServerSocketChannel.class, group, group, negotiator,
|
||||
maxStreamsPerConnection, DEFAULT_WINDOW_SIZE, DEFAULT_MAX_MESSAGE_SIZE, maxHeaderListSize,
|
||||
DEFAULT_SERVER_KEEPALIVE_TIME_NANOS, DEFAULT_SERVER_KEEPALIVE_TIMEOUT_NANOS);
|
||||
DEFAULT_SERVER_KEEPALIVE_TIME_NANOS, DEFAULT_SERVER_KEEPALIVE_TIMEOUT_NANOS, true, 0);
|
||||
server.start(serverListener);
|
||||
address = TestUtils.testServerAddress(server.getPort());
|
||||
authority = GrpcUtil.authorityFromHostAndPort(address.getHostString(), address.getPort());
|
||||
|
|
|
|||
|
|
@ -59,7 +59,9 @@ public class NettyServerTest {
|
|||
1, // ignore
|
||||
1, // ignore
|
||||
1, // ignore
|
||||
1); // ignore
|
||||
1, // ignore
|
||||
true, // ignore
|
||||
0); // ignore
|
||||
ns.start(new ServerListener() {
|
||||
@Override
|
||||
public ServerTransportListener transportCreated(ServerTransport transport) {
|
||||
|
|
@ -91,7 +93,9 @@ public class NettyServerTest {
|
|||
1, // ignore
|
||||
1, // ignore
|
||||
1, // ignore
|
||||
1); // ignore
|
||||
1, // ignore
|
||||
true, // ignore
|
||||
0); // ignore
|
||||
|
||||
assertThat(ns.getPort()).isEqualTo(-1);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue