You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dubbo.apache.org by al...@apache.org on 2021/08/23 05:18:02 UTC

[dubbo] branch 3.0 updated: Fix bug of BroadcastClusterInvoker and add test cases (#8532)

This is an automated email from the ASF dual-hosted git repository.

albumenj pushed a commit to branch 3.0
in repository https://gitbox.apache.org/repos/asf/dubbo.git


The following commit(s) were added to refs/heads/3.0 by this push:
     new d7f2452  Fix bug of BroadcastClusterInvoker and add test cases (#8532)
d7f2452 is described below

commit d7f24524f61a97edd275a838afc1d98d606bf2e3
Author: 灼华 <43...@users.noreply.github.com>
AuthorDate: Mon Aug 23 13:17:43 2021 +0800

    Fix bug of BroadcastClusterInvoker and add test cases (#8532)
---
 .../cluster/support/BroadcastClusterInvoker.java   |   6 +-
 .../support/BroadCastClusterInvokerTest.java       | 164 +++++++++++++++++++++
 2 files changed, 166 insertions(+), 4 deletions(-)

diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/BroadcastClusterInvoker.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/BroadcastClusterInvoker.java
index cf7520f..6b23b3a 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/BroadcastClusterInvoker.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/BroadcastClusterInvoker.java
@@ -72,20 +72,18 @@ public class BroadcastClusterInvoker<T> extends AbstractClusterInvoker<T> {
                     if (null != resultException) {
                         exception = getRpcException(result.getException());
                         logger.warn(exception.getMessage(), exception);
+                        failIndex++;
                         if (failIndex == failThresholdIndex) {
                             break;
-                        } else {
-                            failIndex++;
                         }
                     }
                 }
             } catch (Throwable e) {
                 exception = getRpcException(e);
                 logger.warn(exception.getMessage(), exception);
+                failIndex++;
                 if (failIndex == failThresholdIndex) {
                     break;
-                } else {
-                    failIndex++;
                 }
             }
         }
diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/BroadCastClusterInvokerTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/BroadCastClusterInvokerTest.java
new file mode 100644
index 0000000..ea09d02
--- /dev/null
+++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/BroadCastClusterInvokerTest.java
@@ -0,0 +1,164 @@
+/*
+ * 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.rpc.Invocation;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.Result;
+import org.apache.dubbo.rpc.RpcException;
+import org.apache.dubbo.rpc.RpcInvocation;
+import org.apache.dubbo.rpc.cluster.Directory;
+import org.apache.dubbo.rpc.cluster.filter.DemoService;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.Arrays;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.BDDMockito.given;
+import static org.mockito.Mockito.mock;
+
+/**
+ * @see BroadcastClusterInvoker
+ */
+public class BroadCastClusterInvokerTest {
+    private URL url;
+    private Directory<DemoService> dic;
+    private RpcInvocation invocation;
+    private BroadcastClusterInvoker clusterInvoker;
+
+    private MockInvoker invoker1;
+    private MockInvoker invoker2;
+    private MockInvoker invoker3;
+    private MockInvoker invoker4;
+
+
+    @BeforeEach
+    public void setUp() throws Exception {
+
+        dic = mock(Directory.class);
+
+        invoker1 = new MockInvoker();
+        invoker2 = new MockInvoker();
+        invoker3 = new MockInvoker();
+        invoker4 = new MockInvoker();
+
+        url = URL.valueOf("test://127.0.0.1:8080/test");
+        given(dic.getUrl()).willReturn(url);
+        given(dic.getConsumerUrl()).willReturn(url);
+        given(dic.getInterface()).willReturn(DemoService.class);
+
+        invocation = new RpcInvocation();
+        invocation.setMethodName("test");
+
+        clusterInvoker = new BroadcastClusterInvoker(dic);
+    }
+
+
+    @Test
+    public void testNormal() {
+        given(dic.list(invocation)).willReturn(Arrays.asList(invoker1, invoker2, invoker3, invoker4));
+        // Every invoker will be called
+        clusterInvoker.invoke(invocation);
+        assertTrue(invoker1.isInvoked());
+        assertTrue(invoker2.isInvoked());
+        assertTrue(invoker3.isInvoked());
+        assertTrue(invoker4.isInvoked());
+    }
+
+    @Test
+    public void testEx() {
+        given(dic.list(invocation)).willReturn(Arrays.asList(invoker1, invoker2, invoker3, invoker4));
+        invoker1.invokeThrowEx();
+        assertThrows(RpcException.class, () -> {
+            clusterInvoker.invoke(invocation);
+        });
+        // The default failure percentage is 100, even if a certain invoker#invoke throws an exception, other invokers will still be called
+        assertTrue(invoker1.isInvoked());
+        assertTrue(invoker2.isInvoked());
+        assertTrue(invoker3.isInvoked());
+        assertTrue(invoker4.isInvoked());
+    }
+
+    @Test
+    public void testFailPercent() {
+        given(dic.list(invocation)).willReturn(Arrays.asList(invoker1, invoker2, invoker3, invoker4));
+        // We set the failure percentage to 75, which means that when the number of call failures is 4*(75/100) = 3,
+        // an exception will be thrown directly and subsequent invokers will not be called.
+        url = url.addParameter("broadcast.fail.percent", 75);
+        given(dic.getConsumerUrl()).willReturn(url);
+        invoker1.invokeThrowEx();
+        invoker2.invokeThrowEx();
+        invoker3.invokeThrowEx();
+        invoker4.invokeThrowEx();
+        assertThrows(RpcException.class, () -> {
+            clusterInvoker.invoke(invocation);
+        });
+        assertTrue(invoker1.isInvoked());
+        assertTrue(invoker2.isInvoked());
+        assertTrue(invoker3.isInvoked());
+        assertFalse(invoker4.isInvoked());
+    }
+}
+
+class MockInvoker implements Invoker<DemoService> {
+    private static int count = 0;
+    private URL url = URL.valueOf("test://127.0.0.1:8080/test");
+    private boolean throwEx = false;
+    private boolean invoked = false;
+
+    @Override
+    public URL getUrl() {
+        return url;
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return false;
+    }
+
+    @Override
+    public void destroy() {
+
+    }
+
+    @Override
+    public Class<DemoService> getInterface() {
+        return DemoService.class;
+    }
+
+    @Override
+    public Result invoke(Invocation invocation) throws RpcException {
+        invoked = true;
+        if (throwEx) {
+            throwEx = false;
+            throw new RpcException();
+        }
+        return null;
+    }
+
+    public void invokeThrowEx() {
+        throwEx = true;
+    }
+
+    public boolean isInvoked() {
+        return invoked;
+    }
+}