前情提要
众所周知,Springboot
内置了Tomcat
,默认打jar
包,部署非常方便
bash
$ java -jar test.jar
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v3.2.1)
好,舒服了,下班!
别急
在你关上电脑的那一瞬间,终端与服务器断开连接,Springboot
后端直接BOMB
好的,你可以加班了
Why
SSH终端中开启的进程默认是终端的子进程,终端关闭后,子进程也会一并停止(收到SIGHUP
信号)
肿么办
bash
nohup java -jar test.jar &
问题不大,只要加上nohup
和&
即可
nohup
:No Hang UP,意为忽略挂断信号(SIGHUP
)。即使SSH会话结束,这个进程仍会继续运行&
:将命令放入后台执行。当你在命令的末尾加上&
,这个命令会立即返回,而不会阻塞当前的终端
完美,酱紫Springboot
就可以在后台安稳运行了
下班!
等一下
没五分钟,前端坐不住了:
诶,你这个怎么
500 Internal Server Error
你又回到了工位上
打开电脑,连上服务器,面对空白的终端,心想:
诶,我日志呢
尬住了
不过好在,ls
一下,发现test.jar
同目录多了一个nohup.out
文件,还好里面就是日志,呼呼呼
自定义日志路径
不过,有没有办法自定义一下日志文件名和目录呢
bash
nohup java -jar test.jar > test.log 2>&1 &
可以使用>
符号,来重定向标准输出
> test.log
:重定向标准输出(标识为1)到test.log
文件2>&1
:将标准错误(2)重定向到标准输出(&1)(取地址符!,C++
人狂喜)
好的,酱紫,标准输出,和错误输出,都到test.log
文件了,完美
太Perfect了,我就是命令行的神,下班!
坐下
前端说的500 Internal Server Error
还没解决呢,下啥班
哦哦,那行吧
看了眼日志,在IDEA
里吭哧吭哧一顿改,重新打包,拷贝到服务器上
重启
那么问题来了,如何重启Springboot
这个简单
bash
ps -aux | grep java
然后再根据进程ID,执行kill
完美,接下来只要再依葫芦画瓢进行java -jar
就好啦hhhhhhh
哦 又忘了 nohup
靠
配置文件
话说,Springboot
默认在工作目录下寻找.yml | .properties
配置文件,而不是.jar
同目录下哦
配置文件加载优先级:
- 当前工作目录 (从中启动应用的目录)下的
/config
子目录。 - 当前工作目录。
- classpath 下的
/config
包。 - classpath的根路径。
酱紫最好手动指定一下绝对路径哦
bash
nohup java -jar test.jar --spring.config.location=file:/www/Springboot/TestDir/ > test.log 2>&1 &
好的,怎么好像手敲的压力开始增大了 /汗
(小声:还有虚拟机参数 -Xmx1024M -Xms256M
巴拉巴拉的)
(小声:还有开机自启动巴拉的)
Systemd 服务进程
不会吧,不会吧,不会还有人命令行裸敲java -jar
,美其名曰部署吧
Systemd
systemd 是 Linux 上的一种系统和服务管理器,用于管理系统上运行的进程和服务。systemd 服务是一种可以由 systemd 管理的后台进程或服务
我们可以将Springboot
程序制作为systemd
服务,就可以方便地启动、停止、重启以及状态检测
How
首先,我们需要将Springboot
程序注册为Systemd服务
很简单,只要在/etc/systemd/system/
目录下,创建myapp.service
文件即可(服务单元文件,文件名即服务名)
ini
[Unit]
Description=My Spring Boot Application
# 服务描述:这里描述了服务的功能或用途
After=syslog.target network.target
# 依赖性和顺序:指定了该服务在哪些服务之后启动。这里指定了在syslog和网络服务(network.target)之后启动。
[Service]
User=somebody
# 运行服务的用户
WorkingDirectory=/opt/Test/
# 工作目录
ExecStart=java -jar Test-0.0.1-SNAPSHOT.jar --spring.profiles.active=prod
# 启动命令:指定启动服务时执行的命令
SuccessExitStatus=143
# 成功退出状态码:当服务以这些状态码退出时,将被视为正常退出。143是Spring Boot应用在接收到SIGTERM信号时的标准退出代码
[Install]
WantedBy=multi-user.target
# 安装设置:指定当使用 'systemctl enable' 命令启用服务时,该服务应该被哪个目标所引用
# 这意味着,当系统达到multi-user.target时,你的服务会自动启动
# multi-user.target 适用于多用户的运行级别,这意味着服务将在系统达到多用户模式时启动
有几点需要注意
- 将
Springboot
配置为系统服务后,不需要再nohup
,默认开启新后台进程 - 我们指定了工作目录,会自动寻找配置文件,不需要再指定
- 关于工作目录的选址,最好是放在
/opt/
下,而不是用户的home
目录,这样方便之后Nginx
访问静态资源,而且这个目录是专门用于第三方应用的 - 关于User,随便指定一个普通用户就行了(最好不要root)
- 一般只需要更改
WorkingDirectory
&ExecStart
,其他的无伤大雅
完成之后,我们需要通知systemd
服务重载配置文件
bash
sudo systemctl daemon-reload
然后启动myapp
服务
bash
sudo systemctl start myapp
服务默认是没有开机自启动的,不过也可以方便地开启或关闭
bash
sudo systemctl enable myapp
sudo systemctl disable myapp
停止 & 重启也是信手拈来
bash
sudo systemctl stop myapp
sudo systemctl restart myapp
还可以使用status
命令查看运行状态
bash
sudo systemctl status myapp.service
● myapp.service - My Spring Boot Application
Loaded: loaded (/etc/systemd/system/myapp.service; disabled; vendor preset: enabled)
Active: active (running) since Sat 2024-01-06 20:45:56 CST; 5 days ago
Main PID: 109708 (java)
Tasks: 37 (limit: 4150)
Memory: 210.4M
CPU: 7min 33.637s
CGroup: /system.slice/myapp.service
└─109708 java -jar Test-0.0.1-SNAPSHOT.jar --spring.profiles.active=prod
Jan 06 20:45:58 ubuntu-HK java[109708]: . ____ _ __ _ _
Jan 06 20:45:58 ubuntu-HK java[109708]: /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
Jan 06 20:45:58 ubuntu-HK java[109708]: ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
Jan 06 20:45:58 ubuntu-HK java[109708]: \\/ ___)| |_)| | | | | || (_| | ) ) ) )
Jan 06 20:45:58 ubuntu-HK java[109708]: ' |____| .__|_| |_|_| |_\__, | / / / /
Jan 06 20:45:58 ubuntu-HK java[109708]: =========|_|==============|___/=/_/_/_/
Jan 06 20:45:58 ubuntu-HK java[109708]: :: Spring Boot :: (v3.0.1)
我们可以看到.service
文件的位置,是否开机自启,以及当前的状态(active (running))
同时,我们还能看到程序的部分日志
日志
对了,应用程序变成一个系统服务之后,日志是如何管理的呢
Default
默认情况下,应用的所有标准输出(stdout)和标准错误(stderr)日志都会被systemd
捕获
systemd
使用journald
服务来管理这些日志,可以通过journalctl
命令来查看和管理这些日志
bash
sudo journalctl -u myapp
看似很方便,但是失去了对日志的控制权
如何分割日志,如何清理日志,都变得不太可控
LogBack
Springboot
默认继承了Logback
日志框架(实现了Slf4j
日志门面)
所以只需要修改一下配置文件,即可输出日志到文件
properties
logging.file.name=myapp.log
logging.file.path=/var/log
好的,现在日志已经以文件的形式保存下来了
但是为什么journalctl -u myapp
还是可以看到日志呢?
这是因为日志同时输出到了控制台(标准输出、错误输出) & 文件
所以日志输出到了两个目的地,控制台的输出被journalctl
捕获,这样势必会造成空间浪费
所以我们需要关闭控制台输出,并且为了更精细地控制日志策略(滚动分割等),我们最好还是自己写Logback
的配置文件
Logback配置文件
我们可以在工程的Resources
文件夹下新建logback-spring.xml
文件
其实也可以是logback.xml
,但是这么写是不能读取Springboot
的配置文件的(听说有些版本不能)
xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!--从 yml|properties 配置文件中读取键值 以方便修改日志路径-->
<springProperty scope="context" name="LOG_HOME" source="log.path" defaultValue="logs-default"/>
<!--格式化输出:%d表示日期,%thread表示线程名,%p:级别 %msg:日志消息,%n是换行符-->
<property name="PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} %p [%t] - %logger{50} : %m%n"/>
<property name="PATTERN_COLOR" value="%d{yyyy-MM-dd HH:mm:ss.SSS} %highlight(%p) [%t] - %cyan(%logger{50}) : %m%n"/>
<!--高亮会输出多余字符,所以在FILE中不开启-->
<!--配置控制台输出-->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${PATTERN_COLOR}</pattern>
</encoder>
</appender>
<!--文件日志, 按照每天生成日志文件,优先级高于yml文件中的logging.file.path-->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--日志文件输出的文件名-->
<FileNamePattern>${LOG_HOME}/%d{yyyy-MM-dd}.log</FileNamePattern>
<!--日志文件保留天数-->
<MaxHistory>7</MaxHistory>
</rollingPolicy>
<encoder>
<charset>UTF-8</charset>
<pattern>${PATTERN}</pattern>
</encoder>
</appender>
<!--ALL < TRACE < DEBUG < INFO < WARN < ERROR < OFF-->
<springProfile name="dev"><!-- dev环境不输出到文件 -->
<root level="info">
<appender-ref ref="STDOUT"/>
</root>
</springProfile>
<springProfile name="prod"><!-- prod环境仅输出到文件 -->
<root level="info">
<appender-ref ref="FILE"/>
</root>
</springProfile>
</configuration>
我们可以在.properties
配置文件里指定日志文件路径(自定义键)
properties
log.path=/logs
为了避免和内置的logging.file.path
冲突,我们这里采用了自定义键
小贴士:其实我们在logback-spring.xml
中自定义了输出的文件位置之后,logging.file.path
会被覆盖,用户的xml
配置文件优先级更高
但是为了语义更清晰,还是采用自定义键吧(ba)
Logback多环境配置
注意,我们采用了<springProfile name="dev">
这样标签
ta可以根据Springboot
当前激活的配置文件(环境),来动态得采取不同的日志策略,如:
- 在
dev
开发环境中,仅输出日志到控制台 - 在
prod
生产环境中,仅输出日志到文件(防止输出到控制台被journalctl
捕获,产生两份日志)
Springboot多环境配置
刚刚提到了根据Springboot
当前环境来选择日志策略
那么Springboot
环境配置是什么意思呢
主要原因是我们在开发环境和生产环境中对于同一个配置属性可能需要不同的值
例如:数据库密码、日志存放路径、静态资源服务器地址等等
所以最方便的方式是写两套配置文件,然后再不同场景进行切换,如:
- application.yml
- application-dev.yml
- application-prod.yml
注:命名规则:application-{profile}.yml
注:同名属性优先级:具体配置文件 > 通用配置文件(application.yml) 注:其实你想把不同环境配置文件写在一个文件里也可以,用---
分割,但是别吧,大哥
诶等等,怎么有三个配置文件
你傻啊,通用的配置放
application.yml
就好了难道你要写两遍啊
好的,那要怎么切换配置呢
默认情况下,也就是default
环境,只启用application.yml
or properties(这俩一样的,不多赘述)
有三种方式可以切换
- 在
application.properties
中写上:spring.profiles.active=dev
or prod等等 - 在
java -jar
的时候传入命令行参数--spring.profiles.active=prod
来激活生产环境配置 - 在
IDEA
中配置:编辑运行/调试配置->活动/有效配置(可以找找,这个简单,主要不想放图了)
怎么知道Springboot
现在是什么环境呢?在程序启动的时候会输出
bash
2024-01-12 19:41:19.130 INFO [main] - com.mrbean.test.TestApplication : The following 1 profile is active: "dev"
好的,酱紫就可以完美切换配置文件了,要是还有什么需要修改的,只要在生成环境新建一个application-prod.yml
,就可以覆盖对应的属性了,这个大家应该很懂的,我就不说了
配置文件协作最佳实践
(这文就写不完了是吧!)
我们刚刚说了,有三个配置文件,但是一旦进入Git
多人协作,就会直接爆炸
因为每个人的本地环境是不一样的,所以application-dev.yml
势必会被改来改去,然后还上传Git
然后pull
的时候就会直接覆盖你的开发配置,就会想要 * 队友了
辣怎么办
要么就是不上传application-dev.yml
那不成啊,那想新增/修改配置,其他成员完全阿巴怎么办
其实我们可以建立application-dev-template.yml
文件,作为模板,非必要不修改这个文件
然后每个成员自己根据模板新建application-dev.yml
,且加入.gitignore
不上传Git
Peace
完美,这下总可以下班了吧
Ref
Springboot 指定运行时配置文件的几种方式_dspring.config.location-CSDN博客
关于 springboot 服务制作成 systemd 服务 - 掘金 (juejin.cn)
Spring boot使用logback实现多环境配置 - 知乎 (zhihu.com)
31 SpringBoot多环境的切换(生产环境、开发环境、测试环境)_spring boot生产和开发环境-CSDN博客