Search in sources :

Example 1 with ScheduledFuture

use of io.netty.util.concurrent.ScheduledFuture in project riposte by Nike-Inc.

the class NonblockingEndpointExecutionHandler method doChannelRead.

@Override
public PipelineContinuationBehavior doChannelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    HttpProcessingState state = ChannelAttributes.getHttpProcessingStateForChannel(ctx).get();
    Endpoint<?> endpoint = state.getEndpointForExecution();
    if (shouldHandleDoChannelReadMessage(msg, endpoint)) {
        // We only do something when the last chunk of content has arrived.
        if (msg instanceof LastHttpContent) {
            NonblockingEndpoint nonblockingEndpoint = ((NonblockingEndpoint) endpoint);
            // We're supposed to execute the endpoint. There may be pre-endpoint-execution validation logic or
            //      other work that needs to happen before the endpoint is executed, so set up the
            //      CompletableFuture for the endpoint call to only execute if the pre-endpoint-execution
            //      validation/work chain is successful.
            RequestInfo<?> requestInfo = state.getRequestInfo();
            @SuppressWarnings("unchecked") CompletableFuture<ResponseInfo<?>> responseFuture = state.getPreEndpointExecutionWorkChain().thenCompose(functionWithTracingAndMdc(aVoid -> (CompletableFuture<ResponseInfo<?>>) nonblockingEndpoint.execute(requestInfo, longRunningTaskExecutor, ctx), ctx));
            // Register an on-completion callback so we can be notified when the CompletableFuture finishes.
            responseFuture.whenComplete((responseInfo, throwable) -> {
                if (throwable != null)
                    asyncErrorCallback(ctx, throwable);
                else
                    asyncCallback(ctx, responseInfo);
            });
            // Also schedule a timeout check with our Netty event loop to make sure we kill the
            //      CompletableFuture if it goes on too long.
            long timeoutValueToUse = (nonblockingEndpoint.completableFutureTimeoutOverrideMillis() == null) ? defaultCompletableFutureTimeoutMillis : nonblockingEndpoint.completableFutureTimeoutOverrideMillis();
            ScheduledFuture<?> responseTimeoutScheduledFuture = ctx.channel().eventLoop().schedule(() -> {
                if (!responseFuture.isDone()) {
                    runnableWithTracingAndMdc(() -> logger.error("A non-blocking endpoint's CompletableFuture did not finish within " + "the allotted timeout ({} milliseconds). Forcibly cancelling it.", timeoutValueToUse), ctx).run();
                    @SuppressWarnings("unchecked") Throwable errorToUse = nonblockingEndpoint.getCustomTimeoutExceptionCause(requestInfo, ctx);
                    if (errorToUse == null)
                        errorToUse = new NonblockingEndpointCompletableFutureTimedOut(timeoutValueToUse);
                    responseFuture.completeExceptionally(errorToUse);
                }
            }, timeoutValueToUse, TimeUnit.MILLISECONDS);
            /*
                    The problem with the scheduled timeout check is that it holds on to the RequestInfo,
                    ChannelHandlerContext, and a bunch of other stuff that *should* become garbage the instant the
                    request finishes, but because of the timeout check it has to wait until the check executes
                    before the garbage is collectable. In high volume servers the default 60 second timeout is way
                    too long and acts like a memory leak and results in garbage collection thrashing if the
                    available memory can be filled within the 60 second timeout. To combat this we cancel the
                    timeout future when the endpoint future finishes. Netty will remove the cancelled timeout future
                    from its scheduled list within a short time, thus letting the garbage be collected.
                */
            responseFuture.whenComplete((responseInfo, throwable) -> {
                if (!responseTimeoutScheduledFuture.isDone())
                    responseTimeoutScheduledFuture.cancel(false);
            });
        }
        //      completes (see asyncCallback() and asyncErrorCallback()).
        return PipelineContinuationBehavior.DO_NOT_FIRE_CONTINUE_EVENT;
    }
    //      error to be returned to the client.
    return PipelineContinuationBehavior.CONTINUE;
}
Also used : HttpProcessingState(com.nike.riposte.server.http.HttpProcessingState) EventExecutor(io.netty.util.concurrent.EventExecutor) RequestInfo(com.nike.riposte.server.http.RequestInfo) Logger(org.slf4j.Logger) Executor(java.util.concurrent.Executor) ScheduledFuture(io.netty.util.concurrent.ScheduledFuture) LoggerFactory(org.slf4j.LoggerFactory) AsyncNettyHelper.executeOnlyIfChannelIsActive(com.nike.riposte.util.AsyncNettyHelper.executeOnlyIfChannelIsActive) ResponseInfo(com.nike.riposte.server.http.ResponseInfo) CompletableFuture(java.util.concurrent.CompletableFuture) PipelineContinuationBehavior(com.nike.riposte.server.handler.base.PipelineContinuationBehavior) HttpObject(io.netty.handler.codec.http.HttpObject) AsyncNettyHelper.functionWithTracingAndMdc(com.nike.riposte.util.AsyncNettyHelper.functionWithTracingAndMdc) LastHttpContent(io.netty.handler.codec.http.LastHttpContent) TimeUnit(java.util.concurrent.TimeUnit) NonblockingEndpointCompletableFutureTimedOut(com.nike.riposte.server.error.exception.NonblockingEndpointCompletableFutureTimedOut) BaseInboundHandlerWithTracingAndMdcSupport(com.nike.riposte.server.handler.base.BaseInboundHandlerWithTracingAndMdcSupport) Endpoint(com.nike.riposte.server.http.Endpoint) ChannelHandlerContext(io.netty.channel.ChannelHandlerContext) ChannelAttributes(com.nike.riposte.server.channelpipeline.ChannelAttributes) LastOutboundMessageSendFullResponseInfo(com.nike.riposte.server.channelpipeline.message.LastOutboundMessageSendFullResponseInfo) AsyncNettyHelper.runnableWithTracingAndMdc(com.nike.riposte.util.AsyncNettyHelper.runnableWithTracingAndMdc) NonblockingEndpoint(com.nike.riposte.server.http.NonblockingEndpoint) ResponseInfo(com.nike.riposte.server.http.ResponseInfo) LastOutboundMessageSendFullResponseInfo(com.nike.riposte.server.channelpipeline.message.LastOutboundMessageSendFullResponseInfo) HttpProcessingState(com.nike.riposte.server.http.HttpProcessingState) LastHttpContent(io.netty.handler.codec.http.LastHttpContent) NonblockingEndpointCompletableFutureTimedOut(com.nike.riposte.server.error.exception.NonblockingEndpointCompletableFutureTimedOut) CompletableFuture(java.util.concurrent.CompletableFuture) NonblockingEndpoint(com.nike.riposte.server.http.NonblockingEndpoint)

