【Linux】Linux环境下运行jar包的几种方式

方式一(java -jar jar包名)

1
2
3
# 命令格式:java -jar jar包名

[root@localhost test_jar]# java -jar test_jar-1.0-SNAPSHOT.jar

分析: 执行此命令的时候,当前窗口被锁定,无法进行其它操作,直到main方法执行完毕,当前窗口才解除锁定。
缺点: 锁定窗口。当然,我可以通过CTRL + C打断程序运行,或直接关闭窗口,程序退出,不过在实际的工作环境中,是绝对不会允许这种粗暴的方式终止的运行,毕竟我们的项目都是给用户提供服务的,程序要是退出了,公司还营不营业了。

方式二(java -jar jar包名 &)

1
2
3
命令格式:java -jar jar包名 &

[root@localhost test_jar]# java -jar test_jar-1.0-SNAPSHOT.jar &

分析: &代表在后台运行。优点是当前ssh窗口不被锁定,但是当窗口关闭时,程序终止运行。
那么我们就会想,如何继续改进,让窗口关闭时,程序仍然运行呢?

方式三

方式三主要是引入nohup命令,具体使用主要包括下面三种方式。

命令格式:nohup java -jar jar包名 &

1
2
# 命令格式:nohup java -jar jar包名 &
[root@localhost test_jar]# nohup java -jar test_jar-1.0-SNAPSHOT.jar &

nohup 意思是不挂断运行命令 ,当账户退出或终端关闭时,程序仍然运行。
当用 nohup 命令运行jar包时,缺省情况下该应用的所有输出被重定向到nohup.out的文件中,除非另外指定了输出文件。

命令格式:nohup java -jar jar包名 > 文件名 &

1
2
# nohup java -jar jar包名 > 文件名 &
[root@localhost test_jar]# nohup java -jar test_jar-1.0-SNAPSHOT.jar > test.txt &

command >test.file 是将commandd test.flie文件,即输出内容不打印到屏幕上,而是输出到 test.file文件中

命令格式: nohup java -jar jar包名 >output 2>&1 &

1
2
3
# nohup java -jar jar包名 >output 2>&1 &

[root@localhost test_jar]# nohup java -jar test_jar-1.0-SNAPSHOT.jar >output 2>&1 &

命令格式:nohup java -jar spring-boot-demo.jar > /dev/null 2>&1 &

不输出日志

方式四:脚本方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#!/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 > ./logs/`log+%Y-%m-%d`.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

附录一: nohup 和 &

使用 & 后台运行程序:

  • 结果会输出到终端
  • 使用Ctrl + C,程序免疫
  • 关闭session,程序关闭

使用nohup运行程序

  • 结果默认会输出到nohup.out
  • 使用Ctrl + C,程序关闭
  • 关闭session,程序免疫

平日线上经常使用nohup和&配合来启动程序

附录二:> /dev/null 2>&1

  • > 标准重定向符,允许我们创建一个 0KB的空文件。它通常用于重定向一个命令的输出到一个新文件中。在没有命令的情况下使用重定向符号时,它会创建一个文件。
  • /dev/null 可以看作黑洞,等价于一个只写文件。所有写入它的内容都会永远丢失,尝试从它那儿读取内容则什么也读不到。也就是将所有产生的日志将被丢弃
  • 2>&1符号>&是一个整体代表将标准错误2重定向到标准输出1,如果是2>1的话,代表将标准错误输出到文件1,而不是重定向到标准输出流

先了解下1和2在Linux中代表什么
当Linux执行一个程序时,会自动打开三个流

  • 0:标准输入流(默认是键盘)
  • 1:标准输出流(默认是屏幕)
  • 2:标准错误流(默认是屏幕)
名称 代码 操作符 java中表示 Linux中文件描述符
标准输入(stdin) 0 < 或 << System.in /dev/stdin -> /proc/self/fd/0 -> /dev/pts/0
标准输出(stdout) 1 >, >>, 1> 或 1>> System.out /dev/stdout -> /proc/self/fd/1 -> /dev/pts/0
标准错误输出(stderr) 2 2> 或 2>> System.err /dev/stderr -> /proc/self/fd/2 -> /dev/pts/0

