Add default fallback support for Dubbo
- Update test cases and demo Signed-off-by: Eric Zhao <sczyh16@gmail.com>
This commit is contained in:
parent
0438d530d4
commit
b37c237a61
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
@ -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() {}
|
||||||
|
}
|
||||||
|
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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();
|
||||||
|
|
@ -45,14 +41,14 @@ public class FooConsumerBootstrap {
|
||||||
FooServiceConsumer service = consumerContext.getBean(FooServiceConsumer.class);
|
FooServiceConsumer service = consumerContext.getBean(FooServiceConsumer.class);
|
||||||
|
|
||||||
for (int i = 0; i < 15; i++) {
|
for (int i = 0; i < 15; i++) {
|
||||||
try {
|
try {
|
||||||
String message = service.sayHello("Eric");
|
String message = service.sayHello("Eric");
|
||||||
System.out.println("Success: " + message);
|
System.out.println("Success: " + message);
|
||||||
} catch (SentinelRpcException ex) {
|
} catch (SentinelRpcException ex) {
|
||||||
System.out.println("Blocked");
|
System.out.println("Blocked");
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
ex.printStackTrace();
|
ex.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -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()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue