Cassandra集群批量新增节点失败,寻求基于Consul的引导锁方案
解决方案:基于Consul实现Cassandra批量节点引导的锁机制
针对你遇到的Cassandra批量新增节点时的UJ状态问题,结合已有的Consul环境,以下是几种可行的锁释放方案:
1. 利用Consul Session + 健康检查自动释放锁
Consul Session支持绑定健康检查,当检查通过(节点引导完成)或失败(节点引导异常)时,Session会自动过期并释放锁,无需手动干预:
- 步骤1:注册Cassandra健康检查
在Consul中添加检查节点是否处于UN状态的健康规则:consul check create -name=cassandra-health -script="/usr/bin/nodetool status | grep -q '$(hostname -i).*UN'" -interval=30s -timeout=10s - 步骤2:修改systemd启动前置逻辑
在cassandra.service的ExecPreStart中创建绑定该检查的Session,然后竞争获取锁:
节点引导完成后健康检查通过,Session自动释放锁;若引导失败,检查持续超时也会触发锁释放,不影响其他节点竞争。ExecPreStart=/bin/bash -c 'SESSION_ID=$(consul session create -name=cassandra-bootstrap -checks=cassandra-health -behavior=release -ttl=1800s); until consul kv put -acquire=$SESSION_ID cassandra/bootstrap/lock $(hostname -i); do sleep 10; done'
2. 自定义引导脚本封装锁的完整生命周期
写一个shell脚本替代直接启动Cassandra,统一处理锁获取、节点状态校验和锁释放:
- 示例脚本
cassandra-bootstrap.sh:#!/bin/bash NODE_IP=$(hostname -i) LOCK_KEY="cassandra/bootstrap/lock" TIMEOUT=3600 # 引导超时时间(秒) # 创建带TTL的Session SESSION_ID=$(consul session create -name=cassandra-bootstrap -ttl=1800s) # 循环竞争锁,失败则重试 until consul kv put -acquire=$SESSION_ID $LOCK_KEY $NODE_IP; do echo "Waiting for bootstrap lock..." sleep 15 done # 启动Cassandra服务 systemctl start cassandra.service # 轮询检查节点状态,直到变为UN或超时 ELAPSED=0 while true; do if nodetool status | grep -q "$NODE_IP.*UN"; then echo "Node bootstrapped successfully, releasing lock..." consul kv put -release=$SESSION_ID $LOCK_KEY $NODE_IP exit 0 fi if [ $ELAPSED -ge $TIMEOUT ]; then echo "Bootstrap timed out, releasing lock..." consul kv put -release=$SESSION_ID $LOCK_KEY $NODE_IP exit 1 fi sleep 30 ELAPSED=$((ELAPSED+30)) # 续期锁,避免超时提前释放 consul kv put -acquire=$SESSION_ID $LOCK_KEY $NODE_IP done - 修改systemd服务配置:
将cassandra.service的ExecStart改为调用该脚本,同时添加停止时的锁释放逻辑:ExecStart=/usr/local/bin/cassandra-bootstrap.sh ExecStop=/bin/bash -c 'SESSION_ID=$(consul session list | grep cassandra-bootstrap | awk "{print \$1}"); if [ -n "$SESSION_ID" ]; then consul kv put -release=$SESSION_ID cassandra/bootstrap/lock $(hostname -i); fi'
3. 通过Cassandra JMX事件触发锁释放
利用Cassandra的JMX接口监听joinComplete事件,一旦节点引导完成就主动释放锁:
- Python监听脚本示例(依赖
jpype和python-consul库):import os import jpype import jpype.imports from consul import Consul # 启动JVM连接Cassandra JMX jpype.startJVM(jpype.getDefaultJVMPath(), "-Djava.class.path=/usr/share/cassandra/lib/*") from javax.management import JMXConnectorFactory, JMXServiceURL url = JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:7199/jmxrmi") connector = JMXConnectorFactory.connect(url) mbean_server = connector.getMBeanServerConnection() # 定义事件处理函数 def handle_bootstrap_complete(event): if event.getType() == "joinComplete": print("Bootstrap finished, releasing lock...") c = Consul() c.kv.put("cassandra/bootstrap/lock", "", release=os.environ.get("SESSION_ID")) connector.close() jpype.shutdownJVM() exit(0) # 注册监听器 mbean_server.addNotificationListener("org.apache.cassandra.db:type=StorageService", handle_bootstrap_complete, None, None) # 保持脚本运行直到事件触发 while True: jpype.java.lang.Thread.sleep(1000) - 集成到systemd:
在cassandra.service的ExecStartPost中启动该脚本,并通过环境变量传入Session ID,确保脚本在引导完成后自动释放锁。
关键注意事项
- 每个节点用唯一标识(如IP)作为锁的值,便于排查当前锁持有者
- 根据集群规模设置合理的锁TTL(建议1800秒以上),避免锁提前释放
- 测试异常场景:节点引导失败时,通过Consul Session的
-behavior=release参数确保锁能自动释放 - 批量新增节点时,验证锁竞争逻辑:同一时间只有一个节点能获取锁,其他节点等待,引导完成后锁自动释放
内容的提问来源于stack exchange,提问作者Hills of Eternity
相关产品推荐
相关产品推荐