Example 2 with ScheduledFuture

use of io.netty.util.concurrent.ScheduledFuture in project riposte by Nike-Inc.

the class NonblockingEndpointExecutionHandlerTest method doChannelRead_does_nothing_to_timeout_check_if_timeout_check_is_already_completed_when_response_completes.

@Test
public void doChannelRead_does_nothing_to_timeout_check_if_timeout_check_is_already_completed_when_response_completes() throws Exception {
    // given
    ScheduledFuture timeoutCheckMock = mock(ScheduledFuture.class);
    doReturn(timeoutCheckMock).when(eventLoopMock).schedule(any(Runnable.class), any(Long.class), any(TimeUnit.class));
    handlerSpy.doChannelRead(ctxMock, msg);
    ArgumentCaptor<BiConsumer> timeoutCheckCancellationLogicArgumentCaptor = ArgumentCaptor.forClass(BiConsumer.class);
    // The 2nd whenComplete is for cancelling the timeout check if the response finishes before the timeout
    verify(futureThatWillBeAttachedToSpy, times(2)).whenComplete(timeoutCheckCancellationLogicArgumentCaptor.capture());
    BiConsumer<ResponseInfo<?>, Throwable> timeoutCheckCancellationLogic = timeoutCheckCancellationLogicArgumentCaptor.getAllValues().get(1);
    // when: the timeout check scheduled future is already done
    doReturn(true).when(timeoutCheckMock).isDone();
    timeoutCheckCancellationLogic.accept(mock(ResponseInfo.class), null);
    // then: nothing should be done
    verify(timeoutCheckMock).isDone();
    verify(timeoutCheckMock, times(0)).cancel(any(Boolean.class));
    verifyNoMoreInteractions(timeoutCheckMock);
}
Also used : ResponseInfo(com.nike.riposte.server.http.ResponseInfo) LastOutboundMessageSendFullResponseInfo(com.nike.riposte.server.channelpipeline.message.LastOutboundMessageSendFullResponseInfo) TimeUnit(java.util.concurrent.TimeUnit) ScheduledFuture(io.netty.util.concurrent.ScheduledFuture) BiConsumer(java.util.function.BiConsumer) Test(org.junit.Test)

