前提

当我们在生产环境部署了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. 爬虫之selenium爬取京东商品信息

    import json import time from selenium import webdriver """ 发送请求 1.1生成driver对象 2.1窗口最大 ...

  2. CSPS模拟 78

    大敛好稳啊..居然在模拟赛拿了540.. 有点畏惧.jpg 而我就是什么什么不行级人物了.. 真正在联赛拉开那么多分怎么追啊.. T1kmp?hash? T2 概率小到炸精时,对答案也就没贡献了 然后 ...

  3. 卖饲料——单调队列优化dp

    题目描述 约翰开车来到镇上,他要带K吨饲料回家.运送饲料是需要花钱的,如果他的车上有X吨饲料,每公里就要花费X^2元,开车D公里就需要D* X^2元.约翰可以从N家商店购买饲料,所有商店都在一个坐标轴 ...

  4. UiPath之如何打印PDF

    各位小伙伴,大家好,今天写一点基础知识,如何在UiPath中打印PDF. ---小U的QQ群(714733686):小U的订阅号[UiPath8888]--- 当然,我们最希望的就是有一个Activi ...

  5. php nginx反向代理获取真实ip的教程

    php nginx反向代理获取真实ip的教程 <pre> location /getip { proxy_pass http://newmiracle.cn/ip.php; } proxy ...

  6. Jsp自学2

    Jsp简单来说就是java代码与Html代码的组合,类,方法,属性跟网页展示夹杂在一起.Jsp就是Servlet,但比Servle简单,不需要配置web.xml(当然也可以配置).Jsp由模板数据与元 ...

  7. SqlServer2005 查询 第五讲 top

    今天我们来说sql命令中得参数top top top[ 最前面若干个记录,专属于SqlServer2005的语法,不可移植到其他库.oracle中是用rownum<6来实现输出前5行记录.] 下 ...

  8. tornado的使用-数据库篇

    tornado的使用-数据库篇

  9. VLAN实验(1)Access接口

    1.选择两台S3700的交换机,5台PC机,并按照下图链接好并填写IP,完成此拓扑图 2.由于现在我们还没有划分VLAN,这5台PC,还在同一个VLAN中,现在我们启动所有的设备,这是所有的主机应该是 ...

  10. node后台初始配置(2)

    一.node-app结构 创建成功node-app项目后,会自动生成一些文件一般初始的结构如下图 在bin文件夹里面只有一个文件www      var port = normalizePort(pr ...