Search in sources :

Example 1 with CircuitBreaker

use of com.nike.fastbreak.CircuitBreaker in project riposte by Nike-Inc.

the class ProxyRouterEndpointExecutionHandler method doChannelRead.

@Override
public PipelineContinuationBehavior doChannelRead(ChannelHandlerContext ctx, Object msg) {
    HttpProcessingState state = ChannelAttributes.getHttpProcessingStateForChannel(ctx).get();
    Endpoint<?> endpoint = state.getEndpointForExecution();
    if (shouldHandleDoChannelReadMessage(msg, endpoint)) {
        ProxyRouterProcessingState proxyRouterState = getOrCreateProxyRouterProcessingState(ctx);
        ProxyRouterEndpoint endpointProxyRouter = ((ProxyRouterEndpoint) endpoint);
        RequestInfo<?> requestInfo = state.getRequestInfo();
        if (msg instanceof HttpRequest) {
            if (requestInfo instanceof RiposteInternalRequestInfo) {
                // Tell this RequestInfo that we'll be managing the release of content chunks, so that when
                // RequestInfo.releaseAllResources() is called we don't have extra reference count removals.
                ((RiposteInternalRequestInfo) requestInfo).contentChunksWillBeReleasedExternally();
            }
            // We're supposed to start streaming. 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.
            CompletableFuture<DownstreamRequestFirstChunkInfo> firstChunkFuture = state.getPreEndpointExecutionWorkChain().thenCompose(functionWithTracingAndMdc(aVoid -> endpointProxyRouter.getDownstreamRequestFirstChunkInfo(requestInfo, longRunningTaskExecutor, ctx), ctx));
            Long endpointTimeoutOverride = endpointProxyRouter.completableFutureTimeoutOverrideMillis();
            long callTimeoutValueToUse = (endpointTimeoutOverride == null) ? defaultCompletableFutureTimeoutMillis : endpointTimeoutOverride;
            // When the first chunk is ready, stream it downstream and set up what happens afterward.
            firstChunkFuture.whenComplete((downstreamRequestFirstChunkInfo, throwable) -> {
                Optional<ManualModeTask<HttpResponse>> circuitBreakerManualTask = getCircuitBreaker(downstreamRequestFirstChunkInfo, ctx).map(CircuitBreaker::newManualModeTask);
                StreamingCallback callback = new StreamingCallbackForCtx(ctx, circuitBreakerManualTask, endpointProxyRouter, requestInfo, proxyRouterState);
                if (throwable != null) {
                    // Something blew up trying to determine the first chunk info.
                    callback.unrecoverableErrorOccurred(throwable, true);
                } else if (!ctx.channel().isOpen()) {
                    // The channel was closed for some reason before we were able to start streaming.
                    String errorMsg = "The channel from the original caller was closed before we could begin the " + "downstream call.";
                    Exception channelClosedException = new RuntimeException(errorMsg);
                    runnableWithTracingAndMdc(() -> logger.warn(errorMsg), ctx).run();
                    callback.unrecoverableErrorOccurred(channelClosedException, true);
                } else {
                    try {
                        // Ok we have the first chunk info. Start by setting the downstream call info in the request
                        // info (i.e. for access logs if desired)
                        requestInfo.addRequestAttribute(DOWNSTREAM_CALL_PATH_REQUEST_ATTR_KEY, HttpUtils.extractPath(downstreamRequestFirstChunkInfo.firstChunk.uri()));
                        // Try our circuit breaker (if we have one).
                        Throwable circuitBreakerException = null;
                        try {
                            circuitBreakerManualTask.ifPresent(ManualModeTask::throwExceptionIfCircuitBreakerIsOpen);
                        } catch (Throwable t) {
                            circuitBreakerException = t;
                        }
                        if (circuitBreakerException == null) {
                            // No circuit breaker, or the breaker is closed. We can now stream the first chunk info.
                            String downstreamHost = downstreamRequestFirstChunkInfo.host;
                            int downstreamPort = downstreamRequestFirstChunkInfo.port;
                            HttpRequest downstreamRequestFirstChunk = downstreamRequestFirstChunkInfo.firstChunk;
                            boolean isSecureHttpsCall = downstreamRequestFirstChunkInfo.isHttps;
                            boolean relaxedHttpsValidation = downstreamRequestFirstChunkInfo.relaxedHttpsValidation;
                            boolean performSubSpanAroundDownstreamCall = downstreamRequestFirstChunkInfo.performSubSpanAroundDownstreamCall;
                            boolean addTracingHeadersToDownstreamCall = downstreamRequestFirstChunkInfo.addTracingHeadersToDownstreamCall;
                            // Tell the proxyRouterState about the streaming callback so that
                            // callback.unrecoverableErrorOccurred(...) can be called in the case of an error
                            // on subsequent chunks.
                            proxyRouterState.setStreamingCallback(callback);
                            // Setup the streaming channel future with everything it needs to kick off the
                            // downstream request.
                            proxyRouterState.setStreamingStartTimeNanos(System.nanoTime());
                            CompletableFuture<StreamingChannel> streamingChannel = streamingAsyncHttpClient.streamDownstreamCall(downstreamHost, downstreamPort, downstreamRequestFirstChunk, isSecureHttpsCall, relaxedHttpsValidation, callback, callTimeoutValueToUse, performSubSpanAroundDownstreamCall, addTracingHeadersToDownstreamCall, proxyRouterState, requestInfo, ctx);
                            // Tell the streaming channel future what to do when it completes.
                            streamingChannel = streamingChannel.whenComplete((sc, cause) -> {
                                if (cause == null) {
                                    // Successfully connected and sent the first chunk. We can now safely let
                                    // the remaining content chunks through for streaming.
                                    proxyRouterState.triggerChunkProcessing(sc);
                                } else {
                                    // Something blew up while connecting to the downstream server.
                                    callback.unrecoverableErrorOccurred(cause, true);
                                }
                            });
                            // Set the streaming channel future on the state so it can be connected to.
                            proxyRouterState.setStreamingChannelCompletableFuture(streamingChannel);
                        } else {
                            // Circuit breaker is tripped (or otherwise threw an unexpected exception). Immediately
                            // short circuit the error back to the client.
                            callback.unrecoverableErrorOccurred(circuitBreakerException, true);
                        }
                    } catch (Throwable t) {
                        callback.unrecoverableErrorOccurred(t, true);
                    }
                }
            });
        } else if (msg instanceof HttpContent) {
            HttpContent msgContent = (HttpContent) msg;
            // chunk-streaming behavior and subsequent cleanup for the given HttpContent.
            if (!releaseContentChunkIfStreamAlreadyFailed(msgContent, proxyRouterState)) {
                registerChunkStreamingAction(proxyRouterState, msgContent, ctx);
            }
        }
        return PipelineContinuationBehavior.DO_NOT_FIRE_CONTINUE_EVENT;
    }
    return PipelineContinuationBehavior.CONTINUE;
}
Also used : HttpRequest(io.netty.handler.codec.http.HttpRequest) Span(com.nike.wingtips.Span) LoggerFactory(org.slf4j.LoggerFactory) ResponseInfo(com.nike.riposte.server.http.ResponseInfo) HttpObject(io.netty.handler.codec.http.HttpObject) ProxyRouterEndpoint(com.nike.riposte.server.http.ProxyRouterEndpoint) Map(java.util.Map) HttpRequest(io.netty.handler.codec.http.HttpRequest) CompletionException(java.util.concurrent.CompletionException) EventLoop(io.netty.channel.EventLoop) DownstreamRequestFirstChunkInfo(com.nike.riposte.server.http.ProxyRouterEndpoint.DownstreamRequestFirstChunkInfo) BaseInboundHandlerWithTracingAndMdcSupport(com.nike.riposte.server.handler.base.BaseInboundHandlerWithTracingAndMdcSupport) Endpoint(com.nike.riposte.server.http.Endpoint) HttpUtils(com.nike.riposte.util.HttpUtils) StreamingChannel(com.nike.riposte.client.asynchttp.netty.StreamingAsyncHttpClient.StreamingChannel) ChannelAttributes(com.nike.riposte.server.channelpipeline.ChannelAttributes) CircuitBreaker(com.nike.fastbreak.CircuitBreaker) RiposteInternalRequestInfo(com.nike.riposte.server.http.impl.RiposteInternalRequestInfo) Optional(java.util.Optional) HttpResponse(io.netty.handler.codec.http.HttpResponse) StreamingCallback(com.nike.riposte.client.asynchttp.netty.StreamingAsyncHttpClient.StreamingCallback) HttpProcessingState(com.nike.riposte.server.http.HttpProcessingState) EventExecutor(io.netty.util.concurrent.EventExecutor) RequestInfo(com.nike.riposte.server.http.RequestInfo) CircuitBreakerDelegate(com.nike.fastbreak.CircuitBreakerDelegate) ManualModeTask(com.nike.fastbreak.CircuitBreaker.ManualModeTask) CompletableFuture(java.util.concurrent.CompletableFuture) StreamingAsyncHttpClient(com.nike.riposte.client.asynchttp.netty.StreamingAsyncHttpClient) PipelineContinuationBehavior(com.nike.riposte.server.handler.base.PipelineContinuationBehavior) Deque(java.util.Deque) LastHttpContent(io.netty.handler.codec.http.LastHttpContent) AsyncNettyHelper(com.nike.riposte.util.AsyncNettyHelper) ChannelHandlerContext(io.netty.channel.ChannelHandlerContext) HttpContent(io.netty.handler.codec.http.HttpContent) Attribute(io.netty.util.Attribute) OutboundMessageSendHeadersChunkFromResponseInfo(com.nike.riposte.server.channelpipeline.message.OutboundMessageSendHeadersChunkFromResponseInfo) Logger(org.slf4j.Logger) Executor(java.util.concurrent.Executor) AsyncNettyHelper.executeOnlyIfChannelIsActive(com.nike.riposte.util.AsyncNettyHelper.executeOnlyIfChannelIsActive) CircuitBreakerForHttpStatusCode.getDefaultHttpStatusCodeCircuitBreakerForKey(com.nike.fastbreak.CircuitBreakerForHttpStatusCode.getDefaultHttpStatusCodeCircuitBreakerForKey) AsyncNettyHelper.functionWithTracingAndMdc(com.nike.riposte.util.AsyncNettyHelper.functionWithTracingAndMdc) ChannelFuture(io.netty.channel.ChannelFuture) ExecutionException(java.util.concurrent.ExecutionException) FullHttpResponse(io.netty.handler.codec.http.FullHttpResponse) WrapperException(com.nike.backstopper.exception.WrapperException) OutboundMessageSendContentChunk(com.nike.riposte.server.channelpipeline.message.OutboundMessageSendContentChunk) DistributedTracingConfig(com.nike.riposte.server.config.distributedtracing.DistributedTracingConfig) AsyncNettyHelper.runnableWithTracingAndMdc(com.nike.riposte.util.AsyncNettyHelper.runnableWithTracingAndMdc) LastOutboundMessageSendLastContentChunk(com.nike.riposte.server.channelpipeline.message.LastOutboundMessageSendLastContentChunk) Pair(com.nike.internal.util.Pair) ProxyRouterProcessingState(com.nike.riposte.server.http.ProxyRouterProcessingState) CircuitBreaker(com.nike.fastbreak.CircuitBreaker) StreamingCallback(com.nike.riposte.client.asynchttp.netty.StreamingAsyncHttpClient.StreamingCallback) HttpProcessingState(com.nike.riposte.server.http.HttpProcessingState) ProxyRouterProcessingState(com.nike.riposte.server.http.ProxyRouterProcessingState) DownstreamRequestFirstChunkInfo(com.nike.riposte.server.http.ProxyRouterEndpoint.DownstreamRequestFirstChunkInfo) CompletionException(java.util.concurrent.CompletionException) ExecutionException(java.util.concurrent.ExecutionException) WrapperException(com.nike.backstopper.exception.WrapperException) CompletableFuture(java.util.concurrent.CompletableFuture) ManualModeTask(com.nike.fastbreak.CircuitBreaker.ManualModeTask) ProxyRouterEndpoint(com.nike.riposte.server.http.ProxyRouterEndpoint) RiposteInternalRequestInfo(com.nike.riposte.server.http.impl.RiposteInternalRequestInfo) LastHttpContent(io.netty.handler.codec.http.LastHttpContent) HttpContent(io.netty.handler.codec.http.HttpContent)

