Zookeeper 3.6.3+ 兼容老版本 rmr 命令的方法
背景
大数据软件栈中Zookeeper版本从3.4.14升级到3.6.3,其他组件版本暂时未升级。经过试用发现部分组件工作不正常。其中一个原因是Zookeeper 3.6.3版本移除了rmr
命令,取而代之的是deleteall
命令,存在不兼容的情况。考虑到软件栈其他组件的升级需要时间,决定先为Zookeeper添加回已经废弃的rmr
命令,保持对老版本使用方式的兼容性。
本篇为大家带来Zookeeper 3.6.3添加rmr
命令的方法。
操作步骤
首先我们需要下载Zookeeper 3.6.3的源代码。执行:
git clone https://github.com/apache/zookeeper.git
然后切换分支到tag release-3.6.3
。
分支切换完成之后打开ZookeeperMain
这个类,找到static
代码块。如下所示:
static {
commandMap.put("connect", "host:port");
commandMap.put("history", "");
commandMap.put("redo", "cmdno");
commandMap.put("printwatches", "on|off");
commandMap.put("quit", "");
new CloseCommand().addToMap(commandMapCli);
new CreateCommand().addToMap(commandMapCli);
new DeleteCommand().addToMap(commandMapCli);
new DeleteAllCommand().addToMap(commandMapCli);
new SetCommand().addToMap(commandMapCli);
new GetCommand().addToMap(commandMapCli);
new LsCommand().addToMap(commandMapCli);
new GetAclCommand().addToMap(commandMapCli);
new SetAclCommand().addToMap(commandMapCli);
new StatCommand().addToMap(commandMapCli);
new SyncCommand().addToMap(commandMapCli);
new SetQuotaCommand().addToMap(commandMapCli);
new ListQuotaCommand().addToMap(commandMapCli);
new DelQuotaCommand().addToMap(commandMapCli);
new AddAuthCommand().addToMap(commandMapCli);
new ReconfigCommand().addToMap(commandMapCli);
new GetConfigCommand().addToMap(commandMapCli);
new RemoveWatchesCommand().addToMap(commandMapCli);
new GetEphemeralsCommand().addToMap(commandMapCli);
new GetAllChildrenNumberCommand().addToMap(commandMapCli);
new VersionCommand().addToMap(commandMapCli);
new AddWatchCommand().addToMap(commandMapCli);
// add all to commandMap
for (Entry<String, CliCommand> entry : commandMapCli.entrySet()) {
commandMap.put(entry.getKey(), entry.getValue().getOptionStr());
}
}
很明显可以看到,这个方法注册了一系列ZkCli
中可以使用的命令。我们找到这一行new DeleteAllCommand().addToMap(commandMapCli);
。这一行注册了deleteall
命令。接下来的事情就非常明确了:由于rmr
命令和deleteall
命令逻辑相同,我们根据DeleteAllCommand
“改造”出一个用于rmr
命令的类就能够满足要求。我们查看DeleteAllCommand
这个类的代码。代码关键部分如下所示:
// 无参构造方法
public DeleteAllCommand() {
// 这个方法中传入的是命令字符串,这里是关键
this("deleteall");
}
public DeleteAllCommand(String cmdStr) {
super(cmdStr, "path [-b batch size]");
}
// deleteall命令执行的时候具体需要做什么,在这个方法中体现
@Override
public boolean exec() throws CliException {
int batchSize;
try {
batchSize = cl.hasOption("b") ? Integer.parseInt(cl.getOptionValue("b")) : 1000;
} catch (NumberFormatException e) {
throw new MalformedCommandException("-b argument must be an int value");
}
String path = args[1];
try {
// 使用ZkUtil级联删除path和其下所有的znode
boolean success = ZKUtil.deleteRecursive(zk, path, batchSize);
if (!success) {
err.println("Failed to delete some node(s) in the subtree!");
}
} catch (IllegalArgumentException ex) {
throw new MalformedPathException(ex.getMessage());
} catch (KeeperException | InterruptedException ex) {
throw new CliWrapperException(ex);
}
return false;
}
通过上面的代码分析我们不难发现,执行哪一条命令会调用到DeleteAllCommand
是在这个类的构造方法中定义的。
分析到这里我们有了修改思路:
首先我们复制DeleteAllCommand
这个类,命名为RmrCommand
,修改构造函数名为新的类名。然后修改无参构造函数为如下内容:
public RmrCommand() {
this("rmr");
}
接下来修改ZookeeperMain
的static
代码块,加入这一行:
new RmrCommand().addToMap(commandMapCli);
到这里为止代码修改完毕。最后我们需要重新编译Zookeeper。参考源代码根目录的README_packaging.md
。进入源代码根目录执行:
mvn clean install -DskipTests
编译完成的二进制软件包位于:
zookeeper-assembly/target/apache-zookeeper-<version>-bin.tar.gz
验证下新编译出来Zookeeper的rmr
命令和deleteall
命令是否可用,确保修改生效。
后记
升级须谨慎。大数据各组件之间的版本有依赖关系,形成了一个软件栈。如果对某个组件进行升级,一定要提前了解清楚如下内容:
- 组件数据存储格式和数据组织形式是否有变化。
- 对外接口是否有变化。支持的操作方式是否有变化。
- 部署形式是否有变化。环境和资源要求是否有变化。
- 该组件的配置项是否有变化。
- 依赖该组件的其他大数据组件有哪些,它们是用什么方式和该组件进行交互。这决定了其他组件是否需要同步修改。
- 该组件对其他组件的依赖关系是否有变化。该组件依赖其他组件的版本是否有变化。
本博客为作者原创,欢迎大家参与讨论和批评指正。如需转载请注明出处。