Example 3 with ScheduledFuture

use of io.netty.util.concurrent.ScheduledFuture in project riposte by Nike-Inc.

the class NonblockingEndpointExecutionHandlerTest method doChannelRead_cancels_timeout_check_if_response_finishes_before_timeout_check_occurs.

@Test
public void doChannelRead_cancels_timeout_check_if_response_finishes_before_timeout_check_occurs() throws Exception {
    // given
    ScheduledFuture timeoutCheckMock = mock(ScheduledFuture.class);
    doReturn(timeoutCheckMock).when(eventLoopMock).schedule(any(Runnable.class), any(Long.class), any(TimeUnit.class));
    handlerSpy.doChannelRead(ctxMock, msg);
    ArgumentCaptor<BiConsumer> timeoutCheckCancellationLogicArgumentCaptor = ArgumentCaptor.forClass(BiConsumer.class);
    // The 2nd whenComplete is for cancelling the timeout check if the response finishes before the timeout
    verify(futureThatWillBeAttachedToSpy, times(2)).whenComplete(timeoutCheckCancellationLogicArgumentCaptor.capture());
    BiConsumer<ResponseInfo<?>, Throwable> timeoutCheckCancellationLogic = timeoutCheckCancellationLogicArgumentCaptor.getAllValues().get(1);
    // when: the timeout check scheduled future is not yet complete when the response finishes
    doReturn(false).when(timeoutCheckMock).isDone();
    timeoutCheckCancellationLogic.accept(mock(ResponseInfo.class), null);
    // then: timeout check scheduled future should be cancelled
    verify(timeoutCheckMock).cancel(false);
}
Also used : ResponseInfo(com.nike.riposte.server.http.ResponseInfo) LastOutboundMessageSendFullResponseInfo(com.nike.riposte.server.channelpipeline.message.LastOutboundMessageSendFullResponseInfo) TimeUnit(java.util.concurrent.TimeUnit) ScheduledFuture(io.netty.util.concurrent.ScheduledFuture) BiConsumer(java.util.function.BiConsumer) Test(org.junit.Test)

Example 4 with ScheduledFuture

use of io.netty.util.concurrent.ScheduledFuture in project netty by netty.

the class EmbeddedChannelTest method testScheduling.

@SuppressWarnings({ "rawtypes", "unchecked" })
@Test
public void testScheduling() throws Exception {
    EmbeddedChannel ch = new EmbeddedChannel(new ChannelInboundHandlerAdapter());
    final CountDownLatch latch = new CountDownLatch(2);
    ScheduledFuture future = ch.eventLoop().schedule(new Runnable() {

        @Override
        public void run() {
            latch.countDown();
        }
    }, 1, TimeUnit.SECONDS);
    future.addListener(new FutureListener() {

        @Override
        public void operationComplete(Future future) throws Exception {
            latch.countDown();
        }
    });
    long next = ch.runScheduledPendingTasks();
    assertTrue(next > 0);
    // Sleep for the nanoseconds but also give extra 50ms as the clock my not be very precise and so fail the test
    // otherwise.
    Thread.sleep(TimeUnit.NANOSECONDS.toMillis(next) + 50);
    assertEquals(-1, ch.runScheduledPendingTasks());
    latch.await();
}
Also used : ChannelFutureListener(io.netty.channel.ChannelFutureListener) FutureListener(io.netty.util.concurrent.FutureListener) ScheduledFuture(io.netty.util.concurrent.ScheduledFuture) ChannelFuture(io.netty.channel.ChannelFuture) Future(io.netty.util.concurrent.Future) CountDownLatch(java.util.concurrent.CountDownLatch) ScheduledFuture(io.netty.util.concurrent.ScheduledFuture) ClosedChannelException(java.nio.channels.ClosedChannelException) ChannelInboundHandlerAdapter(io.netty.channel.ChannelInboundHandlerAdapter) Test(org.junit.Test)