Example 2 with CircuitBreaker

use of com.nike.fastbreak.CircuitBreaker in project riposte by Nike-Inc.

the class AsyncCompletionHandlerWithTracingAndMdcSupportTest method constructor_sets_values_exactly_as_given_when_subtracing_is_off.

@Test
public void constructor_sets_values_exactly_as_given_when_subtracing_is_off() {
    // given
    CompletableFuture cfResponse = mock(CompletableFuture.class);
    AsyncResponseHandler responseHandlerFunc = mock(AsyncResponseHandler.class);
    RequestBuilderWrapper rbwMock = mock(RequestBuilderWrapper.class);
    Optional<CircuitBreaker<Response>> circuitBreaker = Optional.of(mock(CircuitBreaker.class));
    Deque<Span> spanStack = mock(Deque.class);
    Map<String, String> mdcInfo = mock(Map.class);
    Deque<Span> spanStackBeforeCall = Tracer.getInstance().getCurrentSpanStackCopy();
    Map<String, String> mdcInfoBeforeCall = MDC.getCopyOfContextMap();
    // when
    AsyncCompletionHandlerWithTracingAndMdcSupport instance = new AsyncCompletionHandlerWithTracingAndMdcSupport(cfResponse, responseHandlerFunc, false, rbwMock, circuitBreaker, spanStack, mdcInfo, tagAndNamingStrategy);
    // then
    assertThat(instance.completableFutureResponse).isSameAs(cfResponse);
    assertThat(instance.responseHandlerFunction).isSameAs(responseHandlerFunc);
    assertThat(instance.performSubSpanAroundDownstreamCalls).isEqualTo(false);
    assertThat(instance.circuitBreakerManualTask).isSameAs(circuitBreaker);
    assertThat(instance.distributedTraceStackToUse).isSameAs(spanStack);
    assertThat(instance.mdcContextToUse).isSameAs(mdcInfo);
    assertThat(Tracer.getInstance().getCurrentSpanStackCopy()).isEqualTo(spanStackBeforeCall);
    assertThat(MDC.getCopyOfContextMap()).isEqualTo(mdcInfoBeforeCall);
}
Also used : CompletableFuture(java.util.concurrent.CompletableFuture) CircuitBreaker(com.nike.fastbreak.CircuitBreaker) Span(com.nike.wingtips.Span) Test(org.junit.Test)

