Search in sources :

Example 1 with ClientKeepAlivePinger

use of io.grpc.internal.KeepAliveManager.ClientKeepAlivePinger in project grpc-java by grpc.

the class NettyClientTransport method start.

@SuppressWarnings("unchecked")
@Override
public Runnable start(Listener transportListener) {
    lifecycleManager = new ClientTransportLifecycleManager(Preconditions.checkNotNull(transportListener, "listener"));
    EventLoop eventLoop = group.next();
    if (keepAliveTimeNanos != KEEPALIVE_TIME_NANOS_DISABLED) {
        keepAliveManager = new KeepAliveManager(new ClientKeepAlivePinger(this), eventLoop, keepAliveTimeNanos, keepAliveTimeoutNanos, keepAliveWithoutCalls);
    }
    handler = NettyClientHandler.newHandler(lifecycleManager, keepAliveManager, autoFlowControl, flowControlWindow, maxHeaderListSize, GrpcUtil.STOPWATCH_SUPPLIER, tooManyPingsRunnable, transportTracer, eagAttributes, authorityString, channelLogger);
    ChannelHandler negotiationHandler = negotiator.newHandler(handler);
    Bootstrap b = new Bootstrap();
    b.option(ALLOCATOR, Utils.getByteBufAllocator(false));
    b.group(eventLoop);
    b.channelFactory(channelFactory);
    // For non-socket based channel, the option will be ignored.
    b.option(SO_KEEPALIVE, true);
    // For non-epoll based channel, the option will be ignored.
    if (keepAliveTimeNanos != KEEPALIVE_TIME_NANOS_DISABLED) {
        ChannelOption<Integer> tcpUserTimeout = Utils.maybeGetTcpUserTimeoutOption();
        if (tcpUserTimeout != null) {
            b.option(tcpUserTimeout, (int) TimeUnit.NANOSECONDS.toMillis(keepAliveTimeoutNanos));
        }
    }
    for (Map.Entry<ChannelOption<?>, ?> entry : channelOptions.entrySet()) {
        // Every entry in the map is obtained from
        // NettyChannelBuilder#withOption(ChannelOption<T> option, T value)
        // so it is safe to pass the key-value pair to b.option().
        b.option((ChannelOption<Object>) entry.getKey(), entry.getValue());
    }
    ChannelHandler bufferingHandler = new WriteBufferingAndExceptionHandler(negotiationHandler);
    /**
     * We don't use a ChannelInitializer in the client bootstrap because its "initChannel" method
     * is executed in the event loop and we need this handler to be in the pipeline immediately so
     * that it may begin buffering writes.
     */
    b.handler(bufferingHandler);
    ChannelFuture regFuture = b.register();
    if (regFuture.isDone() && !regFuture.isSuccess()) {
        channel = null;
        // Initialization has failed badly. All new streams should be made to fail.
        Throwable t = regFuture.cause();
        if (t == null) {
            t = new IllegalStateException("Channel is null, but future doesn't have a cause");
        }
        statusExplainingWhyTheChannelIsNull = Utils.statusFromThrowable(t);
        // Use a Runnable since lifecycleManager calls transportListener
        return new Runnable() {

            @Override
            public void run() {
                // NOTICE: we not are calling lifecycleManager from the event loop. But there isn't really
                // an event loop in this case, so nothing should be accessing the lifecycleManager. We
                // could use GlobalEventExecutor (which is what regFuture would use for notifying
                // listeners in this case), but avoiding on-demand thread creation in an error case seems
                // a good idea and is probably clearer threading.
                lifecycleManager.notifyTerminated(statusExplainingWhyTheChannelIsNull);
            }
        };
    }
    channel = regFuture.channel();
    // Start the write queue as soon as the channel is constructed
    handler.startWriteQueue(channel);
    // This write will have no effect, yet it will only complete once the negotiationHandler
    // flushes any pending writes. We need it to be staged *before* the `connect` so that
    // the channel can't have been closed yet, removing all handlers. This write will sit in the
    // AbstractBufferingHandler's buffer, and will either be flushed on a successful connection,
    // or failed if the connection fails.
    channel.writeAndFlush(NettyClientHandler.NOOP_MESSAGE).addListener(new ChannelFutureListener() {

        @Override
        public void operationComplete(ChannelFuture future) throws Exception {
            if (!future.isSuccess()) {
                // Need to notify of this failure, because NettyClientHandler may not have been added to
                // the pipeline before the error occurred.
                lifecycleManager.notifyTerminated(Utils.statusFromThrowable(future.cause()));
            }
        }
    });
    // Start the connection operation to the server.
    SocketAddress localAddress = localSocketPicker.createSocketAddress(remoteAddress, eagAttributes);
    if (localAddress != null) {
        channel.connect(remoteAddress, localAddress);
    } else {
        channel.connect(remoteAddress);
    }
    if (keepAliveManager != null) {
        keepAliveManager.onTransportStarted();
    }
    return null;
}
Also used : ChannelFuture(io.netty.channel.ChannelFuture) ChannelOption(io.netty.channel.ChannelOption) ChannelHandler(io.netty.channel.ChannelHandler) ChannelFutureListener(io.netty.channel.ChannelFutureListener) Http2ChannelClosedException(io.netty.handler.codec.http2.StreamBufferingEncoder.Http2ChannelClosedException) ClosedChannelException(java.nio.channels.ClosedChannelException) EventLoop(io.netty.channel.EventLoop) ClientKeepAlivePinger(io.grpc.internal.KeepAliveManager.ClientKeepAlivePinger) Bootstrap(io.netty.bootstrap.Bootstrap) KeepAliveManager(io.grpc.internal.KeepAliveManager) SocketAddress(java.net.SocketAddress) Map(java.util.Map)

