全栈范

互联网 & 技术 & 产品 & 阅读 & 生活

0%

从 0 到 1 搭建应用服务器(三):发布应用

4、发布应用

4.1 在服务器中启动 Springboot 程序

Springboot 应用开发完毕,直接在项目根目录执行 mvn install 编译打包即可。打出来的包,通过 ftp 上传到服务器。

对于 jar 包,如果想要在后台启动,可以使用如下指令,

1
nohup java -jar target/包名.jar --spring.profiles.active=lt &

也就是使用 nohup 并且末尾追加了一个 &。如果不使用 nohup 只追加一个 &,也可以后台启动,但是这种方式在 ssh 会话结束之后程序就会停止。

按照上面 nohup 的启动方式,会在当前目录下面生成一个名为 nohup.out 的文件,终端的内容会被写入到这个文件当中。使用这种启动方式的时候终端不会显示启动过程,所以我们只能通过获取 nohup.out 的内容来判断程序是否启动成功。动态获取文件的内容可以使用指令 tail -f nohup.out 来获取。当然,如果希望将输出人日志重定向到某个具体的文件或者不输出日志(重定向到 /dev/null)也是可以的。比如下面的命令用来将日志重定向到 catalina.out 文件:

1
nohup java -jar xxx.jar > catalina.out  2>&1 &

查看 Springboot 工程的进程使用如下命令,

1
ps aux | grep "java -jar" | grep -v "grep"

上述显示的内容中包含了进程的 pid,如果要杀掉某个进程,直接输入下面的命令即可:

1
kill -9 pid

4.2 指定虚拟机启动参数

1. 远程连接虚拟机

首先,我们需要远程连接上我们的应用来观察虚拟机的状况。不论是排查线上问题还是项目启动之前虚拟机参数调整,这个都比较重要。常用的连接工具有 jconsole 和 jvisualvm 两个。让我们的虚拟机可以被远程监控,需要我们做小小的配置。

首先,我们需要修改虚拟机远程连接的密码。进入 jre 安装目录下面的 management 目录中,比如我的 jdk1.8.0_181/jre/lib/management。然后,复制 jmxremote.password.template 并将其重命名为 jmxremote.password,然后注释掉最后两行的注释,这里的 monitorRole 就是默认的远程连接的登录账号,紧随其后的就是登录密码。需要把默认的密码修改掉,增加服务器的安全性

然后,我们需要在项目的启动参数中增加几个参数来配置并启用远程连接,比如我的设置了之后的参数如下:

1
nohup java -jar -Djava.rmi.server.hostname=23.123.122.31 -Dcom.sun.management.jmxremote.port=11162 -Dcom.sun.management.jmxremote.rmi.port=11163 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=true $portal_app_jar > portal.out 2>&1 &

这里我们的参数配置及其含义如下:

  1. java.rmi.server.hostname:远程连接的时候的 ip,就是你的服务器的真实 ip 地址;
  2. com.sun.management.jmxremote.port:远程连接的时候的端口,不能和应用的端口相同;
  3. com.sun.management.jmxremote.rmi.port:远程 rmi 连接的时候的端口,不能和应用的端口相同;
  4. com.sun.management.jmxremote.ssl:是否使用 ssl 连接方式;
  5. com.sun.management.jmxremote.authenticate:是否启用身份认证功能,这个应该启用,否则就别人不需要输入密码就可以直接连接并查看你的虚拟机了。

这样配置完成之后开发上面填写的两个端口,然后重新启动应用。

本地远程连接虚拟机的时候使用 jconsole 和 jvisualvm 皆可。这两个可执行文件放在 jdk 的 bin 目录下面。双击打开之后直接连接即可。

2. 应用启动参数配置

首先是一些官方的参考资料

常用的参数:

1
2
3
4
5
6
7
8
9
-XX:MetaspaceSize=128m (元空间默认大小)
-XX:MaxMetaspaceSize=128m (元空间最大大小)
-Xms1024m (堆默认大小也是最小的大小)
-Xmx1024m (堆最大大小)
-Xmn256m (新生代大小)
-Xss256k (棧最大深度大小)
-XX:SurvivorRatio=8 (新生代分区比例 8:2)
-XX:+UseConcMarkSweepGC (指定使用的垃圾收集器,这里使用CMS收集器)
-XX:+PrintGCDetails (打印详细的GC日志)

具体取值多少要根据自己的应用的类型、并发量和服务器环境综合分析,可以结合上面的一些文档为自己的程序指定参数。

