一皇忿、問(wèn)題背景:
我們Zookeeper正常的情況下是部署在內(nèi)網(wǎng)進(jìn)行服務(wù)注冊(cè)和服務(wù)發(fā)現(xiàn)暖眼,難免有一些特許的情況需要部署到外網(wǎng)酣倾。而部署到外網(wǎng)的中我們需要對(duì)于服務(wù)注冊(cè)和服務(wù)發(fā)現(xiàn)進(jìn)行安全認(rèn)證苗桂。至于怎樣進(jìn)行安全配置就不詳細(xì)的描述矗烛,可以參考這篇文章(https://www.cnblogs.com/smallSevens/p/8064995.html)
在Dubbo registry上配置相應(yīng)的用戶玫镐、密碼倒戏,服務(wù)就注冊(cè)不到Zookeeper上了,會(huì)報(bào)KeeperErrorCode = NoAuth錯(cuò)誤恐似。
二杜跷、問(wèn)題分析
代碼分析
- Dubbo創(chuàng)建注冊(cè)zookeeper是通過(guò)工廠模式,而這個(gè)工廠類(lèi)就是RegistryFactory矫夷,在這個(gè)工廠類(lèi)中Dubbo提供了一個(gè)抽象實(shí)現(xiàn)類(lèi)
AbstractRegistryFactory葛闷,AbstractRegistryFactory實(shí)現(xiàn)了getRegistry方法,并將具體的創(chuàng)建ZookeeperRegistry細(xì)節(jié)由子類(lèi)工廠進(jìn)行實(shí)現(xiàn)双藕,在Dubbo使用ZookeeperRegistryFactory子類(lèi)進(jìn)行創(chuàng)建zookeeper連接和注冊(cè)淑趾。
AbstractRegistryFactory.java
public Registry getRegistry(URL url) {
url = url.setPath(RegistryService.class.getName())
.addParameter(Constants.INTERFACE_KEY, RegistryService.class.getName())
.removeParameters(Constants.EXPORT_KEY, Constants.REFER_KEY);
String key = url.toServiceString();
// 鎖定注冊(cè)中心獲取過(guò)程,保證注冊(cè)中心單一實(shí)例
LOCK.lock();
try {
Registry registry = REGISTRIES.get(key);
if (registry != null) {
return registry;
}
registry = createRegistry(url);
if (registry == null) {
throw new IllegalStateException("Can not create registry " + url);
}
REGISTRIES.put(key, registry);
return registry;
} finally {
// 釋放鎖
LOCK.unlock();
}
}
ZookeeperRegistryFactory.java
public class ZookeeperRegistryFactory extends AbstractRegistryFactory {
private ZookeeperTransporter zookeeperTransporter;
public void setZookeeperTransporter(ZookeeperTransporter zookeeperTransporter) {
this.zookeeperTransporter = zookeeperTransporter;
}
public Registry createRegistry(URL url) {
return new ZookeeperRegistry(url, zookeeperTransporter);
}
}
- 我們通過(guò)ZookeeperRegistry發(fā)現(xiàn)zookeeper的連接是通過(guò)zookeeperTransporter進(jìn)行創(chuàng)建忧陪。
public ZookeeperRegistry(URL url, ZookeeperTransporter zookeeperTransporter) {
super(url);
if (url.isAnyHost()) {
throw new IllegalStateException("registry address == null");
}
String group = url.getParameter(Constants.GROUP_KEY, DEFAULT_ROOT);
if (! group.startsWith(Constants.PATH_SEPARATOR)) {
group = Constants.PATH_SEPARATOR + group;
}
this.root = group;
zkClient = zookeeperTransporter.connect(url);
zkClient.addStateListener(new StateListener() {
public void stateChanged(int state) {
if (state == RECONNECTED) {
try {
recover();
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
}
});
}
- zookeeperTransporter是一個(gè)接口扣泊,而該接口提供了兩種實(shí)現(xiàn):CuratorZookeeperTransporter和ZkclientZookeeperTransporter近范,而這兩個(gè)實(shí)現(xiàn)類(lèi)分別創(chuàng)建CuratorZookeeperClient和ZkclientZookeeperClient。
public class CuratorZookeeperTransporter implements ZookeeperTransporter {
public ZookeeperClient connect(URL url) {
return new CuratorZookeeperClient(url);
}
}
public class ZkclientZookeeperTransporter implements ZookeeperTransporter {
public ZookeeperClient connect(URL url) {
return new ZkclientZookeeperClient(url);
}
}
- 通過(guò)對(duì)CuratorZookeeperClient和ZkclientZookeeperClient的構(gòu)造方法發(fā)現(xiàn)延蟹,ZkclientZookeeperClient是沒(méi)有進(jìn)行設(shè)置zookeeper的auth的賬號(hào)和密碼评矩。
public ZkclientZookeeperClient(URL url) {
super(url);
client = new ZkClient(
url.getBackupAddress(),
url.getParameter(Constants.SESSION_TIMEOUT_KEY, Constants.DEFAULT_SESSION_TIMEOUT),
url.getParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_REGISTRY_CONNECT_TIMEOUT));
client.subscribeStateChanges(new IZkStateListener() {
public void handleStateChanged(KeeperState state) throws Exception {
ZkclientZookeeperClient.this.state = state;
if (state == KeeperState.Disconnected) {
stateChanged(StateListener.DISCONNECTED);
} else if (state == KeeperState.SyncConnected) {
stateChanged(StateListener.CONNECTED);
}
}
public void handleNewSession() throws Exception {
stateChanged(StateListener.RECONNECTED);
}
});
}
public CuratorZookeeperClient(URL url) {
super(url);
Builder builder = CuratorFrameworkFactory.builder()
.connectString(url.getBackupAddress())
.retryPolicy(new RetryNTimes(Integer.MAX_VALUE, 1000))
.connectionTimeoutMs(url.getParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_REGISTRY_CONNECT_TIMEOUT))
.sessionTimeoutMs(url.getParameter(Constants.SESSION_TIMEOUT_KEY, Constants.DEFAULT_SESSION_TIMEOUT));
//設(shè)置zookeeper的auth賬號(hào)和密碼。
String authority = url.getAuthority();
if (authority != null && authority.length() > 0) {
builder = builder.authorization("digest", authority.getBytes());
}
client = builder.build();
client.getConnectionStateListenable().addListener(
new ConnectionStateListener() {
public void stateChanged(CuratorFramework client,
ConnectionState state) {
if (state == ConnectionState.LOST) {
CuratorZookeeperClient.this
.stateChanged(StateListener.DISCONNECTED);
} else if (state == ConnectionState.CONNECTED) {
CuratorZookeeperClient.this
.stateChanged(StateListener.CONNECTED);
} else if (state == ConnectionState.RECONNECTED) {
CuratorZookeeperClient.this
.stateChanged(StateListener.RECONNECTED);
}
}
});
c
總結(jié):通過(guò)源碼的分析發(fā)現(xiàn)使用ZkclientZookeeperClient進(jìn)行連接zookeeper和注冊(cè)服務(wù)是不會(huì)設(shè)置安全配置阱飘。
三稚照、解決方案
將dubbo.registry.client由zkclient改為curator即可。dubbo.registry.client = curator