Skip to content

Commit

Permalink
[SCB-1955] create unify producer invocation flow
Browse files Browse the repository at this point in the history
  • Loading branch information
wujimin authored and liubao68 committed May 30, 2020
1 parent 184a88f commit 42816bc
Show file tree
Hide file tree
Showing 4 changed files with 273 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@
import org.apache.servicecomb.swagger.invocation.Response;

public class FilterNode {
public static final FilterNode EMPTY = new FilterNode(null) {
@Override
public CompletableFuture<Response> onFilter(Invocation invocation1) {
return CompletableFuture.completedFuture(Response.ok(null));
}
};

public static FilterNode buildChain(Filter... filters) {
return buildChain(Arrays.asList(filters));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.servicecomb.core.invocation;

import org.apache.servicecomb.core.Invocation;

/**
* better to named InvocationFactory, but already be used by old version
*/
public interface InvocationCreator {
Invocation create();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.servicecomb.core.invocation;

import java.util.concurrent.CompletableFuture;

import org.apache.servicecomb.core.Invocation;
import org.apache.servicecomb.core.exception.Exceptions;
import org.apache.servicecomb.core.filter.FilterNode;
import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx;
import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx;
import org.apache.servicecomb.swagger.invocation.Response;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public abstract class ProducerInvocationFlow {
private static final Logger LOGGER = LoggerFactory.getLogger(ProducerInvocationFlow.class);

private final long startTime = System.nanoTime();

private final InvocationCreator invocationCreator;

protected final HttpServletRequestEx requestEx;

protected final HttpServletResponseEx responseEx;

public ProducerInvocationFlow(InvocationCreator invocationCreator) {
this(invocationCreator, null, null);
}

public ProducerInvocationFlow(InvocationCreator invocationCreator,
HttpServletRequestEx requestEx, HttpServletResponseEx responseEx) {
this.invocationCreator = invocationCreator;
this.requestEx = requestEx;
this.responseEx = responseEx;
}

public void run() {
CompletableFuture.completedFuture(null)
.thenApply(v -> invocationCreator.create())
.exceptionally(this::sendCreateInvocationException)
.thenAccept(this::tryRunInvocation);
}

private void tryRunInvocation(Invocation invocation) {
if (invocation == null) {
return;
}

invocation.onStart(requestEx, startTime);
getOrCreateFilterChain(invocation)
.onFilter(invocation)
.whenComplete((response, Throwable) -> sendResponse(invocation, response))
.whenComplete((response, Throwable) -> finishInvocation(invocation, response, Throwable));
}

private void finishInvocation(Invocation invocation, Response response, Throwable throwable) {
invocation.onFinish(response);

if (throwable == null) {
return;
}

throwable = Exceptions.unwrap(throwable);
if (requestEx == null) {
LOGGER.error("Failed to finish invocation, operation:{}", invocation.getMicroserviceQualifiedName(), throwable);
return;
}

LOGGER.error("Failed to finish invocation, operation:{}, request uri:{}",
invocation.getMicroserviceQualifiedName(), requestEx.getRequestURI(), throwable);
}

protected abstract FilterNode getOrCreateFilterChain(Invocation invocation);

protected abstract Invocation sendCreateInvocationException(Throwable throwable);

protected abstract void sendResponse(Invocation invocation, Response response);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.servicecomb.core.invocation;

import static org.assertj.core.api.Assertions.assertThat;

import java.util.concurrent.atomic.AtomicLong;

import org.apache.servicecomb.core.Invocation;
import org.apache.servicecomb.core.exception.Exceptions;
import org.apache.servicecomb.core.filter.FilterNode;
import org.apache.servicecomb.foundation.test.scaffolding.exception.RuntimeExceptionWithoutStackTrace;
import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx;
import org.apache.servicecomb.swagger.invocation.Response;
import org.junit.Test;

import mockit.Injectable;
import mockit.Mock;
import mockit.MockUp;

public class ProducerInvocationFlowTest {
class TestFlow extends ProducerInvocationFlow {
public TestFlow(InvocationCreator invocationCreator) {
super(invocationCreator);
}

@Override
protected FilterNode getOrCreateFilterChain(Invocation invocation) {
return filterNode;
}

@Override
protected Invocation sendCreateInvocationException(Throwable throwable) {
sendException = throwable;
return null;
}

@Override
protected void sendResponse(Invocation invocation, Response response) {
sendInvocation = invocation;
}
}

FilterNode filterNode = FilterNode.EMPTY;

Throwable sendException;

Invocation sendInvocation;

@Test
public void should_send_exception_response_when_failed_to_create_invocation() {
RuntimeException exception = new RuntimeExceptionWithoutStackTrace();
TestFlow flow = new TestFlow(() -> {
throw exception;
});

flow.run();

assertThat(Exceptions.unwrap(sendException)).isSameAs(exception);
}

@Test
public void should_start_invocation_when_succeed_to_create_invocation(@Injectable Invocation invocation) {
TestFlow flow = new TestFlow(() -> invocation);
AtomicLong startTime = new AtomicLong();
new MockUp<Invocation>() {
@Mock
void onStart(HttpServletRequestEx requestEx, long start) {
startTime.set(start);
}
};
flow.run();

assertThat(startTime.get()).isNotEqualTo(0);
}

@Test
public void should_send_response_when_invocation_success(@Injectable Invocation invocation) {
TestFlow flow = new TestFlow(() -> invocation);

flow.run();

assertThat(sendInvocation).isSameAs(invocation);
}

@Test
public void should_finish_invocation_when_invocation_success(@Injectable Invocation invocation) {
TestFlow flow = new TestFlow(() -> invocation);
AtomicLong finishTime = new AtomicLong();
new MockUp<Invocation>() {
@Mock
void onFinish(Response response) {
finishTime.set(1);
}
};

flow.run();

assertThat(finishTime.get()).isEqualTo(1);
}

@Test
public void should_send_response_when_invocation_fail(@Injectable Invocation invocation) {
TestFlow flow = new TestFlow(() -> invocation);
filterNode = new FilterNode((_invocation, _node) -> {
throw new RuntimeExceptionWithoutStackTrace();
});

flow.run();

assertThat(sendInvocation).isSameAs(invocation);
}

@Test
public void should_finish_invocation_when_invocation_fail(@Injectable Invocation invocation) {
TestFlow flow = new TestFlow(() -> invocation);
filterNode = new FilterNode((_invocation, _node) -> {
throw new RuntimeExceptionWithoutStackTrace();
});
AtomicLong finishTime = new AtomicLong();
new MockUp<Invocation>() {
@Mock
void onFinish(Response response) {
finishTime.set(1);
}
};

flow.run();

assertThat(finishTime.get()).isEqualTo(1);
}
}

0 comments on commit 42816bc

Please sign in to comment.