https://juejin.cn/post/6844903822771355655
https://blog.csdn.net/qq_24434251/article/details/119055534
https://blog.csdn.net/qq_32252917/article/details/89168096
首先描述一下問題狀況:項目后期規划使用zookeeper做分布式服務配置中心,使用Curator作為客戶端中間件操作訪問zookeeper. 在測試驗證階段,無限“Will not attempt to authenticate using SASL (unknown error)”流讓本人崩潰到淚流滿面。“SASL”什么鬼?!本人沒有設置過“SASL”的說!!!
環境說明:
zookeeper: 3.5.5
curator: 4.2.0
原因猜測:
查看日志,知道“Will not attempt to authenticate using SASL (unknown error)”的鍋。然后各種google,度娘,有各式原因都有,例如因為本地IP映射問題,需要該本地host文件等。本人未采用SASL認證模式,而是使用了簡單的digest認證模式。所以排除本地IP映射問題。此外個人覺得修改本地host文件本身不是一個合理的舉措。最后只能追本溯源,得嘞,還是乖乖看一下源碼吧。
org.apache.zookeeper.ClientCnxn 類中有一段代碼如下:
private void startConnect(InetSocketAddress addr) throws IOException {
// initializing it for new connection
saslLoginFailed = false;
if(!isFirstConnect){
try {
Thread.sleep(r.nextInt(1000));
} catch (InterruptedException e) {
LOG.warn("Unexpected exception", e);
}
}
state = States.CONNECTING;
String hostPort = addr.getHostString() + ":" + addr.getPort();
MDC.put("myid", hostPort);
setName(getName().replaceAll("\\(.*\\)", "(" + hostPort + ")"));
if (clientConfig.isSaslClientEnabled()) {
try {
if (zooKeeperSaslClient != null) {
zooKeeperSaslClient.shutdown();
}
zooKeeperSaslClient = new ZooKeeperSaslClient(SaslServerPrincipal.getServerPrincipal(addr, clientConfig),
clientConfig);
} catch (LoginException e) {
// An authentication error occurred when the SASL client tried to initialize:
// for Kerberos this means that the client failed to authenticate with the KDC.
// This is different from an authentication error that occurs during communication
// with the Zookeeper server, which is handled below.
LOG.warn("SASL configuration failed: " + e + " Will continue connection to Zookeeper server without "
+ "SASL authentication, if Zookeeper server allows it.");
eventThread.queueEvent(new WatchedEvent(
Watcher.Event.EventType.None,
Watcher.Event.KeeperState.AuthFailed, null));
saslLoginFailed = true;
}
}
logStartConnect(addr);
clientCnxnSocket.connect(addr);
}復制代碼
其中clientConfig.isSaslClientEnabled() 引起我的注意,在查看一下它的實現:
public boolean isSaslClientEnabled() {
return Boolean.valueOf(getProperty(ENABLE_CLIENT_SASL_KEY, ENABLE_CLIENT_SASL_DEFAULT));
}復制代碼
發現它默認是true.
最后找到Curator配置生成zookeeper客戶端實例的代碼 (Curator通過ZookeeperFactory來創建zookeeper客戶端實例的)
package org.apache.curator.utils;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
public class DefaultZookeeperFactory implements ZookeeperFactory
{
@Override
public ZooKeeper newZooKeeper(String connectString, int sessionTimeout, Watcher watcher, boolean canBeReadOnly) throws Exception
{
return new ZooKeeper(connectString, sessionTimeout, watcher, canBeReadOnly);
}
} 復制代碼
好吧,看來Curator默認啥都沒配置,那么它就直奔默認值true去了。
解決方案:
解決方案其實就一句話,將ENABLE_CLIENT_SASL_KEY值變成false. 問題是怎么才能在Curator中將其設置為false. 答案還在ZookeeperFactory. 可以在創建CuratorFramework實例時,將CuratorFrameworkFactory.builder的ZookeeperFactory替換成新的ZookeeperFactory實現。
public ZooKeeper newZooKeeper(String connectString, int sessionTimeout, Watcher watcher, boolean canBeReadOnly)
throws Exception {
ZKClientConfig config = new ZKClientConfig();
config.setProperty(ZKClientConfig.ENABLE_CLIENT_SASL_KEY, "false");
return new ZooKeeper(connectString, sessionTimeout, watcher, canBeReadOnly, config);
}復制代碼
最后多說幾句。以上方案適合不用SASL認證模式的zookeeper應用。如果你使用SASL請不要參考以上方案。另外也許還有在服務端disable SASL的解決方案可以考慮,大家也不必一條路走死,興許這種方案才是最佳的。