文章目录
adapter模块概述
- adapter模块负责集成常用流量请求框架
- adapter模块负责对常见框架进行适配
- 构建类似Spring-AOP功能.实现流量拦截,sentinel核心限流逻辑处理
- 针对不同框架的适配协议各不相同
- dubbo采用filter作为代理位点,完成限流增强逻辑
源码分析一filter
- sentinel通过dubbo的过滤器扩展点完成流量管控
org.apache.dubbo.rpc.Filter
filter名称 | class类 | 作用 |
---|---|---|
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 | 为invocation会话对象所在RpcContext上下文设置attachment附件 附件内容为applicationName |
1-DubboAppContextFilter
- 获取invoker执行体对象的applicationName
- 放置到当前会话上下文附件中
@Activate(group = CONSUMER)
public class DubboAppContextFilter implements Filter {
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
获取invoker执行体对象的applicationName
String application = invoker.getUrl().getParameter(CommonConstants.APPLICATION_KEY);
if (application != null) {
放置到当前会话上下文附件中
RpcContext.getContext().setAttachment(DubboUtils.SENTINEL_DUBBO_APPLICATION_KEY, application);
}
return invoker.invoke(invocation);
}
}
2-BaseSentinelDubboFilter
- dubbo从两个维度进行资源限流,该类定义资源获取的相关方法
- 接口维度
- 方法维度
public abstract class BaseSentinelDubboFilter implements Filter {
获取方法名
abstract String getMethodName(Invoker invoker, Invocation invocation, String prefix);
获取接口名
abstract String getInterfaceName(Invoker invoker, String prefix);
}
2.1-限流逻辑SentinelDubboConsumerFilter
EntryType为OUT类型
- 通过filter.invoke进入限流
- 根据dubbo接口信息获取方法和接口两个维度资源名,对两个维度限流
- SphU.entry完成限流处理
- 未限流则执行rpc调用
- Entry.exit完成entry链当前节点出栈
@Activate(group = CONSUMER)
public class SentinelDubboConsumerFilter extends BaseSentinelDubboFilter {
...... 删除其他代码
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
限流入口
InvokeMode invokeMode = RpcUtils.getInvokeMode(invoker.getUrl(), invocation);
同步调用限流入口
if (InvokeMode.SYNC == invokeMode) {
return syncInvoke(invoker, invocation);
} else {
......异步调用限流入口
return asyncInvoke(invoker, invocation);
}
}
private Result syncInvoke(Invoker<?> invoker, Invocation invocation) {
Entry interfaceEntry = null;
Entry methodEntry = null;
String prefix = DubboAdapterGlobalConfig.getDubboConsumerResNamePrefixKey();
获取接口名
String interfaceResourceName = getInterfaceName(invoker, prefix);
获取方法名
String methodResourceName = getMethodName(invoker, invocation, prefix);
try {
相同的contextName interfaceEntry和methodEntry 意味着相同的entranceNode
注意资源名不同 则限流统计数据容器不同
interfaceEntry = SphU.entry(interfaceResourceName, ResourceTypeConstants.COMMON_RPC, EntryType.OUT);
methodEntry = SphU.entry(methodResourceName, ResourceTypeConstants.COMMON_RPC, EntryType.OUT,
invocation.getArguments());
rpc调用
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.getConsumerFallback().handle(invoker, invocation, e);
} catch (RpcException e) {
标记异常信息
Tracer.traceEntry(e, interfaceEntry);
Tracer.traceEntry(e, methodEntry);
throw e;
} finally {
注意exit 使用节点出栈桢
if (methodEntry != null) {
methodEntry.exit(1, invocation.getArguments());
}
if (interfaceEntry != null) {
interfaceEntry.exit();
}
}
}
}
2.2-限流逻辑SentinelDubboProviderFilter
EntryType为IN类型
- 外部构建Context,创建EntranceNode
- 限流逻辑处理
- 触发限流 处理响应
- 否则,rpc远程调用
- 处理rpc异常
- entry出栈
Activate(group = PROVIDER)
public class SentinelDubboProviderFilter extends BaseSentinelDubboFilter {
...... 删除其他代码
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
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).
翻译: 只需要在提供者端创建相关名称的context 对应创建相关entranceNode,因为context仅在调用链入口处生效(用于入站流量)。
理解:Provider采用外部创建context,context name为methodResourceName consumer采用内置创建context
相应的对象的EntranceNode不同,也就是说提供者独占EntranceNode,消费者因为contextName为默认名称,所以共享EntranceNode
ContextUtil.enter(methodResourceName, origin);
限流逻辑
interfaceEntry = SphU.entry(interfaceResourceName, ResourceTypeConstants.COMMON_RPC, EntryType.IN);
methodEntry = SphU.entry(methodResourceName, ResourceTypeConstants.COMMON_RPC, EntryType.IN,
invocation.getArguments());
rpc远程调用
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 {
entry出栈
if (methodEntry != null) {
methodEntry.exit(1, invocation.getArguments());
}
if (interfaceEntry != null) {
interfaceEntry.exit();
}
ContextUtil.exit();
}
}
}
总结
- Adapter负责桥接所有需要限流的通信框架
- dubbo适配器基于filter接入sentinel限流功能