批量启动、停止jar包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
#!/bin/bash
# 端口号
PORTS=(9500 9600 9801 9802 9803 9804 9805 9901 9902)
# 系统模块
MODULES=(gateway auth system job gen file risk supervise ssologin)
# 系统模块名称
MODULE_NAMES=(网关模块 鉴权模块 系统管理模块 定时任务模块 代码生成模块 文件管理模块 风险预警模块)
# jar包数组
JARS=(catsticloud-gateway.jar catsticloud-auth.jar catsticloud-modules-system.jar catsticloud-modules-job.jar catsticloud-modules-gen.jar catsticloud-modules-file.jar catsticloud-modules-risk.jar catsticloud-supervise.jar catsticloud-modules-ssologin.jar)
# jar包路径
JAR_PATH='/home/yunwei/gonggonganquan/jar'
# 日志路径
LOG_PATH='/home/yunwei/gonggonganquan/jar/logs'
start() {
local MODULE=
local MODULE_NAME=
local JAR_NAME=
local command="$1"
local commandOk=0
local count=0
local okCount=0
local port=0
for((i=0;i<${#MODULES[@]};i++))
do
MODULE=${MODULES[$i]}
MODULE_NAME=${MODULE_NAMES[$i]}
JAR_NAME=${JARS[$i]}
PORT=${PORTS[$i]}
if [ "$command" == "all" ] || [ "$command" == "$MODULE" ];then
commandOk=1
count=0
PID=`ps -ef |grep $(echo $JAR_NAME | awk -F/ '{print $NF}') | grep -v grep | awk '{print $2}'`
if [ -n "$PID" ];then
echo "$MODULE---$MODULE_NAME:已经运行,PID=$PID"
else
exec nohup java -jar $JAR_PATH/$JAR_NAME > /dev/null 2>&1 &
PID=`netstat -apn | grep $PORT | awk '{print $7}' | cut -d/ -f 1`
while [ -z "$PID" ]
do
if (($count == 30));then
echo "$MODULE---$MODULE_NAME:$(expr $count \* 10)秒内未启动,请检查!"
break
fi
count=$(($count+1))
echo "$MODULE_NAME启动中.................."
sleep 10s
PID=`netstat -apn | grep $PORT | awk '{print $7}' | cut -d/ -f 1`
done
okCount=$(($okCount+1))
echo "$MODULE---$MODULE_NAME:已经启动成功,PID=$PID"
fi
fi
done
if(($commandOk == 0));then
echo "第二个参数请输入:all|gateway|auth|system|job|gen|file|risk|supervise|ssologin"
else
echo "............本次共启动:$okCount个服务..........."
fi
}

stop() {
local MODULE=
local MODULE_NAME=
local JAR_NAME=
local command="$1"
local commandOk=0
local okCount=0
for((i=0;i<${#MODULES[@]};i++))
do
MODULE=${MODULES[$i]}
MODULE_NAME=${MODULE_NAMES[$i]}
JAR_NAME=${JARS[$i]}
if [ "$command" = "all" ] || [ "$command" = "$MODULE" ];then
commandOk=1
PID=`ps -ef |grep $(echo $JAR_NAME | awk -F/ '{print $NF}') | grep -v grep | awk '{print $2}'`
if [ -n "$PID" ];then
echo "$MODULE---$MODULE_NAME:准备结束,PID=$PID"
kill -9 $PID
PID=`ps -ef |grep $(echo $JAR_NAME | awk -F/ '{print $NF}') | grep -v grep | awk '{print $2}'`
while [ -n "$PID" ]
do
sleep 3s
PID=`ps -ef |grep $(echo $JAR_NAME | awk -F/ '{print $NF}') | grep -v grep | awk '{print $2}'`
done
echo "$MODULE---$MODULE_NAME:成功结束"
okCount=$(($okCount+1))
else
echo "$MODULE---$MODULE_NAME:未运行"
fi
fi
done
if (($commandOk == 0));then
echo "第二个参数请输入:all|gateway|auth|system|job|gen|file|risk|supervise|ssologin"
else
echo "............本次共停止:$okCount个服务............"
fi
}
status() {
local MODULE=
local MODULE_NAME=
local JAR_NAME=
local command="$1"
local commandOk=0
local count=0
local okCount=0
local port=0
for((i=0;i<${#MODULES[@]};i++))
do
MODULE=${MODULES[$i]}
MODULE_NAME=${MODULE_NAMES[$i]}
JAR_NAME=${JARS[$i]}
PORT=${PORTS[$i]}
if [ "$command" == "all" ] || [ "$command" == "$MODULE" ];then
commandOk=1
count=$(($i+1))
PID=`ps -ef |grep $(echo $JAR_NAME | awk -F/ '{print $NF}') | grep -v grep | awk '{print $2}'`
if [ -n "$PID" ];then
echo "$MODULE---$MODULE_NAME:已经运行,PID=$PID"
okCount=$(($okCount+1))
else
echo "$MODULE---$MODULE_NAME:未运行"
fi
fi
done
if(($commandOk == 0));then
echo "第二个参数请输入:all|gateway|auth|system|job|gen|file|risk|supervise|ssologin"
else
echo "............共:$count 个服务,运行中$okCount个服务..........."
fi
}

case "$1" in
start)
start "$2"
;;
stop)
stop "$2"
;;
status)
status "$2"
;;
restart)
stop "$2"
sleep 3s
start "$2"
;;
*)
echo "第一个参数请输入:start|stop|restart|status"
exit 1
;;
esac

启动:

1
2
3
4
5
6
7
8
## 启动所有
./start.sh start all
## 停止所有
./start.sh stop all
## 重启所有
./start.sh restart all
## 启动某一个 这里的gateway与sh中的module中定义的一样即可
./start.sh restart gateway

问题1:-bash: ./start.sh: Permission denied\
原因: 无权限
解决方法:
chmod +x ./文件名.sh

问题2:bad interpreter\:No such file or directory的错误

原因:
.sh脚本在windows系统下编写的,所以可能有不可见字符,从上图错误提示来看,很有可能脚本文件是DOS格式的,即每一行的行尾以\r\n来标识, 其ASCII码分别是0x0D, 0x0A。
解决方法:

  • 通过vim filename 或者vi filename 进入编辑,即我这是vim startup\_heartbeat.sh
  • 然后通过shift+:进入命令模式。接着通过命令set ff 或者 set fileformat 查看文件格式,即我这如下图:从中可以看到我这文件格式是dos
  • 修改文件格式dos为unix。通过命令set ff=unixset fileformat=unix修改,然后再执行set ff 或者 set fileformat,查看是否修改成功。即如下图所示
  • 启动命令,发现正常启动,至此完毕。

服务开机自启

一、新建jar包启动sh文件
新建startup.sh脚本文件,启动项目jar包

nohup java -jar test.jar >/dev/null 2>&1 &

二、设置自动启动命令
1.进入rc.d目录
执行cd /etc/rc.d 命令,进入rc.d目录

如图所示(示例):

2.修改rc.local文件内容
执行命令 vi rc.local,然后按下键盘上的【i】键进入修改模式

在末尾加上如下命令:

1
2
3
4
#进入项目目录
cd /home/test`
#执行sh文件
sh /home/test/startup.sh

然后先按下【esc】键,再按下【:】键,在光标闪烁处输入:wq。按回车,完成修改后,执行相应命令,设置执行权限,重启服务器,验证即可。

1
2
chmod +x /etc/rc.d/rc.local
chmod +x /home/test/startup.sh

有时服务器重启服务很多时,可能需要延迟启动,可以在之前的命令前加上sleep 60命令延迟启动

1
2
3
sleep 60
cd /home/test
sh /home/test/startup.sh

服务方式启动

1
2
3
4
5
可以添加一个 systemd的服务  基本上可以如图进行设置
一般位于 /etc/systemd/system/ 下面
比如可以建立一个文件为 app.service
可以使用 systemctl enable app 设置开机自动启动
然后使用systemctl start app 来启动服务
1
2
3
4
5
6
7
8
9
[Unit]
Description = Redis Server
After = network.target
[Service]
ExecStart=/app/startup.sh
ExecStop=/usr/bin/kill -15 $MAINPID
Restart = always
[Install]
WantedBy = multi-user.target