Example 3 with CircuitBreaker

use of com.nike.fastbreak.CircuitBreaker in project riposte by Nike-Inc.

the class AsyncHttpClientHelperTest method getCircuitBreaker_returns_CircuitBreakerDelegate_wrapping_default_CircuitBreakerForHttpStatusCode_using_host_as_the_key.

@DataProvider(value = { "true", "false" }, splitBy = "\\|")
@Test
public void getCircuitBreaker_returns_CircuitBreakerDelegate_wrapping_default_CircuitBreakerForHttpStatusCode_using_host_as_the_key(boolean useNettyEventLoop) {
    // given
    String host = UUID.randomUUID().toString();
    String url = "http://" + host + "/some/path";
    String method = "GET";
    BoundRequestBuilder reqMock = mock(BoundRequestBuilder.class);
    Optional<CircuitBreaker<Response>> customCb = Optional.empty();
    RequestBuilderWrapper rbw = new RequestBuilderWrapper(url, method, reqMock, customCb, false);
    if (useNettyEventLoop)
        rbw.setCtx(ctxMock);
    // when
    Optional<CircuitBreaker<Response>> result = helperSpy.getCircuitBreaker(rbw);
    // then
    assertThat(result).isPresent();
    assertThat(result.get()).isInstanceOf(CircuitBreakerDelegate.class);
    CircuitBreakerDelegate<Response, Integer> wrapper = (CircuitBreakerDelegate) result.get();
    CircuitBreaker<Integer> delegate = (CircuitBreaker<Integer>) Whitebox.getInternalState(wrapper, "delegate");
    Function<Response, Integer> eventConverter = (Function<Response, Integer>) Whitebox.getInternalState(wrapper, "eventConverter");
    assertThat(delegate).isSameAs(CircuitBreakerForHttpStatusCode.getDefaultHttpStatusCodeCircuitBreakerForKey(host));
    Response responseMock = mock(Response.class);
    doReturn(42).when(responseMock).getStatusCode();
    assertThat(eventConverter.apply(responseMock)).isEqualTo(42);
    assertThat(eventConverter.apply(null)).isNull();
    if (useNettyEventLoop) {
        assertThat(Whitebox.getInternalState(delegate, "scheduler")).isEqualTo(eventLoopMock);
        assertThat(Whitebox.getInternalState(delegate, "stateChangeNotificationExecutor")).isEqualTo(eventLoopMock);
    } else {
        assertThat(Whitebox.getInternalState(delegate, "scheduler")).isNotEqualTo(eventLoopMock);
        assertThat(Whitebox.getInternalState(delegate, "stateChangeNotificationExecutor")).isNotEqualTo(eventLoopMock);
    }
}
Also used : Response(org.asynchttpclient.Response) BoundRequestBuilder(org.asynchttpclient.BoundRequestBuilder) Function(java.util.function.Function) CircuitBreaker(com.nike.fastbreak.CircuitBreaker) ArgumentMatchers.anyString(org.mockito.ArgumentMatchers.anyString) CircuitBreakerDelegate(com.nike.fastbreak.CircuitBreakerDelegate) DataProvider(com.tngtech.java.junit.dataprovider.DataProvider) Test(org.junit.Test)

