天天看點

ZK叢集出現網絡波動導緻一部分provider無法重新注冊

問題産生場景:

    Ephermal節點未及時删除導緻provider不能恢複注冊,在應用日志中,應用重連Zookeeper成功後,provider立刻進行了重新注冊,之後便沒有列印任何日志。而在ZK日志中,注冊節點被删除後,并沒有重新建立注冊節點。

public void register(URL url) {
		super.register(url);
		failedRegistered.remove(url);
		failedUnregistered.remove(url);
		try{
			doRegister(url);
		} catch (Exception e) {
			Throwable t = e;
			boolean check = t instanceof SkipFailbackWrapperException;
			if(check || skipFailback) {
				if(skipFailback) {
					t = t.getCause();
				}
				throw new IllegalStateException("Failed to register " + url + "to registry " + getUrl().getAddress() + ", cause: " + t.getMessage(),t);
			} else {
				logger.error("Failed to register " + url + ", waiting for retry, cause: " + t.getMessage(), t);
			}
			failedRegistered.add(url);
		}
	}
           

Dubbo預設使用curator作為Zookeeper的用戶端,curator與ZK是通過session未知連結的。當curator重連ZK時,若session未過期,則繼續使用原session重新連結。而Ephemeral節點與session是綁定的關系,在session過期後,會删除此session下的Ephemeral節點。

    繼續對doRegister(url)的代碼進行進一步排查,發現在CuratorZookeeperClient.createEphemeral(path)方法中有這麼一段邏輯:在createEphemeral(path)捕獲到了NodeExistsException,建立Ephemeral節點時,若此節點已存在,則認為Ephemeral建立成功。

    但當ZK的session過期與删除Ephemeral節點不是原子性的操作情況下。在用戶端得到session過期的消息時,session對應的Ephemeral節點可能還未被ZK删除。此時Dubbo去建立Ephemeral節點,發現節點仍然存在,是以不再建立新節點。當Ephemeral節點被ZK删除後,便會出現Dubbo認為重新注冊成功但實際未成功的情況,會導緻一部分的provider無法重新注冊到ZK上。

public void createEphemeral(String path) {
		try {
			client.create().withMode(CreateMode.EPHEMERAL).forPath(path);
		} catch (NodeExistsException e) {
		} catch (Exception e) {
			throw new IllegalStateException(e.getMessage(), e);
		}
	}
           

解決方案:

    1.臨時方案:手動重新開機provider。

    2.更新Dubbo版本至2.7.3(需要檢測此版本是否與線上環境相容,目前此版本不與DubboX相容)

    3.修改源碼,當Ephemeral節點捕獲到NodeExistsException時,進行判斷,若Ephemeral節點的SessionId與目前用戶端的SessionId不同,則删除并重建Ephemeral節點。