Dubbo配置参数详解-token
Dubbo是一个由阿里开源的服务治理框架,笔者的公司重度使用Dubbo。Dubbo开源这么多年,配置项已经非常丰富,了解各个配置项的作用也变得非常重要,本系列将从源代码的角度分析Dubbo目前的最新版本(2.7.4)各个常用的配置项的具体含义以及是怎么起作用的。
画外音:目前Dubbo在开源中国举办的2019年度最受欢迎中国开源软件中排名第3名,支持Dubbo的朋友可以去投票哇。2019年度最受欢迎中国开源软件
token有啥用?
token:默认不开启,如果开启,则每次调用provider都会判断consumer端传过来的token是否跟provider的相同,只有相同才能调用。
token怎么使用?
开启token,有两种方式
@Service(token = "true"),这种方式provider会生成一个随机的token
@Service(token = "abcde12345"),这种方式会直接使用abcde12345作为token
token源码分析?
服务端如何生成token?
在provider启动的时候,ServiceConfig会判断provider是否设置了该参数,如果设置,则生成一个token并加入该provider的URL
//此段代码位于ServiceConfig的doExportUrlsFor1Protocol
if (!ConfigUtils.isEmpty(token)) {
if (ConfigUtils.isDefault(token)) {
//如果token="true"或token="default"则生成随机的uuid作为token
map.put(TOKEN_KEY, UUID.randomUUID().toString());
} else {
//否则使用用户填写的token
map.put(TOKEN_KEY, token);
}
}
客户端如何获取token?
在consumer端生成调用链,初始化DubboInvoker时,会获取provider的URL,并将token设置到attachment。
//此段代码位于DubboInvoker的构造方法
public DubboInvoker(Class<T> serviceType, URL url, ExchangeClient[] clients, Set<Invoker<?>> invokers) {
//将interface,token,timeout设置到attachment
super(serviceType, url, new String[]{INTERFACE_KEY, GROUP_KEY, TOKEN_KEY, TIMEOUT_KEY});
this.clients = clients;
// get version.
this.version = url.getParameter(VERSION_KEY, "0.0.0");
this.invokers = invokers;
}
//此段代码位于AbstractInvoker
public AbstractInvoker(Class<T> type, URL url, Map<String, String> attachment) {
if (type == null) {
throw new IllegalArgumentException("service type == null");
}
if (url == null) {
throw new IllegalArgumentException("service url == null");
}
this.type = type;
this.url = url;
this.attachment = attachment == null ? null : Collections.unmodifiableMap(attachment);
}
provider如何判断token的有效性?
consumer发起调用时,会将attachment一起发送到provider端,provider会在TokenFilter判断该token是否合法
@Activate(group = CommonConstants.PROVIDER, value = TOKEN_KEY)
public class TokenFilter implements Filter {
@Override
public Result invoke(Invoker<?> invoker, Invocation inv)
throws RpcException {
String token = invoker.getUrl().getParameter(TOKEN_KEY);
if (ConfigUtils.isNotEmpty(token)) {
Class<?> serviceType = invoker.getInterface();
Map<String, String> attachments = inv.getAttachments();
String remoteToken = attachments == null ? null : attachments.get(TOKEN_KEY);
if (!token.equals(remoteToken)) {
throw new RpcException("Invalid token! Forbid invoke remote service " + serviceType + " method " + inv.getMethodName() + "() from consumer " + RpcContext.getContext().getRemoteHost() + " to provider " + RpcContext.getContext().getLocalHost());
}
}
return invoker.invoke(inv);
}
}
总结
- token的粒度是方法级别,可以针对每个方法设置不同的token;
- token的作用主要是防止consumer端绕过注册中心直接调用provider;