Spring Boot是什么
在Spring
基础上再次进行封装,更加简化企业级应用开发。打破Spring
的整合,把各种框架的整合进行了封装,封装到starter
类库中。
- 更快的入门
- 开箱即用:把与具体业务相关的框架集成到一个
jar
包(starter
)里面,starter
可以涵盖相关业务开发的所有功能 - Spring血统:Spring具有的优点,Spring Boot统统都有;Spring不具备的优点,Spring Boot作为其补充
- 零配置:最大化地减少配置,甚至做到零配置
- 终极目标:简化开发,抛弃了传统Java EE项目繁琐的配置、学习过程,让企业级应用开发过程更简单
Spring Boot
除了可以很好融入Docker
之外,其自身就支持嵌入式的Tomcat
、Jetty
等容器。所以,通过Spring Boot
构建的应用不再需要安装Tomcat
,将应用打包成war
,再部署到Tomcat
这样复杂的构建与部署动作,只需将Spring Boot
应用打成jar
包,并通过java -jar
命令直接运行就能启动一个标准化的Web
应用,这使得Spring Boot
应用变得非常轻便。
我们完全可以通过使用Gradle
和Groovy
来开发Spring Boot
应用。
Spring Boot与其他框架的关系
-
Spring Boot
底层基于Spring
框架 -
Spring Boot
开发web
应用时,可以选择性地采用Spring MVC
,Spring MVC
也是基于Spring
的 -
Spring Cloud
是一套分布式的微服务解决方案,里面涵盖的组件(模块)都基于Spring Boot
来构建。Spring Cloud
是由很多Spring Boot
应用组成的
搭建开发环境
JDK8+
Gradle 4+
-
Eclipse 4.7+
(也可选用别的IDE)
Gradle
Gradle官方下载链接:https://gradle.org/releases/
选择想要安装的发布版本,gradle-x.x-bin.zip
是需要下载的安装发布版,gradle-x.x-src.zip
是源码,gradle-x.x-all.zip
则是下载全部的文件。
例如按照下图选择将会下载gradle-4.10.2-bin.zip
:
将下载的压缩包在本机的目录下直接解压即可,解压后的目录如下图所示:
接下来为Gradle
配置环境变量:在系统变量中新增一个GRADLE_HOME
变量,值为解压后的路径:
接下来还需要修改Path
变量,将Gradle
的bin
目录添加进去。在Path
变量的最后面添加%GRADLE_HOME%\bin
:
配置完成后查看一下本机安装的gradle
的版本信息,使用快捷键Win
+R
弹出的输入框中输入cmd
,然后打开命令窗口,在命令窗口中输入gradle -v
可以查看到gradle
的版本信息,如下图所示即表示配置成功:
Eclipse
打开Eclipse,进入如下设置页面:
开发环境搭建完成。
Spring Initializr
Spring Initializr
是一个开源的项目,基于这个项目,可以采用配置的形式初始化一个应用。
通过https://start.spring.io可访问Spring Initializr
界面,如下图所示:
该界面可提供Spring Boot
应用的生成
会生成如下工程:
将工程解压,解压后的目录如下图所示:
-
.gitignore
:版本管理的忽略文件 -
build.gradle
:gradle
项目的配置文件(核心) -
gradlew
:在Linux
下的执行脚本(基本不会用到) -
gradlew.bat
:在Windows
下的执行脚本(基本不会用到)
//buildscript代码块中脚本优先执行
buildscript {
//ext用于定义动态属性
ext {
springBootVersion = '2.0.0.M4'
}
//使用了Maven的中央仓库及Spring自己的仓库(也可以指定其他仓库)
repositories {
//中央仓库,因为速度慢,在这里注释掉,改用其他国内镜像仓库
//mavenCentral()
//因为本项目使用的是2.0.0.M4版本,不是稳定版本,所以不能保证在镜像仓库中能够找到,所以需要保留Spring自己的仓库
maven{ url "https://repo.spring.io/snapshot" }
maven{ url "https://repo.spring.io/milestone" }
//阿里云镜像仓库
maven{ url "http://maven.aliyun.com/nexus/content/groups/public/" }
}
//依赖关系
dependencies {
//classpath声明了在执行其余的脚本时,ClassLoader可以使用这些依赖项
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
//使用插件
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
//指定了生成的编译文件的版本,默认是打成了jar包
group = 'com.study.spring.cloud'
version = '0.0.1-SNAPSHOT'
//指定编译.java文件的JDK版本
sourceCompatibility = 1.8
//使用了Maven的中央仓库及Spring自己的仓库(也可以指定其他仓库)
repositories {
//mavenCentral()
maven{ url "https://repo.spring.io/snapshot" }
maven{ url "https://repo.spring.io/milestone" }
maven{ url "http://maven.aliyun.com/nexus/content/groups/public/" }
}
//依赖关系
dependencies {
//全栈Web开发模块,包含嵌入式Tomcat、Spring MVC
//该依赖用于编译阶段
compile('org.springframework.boot:spring-boot-starter-web')
//通用测试模块,包含JUnit、Hamcrest、Mockito
//该依赖用于测试阶段
testCompile('org.springframework.boot:spring-boot-starter-test')
}
对项目进行编译:
显示BUILD SUCCESSFUL
即为编译成功
Hello World
build.gradle
- 控制器
HelloController
- 测试用例
HelloControllerTest
- 配置
Gradle Wrapper
(可选) - 运行应用
创建项目
复制前面的initializr-start
项目,将副本重命名为hello-world
:
将项目导入Eclipse
导入项目如下:
修改源码
重命名文件:
Application.java
一定要处于整个工程的根目录下,这样它才能根据配置去扫描子节点下的Spring
的Bean
。
修改build.gradle配置:
将
//指定了生成的编译文件的版本,默认是打成了jar包
group = 'com.study.spring.cloud'
version = '0.0.1-SNAPSHOT'
改为
//指定了生成的编译文件的版本,默认是打成了jar包
group = 'com.study.spring.cloud'
version = '1.0.0'
开发controller
新建一个controller
的包:
新建HelloController.java:
代码如下:
package com.study.spring.cloud.weather.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
//用于处理rest请求的controller
@RestController
public class HelloController {
@RequestMapping("/hello")
public String hello() {
return "Hello World!";
}
}
编写测试用例
新建一个controller
的包:
新建测试类:
代码如下:
package com.study.spring.cloud.weather.controller;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
//junit的规范
@RunWith(SpringRunner.class)
//Spring Boot的测试
@SpringBootTest
//MVC开发Mock测试
@AutoConfigureMockMvc
public class HelloControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
public void testHello() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/hello").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().string(equalTo("Hello World!")));
}
}
运行测试类:
配置文件
Spring Boot
的默认配置文件位置为src/main/resources/application.properties
。关于Spring Boot
应用的配置内容都可以集中在该文件中,根据我们引入的不同Starter
模块,可以在这里定义容器端口号、数据库连接信息、日志级别等各种配置信息。比如,我们需要自定义Web
模块的服务端口号,可以在application.properties
中添加server.port=8888
来指定服务端口为8888
,也可以通过spring.application.name=hello
来指定应用名(该名字在后续Spring Cloud
中会被注册为服务名)。
Spring Boot
的配置文件除了可以使用传统的properties
文件之外,还支持现在被广泛推荐使用的YAML
文件。YAML
是一个可读性高,用来表达资料序列的格式。由于YAML
使用空白符号和分行来分隔资料,使得它特别适合用grep
/Python
/Perl
/Ruby
操作。其让人最容易上手的特色是巧妙避开各种封闭符号,如引号、各种括号等,这些符号在巢状结构时会变得复杂而难以辨认。
YAML
采用的配置格式不像properties
的配置那样以单纯的键值对形式来表示,而是以类似大纲的缩进形式来表示,如下是一段YAML
配置信息:
environments:
dev:
url:http://dev.bar.com
name:Developer Setup
prod:
url:http://foo.bar.com
name:My Cool App
与其等价的properties
配置如下所示:
environments.dev.url=http://dev.bar.com
environments.dev.name=Developer Setup
environments.prod.url=http://foo.bar.com
environments.prod.name=My Cool App
可以看到YAML
配置信息利用阶梯化缩进的方式,其结构更为清晰易读,同时配置内容的字符量也得到显著减少。除此之外,YAML
还可以在一个单个文件中通过使用spring.profiles
属性来定义多个不同的环境配置。例如下面的内容,在指定为test
环境时,server.port
将使用8882
端口;而在prod
环境中,server.port
将使用8883
端口;如果没有指定环境,server.port
将使用8881
端口:
server:
port:8881
---
spring:
profiles:test
server:
port:8882
---
spring:
profiles:prod
server:
port:8883
YAML
目前还无法通过@PropertySource
注解来加载配置。但是,YAML
将属性加载到内存中保存的时候是有序的,所以当配置文件中的信息需要具备顺序含义时,YAML
的配置方式比起properties
配置文件更有优势。
自定义参数
除了可以在Spring Boot
的配置文件中设置各个Starter
模块中预定义的配置属性,也可以在配置文件中定义一些我们需要的自定义属性。比如在application.properties
中添加:
book.name=SpringCloudInAction
book.author=Xxx
然后,在应用中可以通过@Value
注解来加载这些自定义的参数,比如:
@Component
public class Book{
@Value("${book.name}")
private String name;
@Value("${book.author}")
private String author;
//省略getter和setter
}
@Value
注解加载属性值的时候可以支持两种表达式来进行配置:
- 一种是上面介绍的
PlaceHolder
方式,格式为${...}
,大括号内为PlaceHolder
- 另一种是使用
SpEL
表达式(Spring Expression Language
),格式为#{...}
,大括号内为SpEL
表达式
参数引用
在application.properties
中的各个参数之间可以直接通过使用PlaceHolder
的方式来进行引用,就像下面的设置:
book.name=SpringCloud
book.author=Xxx
book.desc=${book.author} is writing 《${book.name}》
book.desc
参数引用了上文中定义的book.name
和book.author
属性,最后该属性的值就是Xxx is writing 《SpringCloud》
使用随机数
在一些特殊情况下,我们希望有些参数每次被加载的时候不是一个固定的值,比如密钥、服务端口等。在Spring Boot
的属性配置文件中,可以通过使用${random}
配置来产生随机的int
值、long
值或者string
字符串,这样我们就可以容易地通过配置随机生成属性,而不是在程序中通过编码来实现这些逻辑。${random}
的配置方式主要有以下几种:
#随机字符串
com.didispace.blog.value=${random.value}
#随机int
com.didispace.blog.number=${random.int}
#随机long
com.didispace.blog.bignumber=${random.long}
#10以内的随机数
com.didispace.blog.test1=${random.int(10)}
#10~20的随机数
com.didispace.blog.test2=${random.int[10,20]}
该配置方式可以设置应用端口等场景,以避免在本地调试时出现端口冲突的麻烦。
命令行参数
在用命令行方式启动Spring Boot
应用时,连续的两个减号--
就是对application.properties
中的属性值进行赋值的标识。所以,java -jar xxx.jar --server.port=8888
命令,等价于在application.properties
中添加属性server.port=8888
。
多环境配置
对于多环境的配置,各种项目构建工具或是框架的基本思路是一致的,通过配置多份不同环境的配置文件,再通过打包命令指定需要打包的内容之后进行区分打包,Spring Boot
也不例外,或者说实现起来更加简单。
在Spring Boot
中,多环境配置的文件名需要满足application-{profile}.properties
的格式,其中{profile}
对应你的环境标识,如下所示:
-
application-dev.properties
:开发环境 -
application-test.properties
:测试环境 -
application-prod.properties
:生产环境
至于具体哪个配置文件会被加载,需要在application.properties
文件中通过spring.profiles.active
属性来设置,其值对应配置文件中的{profile}
值。如spring.profiles.active=test
就会加载application-test.properties
配置文件内容。
多环境的配置思路:
- 在
application.properties
中配置通用内容,并设置spring.profiles.active=dev
,以开发环境为默认配置。 - 在
application-{profile}.properties
中配置各个环境不同的内容。 - 通过命令行方式去激活不同环境的配置。
加载顺序
为了能够更合理地重写各属性的值,Spring Boot
使用了下面这种较为特别的属性加载顺序:
- 在命令行中传入的参数
-
SPRING_APPLICATION_JSON
中的属性。SPRING_APPLICATION_JSON
是以JSON
格式配置在系统环境变量中的内容 -
java:comp/env
中的JNDI
属性 -
Java
的系统属性,可以通过System.getProperties()
获得的内容 - 操作系统的环境变量
- 通过
random.*
配置的随机属性 - 位于当前应用
jar
包之外,针对不同{profile}
环境的配置文件内容,例如application-{profile}.properties
或是YAML
定义的配置文件 - 位于当前应用
jar
包之内,针对不同{profile}
环境的配置文件内容,例如application-{profile}.properties
或是YAML
定义的配置文件 - 位于当前应用
jar
包之外的application.properties
和YAML
配置内容 - 位于当前应用
jar
包之内的application.properties
和YAML
配置内容 - 在
@Configuration
注解修改的类中,通过@PropertySource
注解定义的属性 - 应用默认属性,使用
SpringApplication.setDefaultProperties
定义的内容
优先级按照上面的顺序由高到低,数字越小优先级越高。
可以看到,其中第7
项和第9
项都是从应用jar
包之外读取配置文件,所以,实现外部化配置的原理就是从此切入,为其指定外部配置文件的加载位置来取代jar
包之内的配置内容。通过这样的实现,我们的工程在配置中就变得非常干净,只需在本地放置开发需要的配置即可,而不用关心其他环境的配置,由其对应环境的负责人去维护即可。
监控与管理
在微服务架构中,我们将原本庞大的单体系统拆分成多个提供不同服务的应用。虽然各个应用的内部逻辑因分解而得以简化,但是由于部署应用的数量成倍增长,使得系统的维护复杂度大大提升。为了能对这些成倍增长的应用做到高效运维,传统的运维方式显然是不合适的,所以我们需要实现一套自动化的监控运维机制,而这套机制的运行基础就是不间断地收集各个微服务应用的各项指标情况,并根据这些基础指标信息来制定监控和预警规则,更进一步甚至做到一些自动化的运维操作等。
为了让运维系统能够获取各个微服务应用的相关指标以及实现一些常规操作控制,我们需要开发一套专门用于植入各个微服务应用的接口供监控系统采集信息。而这些接口往往有很大一部分指标都是类似的,比如环境变量、垃圾收集信息、内存信息、线程池信息等。
当我们决定用Spring Boot
来作为微服务框架时,除了它强大的快速开发功能之外,还因为它在Starter POMs
中提供了一个特殊依赖模块spring-boot-starter-actuator
,引入该模块能够自动为Spring Boot
构建的应用提供一系列用于监控的端点。同时,Spring Cloud
在实现各个微服务组件的时候,进一步为该模块做了不少扩展,比如,为原生端点增加了更多的指标和度量信息(比如在整合Eureka
的时候会为/health
端点增加相关的信息),并且根据不同的组件还提供了更多有空的端点(比如,为API
网关组件Zuul
提供了/routes
端点来返回路由信息)。
spring-boot-starter-actuator
模块的实现对于实施微服务的中小团队来说,可以有效地省去或大大减少监控系统在采集应用指标时的开发量。当然,它也并不是万能的,有时候也需要对其做一些简单的扩展来帮助我们实现自身系统个性化的监控需求。
初识actuator
在现有的Spring Boot
应用中引入该模块非常简单,只需要在pom.xml
的dependency
节点中,新增spring-boot-starter-actuator
的依赖即可,具体如下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
增加该依赖之后,重新启动应用,可以在控制台的输出中看到一批端点定义,这些端点并非我们自己在程序中创建的,而是由spring-boot-starter-actuator
模块根据应用依赖和配置自动创建出来的监管和管理端点。通过这些端点,我们可以实时获取应用的各项监控指标,比如访问/health
端点,我们可以获得如下信息:
{
"status":"UP",
"diskSpace":{
"status":"UP",
"total":491270434816,
"free":383870214144,
"threshold":10485760
}
}
在没有引入其他依赖之前,该端点的内容较为简单,后续我们在使用Spring Cloud
的各个组件之后,它的返回会变得非常丰富,这些内容将帮助我们制定更为个性化的监控策略。
原生端点
根据端点的作用,可以将原生端点分为以下三大类:
- 应用配置类:获取应用程序中加载的应用配置、环境变量、自动化配置报告等与
Spring Boot
应用密切相关的配置类信息。 - 度量指标类:获取应用程序运行过程中用于监控的度量指标,比如内存信息、线程池信息、
HTTP
请求统计等。 - 操作控制类:提供了对应用的关闭等操作类功能。
应用配置类
由于Spring Boot
为了改善传统Spring
应用繁杂的配置内容,采用了包扫描和自动化配置的机制来加载原本集中于xml
文件中的各项内容。虽然这样的做法,让我们的代码变得非常简洁,但是整个应用的实例创建和依赖关系等信息都被离散到了各个配置类的注解上,这使得我们分析整个应用中资源和实例的各种关系变得非常的困难。而这类端点就可以帮助我们轻松的获取一系列关于Spring
应用配置内容的详细报告,比如:自动化配置的报告、Bean
创建的报告、环境属性的报告等。
/autoconfig
/autoconfig
:该端点用来获取应用的自动化配置报告,其中包括所有自动化配置的候选项。同时还列出了每个候选项自动化配置的各个先决条件是否满足。所以,该端点可以帮助我们方便的找到一些自动化配置为什么没有生效的具体原因。该报告内容将自动化配置内容分为两部分:
-
positiveMatches
中返回的是条件匹配成功的自动化配置 -
negativeMatches
中返回的是条件匹配不成功的自动化配置
{
"positiveMatches": { // 条件匹配成功的
"EndpointWebMvcAutoConfiguration": [
{
"condition": "OnClassCondition",
"message": "@ConditionalOnClass classes found: javax.servlet.Servlet,org.springframework.web.servlet.DispatcherServlet"
},
{
"condition": "OnWebApplicationCondition",
"message": "found web application StandardServletEnvironment"
}
],
...
},
"negativeMatches": { // 条件不匹配成功的
"HealthIndicatorAutoConfiguration.DataSourcesHealthIndicatorConfiguration": [
{
"condition": "OnClassCondition",
"message": "required @ConditionalOnClass classes not found: org.springframework.jdbc.core.JdbcTemplate"
}
],
...
}
}
从如上示例中我们可以看到,每个自动化配置候选项中都有一系列的条件,比如上面没有成功匹配的HealthIndicatorAutoConfiguration.DataSourcesHealthIndicatorConfiguration
配置,它的先决条件就是需要在工程中包含org.springframework.jdbc.core.JdbcTemplate
类,由于我们没有引入相关的依赖,它就不会执行自动化配置内容。所以,当我们发现有一些期望的配置没有生效时,就可以通过该端点来查看没有生效的具体原因。
/beans
/beans
:该端点用来获取应用上下文中创建的所有Bean
。
[
{
"context": "hello:dev:8881",
"parent": null,
"beans": [
{
"bean": "org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration$DispatcherServletConfiguration",
"scope": "singleton",
"type": "org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration$DispatcherServletConfiguration$$EnhancerBySpringCGLIB$$3440282b",
"resource": "null",
"dependencies": [
"serverProperties",
"spring.mvc.CONFIGURATION_PROPERTIES",
"multipartConfigElement"
]
},
{
"bean": "dispatcherServlet",
"scope": "singleton",
"type": "org.springframework.web.servlet.DispatcherServlet",
"resource": "class path resource [org/springframework/boot/autoconfigure/web/DispatcherServletAutoConfiguration$DispatcherServletConfiguration.class]",
"dependencies": []
}
]
}
]
如上示例中,我们可以看到在每个bean
中都包含了下面这几个信息:
-
bean
:Bean
的名称 -
scope
:Bean
的作用域 -
type
:Bean
的Java
类型 -
reource
:class
文件的具体路径 -
dependencies
:依赖的Bean
名称
/configprops
/configprops
:该端点用来获取应用中配置的属性信息报告。从下面该端点返回示例的片段中,我们看到返回了关于该短信的配置信息,prefix
属性代表了属性的配置前缀,properties
代表了各个属性的名称和值。所以,我们可以通过该报告来看到各个属性的配置路径,比如我们要关闭该端点,就可以通过使用endpoints.configprops.enabled=false
来完成设置。
{
"configurationPropertiesReportEndpoint": {
"prefix": "endpoints.configprops",
"properties": {
"id": "configprops",
"sensitive": true,
"enabled": true
}
},
...
}
/env
/env
:该端点与/configprops
不同,它用来获取应用所有可用的环境属性报告。包括:环境变量、JVM
属性、应用的配置配置、命令行中的参数。从下面该端点返回的示例片段中,我们可以看到它不仅返回了应用的配置属性,还返回了系统属性、环境变量等丰富的配置信息,其中也包括了应用还没有没有使用的配置。所以它可以帮助我们方便地看到当前应用可以加载的配置信息,并配合@ConfigurationProperties
注解将它们引入到我们的应用程序中来进行使用。另外,为了配置属性的安全,对于一些类似密码等敏感信息,该端点都会进行隐私保护,但是我们需要让属性名中包含:password
、secret
、key
这些关键词,这样该端点在返回它们的时候会使用*
来替代实际的属性值。
{
"profiles": [
"dev"
],
"server.ports": {
"local.server.port": 8881
},
"servletContextInitParams": {
},
"systemProperties": {
"idea.version": "2016.1.3",
"java.runtime.name": "Java(TM) SE Runtime Environment",
"sun.boot.library.path": "C:\\Program Files\\Java\\jdk1.8.0_91\\jre\\bin",
"java.vm.version": "25.91-b15",
"java.vm.vendor": "Oracle Corporation",
...
},
"systemEnvironment": {
"configsetroot": "C:\\WINDOWS\\ConfigSetRoot",
"RABBITMQ_BASE": "E:\\tools\\rabbitmq",
...
},
"applicationConfig: [classpath:/application-dev.properties]": {
"server.port": "8881"
},
"applicationConfig: [classpath:/application.properties]": {
"server.port": "8885",
"spring.profiles.active": "dev",
"info.app.name": "spring-boot-hello",
"info.app.version": "v1.0.0",
"spring.application.name": "hello"
}
}
/mappings
/mappings
:该端点用来返回所有Spring MVC
的控制器映射关系报告。从下面的示例片段中,我们可以看该报告的信息与我们在启用Spring MVC
的Web
应用时输出的日志信息类似,其中bean属性标识了该映射关系的请求处理器,method
属性标识了该映射关系的具体处理类和处理函数。
{
"/webjars/**": {
"bean": "resourceHandlerMapping"
},
"/**": {
"bean": "resourceHandlerMapping"
},
"/**/favicon.ico": {
"bean": "faviconHandlerMapping"
},
"{[/hello]}": {
"bean": "requestMappingHandlerMapping",
"method": "public java.lang.String com.didispace.web.HelloController.index()"
},
"{[/mappings || /mappings.json],methods=[GET],produces=[application/json]}": {
"bean": "endpointHandlerMapping",
"method": "public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()"
},
...
}
/info
/info
:该端点用来返回一些应用自定义的信息。默认情况下,该端点只会返回一个空的json
内容。我们可以在application.properties
配置文件中通过info
前缀来设置一些属性,比如下面这样:
info.app.name=spring-boot-hello
info.app.version=v1.0.0
再访问/info
端点,我们可以得到下面的返回报告,其中就包含了上面我们在应用自定义的两个参数。
{
"app": {
"name": "spring-boot-hello",
"version": "v1.0.0"
}
}
度量指标类
上面我们所介绍的应用配置类端点所提供的信息报告在应用启动的时候都已经基本确定了其返回内容,可以说是一个静态报告。而度量指标类端点提供的报告内容则是动态变化的,这些端点提供了应用程序在运行过程中的一些快照信息,比如:内存使用情况、HTTP
请求统计、外部资源指标等。这些端点对于我们构建微服务架构中的监控系统非常有帮助,由于Spring Boot
应用自身实现了这些端点,所以我们可以很方便地利用它们来收集我们想要的信息,以制定出各种自动化策略。下面,我们就来分别看看这些强大的端点功能。
/metrics
/metrics
:该端点用来返回当前应用的各类重要度量指标,比如:内存信息、线程信息、垃圾回收信息等。
{
"mem": 541305,
"mem.free": 317864,
"processors": 8,
"instance.uptime": 33376471,
"uptime": 33385352,
"systemload.average": -1,
"heap.committed": 476672,
"heap.init": 262144,
"heap.used": 158807,
"heap": 3701248,
"nonheap.committed": 65856,
"nonheap.init": 2496,
"nonheap.used": 64633,
"nonheap": 0,
"threads.peak": 22,
"threads.daemon": 20,
"threads.totalStarted": 26,
"threads": 22,
"classes": 7669,
"classes.loaded": 7669,
"classes.unloaded": 0,
"gc.ps_scavenge.count": 7,
"gc.ps_scavenge.time": 118,
"gc.ps_marksweep.count": 2,
"gc.ps_marksweep.time": 234,
"httpsessions.max": -1,
"httpsessions.active": 0,
"gauge.response.beans": 55,
"gauge.response.env": 10,
"gauge.response.hello": 5,
"gauge.response.metrics": 4,
"gauge.response.configprops": 153,
"gauge.response.star-star": 5,
"counter.status.200.beans": 1,
"counter.status.200.metrics": 3,
"counter.status.200.configprops": 1,
"counter.status.404.star-star": 2,
"counter.status.200.hello": 11,
"counter.status.200.env": 1
}
从上面的示例中,我们看到有这些重要的度量值:
- 系统信息:包括处理器数量
processors
、运行时间uptime
和instance.uptime
、系统平均负载systemload.average
。 -
mem.*
:内存概要信息,包括分配给应用的总内存数量以及当前空闲的内存数量。这些信息来自java.lang.Runtime
。 -
heap.*
:堆内存使用情况。这些信息来自java.lang.management.MemoryMXBean
接口中getHeapMemoryUsage
方法获取的java.lang.management.MemoryUsage
。 -
nonheap.*
:非堆内存使用情况。这些信息来自java.lang.management.MemoryMXBean
接口中getNonHeapMemoryUsage
方法获取的java.lang.management.MemoryUsage
。 -
threads.*
:线程使用情况,包括线程数、守护线程数(daemon
)、线程峰值(peak
)等,这些数据均来自java.lang.management.ThreadMXBean
。 -
classes.*
:应用加载和卸载的类统计。这些数据均来自java.lang.management.ClassLoadingMXBean
。 -
gc.*
:垃圾收集器的详细信息,包括垃圾回收次数gc.ps_scavenge.count
、垃圾回收消耗时间gc.ps_scavenge.time
、标记-清除算法的次数gc.ps_marksweep.count
、标记-清除算法的消耗时间gc.ps_marksweep.time
。这些数据均来自java.lang.management.GarbageCollectorMXBean
。 -
httpsessions.*
:Tomcat
容器的会话使用情况。包括最大会话数httpsessions.max
和活跃会话数httpsessions.active
。该度量指标信息仅在引入了嵌入式Tomcat
作为应用容器的时候才会提供。 -
gauge.*
:HTTP
请求的性能指标之一,它主要用来反映一个绝对数值。比如上面示例中的gauge.response.hello: 5
,它表示上一次hello
请求的延迟时间为5
毫秒。 -
counter.*
:HTTP
请求的性能指标之一,它主要作为计数器来使用,记录了增加量和减少量。如上示例中counter.status.200.hello: 11
,它代表了hello
请求返回200
状态的次数为11
。
对于gauge.*
和counter.*
的统计,这里有一个特殊的内容请求star-star
,它代表了对静态资源的访问。这两类度量指标非常有用,我们不仅可以使用它默认的统计指标,还可以在程序中轻松的增加自定义统计值。只需要通过注入org.springframework.boot.actuate.metrics.CounterService
和org.springframework.boot.actuate.metrics.GaugeService
来实现自定义的统计指标信息。比如:我们可以像下面这样自定义实现对hello
接口的访问次数统计:
@RestController
public class HelloController {
@Autowired
private CounterService counterService;
@RequestMapping("/hello")
public String greet() {
counterService.increment("didispace.hello.count");
return "";
}
}
/metrics
端点可以提供应用运行状态的完整度量指标报告,这项功能非常的实用,但是对于监控系统中的各项监控功能,它们的监控内容、数据收集频率都有所不同,如果我们每次都通过全量获取报告的方式来收集,略显粗暴。所以,我们还可以通过/metrics/{name}
接口来更细粒度的获取度量信息,比如我们可以通过访问/metrics/mem.free
来获取当前可用内存数量。
/health
/health
:该端点在一开始的示例中我们已经使用过了,它用来获取应用的各类健康指标信息。在spring-boot-starter-actuator
模块中自带实现了一些常用资源的健康指标检测器。这些检测器都通过HealthIndicator
接口实现,并且会根据依赖关系的引入实现自动化装配,比如下面列出的这些:
检测器 | 功能 |
DiskSpaceHealthIndicator | 低磁盘空间检测 |
DataSourceHealthIndicator | 检测DataSource的连接是否可用 |
MongoHealthIndicator | 检测Mongo数据库是否可用 |
RabbitHealthIndicator | 检测Rabbit服务器是否可用 |
RedisHealthIndicator | 检测Redis服务器是否可用 |
SolrHealthIndicator | 检测Solr服务器是否可用 |
有时候,我们可能还会用到一些Spring Boot
的Starter POMs
中还没有封装的产品来进行开发,比如:当使用RocketMQ
作为消息代理时,由于没有自动化配置的检测器,所以我们需要自己来实现一个用来采集健康信息的检测器。比如,我们可以在Spring Boot
的应用中,为org.springframework.boot.actuate.health.HealthIndicator
接口实现一个对RocketMQ
的检测器类:
@Component
public class RocketMQHealthIndicator implements HealthIndicator {
@Override
public Health health() {
int errorCode = check();
if (errorCode != 0) {
return Health.down().withDetail("Error Code", errorCode).build();
}
return Health.up().build();
}
private int check() {
// 对监控对象的检测操作
}
}
通过重写health()
函数来实现健康检查,返回的Heath
对象中,共有两项内容,一个是状态信息,除了该示例中的UP
与DOWN
之外,还有UNKNOWN
和OUT_OF_SERVICE
,可以根据需要来实现返回;还有一个详细信息,采用Map
的方式存储,在这里通过withDetail
函数,注入了一个Error Code
信息,我们也可以填入一下其他信息,比如,检测对象的IP
地址、端口等。重新启动应用,并访问/health
接口,我们在返回的JSON
字符串中,将会包含了如下信息:
"rocketMQ": {
"status": "UP"
}
/dump
/dump
:该端点用来暴露程序运行中的线程信息。它使用java.lang.management.ThreadMXBean的dumpAllThreads
方法来返回所有含有同步信息的活动线程详情。
/trace
/trace
:该端点用来返回基本的HTTP
跟踪信息。默认情况下,跟踪信息的存储采用org.springframework.boot.actuate.trace.InMemoryTraceRepository
实现的内存方式,始终保留最近的100
条请求记录。它记录的内容格式如下:
[
{
"timestamp": 1482570022463,
"info": {
"method": "GET",
"path": "/metrics/mem",
"headers": {
"request": {
"host": "localhost:8881",
"connection": "keep-alive",
"cache-control": "no-cache",
"user-agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36",
"postman-token": "9817ea4d-ad9d-b2fc-7685-9dff1a1bc193",
"accept": "*/*",
"accept-encoding": "gzip, deflate, sdch",
"accept-language": "zh-CN,zh;q=0.8"
},
"response": {
"X-Application-Context": "hello:dev:8881",
"Content-Type": "application/json;charset=UTF-8",
"Transfer-Encoding": "chunked",
"Date": "Sat, 24 Dec 2016 09:00:22 GMT",
"status": "200"
}
}
}
},
...
]
操作控制类
实际上,由于之前介绍的所有端点都是用来反映应用自身的属性或是运行中的状态,相对于操作控制类端点没有那么敏感,所以他们默认都是启用的。而操作控制类端点拥有更强大的控制能力,如果要使用它们的话,需要通过属性来配置开启。
在原生端点中,只提供了一个用来关闭应用的端点:/shutdown
。我们可以通过如下配置开启它:
endpoints.shutdown.enabled=true
在配置了上述属性之后,只需要访问该应用的/shutdown
端点就能实现关闭该应用的远程操作。由于开放关闭应用的操作本身是一件非常危险的事,所以真正在线上使用的时候,我们需要对其加入一定的保护机制,比如:定制Actuator
的端点路径、整合Spring Security
进行安全校验等。
Spring Boot的三种运行方式
java -jar xxx.jar
在命令行中编译项目:
进入项目中build
→libs
目录,可以看到应用的jar
包:
通过命令行运行jar
包:
运行结果如下:
访问http://localhost:8080/hello
页面:
在命令行使用快捷键Ctrl
+C
可将应用停止运行。
gradle bootRun
通过命令行运行:
IDE里面右键项目
访问http://localhost:8080/hello
页面: