blob: 7711861ec2bc6339246c5665330db97de54e7857 [file] [log] [blame]
/*
* 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.dubbo.rpc.cluster.support;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.utils.DubboAppender;
import org.apache.dubbo.common.utils.LogUtil;
import org.apache.dubbo.rpc.AppResponse;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcContext;
import org.apache.dubbo.rpc.RpcInvocation;
import org.apache.dubbo.rpc.cluster.Directory;
import org.apache.log4j.Level;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import static org.apache.dubbo.common.constants.CommonConstants.RETRIES_KEY;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
/**
* FailbackClusterInvokerTest
* <p>
* add annotation @TestMethodOrder, the testARetryFailed Method must to first execution
*/
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class FailbackClusterInvokerTest {
List<Invoker<FailbackClusterInvokerTest>> invokers = new ArrayList<>();
URL url = URL.valueOf("test://test:11/test?retries=2&failbacktasks=2");
Invoker<FailbackClusterInvokerTest> invoker = mock(Invoker.class);
RpcInvocation invocation = new RpcInvocation();
Directory<FailbackClusterInvokerTest> dic;
Result result = new AppResponse();
/**
* @throws java.lang.Exception
*/
@BeforeEach
public void setUp() throws Exception {
dic = mock(Directory.class);
given(dic.getUrl()).willReturn(url);
given(dic.getConsumerUrl()).willReturn(url);
given(dic.list(invocation)).willReturn(invokers);
given(dic.getInterface()).willReturn(FailbackClusterInvokerTest.class);
invocation.setMethodName("method1");
invokers.add(invoker);
}
@AfterEach
public void tearDown() {
dic = null;
invocation = new RpcInvocation();
invokers.clear();
}
private void resetInvokerToException() {
given(invoker.invoke(invocation)).willThrow(new RuntimeException());
given(invoker.getUrl()).willReturn(url);
given(invoker.getInterface()).willReturn(FailbackClusterInvokerTest.class);
}
private void resetInvokerToNoException() {
given(invoker.invoke(invocation)).willReturn(result);
given(invoker.getUrl()).willReturn(url);
given(invoker.getInterface()).willReturn(FailbackClusterInvokerTest.class);
}
@Test
public void testInvokeWithIllegalRetriesParam() {
URL url = URL.valueOf("test://test:11/test?retries=-1&failbacktasks=2");
Directory<FailbackClusterInvokerTest> dic = mock(Directory.class);
given(dic.getUrl()).willReturn(url);
given(dic.getConsumerUrl()).willReturn(url);
given(dic.getInterface()).willReturn(FailbackClusterInvokerTest.class);
FailbackClusterInvoker<FailbackClusterInvokerTest> invoker = new FailbackClusterInvoker<>(dic);
invoker.invoke(invocation);
Assertions.assertNull(RpcContext.getServiceContext().getInvoker());
DubboAppender.clear();
}
@Test
public void testInvokeWithIllegalFailbacktasksParam() {
URL url = URL.valueOf("test://test:11/test?retries=2&failbacktasks=-1");
Directory<FailbackClusterInvokerTest> dic = mock(Directory.class);
given(dic.getUrl()).willReturn(url);
given(dic.getConsumerUrl()).willReturn(url);
given(dic.getInterface()).willReturn(FailbackClusterInvokerTest.class);
FailbackClusterInvoker<FailbackClusterInvokerTest> invoker = new FailbackClusterInvoker<>(dic);
invoker.invoke(invocation);
Assertions.assertNull(RpcContext.getServiceContext().getInvoker());
DubboAppender.clear();
}
@Test
@Order(1)
public void testInvokeException() {
resetInvokerToException();
FailbackClusterInvoker<FailbackClusterInvokerTest> invoker = new FailbackClusterInvoker<>(dic);
invoker.invoke(invocation);
Assertions.assertNull(RpcContext.getServiceContext().getInvoker());
DubboAppender.clear();
}
@Test
@Order(2)
public void testInvokeNoException() {
resetInvokerToNoException();
FailbackClusterInvoker<FailbackClusterInvokerTest> invoker = new FailbackClusterInvoker<>(dic);
Result ret = invoker.invoke(invocation);
Assertions.assertSame(result, ret);
}
@Test
@Order(3)
public void testNoInvoke() {
dic = mock(Directory.class);
given(dic.getUrl()).willReturn(url);
given(dic.getConsumerUrl()).willReturn(url);
given(dic.list(invocation)).willReturn(null);
given(dic.getInterface()).willReturn(FailbackClusterInvokerTest.class);
invocation.setMethodName("method1");
invokers.add(invoker);
resetInvokerToNoException();
FailbackClusterInvoker<FailbackClusterInvokerTest> invoker = new FailbackClusterInvoker<>(dic);
LogUtil.start();
DubboAppender.clear();
invoker.invoke(invocation);
assertEquals(1, LogUtil.findMessage("Failback to invoke"));
LogUtil.stop();
}
@Disabled
@Test
@Order(4)
public void testARetryFailed() throws Exception {
//Test retries and
resetInvokerToException();
FailbackClusterInvoker<FailbackClusterInvokerTest> invoker = new FailbackClusterInvoker<>(dic);
LogUtil.start();
DubboAppender.clear();
invoker.invoke(invocation);
invoker.invoke(invocation);
invoker.invoke(invocation);
Assertions.assertNull(RpcContext.getServiceContext().getInvoker());
// invoker.retryFailed();// when retry the invoker which get from failed map already is not the mocked invoker,so
//Ensure that the main thread is online
CountDownLatch countDown = new CountDownLatch(1);
countDown.await(15000L, TimeUnit.MILLISECONDS);
LogUtil.stop();
Assertions.assertEquals(4, LogUtil.findMessage(Level.ERROR, "Failed retry to invoke method"), "must have four error message ");
Assertions.assertEquals(2, LogUtil.findMessage(Level.ERROR, "Failed retry times exceed threshold"), "must have two error message ");
Assertions.assertEquals(1, LogUtil.findMessage(Level.ERROR, "Failback background works error"), "must have one error message ");
// it can be invoke successfully
}
private long getRetryFailedPeriod() throws NoSuchFieldException, IllegalAccessException {
Field retryFailedPeriod = FailbackClusterInvoker.class.getDeclaredField("RETRY_FAILED_PERIOD");
retryFailedPeriod.setAccessible(true);
return retryFailedPeriod.getLong(FailbackClusterInvoker.class);
}
@Test
@Order(5)
public void testInvokeRetryTimesWithZeroValue() throws InterruptedException, NoSuchFieldException,
IllegalAccessException {
int retries = 0;
resetInvokerToException();
given(dic.getConsumerUrl()).willReturn(url.addParameter(RETRIES_KEY, retries));
FailbackClusterInvoker<FailbackClusterInvokerTest> invoker = new FailbackClusterInvoker<>(dic);
LogUtil.start();
DubboAppender.clear();
invocation.setMethodName("testInvokeRetryTimesWithZeroValue");
invoker.invoke(invocation);
CountDownLatch countDown = new CountDownLatch(1);
countDown.await(getRetryFailedPeriod() * (retries + 1), TimeUnit.SECONDS);
LogUtil.stop();
Assertions.assertEquals(0, LogUtil.findMessage(Level.INFO, "Attempt to retry to invoke method " +
"testInvokeRetryTimesWithZeroValue"), "No retry messages allowed");
}
@Test
@Order(6)
public void testInvokeRetryTimesWithTwoValue() throws InterruptedException, NoSuchFieldException,
IllegalAccessException {
int retries = 2;
resetInvokerToException();
given(dic.getConsumerUrl()).willReturn(url.addParameter(RETRIES_KEY, retries));
FailbackClusterInvoker<FailbackClusterInvokerTest> invoker = new FailbackClusterInvoker<>(dic);
LogUtil.start();
DubboAppender.clear();
invocation.setMethodName("testInvokeRetryTimesWithTwoValue");
invoker.invoke(invocation);
CountDownLatch countDown = new CountDownLatch(1);
countDown.await(getRetryFailedPeriod() * (retries + 1), TimeUnit.SECONDS);
LogUtil.stop();
Assertions.assertEquals(2, LogUtil.findMessage(Level.INFO, "Attempt to retry to invoke method " +
"testInvokeRetryTimesWithTwoValue"), "Must have two error message ");
}
@Test
@Order(7)
public void testInvokeRetryTimesWithDefaultValue() throws InterruptedException, NoSuchFieldException,
IllegalAccessException {
resetInvokerToException();
given(dic.getConsumerUrl()).willReturn(URL.valueOf("test://test:11/test"));
FailbackClusterInvoker<FailbackClusterInvokerTest> invoker = new FailbackClusterInvoker<>(dic);
LogUtil.start();
DubboAppender.clear();
invocation.setMethodName("testInvokeRetryTimesWithDefaultValue");
invoker.invoke(invocation);
CountDownLatch countDown = new CountDownLatch(1);
countDown.await(getRetryFailedPeriod() * (CommonConstants.DEFAULT_FAILBACK_TIMES + 1), TimeUnit.SECONDS);
LogUtil.stop();
Assertions.assertEquals(3, LogUtil.findMessage(Level.INFO, "Attempt to retry to invoke method " +
"testInvokeRetryTimesWithDefaultValue"), "Must have three error message ");
}
@Test
@Order(8)
public void testInvokeRetryTimesWithIllegalValue() throws InterruptedException, NoSuchFieldException,
IllegalAccessException {
resetInvokerToException();
given(dic.getConsumerUrl()).willReturn(url.addParameter(RETRIES_KEY, -100));
FailbackClusterInvoker<FailbackClusterInvokerTest> invoker = new FailbackClusterInvoker<>(dic);
LogUtil.start();
DubboAppender.clear();
invocation.setMethodName("testInvokeRetryTimesWithIllegalValue");
invoker.invoke(invocation);
CountDownLatch countDown = new CountDownLatch(1);
countDown.await(getRetryFailedPeriod() * (CommonConstants.DEFAULT_FAILBACK_TIMES + 1), TimeUnit.SECONDS);
LogUtil.stop();
Assertions.assertEquals(3, LogUtil.findMessage(Level.INFO, "Attempt to retry to invoke method " +
"testInvokeRetryTimesWithIllegalValue"), "Must have three error message ");
}
}