Example 4 with CircuitBreaker

use of com.nike.fastbreak.CircuitBreaker in project riposte by Nike-Inc.

the class AsyncHttpClientHelperTest method getRequestBuilder_with_circuit_breaker_args_sets_values_as_expected.

@DataProvider(value = { "CONNECT", "DELETE", "GET", "HEAD", "POST", "OPTIONS", "PUT", "PATCH", "TRACE", "FOO_METHOD_DOES_NOT_EXIST" }, splitBy = "\\|")
@Test
public void getRequestBuilder_with_circuit_breaker_args_sets_values_as_expected(String methodName) {
    CircuitBreaker<Response> cbMock = mock(CircuitBreaker.class);
    List<Pair<Optional<CircuitBreaker<Response>>, Boolean>> variations = Arrays.asList(Pair.of(Optional.empty(), true), Pair.of(Optional.empty(), false), Pair.of(Optional.of(cbMock), true), Pair.of(Optional.of(cbMock), false));
    variations.forEach(variation -> {
        // given
        String url = "http://localhost/some/path";
        HttpMethod method = HttpMethod.valueOf(methodName);
        Optional<CircuitBreaker<Response>> cbOpt = variation.getLeft();
        boolean disableCb = variation.getRight();
        // when
        RequestBuilderWrapper rbw = helperSpy.getRequestBuilder(url, method, cbOpt, disableCb);
        // then
        verifyRequestBuilderWrapperGeneratedAsExpected(rbw, url, methodName, cbOpt, disableCb);
    });
}
Also used : Response(com.ning.http.client.Response) CircuitBreaker(com.nike.fastbreak.CircuitBreaker) ArgumentMatchers.anyString(org.mockito.ArgumentMatchers.anyString) HttpMethod(io.netty.handler.codec.http.HttpMethod) Pair(com.nike.internal.util.Pair) DataProvider(com.tngtech.java.junit.dataprovider.DataProvider) Test(org.junit.Test)

