dubbo开启token服务后,使用集群容错策略为FailoverClusterInvoker,当出现服务调用失败进行转移,重试其它服务器时,会出现token invalid错误,provider会拒绝服务调用。
原因:
消费端:
1、com.alibaba.dubbo.rpc.cluster.support.FailoverClusterInvoker#doInvoke:
for (int i = 0; i < len; i++) {
//重试时,进行重新选择,避免重试时invoker列表已发生变化.
//注意:如果列表发生了变化,那么invoked判断会失效,因为invoker示例已经改变
if (i > 0) {
checkWheatherDestoried();
copyinvokers = list(invocation);
//重新检查一下
checkInvokers(copyinvokers, invocation);
}
Invoker<T> invoker = select(loadbalance, invocation, copyinvokers, invoked);
invoked.add(invoker);
RpcContext.getContext().setInvokers((List)invoked);
try {
if (le != null && logger.isWarnEnabled()) {
logger.warn("Although retry the method " + invocation.getMethodName()
+ " in the service " + getInterface().getName()
+ " was successful by the provider " + invoker.getUrl().getAddress()
+ ", but there have been failed providers " + providers
+ " (" + providers.size() + "/" + copyinvokers.size()
+ ") from the registry " + directory.getUrl().getAddress()
+ " on the consumer " + NetUtils.getLocalHost()
+ " using the dubbo version " + Version.getVersion() + ". Last error is: "
+ le.getMessage(), le);
}
return result;
} catch (RpcException e) {
if (e.isBiz()) { // biz exception.
throw e;
}
le = e;
} catch (Throwable e) {
le = new RpcException(e.getMessage(), e);
} finally {
providers.add(invoker.getUrl().getAddress());
}
2、com.alibaba.dubbo.rpc.protocol.AbstractInvoker#invoke
RpcInvocation invocation = (RpcInvocation) inv;
invocation.setInvoker(this);
if (attachment != null && attachment.size() > 0) {
invocation.addAttachmentsIfAbsent(attachment); //添加相关参数到附件attachments,如token
}
Map<String, String> context = RpcContext.getContext().getAttachments();
if (context != null) {
invocation.addAttachmentsIfAbsent(context);
}
3、addAttachmentsIfAbsent实现:
public void setAttachmentIfAbsent(String key, String value) {
if (attachments == null) {
attachments = new HashMap<String, String>();
}
if (! attachments.containsKey(key)) { //key不存在时才进行赋值,因此不能进行覆盖操作
attachments.put(key, value);
}
}
服务提供方:
public class TokenFilter implements Filter {
public Result invoke(Invoker<?> invoker, Invocation inv)
throws RpcException {
String token = invoker.getUrl().getParameter(Constants.TOKEN_KEY);
if (ConfigUtils.isNotEmpty(token)) {
Class<?> serviceType = invoker.getInterface();
Map<String, String> attachments = inv.getAttachments();//解析attachments
String remoteToken = attachments == null ? null : attachments.get(Constants.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);
}
}
通过上面的代码分析可知,当消费者调用服务方服务出现超时进行失败重连时,要重连的Invoker的token没有覆盖上一次的invoker的token,而服务端比较token时比较的是attachment, 进而出现token invalid错误。相当于失败重连无效.
错误现象:
1、请求超时,此时token是一致的
telnet=invoke,status,replacetoken&timeout=1000×tamp=1418813410249&token=020f16e2-e060-4c85-a31a-017aa0ee268c&version=1.0, cause: Waiting server-side response timeout. start time: 2014-12-17 21:42:26.735, end time: 2014-12-17 21:42:27.736, client elapsed: 0 ms, server elapsed: 1001 ms, timeout: 1000 ms, request: Request [id=27484, version=2.0.0, twoway=true, event=false, broken=false, data=RpcInvocation [.........................], attachments={token=020f16e2-e060-4c85-a31a-017aa0ee268c,
。。。。。。。
2、失败重连,此时附件token与url的不一致
....................&pid=29465&revision=1.0.1&side=consumer&status=spring,load&telnet=invoke,status,replacetoken&timeout=1000×tamp=1418813410249
&token=0f3103ad-ac7b-4906-897c-b51e69ab3b96&version=1.0 -> RpcInvocation [..................................],
attachments={token=020f16e2-e060-4c85-a31a-017aa0ee268c,
。。。。。。。
3、出现token invalid
com.alibaba.dubbo.rpc.RpcException: Invalid token! Forbid invoke remote service interface
原文:http://my.oschina.net/mingyuanwang/blog/490979