目录
一、 引言
二、运行jar包
1、方式一
2、方式二
3、方式三
三、脚本方式
一、 引言
在学习过程中,我们大部分都是在windows等非Linux环境下进行开发、测试、部署等。但是在实际的工作当中,应用基本都是部署在Linux环境下,因此我们要熟悉Linux环境下应用的部署测试。如果做开发的还要兼职运维工作的话,那就更有必要掌握Linux环境下应用的部署方式。
在Linux当中,运行jar包主要有以下几种方式。
为了直观,本文还是以一个实际的项目作为演示,当然,是个极其简单的项目。
首先,新建一个maven项目,pom.xml文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>ztt</groupId>
<artifactId>test_jar</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<classpathPrefix>lib/</classpathPrefix>
<mainClass>hello.TestJar</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
<!-- 项目编码-->
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
</project>
注意:在pom.xml里面通过<mainClass>hello.TestJar</mainClass>指定了主类,如果不指定,可能会出现“xxx.jar中没有主清单属性”的问题。
然后,编写一个带main方法的类。
package hello;
public class TestJar {
public static void main(String[] args) {
try {
System.out.println("TestJar start!");
for (int i = 0; i < 3; i++) {
System.out.println("i:" + i);
}
// 暂停
Thread.sleep(1000 * 10);
System.out.println("TestJar finish!");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
如图所示,代码仅实现了简单的打印功能。在程序执行的过程中,我特意让主线程暂停了10秒,是为了在测试过程中方便观察。
然后,将此jar包放到Linux环境下(建议使用winSCP操作),本文示例如下:
然后,cd进入到此目录,下面我们开始就通过不同的方式来运行此jar包。
二、运行jar包
1、方式一
命令格式:java -jar jar包名
[root@localhost test_jar]# java -jar test_jar-1.0-SNAPSHOT.jar
执行结果如下:
10s之后,打印最后一行内容。
分析:执行此命令的时候,程序会先打印前4行内容,然后暂停10s,当前窗口被锁定,无法进行其它操作,直到暂停时间结束,main方法执行完毕,当前窗口才解除锁定。
因此,我们可以看到,此方式运行jar包一个极其严重的缺点:锁定窗口。当然,我可以通过CTRL + C打断程序运行,或直接关闭窗口,程序退出,不过在实际的工作环境中,是绝对不会允许这种粗暴的方式终止的运行,毕竟我们的项目都是给用户提供服务的,程序要是退出了,公司还营不营业了。
2、方式二
命令格式:java -jar jar包名 &
[root@localhost test_jar]# java -jar test_jar-1.0-SNAPSHOT.jar &
执行结果如下:
分析:&代表在后台运行。优点是当前ssh窗口不被锁定,但是当窗口关闭时,程序终止运行。
那么我们就会想,如何继续改进,让窗口关闭时,程序仍然运行呢?
3、方式三
方式三主要是引入nohup命令,具体使用主要包括下面三种方式。
(1)命令格式:nohup java -jar jar包名 &
[root@localhost test_jar]# nohup java -jar test_jar-1.0-SNAPSHOT.jar &
挂断运行命令,当账户退出或终端关闭时,程序仍然运行。
当用 nohup 命令运行jar包时,缺省情况下该应用的所有输出被重定向到nohup.out的文件中,除非另外指定了输出文件。
执行结果如下:
然后,我们在当前目录下看到了nohup.out文件。
打开nohup.out文件,发现前面所有的输出都重定向到了此文件里面。
(2)nohup java -jar jar包名 > 文件名 &
test.txt
执行结果如下:
然后,再当前目录下出现test.txt文件。
大家文件,发现输出都重定向到了test.txt文件里面。
(3)nohup java -jar jar包名 >output 2>&1 &
[root@localhost test_jar]# nohup java -jar test_jar-1.0-SNAPSHOT.jar >output 2>&1 &
linux shell中"2>&1"含
对于& 1 更准确的说应该是文件描述符 1,而1表示标准输出stdout。
对于2 ,表示标准错误stderr。
2>&1 的意思就是将标准错误重定向到标准输出。
而标准输出又导入文件output里面,所以结果是标准错误和标准输出都导入文件output里面了。
至于为什么需要将标准错误重定向到标准输出的原因,那就归结为标准错误没有缓冲区,而stdout有。这就会导致 >output 2>output, 文件output被两次打开,而stdout和stderr将会竞争覆盖,这肯定不是我门想要的。
执行命令,结果如下:
再当前目录下,出现了output文件,打开查看内容,在第一行出现了一行乱码(编码问题,不影响程序运行)。
乱码处理之后的原文是:nohup: 忽略输入。其实就是标准错误的内容,因为>output 2>&1的作用是将标准输出和标准错误都重定向到output文件里。
(4)nohup java -jar jar包名 >/dev/null 2>&1 &
[root@localhost test_jar]# nohup java -jar test_jar-1.0-SNAPSHOT.jar >/dev/null 2>&1 &
/dev/null文件的作用。/dev/null是一个虚拟的空设备,可以看作是一个"黑洞",黑洞的特性是只进不出,哪怕是光都逃不掉,/dev/null亦是如此。
它等价于一个只写文件,所有写入它的内容都会永远丢失。如果尝试从它那儿读取内容,那么什么也读不到。
明白了/dev/null之后,我们再来看这个命令。>/dev/null 表示将标准输出信息重定向到"黑洞",2>&1 表示将标准错误重定向到标准输出,由于标准输出已经重定向到“黑洞”了(即:标准输出此时也是"黑洞"),再将标准错误输出定向到标准输出,相当于标准错误输出也被定向至“黑洞”。
执行上述命令,不同于前面的那些命令,可以看到各种标准输出,这里什么也看不到。
三、脚本方式
脚本方式的本质依然是上面介绍的那些命令,只不过shell脚本更加的方便,并且可以干更多的事情。
这里依然以test_jar-1.0-SNAPSHOT.jar这个小demo为例,编写如下脚本test_jar.sh:
#!/bin/bash
#这里可替换为你自己的执行程序,其他代码无需更改
APP_NAME=test_jar-1.0-SNAPSHOT.jar
#使用说明,用来提示输入参数
usage() {
echo "Usage: sh 脚本名.sh [start|stop|restart|status]"
exit 1
}
#检查程序是否在运行
is_exist(){
pid=`ps -ef|grep $APP_NAME|grep -v grep|awk '{print $2}' `
#如果不存在返回1,存在返回0
if [ -z "${pid}" ]; then
return 1
else
return 0
fi
}
#启动方法
start(){
is_exist
if [ $? -eq "0" ]; then
echo "${APP_NAME} is already running. pid=${pid} ."
else
nohup java -Xmx512m -Xms512m -jar $APP_NAME > test2.txt 2>&1 &
echo "${APP_NAME} start success"
fi
}
#停止方法
stop(){
is_exist
if [ $? -eq "0" ]; then
kill -9 $pid
else
echo "${APP_NAME} is not running"
fi
}
#输出运行状态
status(){
is_exist
if [ $? -eq "0" ]; then
echo "${APP_NAME} is running. Pid is ${pid}"
else
echo "${APP_NAME} is NOT running."
fi
}
#重启
restart(){
stop
start
}
#根据输入参数,选择执行对应方法,不输入则执行使用说明
case "$1" in
"start")
start
;;
"stop")
stop
;;
"status")
status
;;
"restart")
restart
;;
*)
usage
;;
esac
然后,执行这个脚本: