nl.topicus.jdbc.shaded.io.grpc.netty.NettyServerTransport Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of spanner-jdbc Show documentation
Show all versions of spanner-jdbc Show documentation
JDBC Driver for Google Cloud Spanner
/*
* Copyright 2014, gRPC Authors All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package nl.topicus.jdbc.shaded.io.grpc.netty;
import nl.topicus.jdbc.shaded.com.google.common.annotations.VisibleForTesting;
import nl.topicus.jdbc.shaded.com.google.common.base.Preconditions;
import nl.topicus.jdbc.shaded.com.google.common.collect.ImmutableList;
import nl.topicus.jdbc.shaded.com.google.common.util.concurrent.ListenableFuture;
import nl.topicus.jdbc.shaded.com.google.common.util.concurrent.SettableFuture;
import nl.topicus.jdbc.shaded.io.grpc.InternalLogId;
import nl.topicus.jdbc.shaded.io.grpc.InternalTransportStats;
import nl.topicus.jdbc.shaded.io.grpc.ServerStreamTracer;
import nl.topicus.jdbc.shaded.io.grpc.Status;
import nl.topicus.jdbc.shaded.io.grpc.internal.ServerTransport;
import nl.topicus.jdbc.shaded.io.grpc.internal.ServerTransportListener;
import nl.topicus.jdbc.shaded.io.grpc.internal.TransportTracer;
import nl.topicus.jdbc.shaded.io.netty.channel.Channel;
import nl.topicus.jdbc.shaded.io.netty.channel.ChannelFuture;
import nl.topicus.jdbc.shaded.io.netty.channel.ChannelFutureListener;
import nl.topicus.jdbc.shaded.io.netty.channel.ChannelHandler;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.ScheduledExecutorService;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* The Netty-based server transport.
*/
class NettyServerTransport implements ServerTransport {
private static final Logger log = Logger.getLogger(NettyServerTransport.class.getName());
// connectionLog is for connection related messages only
private static final Logger connectionLog = Logger.getLogger(
String.format("%s.connections", NettyServerTransport.class.getName()));
// Some exceptions are not very useful and add too much noise to the log
private static final ImmutableList QUIET_ERRORS = ImmutableList.of(
"Connection reset by peer",
"An existing connection was forcibly closed by the remote host");
private final InternalLogId logId = InternalLogId.allocate(getClass().getName());
private final Channel channel;
private final ProtocolNegotiator protocolNegotiator;
private final int maxStreams;
private ServerTransportListener listener;
private boolean terminated;
private final int flowControlWindow;
private final int maxMessageSize;
private final int maxHeaderListSize;
private final long keepAliveTimeInNanos;
private final long keepAliveTimeoutInNanos;
private final long maxConnectionIdleInNanos;
private final long maxConnectionAgeInNanos;
private final long maxConnectionAgeGraceInNanos;
private final boolean permitKeepAliveWithoutCalls;
private final long permitKeepAliveTimeInNanos;
private final List streamTracerFactories;
private final TransportTracer transportTracer;
NettyServerTransport(
Channel channel, ProtocolNegotiator protocolNegotiator,
List streamTracerFactories,
TransportTracer transportTracer, int maxStreams,
int flowControlWindow, int maxMessageSize, int maxHeaderListSize,
long keepAliveTimeInNanos, long keepAliveTimeoutInNanos,
long maxConnectionIdleInNanos,
long maxConnectionAgeInNanos, long maxConnectionAgeGraceInNanos,
boolean permitKeepAliveWithoutCalls,long permitKeepAliveTimeInNanos) {
this.channel = Preconditions.checkNotNull(channel, "channel");
this.protocolNegotiator = Preconditions.checkNotNull(protocolNegotiator, "protocolNegotiator");
this.streamTracerFactories =
Preconditions.checkNotNull(streamTracerFactories, "streamTracerFactories");
this.transportTracer = Preconditions.checkNotNull(transportTracer, "transportTracer");
this.maxStreams = maxStreams;
this.flowControlWindow = flowControlWindow;
this.maxMessageSize = maxMessageSize;
this.maxHeaderListSize = maxHeaderListSize;
this.keepAliveTimeInNanos = keepAliveTimeInNanos;
this.keepAliveTimeoutInNanos = keepAliveTimeoutInNanos;
this.maxConnectionIdleInNanos = maxConnectionIdleInNanos;
this.maxConnectionAgeInNanos = maxConnectionAgeInNanos;
this.maxConnectionAgeGraceInNanos = maxConnectionAgeGraceInNanos;
this.permitKeepAliveWithoutCalls = permitKeepAliveWithoutCalls;
this.permitKeepAliveTimeInNanos = permitKeepAliveTimeInNanos;
}
public void start(ServerTransportListener listener) {
Preconditions.checkState(this.listener == null, "Handler already registered");
this.listener = listener;
// Create the Netty handler for the pipeline.
final NettyServerHandler grpcHandler = createHandler(listener);
NettyHandlerSettings.setAutoWindow(grpcHandler);
// Notify when the channel closes.
channel.closeFuture().addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
notifyTerminated(grpcHandler.connectionError());
}
});
ChannelHandler negotiationHandler = protocolNegotiator.newHandler(grpcHandler);
channel.pipeline().addLast(negotiationHandler);
}
@Override
public ScheduledExecutorService getScheduledExecutorService() {
return channel.eventLoop();
}
@Override
public void shutdown() {
if (channel.isOpen()) {
channel.close();
}
}
@Override
public void shutdownNow(Status reason) {
if (channel.isOpen()) {
channel.writeAndFlush(new ForcefulCloseCommand(reason));
}
}
@Override
public InternalLogId getLogId() {
return logId;
}
/**
* For testing purposes only.
*/
Channel channel() {
return channel;
}
/**
* Accepts a throwable and returns the appropriate logging level. Uninteresting exceptions
* should not clutter the log.
*/
@VisibleForTesting
static Level getLogLevel(Throwable t) {
if (t instanceof IOException && t.getMessage() != null) {
for (String msg : QUIET_ERRORS) {
if (t.getMessage().equals(msg)) {
return Level.FINE;
}
}
}
return Level.INFO;
}
private void notifyTerminated(Throwable t) {
if (t != null) {
connectionLog.log(getLogLevel(t), "Transport failed", t);
}
if (!terminated) {
terminated = true;
listener.transportTerminated();
}
}
@Override
public ListenableFuture getStats() {
final SettableFuture result = SettableFuture.create();
if (channel.eventLoop().inEventLoop()) {
// This is necessary, otherwise we will block forever if we get the future from inside
// the event loop.
result.set(transportTracer.getStats());
return result;
}
channel.eventLoop().submit(
new Runnable() {
@Override
public void run() {
result.set(transportTracer.getStats());
}
});
return result;
}
/**
* Creates the Netty handler to be used in the channel pipeline.
*/
private NettyServerHandler createHandler(ServerTransportListener transportListener) {
return NettyServerHandler.newHandler(
transportListener, streamTracerFactories, transportTracer, maxStreams,
flowControlWindow, maxHeaderListSize, maxMessageSize,
keepAliveTimeInNanos, keepAliveTimeoutInNanos,
maxConnectionIdleInNanos,
maxConnectionAgeInNanos, maxConnectionAgeGraceInNanos,
permitKeepAliveWithoutCalls, permitKeepAliveTimeInNanos);
}
}