Example 2 with ClientKeepAlivePinger

use of io.grpc.internal.KeepAliveManager.ClientKeepAlivePinger in project grpc-java by grpc.

the class OkHttpClientTransport method start.

@Override
public Runnable start(Listener listener) {
    this.listener = Preconditions.checkNotNull(listener, "listener");
    if (enableKeepAlive) {
        scheduler = SharedResourceHolder.get(TIMER_SERVICE);
        keepAliveManager = new KeepAliveManager(new ClientKeepAlivePinger(this), scheduler, keepAliveTimeNanos, keepAliveTimeoutNanos, keepAliveWithoutCalls);
        keepAliveManager.onTransportStarted();
    }
    if (isForTest()) {
        synchronized (lock) {
            frameWriter = new ExceptionHandlingFrameWriter(OkHttpClientTransport.this, testFrameWriter, testFrameLogger);
            outboundFlow = new OutboundFlowController(OkHttpClientTransport.this, frameWriter);
        }
        serializingExecutor.execute(new Runnable() {

            @Override
            public void run() {
                if (connectingCallback != null) {
                    connectingCallback.run();
                }
                clientFrameHandler = new ClientFrameHandler(testFrameReader, testFrameLogger);
                executor.execute(clientFrameHandler);
                synchronized (lock) {
                    maxConcurrentStreams = Integer.MAX_VALUE;
                    startPendingStreams();
                }
                connectedFuture.set(null);
            }
        });
        return null;
    }
    final AsyncSink asyncSink = AsyncSink.sink(serializingExecutor, this);
    final Variant variant = new Http2();
    FrameWriter rawFrameWriter = variant.newWriter(Okio.buffer(asyncSink), true);
    synchronized (lock) {
        frameWriter = new ExceptionHandlingFrameWriter(this, rawFrameWriter);
        outboundFlow = new OutboundFlowController(this, frameWriter);
    }
    final CountDownLatch latch = new CountDownLatch(1);
    // Connecting in the serializingExecutor, so that some stream operations like synStream
    // will be executed after connected.
    serializingExecutor.execute(new Runnable() {

        @Override
        public void run() {
            // initial preface.
            try {
                latch.await();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            // Use closed source on failure so that the reader immediately shuts down.
            BufferedSource source = Okio.buffer(new Source() {

                @Override
                public long read(Buffer sink, long byteCount) {
                    return -1;
                }

                @Override
                public Timeout timeout() {
                    return Timeout.NONE;
                }

                @Override
                public void close() {
                }
            });
            Socket sock;
            SSLSession sslSession = null;
            try {
                if (proxiedAddr == null) {
                    sock = socketFactory.createSocket(address.getAddress(), address.getPort());
                } else {
                    if (proxiedAddr.getProxyAddress() instanceof InetSocketAddress) {
                        sock = createHttpProxySocket(proxiedAddr.getTargetAddress(), (InetSocketAddress) proxiedAddr.getProxyAddress(), proxiedAddr.getUsername(), proxiedAddr.getPassword());
                    } else {
                        throw Status.INTERNAL.withDescription("Unsupported SocketAddress implementation " + proxiedAddr.getProxyAddress().getClass()).asException();
                    }
                }
                if (sslSocketFactory != null) {
                    SSLSocket sslSocket = OkHttpTlsUpgrader.upgrade(sslSocketFactory, hostnameVerifier, sock, getOverridenHost(), getOverridenPort(), connectionSpec);
                    sslSession = sslSocket.getSession();
                    sock = sslSocket;
                }
                sock.setTcpNoDelay(true);
                source = Okio.buffer(Okio.source(sock));
                asyncSink.becomeConnected(Okio.sink(sock), sock);
                // The return value of OkHttpTlsUpgrader.upgrade is an SSLSocket that has this info
                attributes = attributes.toBuilder().set(Grpc.TRANSPORT_ATTR_REMOTE_ADDR, sock.getRemoteSocketAddress()).set(Grpc.TRANSPORT_ATTR_LOCAL_ADDR, sock.getLocalSocketAddress()).set(Grpc.TRANSPORT_ATTR_SSL_SESSION, sslSession).set(GrpcAttributes.ATTR_SECURITY_LEVEL, sslSession == null ? SecurityLevel.NONE : SecurityLevel.PRIVACY_AND_INTEGRITY).build();
            } catch (StatusException e) {
                startGoAway(0, ErrorCode.INTERNAL_ERROR, e.getStatus());
                return;
            } catch (Exception e) {
                onException(e);
                return;
            } finally {
                clientFrameHandler = new ClientFrameHandler(variant.newReader(source, true));
            }
            synchronized (lock) {
                socket = Preconditions.checkNotNull(sock, "socket");
                if (sslSession != null) {
                    securityInfo = new InternalChannelz.Security(new InternalChannelz.Tls(sslSession));
                }
            }
        }
    });
    // Schedule to send connection preface & settings before any other write.
    try {
        sendConnectionPrefaceAndSettings();
    } finally {
        latch.countDown();
    }
    serializingExecutor.execute(new Runnable() {

        @Override
        public void run() {
            // ClientFrameHandler need to be started after connectionPreface / settings, otherwise it
            // may send goAway immediately.
            executor.execute(clientFrameHandler);
            synchronized (lock) {
                maxConcurrentStreams = Integer.MAX_VALUE;
                startPendingStreams();
            }
        }
    });
    return null;
}
Also used : InetSocketAddress(java.net.InetSocketAddress) SSLSocket(javax.net.ssl.SSLSocket) BufferedSource(okio.BufferedSource) Source(okio.Source) StatusException(io.grpc.StatusException) ClientKeepAlivePinger(io.grpc.internal.KeepAliveManager.ClientKeepAlivePinger) KeepAliveManager(io.grpc.internal.KeepAliveManager) BufferedSource(okio.BufferedSource) Buffer(okio.Buffer) Http2(io.grpc.okhttp.internal.framed.Http2) SSLSession(javax.net.ssl.SSLSession) CountDownLatch(java.util.concurrent.CountDownLatch) FrameWriter(io.grpc.okhttp.internal.framed.FrameWriter) EOFException(java.io.EOFException) StatusException(io.grpc.StatusException) IOException(java.io.IOException) Variant(io.grpc.okhttp.internal.framed.Variant) InternalChannelz(io.grpc.InternalChannelz) SSLSocket(javax.net.ssl.SSLSocket) Socket(java.net.Socket)

Example 3 with ClientKeepAlivePinger

use of io.grpc.internal.KeepAliveManager.ClientKeepAlivePinger in project grpc-java by grpc.

the class KeepAliveManagerTest method clientKeepAlivePinger_pingTimeout.

@Test
public void clientKeepAlivePinger_pingTimeout() {
    ConnectionClientTransport transport = mock(ConnectionClientTransport.class);
    keepAlivePinger = new ClientKeepAlivePinger(transport);
    keepAlivePinger.onPingTimeout();
    ArgumentCaptor<Status> statusCaptor = ArgumentCaptor.forClass(Status.class);
    verify(transport).shutdownNow(statusCaptor.capture());
    Status status = statusCaptor.getValue();
    assertThat(status.getCode()).isEqualTo(Status.Code.UNAVAILABLE);
    assertThat(status.getDescription()).isEqualTo("Keepalive failed. The connection is likely gone");
}
Also used : Status(io.grpc.Status) ClientKeepAlivePinger(io.grpc.internal.KeepAliveManager.ClientKeepAlivePinger) Test(org.junit.Test)

Example 4 with ClientKeepAlivePinger

use of io.grpc.internal.KeepAliveManager.ClientKeepAlivePinger in project grpc-java by grpc.

the class KeepAliveManagerTest method clientKeepAlivePinger_pingFailure.

@Test
public void clientKeepAlivePinger_pingFailure() {
    ConnectionClientTransport transport = mock(ConnectionClientTransport.class);
    keepAlivePinger = new ClientKeepAlivePinger(transport);
    keepAlivePinger.ping();
    ArgumentCaptor<ClientTransport.PingCallback> pingCallbackCaptor = ArgumentCaptor.forClass(ClientTransport.PingCallback.class);
    verify(transport).ping(pingCallbackCaptor.capture(), isA(Executor.class));
    ClientTransport.PingCallback pingCallback = pingCallbackCaptor.getValue();
    pingCallback.onFailure(new Throwable());
    ArgumentCaptor<Status> statusCaptor = ArgumentCaptor.forClass(Status.class);
    verify(transport).shutdownNow(statusCaptor.capture());
    Status status = statusCaptor.getValue();
    assertThat(status.getCode()).isEqualTo(Status.Code.UNAVAILABLE);
    assertThat(status.getDescription()).isEqualTo("Keepalive failed. The connection is likely gone");
}
Also used : Status(io.grpc.Status) Executor(java.util.concurrent.Executor) ClientKeepAlivePinger(io.grpc.internal.KeepAliveManager.ClientKeepAlivePinger) Test(org.junit.Test)

Aggregations

ClientKeepAlivePinger (io.grpc.internal.KeepAliveManager.ClientKeepAlivePinger)4 Status (io.grpc.Status)2 KeepAliveManager (io.grpc.internal.KeepAliveManager)2 Test (org.junit.Test)2 InternalChannelz (io.grpc.InternalChannelz)1 StatusException (io.grpc.StatusException)1 FrameWriter (io.grpc.okhttp.internal.framed.FrameWriter)1 Http2 (io.grpc.okhttp.internal.framed.Http2)1 Variant (io.grpc.okhttp.internal.framed.Variant)1 Bootstrap (io.netty.bootstrap.Bootstrap)1 ChannelFuture (io.netty.channel.ChannelFuture)1 ChannelFutureListener (io.netty.channel.ChannelFutureListener)1 ChannelHandler (io.netty.channel.ChannelHandler)1 ChannelOption (io.netty.channel.ChannelOption)1 EventLoop (io.netty.channel.EventLoop)1 Http2ChannelClosedException (io.netty.handler.codec.http2.StreamBufferingEncoder.Http2ChannelClosedException)1 EOFException (java.io.EOFException)1 IOException (java.io.IOException)1 InetSocketAddress (java.net.InetSocketAddress)1 Socket (java.net.Socket)1