8-配置信息是如何加载到内存空间的

8.1 配置信息的加载过程

在执行zkServer命令启动Zookeeper的时候,Zookeeper启动脚本在启动java进程时会主动为Java进程加上java参数conf文件位置,默认为Zookeeper文件夹的conf目录下的zoo.cfg文件,在Zookeeper入口函数QuorumPeerMain类中的main方法执行时可以通过args[0]获取conf文件路径在初始化的时候执行如下代码进行解析配置:

QuorumPeerConfig config = new QuorumPeerConfig();

//指定了conf配置文件则开始解析配置文件

if (args.length == 1) {
     config.parse(args[0]);
 }

如果存在Zookeeper配置文件参数则先调用QuorumPeerConfig的parse方法来解析当前配置类中存在的配置属性解析过程如下:

/**
 * Parse a ZooKeeper configuration file
 * @param path the patch of the configuration file
 * @throws ConfigException error processing configuration
 */
public void parse(String path) throws ConfigException {
    File configFile = new File(path);

    LOG.info("Reading configuration from: " + configFile);

    try {
        if (!configFile.exists()) {
            throw new IllegalArgumentException(configFile.toString()
                    + " file is missing");
        }

        Properties cfg = new Properties();
        FileInputStream in = new FileInputStream(configFile);
        try {
            cfg.load(in);
        } finally {
            in.close();
        }

        parseProperties(cfg);
    } catch (IOException e) {
        throw new ConfigException("Error processing " + path, e);
    } catch (IllegalArgumentException e) {
        throw new ConfigException("Error processing " + path, e);
    }
}
解析过程就是使用File读取配置文件然后赋值给当前对象的成员变量。具体代码如下:
/**
 * Parse config from a Properties.
 * @param zkProp Properties to parse from.
 * @throws IOException
 * @throws ConfigException
 */
