前提

当我们在生产环境部署了SpringBoot应用的时候,虽然可以通过Jenkins的构建状态和Linuxps命令去感知应用是否在新的一次发布中部署和启动成功,但是这种监控手段是运维层面的。那么,可以提供一种手段能够在应用层面感知服务在新的一次发布中的构建部署和启动是否成功吗?这个问题笔者花了一点时间想通了这个问题,通过这篇文章提供一个简单的实现思路。

基本思路

其实基本思路很简单,一般SpringBoot应用会使用Maven插件打包(笔者不熟悉Gradle,所以暂时不对Gradle做分析),所以可以这样考虑:

  1. Maven插件打包的时候,把构建时间pom文件中的版本号都写到jar包的描述文件中,正确来说就是MANIFEST.MF文件中。
  2. 引入spring-boot-starter-actuator,通过/actuator/info端点去暴露应用的信息(最好控制网络访问权限为只允许内网访问)。
  3. 把第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-ArchiverAvailable VariablesSpringBoot的配置文件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一些实用技巧实现了应用层面监控应用是否正常打包部署更新和启动成功的问题。

原文链接

(本文完 e-a-20191209:1:39 c-1-d)

你的SpringBoot应用真的部署更新成功了吗的更多相关文章

  1. Springboot静态文件不更新的解决办法,以及Springboot实现热部署

    Springboot静态文件不更新的解决办法,以及Springboot实现热部署 原文链接:https://www.cnblogs.com/blog5277/p/9271882.html 原文作者:博 ...

  2. springboot的热部署

    SpringBoot 4.SpringBoot 整合 devtools 实现热部署   一.添加 devtools 依赖 <!-- Spring boot 热部署 : 此热部署会遇到 java. ...

  3. SpringBoot工程+热部署进行远程调试

    本文转载自:https://blog.csdn.net/qq_31868349/article/details/78553901 SpringBoot工程+热部署进行远程调试 本地端添加配置 在pom ...

  4. spring-boot项目热部署以及spring-devtools导致同类不能转换

    <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring- ...

  5. 从零开始学习springboot之热部署的配置

    各位看官大家好,博主之前因为毕业设计以及毕业旅游耽搁了好长一段时间没有更新博客了,从今天起又会慢慢开始学习啦. 今天主要是来学习springboot热部署的配置. 一. 热部署 我们通常在修改某些文件 ...

  6. Springboot War包部署下nacos无法注册问题

    目录 1. @EnableDiscoveryClient的使用 2. EnableDiscoveryClientImportSelector类的作用 3.AutoServiceRegistration ...

  7. 开发阶段,将SpringBoot应用快速部署到K8S

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  8. IDEA中Springboot启动热部署

    在IDEA中开发springboot项目时,每次修改代码后都需要手动重启项目比较麻烦,可以通过添加一定的配置使每次修改代码后项目进行自动重启 在IDEA中开发springboot项目时,每次修改代码后 ...

  9. 利用WSUS部署更新程序

    WSUS概述 为了让用户的windows系统与其他microsoft产品能够更安全,更稳定,因此microsoft会不定期在网站上推出最新的更新程序供用户下载与安装,而用户可以通过以下方式来取得这些程 ...

随机推荐

  1. Codeforces 1178G. The Awesomest Vertex

    传送门 首先通过dfs序把子树操作转化为区间操作,求最大值可以用斜率优化. 然后分个块,对每个块维护个凸包.修改时中间的打个标记,边角暴力重构:询问时中间的用斜率优化的方法求,边角的暴力求. 由于此题 ...

  2. NOIP模拟测试7

    期望得分:60+60+60 实际得分:60+60+0 这次考试主要是T3搜索打挂了(我可是靠搜索吃饭的); 1.数组开小了,不过开大数组只拿到了10分的好成绩. 2.题意没审清(其实是他没说清). 以 ...

  3. m113

    今天的比赛很有感触,所以来写一下题解: T1可以发现一些规律是:面积扩大的速度显然比周长扩大的速度快,然后就可以枚举周长来看能为成的面积,其实最优的情况一定是六边型的情况,通过手膜我们可以发现对于边长 ...

  4. Spring Boot 配置 - Consul 配置中心

    ▶ Spring Boot 依赖与配置 Maven 依赖 <dependencyManagement> <dependencies> <dependency> &l ...

  5. [ PyQt入门教程 ] PyQt5中数据表格控件QTableWidget使用方法

    如果你想让你开发的PyQt5工具展示的数据显得整齐.美观.好看,显得符合你的气质,可以考虑使用QTableWidget控件.之前一直使用的是textBrowser文本框控件,数据展示还是不太美观.其中 ...

  6. 反射与泛型--使用泛型反射API打印出给定类的所有内容

    package chapter8Demos; import java.lang.reflect.*; import java.util.Arrays; import java.util.Scanner ...

  7. Java面向对象程序设计第15章5

    5. 利用URLConnetction对象编写程序返回某网站的首页,并将首页的内容存放到文件当中. import java.net.*; import java.io.*; public class ...

  8. Spring简单的示例

    参考资料:https://how2j.cn/k/spring/spring-ioc-di/87.html.https://www.w3cschool.cn/wkspring/dgte1ica.html ...

  9. SpringBoot系列之i18n集成教程

    目录 1.环境搭建 2.resource bundle资源配置 3.LocaleResolver类 4.I18n配置类 5.Thymeleaf集成 SpringBoot系统之i18n国际化语言集成教程 ...

  10. rhel-6.3-i386安装samba

    1.安装samba 1).安装软件 rpm –ivh /mnt/Packages/samba-3.5.10-125.el6.i686.rpm 2).创建用户 useradd myadmin 3).设置 ...