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.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 @@ import com.alibaba.dubbo.rpc.RpcException;
* </pre>
*
* @author leyou
* @author Eric Zhao
*/
@Activate(group = "consumer")
public class SentinelDubboConsumerFilter extends AbstractDubboFilter implements Filter {
@ -58,7 +59,7 @@ public class SentinelDubboConsumerFilter extends AbstractDubboFilter implements
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;

View File

@ -19,6 +19,7 @@ import com.alibaba.csp.sentinel.Entry;
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 @@ import com.alibaba.dubbo.rpc.RpcException;
* </pre>
*
* @author leyou
* @author Eric Zhao
*/
@Activate(group = "provider")
public class SentinelDubboProviderFilter extends AbstractDubboFilter implements Filter {
@ -63,7 +65,7 @@ public class SentinelDubboProviderFilter extends AbstractDubboFilter implements
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;

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;
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 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext
public class FooConsumerBootstrap {
public static void main(String[] args) {
InitExecutor.doInit();
AnnotationConfigApplicationContext consumerContext = new AnnotationConfigApplicationContext();
consumerContext.register(ConsumerConfiguration.class);
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";
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();

View File

@ -19,6 +19,7 @@ import java.util.Collections;
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.RuleConstant;
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 class FooConsumerBootstrap {
ex.printStackTrace();
}
});
pool.submit(() -> {
System.out.println("Another: " + service.doAnother());
});
pool.submit(() -> System.out.println("Another: " + service.doAnother()));
}
}
@ -82,4 +82,12 @@ public class FooConsumerBootstrap {
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()));
}
}

View File

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