Example 5 with CircuitBreaker

use of com.nike.fastbreak.CircuitBreaker in project riposte by Nike-Inc.

the class AsyncHttpClientHelper method getCircuitBreaker.

protected Optional<CircuitBreaker<Response>> getCircuitBreaker(RequestBuilderWrapper requestBuilderWrapper) {
    if (requestBuilderWrapper.disableCircuitBreaker)
        return Optional.empty();
    // custom one is not specified.
    if (requestBuilderWrapper.customCircuitBreaker.isPresent())
        return requestBuilderWrapper.customCircuitBreaker;
    // No custom circuit breaker. Use the default for the given request's host.
    Uri uri = Uri.create(requestBuilderWrapper.url);
    String host = uri.getHost();
    EventLoop nettyEventLoop = requestBuilderWrapper.getCtx() == null ? null : requestBuilderWrapper.getCtx().channel().eventLoop();
    CircuitBreaker<Integer> defaultStatusCodeCircuitBreaker = getDefaultHttpStatusCodeCircuitBreakerForKey(host, Optional.ofNullable(nettyEventLoop), Optional.ofNullable(nettyEventLoop));
    return Optional.of(new CircuitBreakerDelegate<>(defaultStatusCodeCircuitBreaker, response -> (response == null ? null : response.getStatusCode())));
}
Also used : AtomicInteger(java.util.concurrent.atomic.AtomicInteger) HttpProcessingState(com.nike.riposte.server.http.HttpProcessingState) Span(com.nike.wingtips.Span) CircuitBreakerDelegate(com.nike.fastbreak.CircuitBreakerDelegate) NameResolver(com.ning.http.client.NameResolver) ManualModeTask(com.nike.fastbreak.CircuitBreaker.ManualModeTask) LoggerFactory(org.slf4j.LoggerFactory) Tracer(com.nike.wingtips.Tracer) CompletableFuture(java.util.concurrent.CompletableFuture) Deque(java.util.Deque) CircuitBreakerOpenException(com.nike.fastbreak.exception.CircuitBreakerOpenException) ConcurrentMap(java.util.concurrent.ConcurrentMap) InetAddress(java.net.InetAddress) ChannelHandlerContext(io.netty.channel.ChannelHandlerContext) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) HttpRequestTracingUtils(com.nike.wingtips.http.HttpRequestTracingUtils) Map(java.util.Map) AsyncHttpClient(com.ning.http.client.AsyncHttpClient) AsyncHttpClientConfig(com.ning.http.client.AsyncHttpClientConfig) Response(com.ning.http.client.Response) Logger(org.slf4j.Logger) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) HttpMethod(io.netty.handler.codec.http.HttpMethod) CircuitBreakerForHttpStatusCode.getDefaultHttpStatusCodeCircuitBreakerForKey(com.nike.fastbreak.CircuitBreakerForHttpStatusCode.getDefaultHttpStatusCodeCircuitBreakerForKey) EventLoop(io.netty.channel.EventLoop) UnknownHostException(java.net.UnknownHostException) TimeUnit(java.util.concurrent.TimeUnit) ChannelAttributes(com.nike.riposte.server.channelpipeline.ChannelAttributes) CircuitBreaker(com.nike.fastbreak.CircuitBreaker) SpanNamingAndTaggingStrategy(com.nike.riposte.server.config.distributedtracing.SpanNamingAndTaggingStrategy) MDC(org.slf4j.MDC) Optional(java.util.Optional) SignatureCalculator(com.ning.http.client.SignatureCalculator) Uri(com.ning.http.client.uri.Uri) EventLoop(io.netty.channel.EventLoop) Uri(com.ning.http.client.uri.Uri)

