Sentenel对Dubbo支持
(1) Dubbo中使用Sentinel
Sentinel与dubbo结合的基本思路是sentinel利用了Dubbo Filter和spi机制进行拓展,这样做的好处可以做到无需改动业务代码就能支持限流、熔断等功能。
SPI对应的配置文件 /resources/META-INF/dubbo/org.apache.dubbo.rpc.Filter
sentinel.dubbo.provider.filter=com.alibaba.csp.sentinel.adapter.dubbo.SentinelDubboProviderFilter
sentinel.dubbo.consumer.filter=com.alibaba.csp.sentinel.adapter.dubbo.SentinelDubboConsumerFilter
dubbo.application.context.name.filter=com.alibaba.csp.sentinel.adapter.dubbo.DubboAppContextFilter
(2) 源码解析
package com.alibaba.csp.sentinel.adapter.dubbo;
/**
* 支持与Sentinel集成的Apache Dubbo服务提供商筛选器。默认情况下自动激活。
* 注意:这只适用于Apache Dubbo 2.7.x或更高版本。
*
* 如果要禁用提供程序筛选器,可以配置: dubbo:provider filter="-sentinel.dubbo.provider.filter"
*/
@Activate(group = PROVIDER)
public class SentinelDubboProviderFilter extends BaseSentinelDubboFilter {
public SentinelDubboProviderFilter() {
RecordLog.info("Sentinel Apache Dubbo provider filter initialized");
}
@Override
String getMethodName(Invoker invoker, Invocation invocation, String prefix) {
return DubboUtils.getMethodResourceName(invoker, invocation, prefix);
}
@Override
String getInterfaceName(Invoker invoker, String prefix) {
return DubboUtils.getInterfaceName(invoker, prefix);
}
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
// Get origin caller.
String origin = DubboAdapterGlobalConfig.getOriginParser().parse(invoker, invocation);
if (null == origin) {
origin = "";
}
Entry interfaceEntry = null;
Entry methodEntry = null;
String prefix = DubboAdapterGlobalConfig.getDubboProviderResNamePrefixKey();
// 接口名
String interfaceResourceName = getInterfaceName(invoker, prefix);
// 方法名
String methodResourceName = getMethodName(invoker, invocation, prefix);
try {
// Only need to create entrance context at provider side, as context will take effect
// at entrance of invocation chain only (for inbound traffic).
// 使用方法名作为上下文名称
ContextUtil.enter(methodResourceName, origin);
// 使用接口名作为interfaceEntry资源名
interfaceEntry = SphU.entry(interfaceResourceName, ResourceTypeConstants.COMMON_RPC, EntryType.IN);
// 使用方法名作为methodEntry资源名
methodEntry = SphU.entry(methodResourceName, ResourceTypeConstants.COMMON_RPC, EntryType.IN,
invocation.getArguments());
// 调用方法
Result result = invoker.invoke(invocation);
if (result.hasException()) {
Tracer.traceEntry(result.getException(), interfaceEntry);
Tracer.traceEntry(result.getException(), methodEntry);
}
return result;
} catch (BlockException e) {
// 触发限流或熔断
return DubboAdapterGlobalConfig.getProviderFallback().handle(invoker, invocation, e);
} catch (RpcException e) {
Tracer.traceEntry(e, interfaceEntry);
Tracer.traceEntry(e, methodEntry);
throw e;
} finally {
if (methodEntry != null) {
// 退出
methodEntry.exit(1, invocation.getArguments());
}
if (interfaceEntry != null) {
// 退出
interfaceEntry.exit();
}
ContextUtil.exit();
}
}
}
限流或熔断后调用的方法 DefaultDubboFallback::handle()
package com.alibaba.csp.sentinel.adapter.dubbo.fallback;
/**
*
*/
public class DefaultDubboFallback implements DubboFallback {
@Override
public Result handle(Invoker<?> invoker, Invocation invocation, BlockException ex) {
// Just wrap the exception.
return AsyncRpcResult.newDefaultAsyncResult(ex.toRuntimeException(), invocation);
}
}