dubbo開啓token服務後,使用集羣容錯策略爲FailoverClusterInvoker,當出現服務調用失敗進行轉移,重試其它服務器時,會出現token invalid錯誤,provider會拒絕服務調用。java
緣由:spring
消費端:
服務器
一、com.alibaba.dubbo.rpc.cluster.support.FailoverClusterInvoker#doInvoke:ide
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 { Result result = invoker.invoke(invocation);//失敗重連 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()); } 二、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); } 三、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); } }
服務提供方:this
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錯誤。至關於失敗重連無效.url
錯誤現象:code
一、請求超時,此時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,
。。。。。。。
二、失敗重連,此時附件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,
。。。。。。。
三、出現token invalid
com.alibaba.dubbo.rpc.RpcException: Invalid token! Forbid invoke remote service interface
server