Example 5 with ScheduledFuture

use of io.netty.util.concurrent.ScheduledFuture in project riposte by Nike-Inc.

the class OpenChannelLimitHandler method doChannelActive.

@Override
public PipelineContinuationBehavior doChannelActive(ChannelHandlerContext ctx) throws Exception {
    // New channel opening. See if we have too many open channels.
    int actualOpenChannelsCount = openChannelsGroup.size();
    if (actualOpenChannelsCount >= maxOpenChannelsThreshold) {
        Channel channel = ctx.channel();
        // Mark this channel as needing to be closed.
        ctx.channel().attr(TOO_MANY_OPEN_CONNECTIONS_THIS_CHANNEL_SHOULD_CLOSE).set(actualOpenChannelsCount);
        // Schedule a double-check event to make sure the channel gets closed.
        ScheduledFuture doubleCheckScheduledFuture = ctx.channel().eventLoop().schedule(() -> {
            if (channel.isOpen())
                channel.close();
        }, 100, TimeUnit.MILLISECONDS);
        // Add a channel close future listener to cancel the double-check scheduled event immediately if the channel
        //      is closed quickly. Even though the double-check event will execute in 100 milliseconds that's 100
        //      milliseconds of potential garbage accumulating when it shouldn't. Could be a lot for a high traffic
        //      server (which this likely is if the open channels limit is being hit).
        channel.closeFuture().addListener(future -> {
            if (!doubleCheckScheduledFuture.isDone())
                doubleCheckScheduledFuture.cancel(false);
        });
    } else {
        // Not at the threshold. Add this channel to the open channel group.
        openChannelsGroup.add(ctx.channel());
    }
    return PipelineContinuationBehavior.CONTINUE;
}
Also used : Channel(io.netty.channel.Channel) ScheduledFuture(io.netty.util.concurrent.ScheduledFuture)

Aggregations

ScheduledFuture (io.netty.util.concurrent.ScheduledFuture)5 LastOutboundMessageSendFullResponseInfo (com.nike.riposte.server.channelpipeline.message.LastOutboundMessageSendFullResponseInfo)3 ResponseInfo (com.nike.riposte.server.http.ResponseInfo)3 TimeUnit (java.util.concurrent.TimeUnit)3 Test (org.junit.Test)3 BiConsumer (java.util.function.BiConsumer)2 ChannelAttributes (com.nike.riposte.server.channelpipeline.ChannelAttributes)1 NonblockingEndpointCompletableFutureTimedOut (com.nike.riposte.server.error.exception.NonblockingEndpointCompletableFutureTimedOut)1 BaseInboundHandlerWithTracingAndMdcSupport (com.nike.riposte.server.handler.base.BaseInboundHandlerWithTracingAndMdcSupport)1 PipelineContinuationBehavior (com.nike.riposte.server.handler.base.PipelineContinuationBehavior)1 Endpoint (com.nike.riposte.server.http.Endpoint)1 HttpProcessingState (com.nike.riposte.server.http.HttpProcessingState)1 NonblockingEndpoint (com.nike.riposte.server.http.NonblockingEndpoint)1 RequestInfo (com.nike.riposte.server.http.RequestInfo)1 AsyncNettyHelper.executeOnlyIfChannelIsActive (com.nike.riposte.util.AsyncNettyHelper.executeOnlyIfChannelIsActive)1 AsyncNettyHelper.functionWithTracingAndMdc (com.nike.riposte.util.AsyncNettyHelper.functionWithTracingAndMdc)1 AsyncNettyHelper.runnableWithTracingAndMdc (com.nike.riposte.util.AsyncNettyHelper.runnableWithTracingAndMdc)1 Channel (io.netty.channel.Channel)1 ChannelFuture (io.netty.channel.ChannelFuture)1 ChannelFutureListener (io.netty.channel.ChannelFutureListener)1