消除 Curator 连接 ZooKeeper 时的 SASL 认证告警
在使用 Curator 作为 ZooKeeper 客户端进行分布式配置管理时,控制台会持续输出 "Will not attempt to authenticate using SASL (unknown error)" 的警告信息。该问题在 ZooKeeper 3.5.5 与 Curator 4.2.0 的组合环境下尤为常见,严重影响日志可读性。
问题根因
ZooKeeper 客户端在建立连接时会检查 SASL 认证开关。相关逻辑位于 org.apache.zookeeper.ClientCnxn 的 startConnect 方法中:
private void startConnect(InetSocketAddress addr) throws IOException {
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) {
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() 的默认返回值为 true,由以下配置决定:
public boolean isSaslClientEnabled() {
return Boolean.valueOf(getProperty(ENABLE_CLIENT_SASL_KEY, ENABLE_CLIENT_SASL_DEFAULT));
}
而 Curator 的默认工厂类 DefaultZookeeperFactory 在构建 ZooKeeper 实例时,并未覆盖此配置:
package org.apache.curator.utils;
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);
}
}
解决思路
核心在于将 ENABLE_CLIENT_SASL_KEY 设为 false。可通过自定义 ZookeeperFactory 实现,在创建客户端实例时注入关闭 SASL 的配置。
具体实现
自定义工厂类,显式构造带配置的 ZooKeeper 实例:
import org.apache.curator.utils.ZookeeperFactory;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.client.ZKClientConfig;
public class NoSaslZookeeperFactory implements ZookeeperFactory {
@Override
public ZooKeeper newZooKeeper(String connectString, int sessionTimeout,
Watcher watcher, boolean canBeReadOnly) throws Exception {
ZKClientConfig cfg = new ZKClientConfig();
cfg.setProperty(ZKClientConfig.ENABLE_CLIENT_SASL_KEY, "false");
return new ZooKeeper(connectString, sessionTimeout, watcher, canBeReadOnly, cfg);
}
}
构建 CuratorFramework 时应用该工厂:
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
public class ZkClientBuilder {
public CuratorFramework build(String connectionString) {
return CuratorFrameworkFactory.builder()
.connectString(connectionString)
.zookeeperFactory(new NoSaslZookeeperFactory())
.retryPolicy(new ExponentialBackoffRetry(1000, 3))
.build();
}
}
注意事项
- 此方案仅适用于未启用 SASL 认证的场景。若生产环境依赖 Kerberos 等 SASL 机制,需排查认证配置而非简单关闭。
- 也可考虑在 ZooKeeper 服务端调整配置,但涉及集群变更,需评估影响范围。