Aggregations

CircuitBreaker (com.nike.fastbreak.CircuitBreaker)15 Span (com.nike.wingtips.Span)10 CompletableFuture (java.util.concurrent.CompletableFuture)10 Test (org.junit.Test)9 DataProvider (com.tngtech.java.junit.dataprovider.DataProvider)7 ManualModeTask (com.nike.fastbreak.CircuitBreaker.ManualModeTask)6 CircuitBreakerDelegate (com.nike.fastbreak.CircuitBreakerDelegate)6 CircuitBreakerForHttpStatusCode.getDefaultHttpStatusCodeCircuitBreakerForKey (com.nike.fastbreak.CircuitBreakerForHttpStatusCode.getDefaultHttpStatusCodeCircuitBreakerForKey)4 CircuitBreakerOpenException (com.nike.fastbreak.exception.CircuitBreakerOpenException)4 Pair (com.nike.internal.util.Pair)4 ChannelAttributes (com.nike.riposte.server.channelpipeline.ChannelAttributes)4 HttpProcessingState (com.nike.riposte.server.http.HttpProcessingState)4 ChannelHandlerContext (io.netty.channel.ChannelHandlerContext)4 EventLoop (io.netty.channel.EventLoop)4 ArgumentMatchers.anyString (org.mockito.ArgumentMatchers.anyString)4 Response (com.ning.http.client.Response)3 HttpMethod (io.netty.handler.codec.http.HttpMethod)3 Deque (java.util.Deque)3 Map (java.util.Map)3 Optional (java.util.Optional)3