版本兼容性问题的来源

在迁移新机房的过程中,我决定将各种软件升级到最新的稳定版,最常用的三个软件都用到了Guava,具体如下表:

名称 版本 Guava依赖版本
Hadoop 2.7.1 11.0.2
Hbase 1.1.2 12.0.1
Spark 1.5.2 14.0.1
ElasticSearch 2.1.0 18.0

Guava的11和18版本的API更改很大,比如StopWatch的创建和时间获取,比如com.google.common.util.concurrent.Service的基本函数,在hbase-clienthbase-server中都有使用。ElasticSearch中也有大量的使用Guava,正在计划将Guava移除出去,但并未合并到Release中(参见这里)。

我们的MR程序需要从各种数据源中读取数据,按照知识图谱中的schema定义生成混合数据,然后写入到Hbase和ES中。为了加速任务的执行,我们正在向Spark迁移,这样就导致了4个不同版本的Guava依赖。下面我来简要介绍一下为解决这个问题所做的努力。

Hbase Client 依赖升级

官方Jira中一通搜索只有没发现社区有人讨论这个问题,稍微看了一下代码,发现Client和Common中对Guava的使用并不多,仅有几处,遂手动修改并部署到公司内部的maven仓库中。值得注意的是,为了防止其他人用错,我更改了Guava 18.0版本的Hbase的artifactId。

这样编译出来的assembly包依然无法直接在Hadoop上执行,因为Hadoop自己的安装目录中还有个11.0.2版本的Guava,我们必须在执行Hadoop命令前将assembly的路径放到java的classpath的前面。有如下的三个环境变量可以使用(具体参见Hadoop的各种启动脚本如bin/hadoop、etc/hadoop/hadoop-env.sh、hadoop-config.sh等,以及mapreduce的参数):

  1. HADOOP_CLASSPATH : Hadoop用户可以将额外的jar包的路径写到这个环境变量里面。
  2. HADOOP_USER_CLASSPATH_FIRST : 默认情况下HADOOP_CLASSPATH会放到classpath的最后面,如果设置了这个环境变量,HADOOP_CLASSPATH会放到classpath的最前面。
  3. 参数mapreduce.user.classpath.first : 启动MR任务的时候,将这个参数设置成true,那么在启动子进程执行任务的时候,用户的jar包会放到classpath的最前端。

Hbase Server 依赖升级

Hadoop 依赖升级

Hadoop的Guava升级较为简单,社区中有现成的patch,但是并未release,可以自行打补丁并编译安装。

Spark 依赖升级

Spark的对Guava的依赖并没有涉及到变动的那部分API,直接在pom.xml里面把Guava的版本升级到18.0就能编译,之后的部署暂且不表。

结论

没事别随便改API,真有必要的话,也请换个名字,要不会给使用方带来巨大的麻烦。