Add default fallback support for Dubbo

- Update test cases and demo

Signed-off-by: Eric Zhao <sczyh16@gmail.com>
This commit is contained in:
Eric Zhao 2018-08-08 10:09:13 +08:00
parent 0438d530d4
commit b37c237a61
10 changed files with 207 additions and 20 deletions

View File

@ -19,10 +19,10 @@ import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.EntryType; import com.alibaba.csp.sentinel.EntryType;
import com.alibaba.csp.sentinel.SphU; import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.Tracer; 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.context.ContextUtil;
import com.alibaba.csp.sentinel.log.RecordLog; import com.alibaba.csp.sentinel.log.RecordLog;
import com.alibaba.csp.sentinel.slots.block.BlockException; 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.common.extension.Activate;
import com.alibaba.dubbo.rpc.Filter; import com.alibaba.dubbo.rpc.Filter;
import com.alibaba.dubbo.rpc.Invocation; import com.alibaba.dubbo.rpc.Invocation;
@ -39,6 +39,7 @@ import com.alibaba.dubbo.rpc.RpcException;
* </pre> * </pre>
* *
* @author leyou * @author leyou
* @author Eric Zhao
*/ */
@Activate(group = "consumer") @Activate(group = "consumer")
public class SentinelDubboConsumerFilter extends AbstractDubboFilter implements Filter { public class SentinelDubboConsumerFilter extends AbstractDubboFilter implements Filter {
@ -58,7 +59,7 @@ public class SentinelDubboConsumerFilter extends AbstractDubboFilter implements
methodEntry = SphU.entry(resourceName, EntryType.OUT); methodEntry = SphU.entry(resourceName, EntryType.OUT);
return invoker.invoke(invocation); return invoker.invoke(invocation);
} catch (BlockException e) { } catch (BlockException e) {
throw new SentinelRpcException(e); return DubboFallbackRegistry.getConsumerFallback().handle(invoker, invocation, e);
} catch (RpcException e) { } catch (RpcException e) {
Tracer.trace(e); Tracer.trace(e);
throw e; throw e;

View File

@ -19,6 +19,7 @@ import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.EntryType; import com.alibaba.csp.sentinel.EntryType;
import com.alibaba.csp.sentinel.SphU; import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.Tracer; 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.context.ContextUtil;
import com.alibaba.csp.sentinel.log.RecordLog; import com.alibaba.csp.sentinel.log.RecordLog;
import com.alibaba.csp.sentinel.slots.block.BlockException; import com.alibaba.csp.sentinel.slots.block.BlockException;
@ -39,6 +40,7 @@ import com.alibaba.dubbo.rpc.RpcException;
* </pre> * </pre>
* *
* @author leyou * @author leyou
* @author Eric Zhao
*/ */
@Activate(group = "provider") @Activate(group = "provider")
public class SentinelDubboProviderFilter extends AbstractDubboFilter implements Filter { public class SentinelDubboProviderFilter extends AbstractDubboFilter implements Filter {
@ -63,7 +65,7 @@ public class SentinelDubboProviderFilter extends AbstractDubboFilter implements
return invoker.invoke(invocation); return invoker.invoke(invocation);
} catch (BlockException e) { } catch (BlockException e) {
throw new SentinelRpcException(e); return DubboFallbackRegistry.getProviderFallback().handle(invoker, invocation, e);
} catch (RpcException e) { } catch (RpcException e) {
Tracer.trace(e); Tracer.trace(e);
throw e; throw e;

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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() {}
}

View File

@ -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());
}
}

View File

@ -15,10 +15,8 @@
*/ */
package com.alibaba.csp.sentinel.demo.dubbo.demo1; 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.ConsumerConfiguration;
import com.alibaba.csp.sentinel.demo.dubbo.consumer.FooServiceConsumer; 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 com.alibaba.csp.sentinel.slots.block.SentinelRpcException;
import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext;
@ -36,8 +34,6 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext
public class FooConsumerBootstrap { public class FooConsumerBootstrap {
public static void main(String[] args) { public static void main(String[] args) {
InitExecutor.doInit();
AnnotationConfigApplicationContext consumerContext = new AnnotationConfigApplicationContext(); AnnotationConfigApplicationContext consumerContext = new AnnotationConfigApplicationContext();
consumerContext.register(ConsumerConfiguration.class); consumerContext.register(ConsumerConfiguration.class);
consumerContext.refresh(); consumerContext.refresh();

View File

@ -40,9 +40,11 @@ public class FooProviderBootstrap {
private static final String INTERFACE_RES_KEY = "com.alibaba.csp.sentinel.demo.dubbo.FooService"; private static final String INTERFACE_RES_KEY = "com.alibaba.csp.sentinel.demo.dubbo.FooService";
public static void main(String[] args) { public static void main(String[] args) {
initFlowRule(); // Users don't need to manually call this method.
InitExecutor.doInit(); InitExecutor.doInit();
initFlowRule();
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(ProviderConfiguration.class); context.register(ProviderConfiguration.class);
context.refresh(); context.refresh();

View File

@ -19,6 +19,7 @@ import java.util.Collections;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; 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.concurrent.NamedThreadFactory;
import com.alibaba.csp.sentinel.demo.dubbo.consumer.ConsumerConfiguration; import com.alibaba.csp.sentinel.demo.dubbo.consumer.ConsumerConfiguration;
import com.alibaba.csp.sentinel.demo.dubbo.consumer.FooServiceConsumer; import com.alibaba.csp.sentinel.demo.dubbo.consumer.FooServiceConsumer;
@ -27,6 +28,8 @@ import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.SentinelRpcException; 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.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager; 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; import org.springframework.context.annotation.AnnotationConfigApplicationContext;
@ -50,7 +53,6 @@ public class FooConsumerBootstrap {
public static void main(String[] args) { public static void main(String[] args) {
initFlowRule(); initFlowRule();
InitExecutor.doInit();
AnnotationConfigApplicationContext consumerContext = new AnnotationConfigApplicationContext(); AnnotationConfigApplicationContext consumerContext = new AnnotationConfigApplicationContext();
consumerContext.register(ConsumerConfiguration.class); consumerContext.register(ConsumerConfiguration.class);
@ -68,9 +70,7 @@ public class FooConsumerBootstrap {
ex.printStackTrace(); ex.printStackTrace();
} }
}); });
pool.submit(() -> { pool.submit(() -> System.out.println("Another: " + service.doAnother()));
System.out.println("Another: " + service.doAnother());
});
} }
} }
@ -82,4 +82,12 @@ public class FooConsumerBootstrap {
flowRule.setLimitApp("default"); flowRule.setLimitApp("default");
FlowRuleManager.loadRules(Collections.singletonList(flowRule)); 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()));
}
} }

View File

@ -32,6 +32,7 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext
public class FooProviderBootstrap { public class FooProviderBootstrap {
public static void main(String[] args) { public static void main(String[] args) {
// Users don't need to manually call this method.
InitExecutor.doInit(); InitExecutor.doInit();
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();