From b37c237a6150240178d1392172f56c0ee83ad85e Mon Sep 17 00:00:00 2001 From: Eric Zhao Date: Wed, 8 Aug 2018 10:09:13 +0800 Subject: [PATCH] Add default fallback support for Dubbo - Update test cases and demo Signed-off-by: Eric Zhao --- .../dubbo/SentinelDubboConsumerFilter.java | 5 +- .../dubbo/SentinelDubboProviderFilter.java | 4 +- .../dubbo/fallback/DefaultDubboFallback.java | 34 +++++++++++ .../adapter/dubbo/fallback/DubboFallback.java | 39 +++++++++++++ .../dubbo/fallback/DubboFallbackRegistry.java | 48 ++++++++++++++++ .../fallback/DubboFallbackRegistryTest.java | 56 +++++++++++++++++++ .../dubbo/demo1/FooConsumerBootstrap.java | 20 +++---- .../dubbo/demo1/FooProviderBootstrap.java | 4 +- .../dubbo/demo2/FooConsumerBootstrap.java | 16 ++++-- .../dubbo/demo2/FooProviderBootstrap.java | 1 + 10 files changed, 207 insertions(+), 20 deletions(-) create mode 100644 sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/fallback/DefaultDubboFallback.java create mode 100644 sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/fallback/DubboFallback.java create mode 100644 sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/fallback/DubboFallbackRegistry.java create mode 100644 sentinel-adapter/sentinel-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/fallback/DubboFallbackRegistryTest.java diff --git a/sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboConsumerFilter.java b/sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboConsumerFilter.java index 1043ec992d..4df20d4606 100755 --- a/sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboConsumerFilter.java +++ b/sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboConsumerFilter.java @@ -19,10 +19,10 @@ import com.alibaba.csp.sentinel.EntryType; import com.alibaba.csp.sentinel.SphU; import com.alibaba.csp.sentinel.Tracer; +import com.alibaba.csp.sentinel.adapter.dubbo.fallback.DubboFallbackRegistry; import com.alibaba.csp.sentinel.context.ContextUtil; import com.alibaba.csp.sentinel.log.RecordLog; import com.alibaba.csp.sentinel.slots.block.BlockException; -import com.alibaba.csp.sentinel.slots.block.SentinelRpcException; import com.alibaba.dubbo.common.extension.Activate; import com.alibaba.dubbo.rpc.Filter; import com.alibaba.dubbo.rpc.Invocation; @@ -39,6 +39,7 @@ * * * @author leyou + * @author Eric Zhao */ @Activate(group = "consumer") public class SentinelDubboConsumerFilter extends AbstractDubboFilter implements Filter { @@ -58,7 +59,7 @@ public Result invoke(Invoker invoker, Invocation invocation) throws RpcExcept methodEntry = SphU.entry(resourceName, EntryType.OUT); return invoker.invoke(invocation); } catch (BlockException e) { - throw new SentinelRpcException(e); + return DubboFallbackRegistry.getConsumerFallback().handle(invoker, invocation, e); } catch (RpcException e) { Tracer.trace(e); throw e; diff --git a/sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboProviderFilter.java b/sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboProviderFilter.java index 18b02b2aac..0eacab2ded 100755 --- a/sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboProviderFilter.java +++ b/sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboProviderFilter.java @@ -19,6 +19,7 @@ import com.alibaba.csp.sentinel.EntryType; import com.alibaba.csp.sentinel.SphU; import com.alibaba.csp.sentinel.Tracer; +import com.alibaba.csp.sentinel.adapter.dubbo.fallback.DubboFallbackRegistry; import com.alibaba.csp.sentinel.context.ContextUtil; import com.alibaba.csp.sentinel.log.RecordLog; import com.alibaba.csp.sentinel.slots.block.BlockException; @@ -39,6 +40,7 @@ * * * @author leyou + * @author Eric Zhao */ @Activate(group = "provider") public class SentinelDubboProviderFilter extends AbstractDubboFilter implements Filter { @@ -63,7 +65,7 @@ public Result invoke(Invoker invoker, Invocation invocation) throws RpcExcept return invoker.invoke(invocation); } catch (BlockException e) { - throw new SentinelRpcException(e); + return DubboFallbackRegistry.getProviderFallback().handle(invoker, invocation, e); } catch (RpcException e) { Tracer.trace(e); throw e; diff --git a/sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/fallback/DefaultDubboFallback.java b/sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/fallback/DefaultDubboFallback.java new file mode 100644 index 0000000000..b97f703036 --- /dev/null +++ b/sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/fallback/DefaultDubboFallback.java @@ -0,0 +1,34 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * Licensed 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 com.alibaba.csp.sentinel.adapter.dubbo.fallback; + +import com.alibaba.csp.sentinel.slots.block.BlockException; +import com.alibaba.csp.sentinel.slots.block.SentinelRpcException; +import com.alibaba.dubbo.rpc.Invocation; +import com.alibaba.dubbo.rpc.Invoker; +import com.alibaba.dubbo.rpc.Result; + +/** + * @author Eric Zhao + */ +public class DefaultDubboFallback implements DubboFallback { + + @Override + public Result handle(Invoker invoker, Invocation invocation, BlockException ex) { + // Just wrap and throw the exception. + throw new SentinelRpcException(ex); + } +} diff --git a/sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/fallback/DubboFallback.java b/sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/fallback/DubboFallback.java new file mode 100644 index 0000000000..d077ee3eca --- /dev/null +++ b/sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/fallback/DubboFallback.java @@ -0,0 +1,39 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * Licensed 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 com.alibaba.csp.sentinel.adapter.dubbo.fallback; + +import com.alibaba.csp.sentinel.slots.block.BlockException; +import com.alibaba.dubbo.rpc.Invocation; +import com.alibaba.dubbo.rpc.Invoker; +import com.alibaba.dubbo.rpc.Result; + +/** + * Fallback handler for Dubbo services. + * + * @author Eric Zhao + */ +public interface DubboFallback { + + /** + * Handle the block exception and provide fallback result. + * + * @param invoker Dubbo invoker + * @param invocation Dubbo invocation + * @param ex block exception + * @return fallback result + */ + Result handle(Invoker invoker, Invocation invocation, BlockException ex); +} diff --git a/sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/fallback/DubboFallbackRegistry.java b/sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/fallback/DubboFallbackRegistry.java new file mode 100644 index 0000000000..fe06741bbd --- /dev/null +++ b/sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/fallback/DubboFallbackRegistry.java @@ -0,0 +1,48 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * Licensed 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 com.alibaba.csp.sentinel.adapter.dubbo.fallback; + +/** + * Global fallback registry for Dubbo. + * + * Note: Degrading is mainly designed for consumer. The provider should not + * give fallback result in most circumstances. + * + * @author Eric Zhao + */ +public final class DubboFallbackRegistry { + + private static volatile DubboFallback consumerFallback = new DefaultDubboFallback(); + private static volatile DubboFallback providerFallback = new DefaultDubboFallback(); + + public static DubboFallback getConsumerFallback() { + return consumerFallback; + } + + public static void setConsumerFallback(DubboFallback consumerFallback) { + DubboFallbackRegistry.consumerFallback = consumerFallback; + } + + public static DubboFallback getProviderFallback() { + return providerFallback; + } + + public static void setProviderFallback(DubboFallback providerFallback) { + DubboFallbackRegistry.providerFallback = providerFallback; + } + + private DubboFallbackRegistry() {} +} diff --git a/sentinel-adapter/sentinel-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/fallback/DubboFallbackRegistryTest.java b/sentinel-adapter/sentinel-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/fallback/DubboFallbackRegistryTest.java new file mode 100644 index 0000000000..f75e0c34e8 --- /dev/null +++ b/sentinel-adapter/sentinel-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/fallback/DubboFallbackRegistryTest.java @@ -0,0 +1,56 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * Licensed 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 com.alibaba.csp.sentinel.adapter.dubbo.fallback; + +import com.alibaba.csp.sentinel.slots.block.BlockException; +import com.alibaba.csp.sentinel.slots.block.SentinelRpcException; +import com.alibaba.csp.sentinel.slots.block.flow.FlowException; +import com.alibaba.dubbo.rpc.Invocation; +import com.alibaba.dubbo.rpc.Invoker; +import com.alibaba.dubbo.rpc.Result; +import com.alibaba.dubbo.rpc.RpcResult; + +import org.junit.Assert; +import org.junit.Test; + +/** + * @author Eric Zhao + */ +public class DubboFallbackRegistryTest { + + @Test(expected = SentinelRpcException.class) + public void testDefaultFallback() { + // Test for default. + BlockException ex = new FlowException("xxx"); + DubboFallbackRegistry.getConsumerFallback() + .handle(null, null, ex); + } + + @Test + public void testCustomFallback() { + BlockException ex = new FlowException("xxx"); + DubboFallbackRegistry.setConsumerFallback(new DubboFallback() { + @Override + public Result handle(Invoker invoker, Invocation invocation, BlockException e) { + return new RpcResult("Error: " + e.getClass().getName()); + } + }); + Result result = DubboFallbackRegistry.getConsumerFallback() + .handle(null, null, ex); + Assert.assertFalse("The invocation should not fail", result.hasException()); + Assert.assertEquals("Error: " + ex.getClass().getName(), result.getValue()); + } +} diff --git a/sentinel-demo/sentinel-demo-dubbo/src/main/java/com/alibaba/csp/sentinel/demo/dubbo/demo1/FooConsumerBootstrap.java b/sentinel-demo/sentinel-demo-dubbo/src/main/java/com/alibaba/csp/sentinel/demo/dubbo/demo1/FooConsumerBootstrap.java index 949f7a83f8..08920691ef 100644 --- a/sentinel-demo/sentinel-demo-dubbo/src/main/java/com/alibaba/csp/sentinel/demo/dubbo/demo1/FooConsumerBootstrap.java +++ b/sentinel-demo/sentinel-demo-dubbo/src/main/java/com/alibaba/csp/sentinel/demo/dubbo/demo1/FooConsumerBootstrap.java @@ -15,10 +15,8 @@ */ package com.alibaba.csp.sentinel.demo.dubbo.demo1; - import com.alibaba.csp.sentinel.demo.dubbo.consumer.ConsumerConfiguration; import com.alibaba.csp.sentinel.demo.dubbo.consumer.FooServiceConsumer; -import com.alibaba.csp.sentinel.init.InitExecutor; import com.alibaba.csp.sentinel.slots.block.SentinelRpcException; import org.springframework.context.annotation.AnnotationConfigApplicationContext; @@ -36,8 +34,6 @@ public class FooConsumerBootstrap { public static void main(String[] args) { - InitExecutor.doInit(); - AnnotationConfigApplicationContext consumerContext = new AnnotationConfigApplicationContext(); consumerContext.register(ConsumerConfiguration.class); consumerContext.refresh(); @@ -45,14 +41,14 @@ public static void main(String[] args) { FooServiceConsumer service = consumerContext.getBean(FooServiceConsumer.class); for (int i = 0; i < 15; i++) { - try { - String message = service.sayHello("Eric"); - System.out.println("Success: " + message); - } catch (SentinelRpcException ex) { - System.out.println("Blocked"); - } catch (Exception ex) { - ex.printStackTrace(); - } + try { + String message = service.sayHello("Eric"); + System.out.println("Success: " + message); + } catch (SentinelRpcException ex) { + System.out.println("Blocked"); + } catch (Exception ex) { + ex.printStackTrace(); + } } } } diff --git a/sentinel-demo/sentinel-demo-dubbo/src/main/java/com/alibaba/csp/sentinel/demo/dubbo/demo1/FooProviderBootstrap.java b/sentinel-demo/sentinel-demo-dubbo/src/main/java/com/alibaba/csp/sentinel/demo/dubbo/demo1/FooProviderBootstrap.java index ceafae514d..49cd466999 100644 --- a/sentinel-demo/sentinel-demo-dubbo/src/main/java/com/alibaba/csp/sentinel/demo/dubbo/demo1/FooProviderBootstrap.java +++ b/sentinel-demo/sentinel-demo-dubbo/src/main/java/com/alibaba/csp/sentinel/demo/dubbo/demo1/FooProviderBootstrap.java @@ -40,9 +40,11 @@ public class FooProviderBootstrap { private static final String INTERFACE_RES_KEY = "com.alibaba.csp.sentinel.demo.dubbo.FooService"; public static void main(String[] args) { - initFlowRule(); + // Users don't need to manually call this method. InitExecutor.doInit(); + initFlowRule(); + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.register(ProviderConfiguration.class); context.refresh(); diff --git a/sentinel-demo/sentinel-demo-dubbo/src/main/java/com/alibaba/csp/sentinel/demo/dubbo/demo2/FooConsumerBootstrap.java b/sentinel-demo/sentinel-demo-dubbo/src/main/java/com/alibaba/csp/sentinel/demo/dubbo/demo2/FooConsumerBootstrap.java index b4ab226dfd..2eb717d561 100644 --- a/sentinel-demo/sentinel-demo-dubbo/src/main/java/com/alibaba/csp/sentinel/demo/dubbo/demo2/FooConsumerBootstrap.java +++ b/sentinel-demo/sentinel-demo-dubbo/src/main/java/com/alibaba/csp/sentinel/demo/dubbo/demo2/FooConsumerBootstrap.java @@ -19,6 +19,7 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import com.alibaba.csp.sentinel.adapter.dubbo.fallback.DubboFallbackRegistry; import com.alibaba.csp.sentinel.concurrent.NamedThreadFactory; import com.alibaba.csp.sentinel.demo.dubbo.consumer.ConsumerConfiguration; import com.alibaba.csp.sentinel.demo.dubbo.consumer.FooServiceConsumer; @@ -27,6 +28,8 @@ import com.alibaba.csp.sentinel.slots.block.SentinelRpcException; import com.alibaba.csp.sentinel.slots.block.flow.FlowRule; import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager; +import com.alibaba.dubbo.rpc.Result; +import com.alibaba.dubbo.rpc.RpcResult; import org.springframework.context.annotation.AnnotationConfigApplicationContext; @@ -50,7 +53,6 @@ public class FooConsumerBootstrap { public static void main(String[] args) { initFlowRule(); - InitExecutor.doInit(); AnnotationConfigApplicationContext consumerContext = new AnnotationConfigApplicationContext(); consumerContext.register(ConsumerConfiguration.class); @@ -68,9 +70,7 @@ public static void main(String[] args) { ex.printStackTrace(); } }); - pool.submit(() -> { - System.out.println("Another: " + service.doAnother()); - }); + pool.submit(() -> System.out.println("Another: " + service.doAnother())); } } @@ -82,4 +82,12 @@ private static void initFlowRule() { flowRule.setLimitApp("default"); FlowRuleManager.loadRules(Collections.singletonList(flowRule)); } + + private static void registerFallback() { + // Register fallback handler for consumer. + // If you only want to handle degrading, you need to + // check the type of BlockException. + DubboFallbackRegistry.setConsumerFallback((a, b, ex) -> + new RpcResult("Error: " + ex.getClass().getTypeName())); + } } diff --git a/sentinel-demo/sentinel-demo-dubbo/src/main/java/com/alibaba/csp/sentinel/demo/dubbo/demo2/FooProviderBootstrap.java b/sentinel-demo/sentinel-demo-dubbo/src/main/java/com/alibaba/csp/sentinel/demo/dubbo/demo2/FooProviderBootstrap.java index 513b0d0d70..46e94591f4 100644 --- a/sentinel-demo/sentinel-demo-dubbo/src/main/java/com/alibaba/csp/sentinel/demo/dubbo/demo2/FooProviderBootstrap.java +++ b/sentinel-demo/sentinel-demo-dubbo/src/main/java/com/alibaba/csp/sentinel/demo/dubbo/demo2/FooProviderBootstrap.java @@ -32,6 +32,7 @@ public class FooProviderBootstrap { public static void main(String[] args) { + // Users don't need to manually call this method. InitExecutor.doInit(); AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();