你的SpringBoot应用真的部署更新成功了吗
前提
当我们在生产环境部署了SpringBoot应用的时候,虽然可以通过Jenkins的构建状态和Linux的ps命令去感知应用是否在新的一次发布中部署和启动成功,但是这种监控手段是运维层面的。那么,可以提供一种手段能够在应用层面感知服务在新的一次发布中的构建部署和启动是否成功吗?这个问题笔者花了一点时间想通了这个问题,通过这篇文章提供一个简单的实现思路。
基本思路
其实基本思路很简单,一般SpringBoot应用会使用Maven插件打包(笔者不熟悉Gradle,所以暂时不对Gradle做分析),所以可以这样考虑:
Maven插件打包的时候,把构建时间和pom文件中的版本号都写到jar包的描述文件中,正确来说就是MANIFEST.MF文件中。- 引入
spring-boot-starter-actuator,通过/actuator/info端点去暴露应用的信息(最好控制网络访问权限为只允许内网访问)。 - 把第1步中打包到
jar包中的MANIFEST.MF文件的内容读取并且加载到SpringBoot环境属性中的info.*属性中,以便可以通过/actuator/info访问。
思路定好了,那么下面开始实施编码。
编码实现
最近刚好在调研蚂蚁金服的SofaStack体系,这里引入SofaBoot编写示例。pom文件如下:
<?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>club.throwable</groupId>
<artifactId>sofa-boot-sample</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>sofa-boot-sample</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<sofa.boot.version>3.2.0</sofa.boot.version>
<spring.boot.version>2.1.0.RELEASE</spring.boot.version>
<maven.build.timestamp.format>yyyy-MM-dd HH:mm:ss.SSS</maven.build.timestamp.format>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alipay.sofa</groupId>
<artifactId>sofaboot-dependencies</artifactId>
<version>${sofa.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.alipay.sofa</groupId>
<artifactId>healthcheck-sofa-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<finalName>sofa-boot-sample</finalName>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
<encoding>${project.build.sourceEncoding}</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring.boot.version}</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.2.0</version>
<configuration>
<archive>
<manifest>
<addBuildEnvironmentEntries>true</addBuildEnvironmentEntries>
</manifest>
<manifestEntries>
<Application-Name>${project.groupId}:${project.artifactId}:${project.version}</Application-Name>
<Build-Timestamp>${maven.build.timestamp}</Build-Timestamp>
</manifestEntries>
</archive>
</configuration>
</plugin>
</plugins>
</build>
</project>
pom文件中一些属性和占位符的设置,可以参考一下这两个链接:Maven-Archiver和Available Variables。SpringBoot的配置文件application.yaml如下:
server:
port: 9091
management:
server:
port: 10091
endpoints:
enabled-by-default: false
web:
exposure:
include: info
endpoint:
info:
enabled: true
spring:
application:
name: sofa-boot-sample
这里要注意一点:SpringBoot应用通过其Maven插件打出来的jar包解压后的目录如下:
sofa-boot-sample.jar
- META-INF
- MANIFEST.MF
- maven ...
- org
- springframework
- boot ...
- BOOT-INF
- classes ...
- lib ...
了解此解压目录是我们编写MANIFEST.MF文件的解析实现过程的前提。编写MANIFEST.MF文件的解析类:
@SuppressWarnings("ConstantConditions")
public enum ManiFestFileExtractUtils {
/**
* 单例
*/
X;
private static Map<String, String> RESULT = new HashMap<>(16);
private static final Logger LOGGER = LoggerFactory.getLogger(ManiFestFileExtractUtils.class);
static {
String jarFilePath = ClassUtils.getDefaultClassLoader().getResource("").getPath().replace("!/BOOT-INF/classes!/", "");
if (jarFilePath.startsWith("file")) {
jarFilePath = jarFilePath.substring(5);
}
LOGGER.info("读取的Jar路径为:{}", jarFilePath);
try (JarFile jarFile = new JarFile(jarFilePath)) {
JarEntry entry = jarFile.getJarEntry("META-INF/MANIFEST.MF");
if (null != entry) {
BufferedReader reader = new BufferedReader(new InputStreamReader(jarFile.getInputStream(entry), StandardCharsets.UTF_8));
String line;
while (null != (line = reader.readLine())) {
LOGGER.info("读取到行:{}", line);
int i = line.indexOf(":");
if (i > -1) {
String key = line.substring(0, i).trim();
String value = line.substring(i + 1).trim();
RESULT.put(key, value);
}
}
}
} catch (Exception e) {
LOGGER.warn("解析MANIFEST.MF文件异常", e);
}
}
public Map<String, String> extract() {
return RESULT;
}
}
可以通过一个CommandLineRunner的实现把MANIFEST.MF文件的内容写到Environment实例中:
@Component
public class SofaBootSampleRunner implements CommandLineRunner {
@Autowired
private ConfigurableEnvironment configurableEnvironment;
@Override
public void run(String... args) throws Exception {
MutablePropertySources propertySources = configurableEnvironment.getPropertySources();
Map<String, String> result = ManiFestFileExtractUtils.X.extract();
Properties properties = new Properties();
for (Map.Entry<String, String> entry : result.entrySet()) {
String key = "info." + entry.getKey();
properties.setProperty(key, entry.getValue());
}
if (!properties.isEmpty()) {
propertySources.addFirst(new PropertiesPropertySource("infoProperties", properties));
}
}
}
启动类如下:
@SpringBootApplication
public class SofaBootSampleApplication {
public static void main(String[] args) {
SpringApplication.run(SofaBootSampleApplication.class, args);
}
}
最终效果
在项目的根目录使用命令mvn package,打出jar包后直接启动:
cd Jar包的目录
java -jar sofa-boot-sample.jar
调用http://localhost:10091/actuator/info接口输出如下:
{
"Spring-Boot-Version": "2.1.0.RELEASE",
"Start-Class": "club.throwable.sofa.SofaBootSampleApplication",
"Main-Class": "org.springframework.boot.loader.JarLauncher",
"Manifest-Version": "1.0",
"Build-Jdk-Spec": "1.8",
"Spring-Boot-Classes": "BOOT-INF/classes/",
"Created-By": "Maven Jar Plugin 3.2.0",
"Build-Timestamp": "2019-12-08 17:41:21.844",
"Spring-Boot-Lib": "BOOT-INF/lib/",
"Application-Name": "club.throwable:sofa-boot-sample:1.0-SNAPSHOT"
}
改变pom文件中的版本标签<version>为1.0.0,再次打包并且启动成功后调用http://localhost:10091/actuator/info接口输出如下:
{
"Spring-Boot-Version": "2.1.0.RELEASE",
"Start-Class": "club.throwable.sofa.SofaBootSampleApplication",
"Main-Class": "org.springframework.boot.loader.JarLauncher",
"Manifest-Version": "1.0",
"Build-Jdk-Spec": "1.8",
"Spring-Boot-Classes": "BOOT-INF/classes/",
"Created-By": "Maven Jar Plugin 3.2.0",
"Build-Timestamp": "2019-12-08 17:42:07.273",
"Spring-Boot-Lib": "BOOT-INF/lib/",
"Application-Name": "club.throwable:sofa-boot-sample:1.0.0"
}
可见构建时间戳Build-Timestamp和服务名Application-Name都发生了变化,达到了监控服务是否正常部署和启动的目的。如果有多个服务节点,可以添加一个ip属性加以区分。
小结
这篇文章通过SpringBoot一些实用技巧实现了应用层面监控应用是否正常打包部署更新和启动成功的问题。
原文链接
- Github Page:http://throwable.club/2019/12/09/spring-boot-server-deploy-monitor
- Coding Page:http://throwable.coding.me/2019/12/09/spring-boot-server-deploy-monitor
(本文完 e-a-20191209:1:39 c-1-d)
你的SpringBoot应用真的部署更新成功了吗的更多相关文章
- Springboot静态文件不更新的解决办法,以及Springboot实现热部署
Springboot静态文件不更新的解决办法,以及Springboot实现热部署 原文链接:https://www.cnblogs.com/blog5277/p/9271882.html 原文作者:博 ...
- springboot的热部署
SpringBoot 4.SpringBoot 整合 devtools 实现热部署 一.添加 devtools 依赖 <!-- Spring boot 热部署 : 此热部署会遇到 java. ...
- SpringBoot工程+热部署进行远程调试
本文转载自:https://blog.csdn.net/qq_31868349/article/details/78553901 SpringBoot工程+热部署进行远程调试 本地端添加配置 在pom ...
- spring-boot项目热部署以及spring-devtools导致同类不能转换
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring- ...
- 从零开始学习springboot之热部署的配置
各位看官大家好,博主之前因为毕业设计以及毕业旅游耽搁了好长一段时间没有更新博客了,从今天起又会慢慢开始学习啦. 今天主要是来学习springboot热部署的配置. 一. 热部署 我们通常在修改某些文件 ...
- Springboot War包部署下nacos无法注册问题
目录 1. @EnableDiscoveryClient的使用 2. EnableDiscoveryClientImportSelector类的作用 3.AutoServiceRegistration ...
- 开发阶段,将SpringBoot应用快速部署到K8S
欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...
- IDEA中Springboot启动热部署
在IDEA中开发springboot项目时,每次修改代码后都需要手动重启项目比较麻烦,可以通过添加一定的配置使每次修改代码后项目进行自动重启 在IDEA中开发springboot项目时,每次修改代码后 ...
- 利用WSUS部署更新程序
WSUS概述 为了让用户的windows系统与其他microsoft产品能够更安全,更稳定,因此microsoft会不定期在网站上推出最新的更新程序供用户下载与安装,而用户可以通过以下方式来取得这些程 ...
随机推荐
- C++ 11新标准实现POJ No.1002-487-3279
487-3279(重复的电话号码查询)(标签:优先队列,哈希表) 题目描述 企业喜欢用容易被记住的电话号码.让电话号码容易被记住的一个办法是将它写成一个容易记住的单词或者短语.例如,你需要给滑铁卢大学 ...
- 19.7.29 NOIP模拟10
话说这次三道考试题直接可以连成一个段子:我一个辣鸡,连模板都不会打,只能跪倒在大佬面前; T1 辣鸡 但是我实在是太辣鸡了,最后干的T1,时间不够用,连暴力都没打对,无奈之下交了一个qj程序,60分( ...
- Scss的使用场景
一.Scss 1.CSS有几个缺点 语法不够强大,没有变量和合理的样式复用机制 使得逻辑上相关的属性值必须以字面的形式重复输出,难以维护 动态的样式语言为css富裕了动态语言的特性 极大的提高了样式语 ...
- python函数的基本语法<一>
函数: 一次定义,多次调用,函数可以变相看成变量函数的阶段: 1.定义阶段 2调用阶段 形参和实参: 定义阶段的参数叫形参,调用阶段的参数叫实参 函数的几种基本用法: #多变量 def test(na ...
- 支付宝小程序和微信小程序的区别(部分)
支付宝小程序和微信小程序之间的互相转换 1.首先是文件名 微信小程序 wxss ------ 支付宝小程序 acss 微信小程序 wxml ------ 支付宝小程序 axml 2.调用方法前缀 微信 ...
- Resource Path Location Type Target runtime Apache Tomcat v6.0 is not defined(项目报错)已解决
我换了开发工具后,导入的项目不是这里报错就是那里不错.不过,我喜欢.在tomcat里面部署项目后,定位到报错行时,总是提示我这句话:Description Resource Path Location ...
- js正则匹配的出链接地址
content为需要匹配的值 var b=/<a([\s]+|[\s]+[^<>]+[\s]+)href=(\"([^<>"\']*)\"| ...
- Android Debug 之 Log 最佳实践
本文微信公众号「AndroidTraveler」首发. 背景 在开发过程中,调试是必不可少的一项工作. 当我们要确定项目的逻辑时,当我们要了解界面的生命周期时,当我们发现新写的逻辑与期望效果不一致时, ...
- 字体图标转base64
如果你在阿里矢量库下载了字体图标在项目引入无法显示时,可以把图标转成base64 在线转换的链接 https://transfonter.org/ css字体图标的制作
- C++中对封装的语法支持——重载运算符
重载运算符 1.对于自定义类型,编译器不知道运算规则,而重载运算符会将两个对象相加转换为函数调用. 2.运算符重载转换的函数调用,函数名字是固定的规则. (1) 如果重载+号运算符,函数名字就是:op ...