public void parseProperties(Properties zkProp) throws IOException, ConfigException {
    int clientPort = 0;
    int secureClientPort = 0;
    int observerMasterPort = 0;
    String clientPortAddress = null;
    String secureClientPortAddress = null;
    VerifyingFileFactory vff = new VerifyingFileFactory.Builder(LOG).warnForRelativePath().build();
    for (Entry<Object, Object> entry : zkProp.entrySet()) {
        String key = entry.getKey().toString().trim();
        String value = entry.getValue().toString().trim();
        if (key.equals("dataDir")) {
            dataDir = vff.create(value);
        } else if (key.equals("dataLogDir")) {
            dataLogDir = vff.create(value);
        } else if (key.equals("clientPort")) {
            clientPort = Integer.parseInt(value);
        } else if (key.equals("localSessionsEnabled")) {
            localSessionsEnabled = Boolean.parseBoolean(value);
        } else if (key.equals("localSessionsUpgradingEnabled")) {
            localSessionsUpgradingEnabled = Boolean.parseBoolean(value);
        } else if (key.equals("clientPortAddress")) {
            clientPortAddress = value.trim();
        } else if (key.equals("secureClientPort")) {
            secureClientPort = Integer.parseInt(value);
        } else if (key.equals("secureClientPortAddress")) {
            secureClientPortAddress = value.trim();
        } else if (key.equals("observerMasterPort")) {
            observerMasterPort = Integer.parseInt(value);
        } else if (key.equals("clientPortListenBacklog")) {
            clientPortListenBacklog = Integer.parseInt(value);
        } else if (key.equals("tickTime")) {
            tickTime = Integer.parseInt(value);
        } else if (key.equals("maxClientCnxns")) {
            maxClientCnxns = Integer.parseInt(value);
        } else if (key.equals("minSessionTimeout")) {
            minSessionTimeout = Integer.parseInt(value);
        } else if (key.equals("maxSessionTimeout")) {
            maxSessionTimeout = Integer.parseInt(value);
        } else if (key.equals("initLimit")) {
            initLimit = Integer.parseInt(value);
        } else if (key.equals("syncLimit")) {
            syncLimit = Integer.parseInt(value);
        } else if (key.equals("connectToLearnerMasterLimit")) {
            connectToLearnerMasterLimit = Integer.parseInt(value);
        } else if (key.equals("electionAlg")) {
            electionAlg = Integer.parseInt(value);
            if (electionAlg != 3) {
                throw new ConfigException("Invalid electionAlg value. Only 3 is supported.");
            }
        } else if (key.equals("quorumListenOnAllIPs")) {
            quorumListenOnAllIPs = Boolean.parseBoolean(value);
        } else if (key.equals("peerType")) {
            if (value.toLowerCase().equals("observer")) {
                peerType = LearnerType.OBSERVER;
            } else if (value.toLowerCase().equals("participant")) {
                peerType = LearnerType.PARTICIPANT;
            } else {
                throw new ConfigException("Unrecognised peertype: " + value);
            }
        } else if (key.equals("syncEnabled")) {
            syncEnabled = Boolean.parseBoolean(value);
        } else if (key.equals("dynamicConfigFile")) {
            dynamicConfigFileStr = value;
        } else if (key.equals("autopurge.snapRetainCount")) {
            snapRetainCount = Integer.parseInt(value);
        } else if (key.equals("autopurge.purgeInterval")) {
            purgeInterval = Integer.parseInt(value);
        } else if (key.equals("standaloneEnabled")) {
            if (value.toLowerCase().equals("true")) {
                setStandaloneEnabled(true);
            } else if (value.toLowerCase().equals("false")) {
                setStandaloneEnabled(false);
            } else {
                throw new ConfigException("Invalid option "
                                          + value
                                          + " for standalone mode. Choose 'true' or 'false.'");
            }
        } else if (key.equals("reconfigEnabled")) {
            if (value.toLowerCase().equals("true")) {
                setReconfigEnabled(true);
            } else if (value.toLowerCase().equals("false")) {
                setReconfigEnabled(false);
            } else {
                throw new ConfigException("Invalid option "
                                          + value
                                          + " for reconfigEnabled flag. Choose 'true' or 'false.'");
            }
        } else if (key.equals("sslQuorum")) {
            sslQuorum = Boolean.parseBoolean(value);
        } else if (key.equals("portUnification")) {
            shouldUsePortUnification = Boolean.parseBoolean(value);
        } else if (key.equals("sslQuorumReloadCertFiles")) {
            sslQuorumReloadCertFiles = Boolean.parseBoolean(value);
        } else if ((key.startsWith("server.") || key.startsWith("group") || key.startsWith("weight"))
                   && zkProp.containsKey("dynamicConfigFile")) {
            throw new ConfigException("parameter: " + key + " must be in a separate dynamic config file");
        } else if (key.equals(QuorumAuth.QUORUM_SASL_AUTH_ENABLED)) {
            quorumEnableSasl = Boolean.parseBoolean(value);
        } else if (key.equals(QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED)) {
            quorumServerRequireSasl = Boolean.parseBoolean(value);
        } else if (key.equals(QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED)) {
            quorumLearnerRequireSasl = Boolean.parseBoolean(value);
        } else if (key.equals(QuorumAuth.QUORUM_LEARNER_SASL_LOGIN_CONTEXT)) {
            quorumLearnerLoginContext = value;
        } else if (key.equals(QuorumAuth.QUORUM_SERVER_SASL_LOGIN_CONTEXT)) {
            quorumServerLoginContext = value;
        } else if (key.equals(QuorumAuth.QUORUM_KERBEROS_SERVICE_PRINCIPAL)) {
            quorumServicePrincipal = value;
        } else if (key.equals("quorum.cnxn.threads.size")) {
            quorumCnxnThreadsSize = Integer.parseInt(value);
        } else if (key.equals(JvmPauseMonitor.INFO_THRESHOLD_KEY)) {
            jvmPauseInfoThresholdMs = Long.parseLong(value);
        } else if (key.equals(JvmPauseMonitor.WARN_THRESHOLD_KEY)) {
            jvmPauseWarnThresholdMs = Long.parseLong(value);
        } else if (key.equals(JvmPauseMonitor.SLEEP_TIME_MS_KEY)) {
            jvmPauseSleepTimeMs = Long.parseLong(value);
        } else if (key.equals(JvmPauseMonitor.JVM_PAUSE_MONITOR_FEATURE_SWITCH_KEY)) {
            jvmPauseMonitorToRun = Boolean.parseBoolean(value);
        } else if (key.equals("metricsProvider.className")) {
            metricsProviderClassName = value;
        } else if (key.startsWith("metricsProvider.")) {
            String keyForMetricsProvider = key.substring(16);
            metricsProviderConfiguration.put(keyForMetricsProvider, value);
        } else if (key.equals("multiAddress.enabled")) {
            multiAddressEnabled = Boolean.parseBoolean(value);
        } else if (key.equals("multiAddress.reachabilityCheckTimeoutMs")) {
            multiAddressReachabilityCheckTimeoutMs = Integer.parseInt(value);
        } else if (key.equals("multiAddress.reachabilityCheckEnabled")) {
            multiAddressReachabilityCheckEnabled = Boolean.parseBoolean(value);
        } else {
            System.setProperty("zookeeper." + key, value);
        }
    }

    if (!quorumEnableSasl && quorumServerRequireSasl) {
        throw new IllegalArgumentException(QuorumAuth.QUORUM_SASL_AUTH_ENABLED
                                           + " is disabled, so cannot enable "
                                           + QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED);
    }
    if (!quorumEnableSasl && quorumLearnerRequireSasl) {
        throw new IllegalArgumentException(QuorumAuth.QUORUM_SASL_AUTH_ENABLED
                                           + " is disabled, so cannot enable "
                                           + QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED);
    }
    // If quorumpeer learner is not auth enabled then self won't be able to
    // join quorum. So this condition is ensuring that the quorumpeer learner
    // is also auth enabled while enabling quorum server require sasl.
    if (!quorumLearnerRequireSasl && quorumServerRequireSasl) {
        throw new IllegalArgumentException(QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED
                                           + " is disabled, so cannot enable "
                                           + QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED);
    }

    // Reset to MIN_SNAP_RETAIN_COUNT if invalid (less than 3)
    // PurgeTxnLog.purge(File, File, int) will not allow to purge less
    // than 3.
    if (snapRetainCount < MIN_SNAP_RETAIN_COUNT) {
        LOG.warn("Invalid autopurge.snapRetainCount: "
                 + snapRetainCount
                 + ". Defaulting to "
                 + MIN_SNAP_RETAIN_COUNT);
        snapRetainCount = MIN_SNAP_RETAIN_COUNT;
    }

    if (dataDir == null) {
        throw new IllegalArgumentException("dataDir is not set");
    }
    if (dataLogDir == null) {
        dataLogDir = dataDir;
    }

    if (clientPort == 0) {
        LOG.info("clientPort is not set");
        if (clientPortAddress != null) {
            throw new IllegalArgumentException("clientPortAddress is set but clientPort is not set");
        }
    } else if (clientPortAddress != null) {
        this.clientPortAddress = new InetSocketAddress(InetAddress.getByName(clientPortAddress), clientPort);
        LOG.info("clientPortAddress is {}", formatInetAddr(this.clientPortAddress));
    } else {
        this.clientPortAddress = new InetSocketAddress(clientPort);
        LOG.info("clientPortAddress is {}", formatInetAddr(this.clientPortAddress));
    }

    if (secureClientPort == 0) {
        LOG.info("secureClientPort is not set");
        if (secureClientPortAddress != null) {
            throw new IllegalArgumentException("secureClientPortAddress is set but secureClientPort is not set");
        }
    } else if (secureClientPortAddress != null) {
        this.secureClientPortAddress = new InetSocketAddress(InetAddress.getByName(secureClientPortAddress), secureClientPort);
        LOG.info("secureClientPortAddress is {}", formatInetAddr(this.secureClientPortAddress));
    } else {
        this.secureClientPortAddress = new InetSocketAddress(secureClientPort);
        LOG.info("secureClientPortAddress is {}", formatInetAddr(this.secureClientPortAddress));
    }
    if (this.secureClientPortAddress != null) {
        configureSSLAuth();
    }

    if (observerMasterPort <= 0) {
        LOG.info("observerMasterPort is not set");
    } else {
        this.observerMasterPort = observerMasterPort;
        LOG.info("observerMasterPort is {}", observerMasterPort);
    }

    if (tickTime == 0) {
        throw new IllegalArgumentException("tickTime is not set");
    }

    minSessionTimeout = minSessionTimeout == -1 ? tickTime * 2 : minSessionTimeout;
    maxSessionTimeout = maxSessionTimeout == -1 ? tickTime * 20 : maxSessionTimeout;

    if (minSessionTimeout > maxSessionTimeout) {
        throw new IllegalArgumentException("minSessionTimeout must not be larger than maxSessionTimeout");
    }

    LOG.info("metricsProvider.className is {}", metricsProviderClassName);
    try {
        Class.forName(metricsProviderClassName, false, Thread.currentThread().getContextClassLoader());
    } catch (ClassNotFoundException error) {
        throw new IllegalArgumentException("metrics provider class was not found", error);
    }

    // backward compatibility - dynamic configuration in the same file as
    // static configuration params see writeDynamicConfig()
    if (dynamicConfigFileStr == null) {
        setupQuorumPeerConfig(zkProp, true);
        if (isDistributed() && isReconfigEnabled()) {
            // we don't backup static config for standalone mode.
            // we also don't backup if reconfig feature is disabled.
            backupOldConfig();
        }
    }
}

