1. 程式人生 > >解決dubbo問題:forbid consumer

解決dubbo問題:forbid consumer

線下環境經常出現類似這種異常:

com.alibaba.dubbo.rpc.RpcException: Forbid consumer   access service   from registry   use dubbo version 2.5.3, Please check registry access list (whitelist/blacklist).
大致意思是當前呼叫者被禁止訪問某個服務,請檢查下注冊中心訪問列表,還有黑名單和白名單。 其實線下環境根本沒有對服務做白名單和黑名單機制,因為線下環境給開發人員的賬號是guest,沒有許可權做黑白名單。今天有好幾個人問我這個問題,我仔細看了原始碼,找出了根源所在。 根據異常棧,丟擲這個異常的程式碼在RegistryDirectory的第579行,如下:
public
 List<Invoker<T>> doList(Invocation invocation) { if (forbidden ) { throw new RpcException(RpcException.FORBIDDEN_EXCEPTION , ” Forbid consumer “ +  NetUtils. getLocalHost() + ” access service “ +        getInterface().getName() + ” from registry “ + getUrl().getAddress() + ” use dubbo version “
 + Version.getVersion() + “, Please check registry access list (whitelist/blacklist).”); }
如果forbidden變數為true,則丟擲該異常。forbidden變數預設為false,那麼什麼時候變成true了呢?看RegistryDirectory的這段程式碼:
private void refreshInvoker(List<URL> invokerUrls){ if (invokerUrls != null && invokerUrls.size() == 1 && invokerUrls.get(0) !=
 null && Constants.EMPTY_PROTOCOL .equals(invokerUrls.get(0).getProtocol())) {  this.forbidden = true; // 禁止訪問  this.methodInvokerMap = null; // 置空列表             destroyAllInvokers(); // 關閉所有Invoker    }
意思是如果invokerUrls的size為1,並且url的協議頭是Constants.EMPTY_PROTOCOL時,則設定forbidden為false,Constants.EMPTY_PROTOCOL的值是empty。 refreshInvoker方法什麼時候被呼叫呢?當某個服務的provider有變化時就會被呼叫,例如zookeeper上某個服務的provider目錄裡的內容發生變化,則zk監聽器會被觸發,由於provider的數量會發生變化,例如有一個新的provider啟動了,有一個provider下線了,所以必須重新整理本地的對provider的連線,具體邏輯就在refreshInvoker方法裡,這個方法的呼叫棧如下: image_1 可以確定的是,zookeeper推送的URL的protocol部分不可能無緣無故變成了empty,肯定是由某個地方更改了,於是看一下Constants.EMPTY_PROTOCOL到底有哪些地方呼叫了,如下: image_2 見圖中紅色圈圈部分,當zookeeper初次訂閱或者訂閱的資訊有變更時,都會觸發toUrlsChanged方法,看看這個方法內部都做了什麼,完整程式碼如下:
 private List<URL> toUrlsWithEmpty(URL consumer, String path, List<String> providers) {         List<URL> urls = toUrlsWithoutEmpty(consumer, providers); if (urls == null || urls.isEmpty()) {  int i = path.lastIndexOf(‘/’ );           String category = i < 0 ? path : path.substring(i + 1);           URL empty = consumer.setProtocol(Constants.EMPTY_PROTOCOL ).addParameter(Constants. CATEGORY_KEY, category);             urls.add(empty);         } return urls;     }
可見如果toUrlsWithoutEmpty的結果是空或者size為0,則強制返回一個protocol為empty的url,看來源頭就在這裡了。傳入的List<String> providers實際上就是最新的服務提供者資訊,當某個服務沒有任何provider時,providers就變為一個size為o的List了,導致返回一個協議頭為empty的url,進而導致forbidden為true,遮蔽了consumer呼叫。