5.3.2 依赖编译型语言的Java应用迁移
对于大型项目来说,很多时候不仅仅使用一种语言来开发应用,有时候会使用多种语言进行混合编程,例如著名的开源项目Netty,主体开发语言是Java,但是在部分项目里还使用了C语言。出现这种情况的一个原因是Netty在Linux下的异步/非阻塞网络传输中,使用了Epoll——一个基于I/O事件通知的高性能多路复用机制。Netty是通过JNI方式提供Native Socket Transport的,在Netty的transport-native-epoll项目中,有相关调用的C代码。
除此之外,在Netty的依赖项目netty-tcnative-parent中,也有JNI方式提供的C语言调用。
笔者负责的一款基于Java的物联网平台中也使用了Netty,在进行应用迁移时经过多次尝试,解决了多个问题,最后迁移成功,这里通过Netty项目,演示一下依赖编译型语言的Java应用的迁移。
1.迁移过程分析
Netty是开源的项目,在获得所有的源代码后,可以通过对代码进行重新编译的方式来执行迁移。因为代码里有Java和C语言,并且Netty项目是通过Pom进行项目组织管理的,在迁移时不但要安装C的编译环境,还要安装openjdk和Maven。
2.安装依赖项
要安装的依赖项较多,大部分可以通过yum安装,命令如下:
yum install gcc gcc-c++ make cmake3 libtool autoconf automake ant wget git openssl openssl - devel apr-devel ninja-build java-1.8.0-openjdk.aarch64 – y
安装依赖项的时间有点长,根据系统中已安装的软件情况,可能需要几分钟到十几分钟,最后回显如下:
因为后续步骤在编译libressl-static模块的时候需要cmake版本号大于3,并且需要ninja,这里提前做好软连接,命令如下:
ln -s /usr/bin/cmake3 /usr/bin/cmake ln -s /usr/bin/ninja-build /usr/bin/ninja
3.安装Maven
Java应用的编译打包需要Maven,安装步骤如下:
步骤1:下载Maven 3.6.3安装包,为了提高下载速度,可以使用国内的下载源,命令如下:
wget https://mirrors.tuna.tsinghua.edu.cn/apache/maven/maven - 3/3.6.3/binaries/apache - maven-3.6.3 -bin.tar.gz
步骤2:解压Maven安装包,命令如下:
tar -zvxf apache-maven-3.6.3 -bin.tar.gz
步骤3:移动Maven到指定目录,命令如下:
mv apache-maven-3.6.3 /opt/tools/
步骤4:配置环境变量,修改/etc/profile文件,在文件最后增加Maven的环境信息,增加的内容如下:
MAVEN_HOME=/opt/tools/apache-maven-3.6.3 PATH= $ MAVEN_HOME/bin:$ JAVA_HOME/bin: $ PATH export MAVEN_HOME JAVA_HOME PATH
步骤5:使环境变量生效,命令如下:
source /etc/profile
步骤6:由于Maven中央仓库的下载速度受限,所以这里配置Maven的镜像仓库网址为国内的镜像,要修改的配置文件路径为/opt/tools/apache-maven-3.6.3/conf/settings.xml,在该文件的<mirrors>节中添加新的镜像,添加的内容如下:
步骤7:查看Maven是否安装成功,命令及回显如下:
如果出现类似上面的回显,表示Maven安装配置成功了。
4.处理鲲鹏架构中char类型为无符号型的默认设置
直接对代码中char类型进行更改风险较高,工作量也很大,这里通过设置gcc和g++的编译选项来处理,也就是把这两个编译器的编译加上-fsigned-char的选项。
1)修改gcc编译选项
步骤1:确认gcc的位置,命令及回显如下:
[root@ecs-kunpeng ~]#command -v gcc /usr/bin/gcc
根据系统不同,位置可能有差异,笔者本机的位置在/usr/bin/gcc。
步骤2:修改gcc的名字为gcc-ori,命令如下:
mv/usr/bin/gcc /usr/bin/gcc-ori
步骤3:创建/usr/bin/gcc文件,命令如下:
vi /usr/bin/gcc
步骤4:编辑/bin/gcc文件,输入内容如下:
#! /bin/sh /usr/bin/gcc-ori -fsigned-char "$@"
步骤5:给/bin/gcc添加执行权限,命令如下:
chmod +x /usr/bin/gcc
步骤6:查看gcc是否可以成功执行,命令及回显如下:
[root@ecs-kunpeng ~]#gcc --version gcc-ori (GCC) 4.8.5 20150623 (Red Hat 4.8.5-44) Copyright (C) 2015 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
如果看到类似上面的回显,表示gcc修改成功了。
2)修改g++编译选项
步骤1:确认g++的位置,命令及回显如下:
[root@ecs-kunpeng ~]#command -v g++ /usr/bin/g++
本机位置是/usr/bin/g++,不同的服务器位置可能不同。
步骤2:修改g++的名字为g++-ori,命令如下:
mv /usr/bin/g++ /usr/bin/g++ -ori
步骤3:创建/usr/bin/g++文件,命令如下:
vi /usr/bin/g++
步骤4:编辑/bin/g++文件,输入内容如下:
#! /bin/sh /usr/bin/g++ -ori -fsigned-char "$@"
步骤5:给/bin/g++添加执行权限,命令如下:
chmod +x /usr/bin/g++
步骤6:查看g++是否可以成功执行,命令及回显如下:
[root@ecs-kunpeng ~]#g++ --version g++ -ori (GCC) 4.8.5 20150623 (Red Hat 4.8.5-44) Copyright (C) 2015 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
如果看到类似上面的回显,表示g++修改成功了。
5.加速编译准备
在正式编译以前,需要先下载3个安装包。后面的编译过程需要从多个网站下载安装包,这些网站的服务器一般都在境外,下载速度较慢,可能会因为下载不成功导致编译失败。
步骤1:下载apr-1.6.5,进入/data/soft/文件夹,下载命令如下:
wget https://mirrors.tuna.tsinghua.edu.cn/apache/apr/apr-1.6.5.tar.gz
步骤2:下载libressl-3.1.1,下载命令如下:
wget https://mirrors.tuna.tsinghua.edu.cn/OpenBSD/LibreSSL/libressl-3.1.1.tar.gz
步骤3:下载openssl-1.1.1g,下载命令如下:
wget https://www.openssl.org/source/openssl-1.1.1g.tar.gz
6.编译netty-tcnative-2.0.34
步骤1:进入/data/soft/下载netty-tcnative源码包,下载命令如下:
wget https://GitHub.com/netty/netty - tcnative/archive/netty - tcnative - parent - 2.0.34. Final.tar.gz
步骤2:解压源码包,并进入解压后目录,命令如下:
tar -zxvf netty-tcnative-parent-2.0.34.Final.tar.gz cd netty-tcnative-netty-tcnative-parent-2.0.34.Final/
步骤3:修改pom文件,注释掉对apr的下载,对于2.0.34版本来说,注释行在第474行,修改后的该段配置如下:
注释掉该行后,mvn编译时将不再从这里下载。
步骤4:进入libressl-static目录,修改pom文件,注释掉对libssl的下载,注释行在第263行,修改后的该段配置如下:
步骤5:进入openssl-static目录,修改pom文件,注释掉对openssl的下载,注释行在第334行和第338行,修改后的该段配置如下:
步骤6:注释掉对boringssl-static的编译(在第603行),因为boringssl-static需要从谷歌服务器获取资源,由于无法获取成功,这里就取消对它的编译,但不影响后续的使用(如果确实要用,可以把获取源码网址改为GitHub上的源码网址,这里就不演示了)。编辑源代码主目录的pom文件,修改后的该段配置如下:
步骤7:提前创建好openssl-static和libressl-static项目的target目录,命令如下:
mkdir /data/soft/netty- tcnative-netty- tcnative-parent- 2.0.34.Final/openssl - static/ target/ mkdir /data/soft/netty- tcnative-netty- tcnative-parent- 2.0.34.Final/libressl - static/ target/
步骤8:复制预先下载的文件到target目录,命令如下:
cp /data/soft/apr-1.6.5.tar.gz /data/soft/netty-tcnative-netty-tcnative-parent-2.0. 34.Final/openssl-static/target/ cp /data/soft/openssl-1.1.1g.tar.gz /data/soft/netty-tcnative-netty-tcnative-parent- 2.0.34.Final/openssl-static/target/ cp /data/soft/apr-1.6.5.tar.gz /data/soft/netty-tcnative-netty-tcnative-parent-2.0. 34.Final/libressl-static/target/ cp /data/soft/libressl-3.1.1.tar.gz /data/soft/netty-tcnative-netty-tcnative-parent- 2.0.34.Final/libressl-static/target/
步骤9:进入主目录,执行编译,命令如下:
cd /data/soft/netty-tcnative-netty-tcnative-parent-2.0.34.Final/ mvn install
最后编译成功的回显如下:
7.编译netty-all-4.1.52
步骤1:进入/data/soft/下载netty-all源码包,下载命令如下:
wget https://GitHub.com/netty/netty/archive/netty-4.1.52.Final.tar.gz
步骤2:解压源码包,并进入解压后目录,命令如下:
tar -zxvf netty-4.1.52.Final.tar.gz cd netty-netty-4.1.52.Final/
步骤3:处理jni.h问题。在后续的编译中,可能会出现找不到jni.h和jni_md.h的错误,如图5-9所示。
图5-9 编译错误
出现这种错误的原因是C编译器找不到头文件的位置,所以需要直接告诉编译器头文件在哪里,就是通过C编译器选项CFLAGS传过去头文件的路径。本机的jni.h文件在/usr/lib/jvm/java/include目录下,jni_md.h文件在/usr/lib/jvm/java/include/Linux/目录下。编辑transport-native-UNIX-common下的pom文件,命令如下:
vim /data/soft/netty-netty-4.1.52.Final/transport-native-UNIX-common/pom.xml
要修改的CFLAGS选项在第198行和第263行,在值的后面加上头文件的位置,代码如下:
-I/usr/lib/jvm/java/include -I/usr/lib/jvm/java/include/Linux/
修改后的效果如下所示,注意修改后的字符串也是全部在value值的引号里面,代码如下:
步骤4:编译netty-all,进入源码主目录,执行编译,命令如下:
mvn install -DskipTests
该命令将跳过测试过程,经过十几分钟的编译后,可以看到成功编译的回显如下所示:
鲲鹏架构的jar包就在各个项目的target目录下,例如transport-native-epoll项目下的jar包,显示如下:
包含aarch_64的jar包就是鲲鹏架构下适用的jar包。