4.3 编写服务器启动脚本程序

当然了,指定服务器启动参数还是比较繁琐的,所以,通常我们会编写启动脚本,然后通过执行脚本来启动和终止程序。下面是一段参考代码,

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
#!/bin/bash
# 程序运行脚本程序
# 作者:王守恒
# 定义要启动的程序的 Jar 文件,不指定 Jar 文件的版本
app_jar=box.jar

# 脚本文件使用说明
usage() {
echo "|====================================================================="
echo "| 脚本文件用法说明 |"
echo "|====================================================================="
echo "| sh 脚本名称.sh [start|stop|restart|status] [prev] |"
echo "| |"
echo "| 参数说明 |"
echo "| |"
echo "| start: 启动程序 |"
echo "| stop: 停止程序 |"
echo "| restart: 重新启动程序 |"
echo "| status: 程序状态 |"
echo "| |"
echo "| prev: 可选参数,如果指定该参数,则会将程序的运行日志输出到指定 |"
echo "| 的文件中,否则不输出,建议项目正式发布的时候不启用该选项 |"
echo "|====================================================================="
}

# 程序启动函数
# 函数体内使用 $ 获取的参数是函数调用的时候传入的参数,
# 而不是整个脚本被调用的时候传入的参数
start() {
# 运行portal程序
vm_opt='-XX:NewSize=216m -XX:MaxNewSize=216m -XX:SurvivorRatio=8 -Xms1024m -Xmx2048m -XX:+PrintGCDetails -Djava.rmi.server.hostname=你的ip -Dcom.sun.management.jmxremote.port=你的端口 -Dcom.sun.management.jmxremote.rmi.port=你的端口 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=true'
if [[ $1 == "prev" ]]; then
# 预览
nohup java -jar $vm_opt $app_jar > box.out 2>&1 &
echo "box[prev] start succeed"
else
nohup java -jar $vm_opt $app_jar > /dev/null &
echo "box app start succeed"
fi
}

# 程序停止函数
stop() {
# 获取程序的pid并kill之
is_exist $app_jar
if [ $? -eq "0" ]; then
kill -9 $pid
echo "Box was killed with pid ${pid}"
else
echo "Box is NOT running"
fi
}

# 程序状态函数
status() {
is_exist $app_jar
if [ $? -eq "0" ]; then
echo "Box is running with pid ${pid}"
else
echo "Box is NOT running"
fi
}

# 程序重新启动函数
restart() {
stop $app_jar
start $app_jar
}

# 判断指定的进程存不存在
is_exist() {
pid=`ps -ef | grep $1|grep -v grep | awk '{print $2}'`
if [ -z "${pid}" ]; then
return 1
else
return 0
fi
}

# 根据用户输入参数选择执行的函数
case "$1" in
"start")
start $2
;;
"stop")
stop
;;
"status")
status
;;
"restart")
restart
;;
*)
usage
;;
esac

# End
# =========================================================================

这里我区分了预览和正式环境,因为有时候应用启动之前就因为某些配置错误无法启动,此时如果使用正式环境的话,日志会按照我们上述的配置被丢掉,从而无法配查问题。可以通过启动的时候指定一个 prev 参数将启动过程中的日志重定向到当前的目录下的指定文件中。这里主要有几个功能吧:启动、重启、判断是否启动以及指令说明。

4.4 配置域名

按照上面的配置我们已经可以可以使用 ip 地址进行访问了,但是我们还是要配置一下域名。这里我使用的是之前的一个子域名。当然,除了配置域名之外还要配置一下 CDN,配置了 CDN 之后的访问逻辑如下:

1
box.meiyan.tech -> CDN 域名 -> CDN 域名指定的 ip 地址或者 ip 地址加端口号

配置 CDN 比直接使用域名映射到 ip 地址的方式还有一个好处,就是在 ping 指定的域名的时候不会直接暴露 ip 地址,这样可以阻挡一步分流量攻击。

总结

这里介绍了搭建服务器和服务器安全配置相关的知识,也涉及到了一些 linux 指令,因篇幅的原因,上面仅仅介绍了一部分,更无法详细进行介绍。此外,我写了完整的十几篇文章,从基础的 linux 指令到各种常用的中间件安全配置等详细的知识,感兴趣的话关注下吧。这里提到的 移动工具箱 是我最近开发的一款 Android 工具软件,非常实用,其中包含了很多对开发者非常实用的功能,感兴趣可以下载尝试。


-----本文结束 感谢阅读---------

欢迎关注我的其它发布渠道