配置解析主要包含了两部分内容:

  • 配置加载解析
  • 配置合法性验证

8.2 配置大全

这里将常用配置列举了出来希望能对大家有所帮助

配置加载解析的过程主要是将zoo.cfg配置文件中的配置赋值给QuorumPeerConfig类型的成员变量中,方便在程序中使用用户的配置,那具体配置有哪些,配置的类型是什么,默认值又有哪些呢,我们可以详细来看下如下表格:

变量名 类型 默认值 必配项 说明
dataDir String Zookeeper保存数据的目录,主要内存存储快照数据文件路径不存在,或者未配置则抛出异常
dataLogDir String dataDir 事务日志存储在该路径下,比较重要,这个日志存储的设备效率会影响ZK的写吞吐量文件路径不存在则抛出异常,
clientPort Integer 监听客户端连接的端口与clientPortAddress配置一起构成本地监听地址clientPortAddress为空时候默认为本地地址0.0.0.0clientPort为空时候clientPortAddress不可以配置
localSessionsEnabled Boolean false 在ZooKeeper中创建和关闭会话非常昂贵,因为它们需要仲裁确认,当需要处理数千个客户端连接时,它们成为ZooKeeper集成的瓶颈。因此,在3.5.0之后,我们引入了一种新的会话类型:本地会话,它不具有普通(全局)会话的全部功能,因此可以通过打开localSessionsEnabled来使用此功能。
localSessionsUpgradingEnabled Boolean false ZooKeeper的全局会话需要法定确认,开销会很大,所以引入本地会话,当localSessionsUpgradingEnabled开启时,LeaderZookeeper的本地会话可以自动升级为全局会话,本地会话不能创建临时节点,全局会话可以创建,但FollowerZooKeeperServer(追随者)和ObserverZooKeeperServer(观察者)为了避免创建临时节点和大量的会话,所以我们尽量将localSessionsUpgradingEnabled关闭
clientPortAddress String 监听客户端连接的地址与clientPortAddress配置一起构成本地监听地址
secureClientPort Integer 监听客户端ssl连接的端口
secureClientPortAddress String 监听客户端ssl连接的地址
observerMasterPort Integer :监听观察者连接的端口;也就是说,观察者试图连接的端口。如果该属性被设置,那么服务器将在follower模式和leader模式下托管观察者连接,并相应地尝试在观察者模式下连接到任何投票的对等体。
clientPortListenBacklog Integer -1 ZooKeeper服务器套接字的套接字积压长度。这控制了将被ZooKeeper服务器处理的服务器端排队请求的数量。超过这个长度的连接将会收到一个网络超时(30s),这可能会导致ZooKeeper会话超时问题。默认情况下,这个值是未设置的(-1)。这个值必须是正数。
tickTime Integer 3000毫秒 基本事件单元,以毫秒为单位,这个时间作为 Zookeeper 服务器之间或客户端之间维持心跳的时间间隔,不能为0一个滴答的长度,这是ZooKeeper使用的基本时间单位,以毫秒为单位。它用于调节心跳和超时。例如,会话超时的最小值minSessionTimeout是2个tickTime时间单位。默认会话超时时间最大值maxSessionTimeout是20个tickTime时间单位
maxClientCnxns Integer 60 maxClientCnxns:限制单个客户端(通过IP地址标识)对ZooKeeper集合中的单个成员的并发连接数(在套接字级别)。这用于防止某些类型的DoS攻击,包括文件描述符耗尽。缺省值是60。将其设置为0将完全消除对并发连接的限制。
minSessionTimeout Integer 2* tickTime 服务器允许客户端协商的最小会话超时(以毫秒为单位)。默认为tickTime的2倍
maxSessionTimeout Integer 20* tickTime 服务器允许客户端协商的最大会话超时时间(以毫秒为单位)。默认值为tickTime的20倍。
initLimit Integer 分布式环境下必须配置 集群中的follower服务器(F)与leader服务器(L)之间 初始连接 时能容忍的最多心跳数(tickTime的数量)。
syncLimit Integer 分布式环境下必须配置 集群中的follower服务器(F)与leader服务器(L)之间 请求和应答 之间能容忍的最多心跳数(tickTime的数量)。
connectToLearnerMasterLimit Integer 允许追随者连接到leader后,leader选举。默认为initLimit的值。当initLimit很高时使用,可以配置这个值来覆盖链接到master的超时时间,这样连接到leader不会导致更高的超时。可以参考类型LeaderConnector
electionAlg Integer 3 默认值是3并且只能配置3,低版本可以配置其他值用于选举的实现的参数,0为以原始的基于UDP的方式协作,1为不进行用户验证的基于UDP的快速选举,2为进行用户验证的基于UDP的快速选举,3为基于TCP的快速选举,默认值为3
quorumListenOnAllIPs Boolean false 当设置为true时,ZooKeeper服务器将会在所有可用的网卡IP地址上监听来自其对等点的连接请求,而不仅是配置文件的服务器列表中配置的地址。它会影响处理ZAB协议和Fast Leader Election协议的连接。默认值是false
peerType String LearnerType.PARTICIPANT 可以配置的值为”observer”和”participant”。就是说如果配置文件中配了peerType=observer就是观察者模式,不会参与投票 如果配置文件中配了参与者peerType=participant就是参与者模式,可以参与投票 。
syncEnabled Boolean true 和参与者一样,观察者现在默认将事务日志以及数据快照写到磁盘上,这将减少观察者在服务器重启时的恢复时间。将其值设置为“false”可以禁用该特性。默认值是 “true”。
dynamicConfigFile String 从3.5.0开始,我们区分了动态配置参数和静态配置参数,前者可以在运行时更改,后者在服务器启动时从配置文件中读取,在执行过程中不会更改。目前,以下配置关键字被认为是动态配置的一部分:服务器、组和权重。 动态配置参数存储在服务器上的一个单独文件中(我们称之为动态配置文件)。使用新的dynamicConfigFile关键字将该文件链接到静态配置文件。
autopurge.snapRetainCount Integer 3 指定了需要保留的文件数目。默认是保留3个。启用后,ZooKeeper 自动清除功能会分别在****dataDir**dataLogDir 中*保留*autopurge.snapRetainCount****最近的快照和相应的事务日志, 并删除其余部分。默认为 3。最小值为 3。可参考DatadirCleanupManager类型
autopurge.purgeInterval Integer 0 必须触发清除任务的时间间隔(以小时为单位)。设置为正整数(1 及以上)以启用自动清除。默认为 0不启用。可参考DatadirCleanupManager类型
standaloneEnabled String true ****3.5.0 新增功能:****设置为 false 时,可以在复制模式下启动单个服务器,单独的参与者可以与观察者一起运行,集群可以重新配置为一个节点,从一个节点。对于向后兼容性,默认值为 true。可以使用 QuorumPeerConfig 的 setStandaloneEnabled 方法或通过将“standaloneEnabled=false”或“standaloneEnabled=true”添加到服务器的配置文件来设置它。
reconfigEnabled String false ****3.5.3 中的新增功能:*这控制启用或禁用动态重新配置功能。启用该功能后,用户可以通过 ZooKeeper 客户端 API 或通过 ZooKeeper 命令行工具执行重新配置操作,前提是用户有权执行此类操作。当该功能被禁用时,包括超级用户在内的任何用户都不能执行重新配置。任何重新配置的尝试都将返回错误。*“reconfigEnabled”*选项可以设置为*“reconfigEnabled=false”**“reconfigEnabled=true”*到服务器的配置文件,或使用 QuorumPeerConfig 的 setReconfigEnabled 方法。默认值为假。如果存在,该值应该在整个整体中的每个服务器上保持一致。在某些服务器上将该值设置为 true 而在其他服务器上设置为 false 会导致不一致的行为,具体取决于哪个服务器被选为领导者。如果领导者的设置为*“reconfigEnabled=true”*,那么集成将启用重新配置功能。如果领导者的设置为*“reconfigEnabled=false”*,则集成将禁用重新配置功能。因此,建议在整体中跨服务器的*“reconfigEnabled”****具有一致的值。
sslQuorum Boolean false ****3.5.5 中的新增功能:****启用加密的仲裁通信。默认为false。
portUnification Boolean false 端口统一使用同一端口提供不同协议的服务
sslQuorumReloadCertFiles Boolean false 当证书发生变更时是否自动重新加载证书
quorum.auth.enableSasl Boolean false 开启sasl开关
quorum.auth.serverRequireSasl Boolean false zk作为server,learner链接的时候,需要发送认证消息
quorum.auth.learnerRequireSasl Boolean false zk作为learner的时候,会发送认证消息
quorum.auth.learner.saslLoginContext String QuorumLearner Quorum learner登录上下文名称在jaas-conf文件中默认“QuorumLearner”。
quorum.auth.server.saslLoginContext String QuorumServer Quorum learner登录上下文名称在jaas-conf文件中默认“QuorumServer”。
quorum.auth.kerberos.servicePrincipal String zkquorum/localhost Kerberos仲裁服务主体。默认为zkquorum/localhost
quorum.cnxn.threads.size Integer 20 zookeeper Quorum 之间异步连接的线程池大小最小值是20 当配置的值超过这个值的时候则会以配置值为准
jvm.pause.info-threshold.ms Long 1000毫秒 gc停顿时间超过这个值将会打印info日志日志,具体细节可以参考JvmPauseMonitor,默认值为1000毫秒
jvm.pause.warn-threshold.ms Long 10000毫秒 gc停顿时间超过这个值将会打印warn日志,具体细节可以参考JvmPauseMonitor,默认值为1000毫秒
jvm.pause.sleep.time.ms Long 500毫秒 GC信息统计间隔,多久统计一次GC信息
jvm.pause.monitor Boolean false JVM GC停顿监控,如果设置为true则开启GC停顿监控GC参数才生效具体业务可以参考JvmPauseMonitor类型
metricsProvider.className String DefaultMetricsProvider ****3.6.0****设置为 “org.apache.zookeeper.metrics.prometheus.PrometheusMetricsProvider” 来开启Prometheus.io exporter.
metricsProvider.httpPort String 7000 Prometheus.io exporter 将会开启 Jetty server 来绑定这个端口 默认是 7000. Prometheus 访问地址为 http://hostname:httPort/metrics.
metricsProvider.exportJvmInfo true 如果设置true将返回有用的GC信息
multiAddress.enabled Boolean false 3.6.0新增你也可以为每个ZooKeeper服务器实例指定多个地址(当多个物理网络接口可以在集群中并行使用时,这可以增加可用性)。将此参数设置为true将启用此功能。请注意,如果旧的ZooKeeper集群版本在3.6.0之前,在滚动升级过程中不能启用该特性。缺省值为false。
multiAddress.reachabilityCheckTimeoutMs 1000ms 3.6.0新增:你也可以为每个ZooKeeper服务器实例指定多个地址(当多个物理网络接口可以在集群中并行使用时,这可以增加可用性)。ZooKeeper会执行ICMP ECHO请求或尝试在目的主机的7 (ECHO)端口上建立TCP连接,以找到可到达的地址。只有在配置中提供多个地址时才会发生这种情况。在此属性中,可以设置以毫秒为单位的可达性检查超时时间。对不同地址的检查是并行进行的,因此您在这里设置的超时是检查所有地址可达性所花费的最大时间。缺省值是1000。
multiAddress.reachabilityCheckEnabled Boolean true 3.6.0新增你也可以为每个ZooKeeper服务器实例指定多个地址(当多个物理网络接口可以在集群中并行使用时,这可以增加可用性)。ZooKeeper会执行ICMP ECHO请求或尝试在目的主机的7 (ECHO)端口上建立TCP连接,以找到可到达的地址。只有在配置中提供多个地址时才会发生这种情况。在此属性中,可以设置以毫秒为单位的可达性检查超时时间。对不同地址的检查是并行进行的,因此您在这里设置的超时是检查所有地址可达性所花费的最大时间。缺省值是1000。

配置的加载就是将zoo.cfg中的配置信息设置到内存中,这里有个checkValidity()方法如果是在分布式模式下部署则必须设置initLimit,syncLimit,serverId(serverId在myid文件中必须提前创建好)

技术咨询支持,可以扫描微信公众号进行回复咨询
在这里插入图片描述


, ,