Dubbo的架构如下图所示:图片来自http://dubbo.apache.org/zh-cn/docs/user/preface/architecture.html

节点角色说明
Provider 暴露服务的服务提供方
Consumer 调用远程服务的服务消费方
Registry 服务注册与发现的注册中心
Monitor 统计服务的调用次数和调用时间的监控中心
Container 服务运行容器
调用关系说明
0.服务容器负责启动,加载,运行服务提供者。
1.服务提供者在启动时,向注册中心注册自己提供的服务。
2.服务消费者在启动时,向注册中心订阅自己所需的服务。
3.注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
4.服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
5.服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。
简单以代码为例描述一次调用的过程,首先当应用需要调用服务时,会通过invoke方法发起调用AbstractClusterInvoker:
此时会将RpcContext.getContext().getAttachments()的值set到invocation上,然后将invocation作为参数获取可以匹配到的Provider端提供的方法
public Result invoke(final Invocation invocation) throws RpcException {
checkWhetherDestroyed();
// binding attachments into invocation.
Map<String, String> contextAttachments = RpcContext.getContext().getAttachments();
if (contextAttachments != null && contextAttachments.size() != 0) {
((RpcInvocation) invocation).addAttachments(contextAttachments);
}
List<Invoker<T>> invokers = list(invocation);
LoadBalance loadbalance = initLoadBalance(invokers, invocation);
RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);
return doInvoke(invocation, invokers, loadbalance);
}
这里先跳过mock的部分,主要说明静态标签路由的部分,在获取服务方提供的List<Invoker>的方法中,Dubbo中的Router 负责从多个 Invoker 中按路由规则选出子集,也就是说通过 Directory 选出当前可用的服务提供者,然后再通过 Router 按规则过滤出服务提供者的子集。
针对TagRouter,在这次服务消费的请求中,invocation包含了tag的信息,如果提供方没有完全匹配的服务则进行服务降级,在提供方中查找没有tag的服务。
通过Router后才会进行负载均衡,以及Filter的拦截。
public List<Invoker<T>> route(URL url, Invocation invocation) {
List<Invoker<T>> finalInvokers = invokers;
for (Router router : routers) {
finalInvokers = router.route(finalInvokers, url, invocation);
}
return finalInvokers;
}
所以采用基于Filter向RpcContext写入tag的方式是没有用的。
@Activate(group = Constants.CONSUMER)
public class FilterSpi implements Filter {
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
RpcContext.getContext().setAttachment(Constants.TAG_KEY,"tag1");
Result result = invoker.invoke(invocation);
return result;
}
}
这里可以采用Spring的AOP的方式进行TAG_KEY的写入。或者不使用Duboo原生的TagRouter,自己定制一个新的TagRouter。
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
Map<String, String> attachments = invocation.getAttachments();
if (attachments != null) {
attachments = new HashMap<>(attachments);
attachments.remove(PATH_KEY);
attachments.remove(INTERFACE_KEY);
attachments.remove(GROUP_KEY);
attachments.remove(VERSION_KEY);
attachments.remove(DUBBO_VERSION_KEY);
attachments.remove(TOKEN_KEY);
attachments.remove(TIMEOUT_KEY);
// Remove async property to avoid being passed to the following invoke chain.
attachments.remove(ASYNC_KEY);
attachments.remove(TAG_KEY);
attachments.remove(FORCE_USE_TAG);
}
PROVIDER在ContextFilter会执行attachments.remove(TAG_KEY);从而无法完成链路的调用,而我想将该tag值进行保留,所以不使用ContextFilter,并使用定制的Filter进行替换。
将定制的Filter用以下的方式进行配置

contextTag=xxx.xxx.xxx.TagContextFilter
并不再执行原生的ContextFilter
dubbo.provider.filter=-context