Spring Boot Admin 监控介绍
Spring Boot Admin是一个Web应用,用于管理和监视Spring Boot应用程序的运行状态。
每个Spring Boot应用程序都被视为客户端并注册到管理服务器。
背后的数据采集是由Spring Boot Actuator端点提供。
Spring Boot Admin 是由服务端和客户端组成
在 Spring Boot 项目中,Spring Boot Admin 作为 Server 端,其他的要被监控的应用作为 Client 端
Spring Boot Admin 设计目的
问题:在微服务生态中,由于一个项目中服务过多,导致排查问题等造成很大的困难。
目的:实时监控各个服务的健康状态,日志及时查看,服务异常及时通知人员进行修复等。
Spring Boot Admin 实现原理
1.所有需要被监控的服务,均加上SpringBoot提供的Actuator包
2.启动Admin Server端,作为注册中心,监控所有客户端当前状态(自己也需要被注册并且被监控)
3.启动Admin Clinet端,第一次主动向Admin Server端提供健康信息
4.Admin Server端定时轮询所有监控Admin Client端的节点及时获得最新信息
5.Admin Client端如果发生异常,Admin Server端提供了邮件功能等,及时通知用户进行修复
Spring Boot Admin 提供了哪些功能
- 显示健康状况
- 显示详细信息,例如
-
- JVM和内存指标
-
- micrometer.io指标
-
- 数据源指标
-
- 缓存指标
- 显示内部编号
- 关注并下载日志文件
- 查看JVM系统和环境属性
- 查看Spring Boot配置属性
- 支持Spring Cloud的可发布/ env-和// refresh-endpoint
- 轻松的日志级别管理
- 与JMX-beans交互
- 查看线程转储
- 查看http跟踪
- 查看审核事件
- 查看http端点
- 查看预定的任务
- 查看和删除活动会话(使用spring-session)
- 查看Flyway / Liquibase数据库迁移
- 下载heapdump
- 状态更改通知(通过电子邮件,Slack,Hipchat等)
- 状态更改的事件日志(非持久性)
SpringBootAdmin2.0集成eureka
创建sunny-eureka-service
这是eureka-server端,注册中心。
pom文件
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
application.yml
配置应用名和端口信息,以及向sunny-admin-server-service注册的地址为http://localhost:8888,最后暴露自己的actuator的所有端口信息,具体配置如下:
#服务端口号
server:
port: 8888
spring:
application:
name: sunny-eureka-service
eureka:
instance:
#为false时,那么注册到Eureka中的Ip地址就是本机的Ip地址
prefer-ip-address: false
#服务注册中心实例的主机名
hostname: localhost
health-check-url-path: /actuator/health
status-page-url-path: /actuator/info
client:
# 表示是否从 eureka server 中获取注册信息(检索服务),默认是true
fetch-registry: false
# 表示是否将自己注册到 eureka server(向服务注册中心注册自己),默认是true
register-with-eureka: false
service-url:
#服务注册中心的配置内容,指定服务注册中心的位置,eureka 服务器的地址(注意:地址最后面的 /eureka/ 这个是固定值)
#defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
#2、在原先的基础上添加security用户名和密码(例如:http://username:password@localhost:8000/eureka/)
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
management:
endpoints:
web:
exposure:
include: "*"
endpoint:
health:
show-details: ALWAYS #显示详细信息
启动类
@EnableEurekaServer
@SpringBootApplication
public class SpringBootApplicationEurekaServer {
public static void main(String[] args) {
SpringApplication.run(SpringBootApplicationEurekaServer.class);
}
}
创建sunny-admin-server-service
这是SpringBootAdmin Server端
pom文件
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--如果spring-boot-starter-web 排除掉tomcat,则可以引入jetty-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
<!-- admin server -->
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-server</artifactId>
<version>2.2.1</version>
</dependency>
<!-- eureka客户端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- 健康监控 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
application.yml
management.endpoints.web.exposure.include 配置,我这里的配置暴露的所有节点进行监控
server:
port: 8889
spring:
application:
name: sunny-admin-server-service
eureka:
instance:
#服务注册中心实例的主机名
hostname: localhost
health-check-url-path: /actuator/health
status-page-url-path: /actuator/info
client:
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:8888/eureka/
management:
endpoints:
web:
exposure:
include: "*"
endpoint:
health:
show-details: ALWAYS #显示详细信息
启动类
启动类添加@EnableAdminServer注解,开启监控
@EnableDiscoveryClient
@SpringBootApplication
public class SpringBootApplicationMainAdminServer {
public static void main(String[] args) {
SpringApplication.run(SpringBootApplicationMainAdminServer.class);
}
}
创建sunny-admin-client-service
这是SpringBootAdmin Client端
pom文件
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
application.yml
server:
port: 8183
spring:
application:
name: sunny-admin-client-service
eureka:
instance:
#服务注册中心实例的主机名
hostname: localhost
health-check-url-path: /actuator/health
status-page-url-path: /actuator/info
client:
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:8888/eureka/
management:
endpoints:
web:
exposure:
include: "*"
endpoint:
health:
show-details: ALWAYS #显示详细信息
启动类
@EnableDiscoveryClient
@SpringBootApplication
public class SpringBootApplicationMainAdminClient {
public static void main(String[] args) {
SpringApplication.run(SpringBootApplicationMainAdminClient.class);
}
}
启动三个工程,在浏览器上输入localhost:8889 ,监控平台显示的界面如下:
Spring Boot Admin Server 可以监控的功能很多,使用起来没有难度,下面描述下可以监测的部分内容:
- 应用运行状态,如时间、垃圾回收次数,线程数量,内存使用走势。
- 应用性能监测,通过选择 JVM 或者 Tomcat 参数,查看当前数值。
- 应用环境监测,查看系统环境变量,应用配置参数,自动配置参数。
- 应用 bean 管理,查看 Spring Bean ,并且可以查看是否单例。
- 应用计划任务,查看应用的计划任务列表。
- 应用日志管理,动态更改日志级别,查看日志。
- 应用 JVM 管理,查看当前线程运行情况,dump 内存堆栈信息。
- 应用映射管理,查看应用接口调用方法、返回类型、处理类等信息。
SpringBootAdmin2.0集成eureka+securty认证
Web应用程序中的身份验证和授权有多种方法,因此Spring Boot Admin不提供默认方法。默认情况下,spring-boot-admin-server-ui提供登录页面和注销按钮。我们结合 Spring Security 实现需要用户名和密码登录的安全认证
创建sunny-eureka-service
这是eureka-server端,注册中心。
pom文件
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
application.yml
改动点:
1.默认设置security登录账号密码
2.注册中心的地址,需要加上自己设置的账号密码
#服务端口号
server:
port: 8888
spring:
application:
name: sunny-eureka-service
security:
user:
name: admin
password: 123456
eureka:
instance:
#为false时,那么注册到Eureka中的Ip地址就是本机的Ip地址
prefer-ip-address: false
#服务注册中心实例的主机名
hostname: localhost
health-check-url-path: /actuator/health
status-page-url-path: /actuator/info
metadata-map:
user.name: ${spring.security.user.name}
user.password: ${spring.security.user.password}
client:
# 表示是否从 eureka server 中获取注册信息(检索服务),默认是true
fetch-registry: false
# 表示是否将自己注册到 eureka server(向服务注册中心注册自己),默认是true
register-with-eureka: false
service-url:
#服务注册中心的配置内容,指定服务注册中心的位置,eureka 服务器的地址(注意:地址最后面的 /eureka/ 这个是固定值)
#defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
#2、在原先的基础上添加security用户名和密码(例如:http://username:password@localhost:8000/eureka/)
defaultZone: http://${spring.security.user.name}:${spring.security.user.password}@${eureka.instance.hostname}:${server.port}/eureka/
management:
endpoints:
web:
exposure:
include: "*"
endpoint:
health:
show-details: ALWAYS #显示详细信息
config类
新版本的spring-cloud2.0中: Spring Security默认开启了CSRF攻击防御
CSRF会将微服务的注册也给过滤了,虽然不会影响注册中心,但是其他客户端是注册不了的,这里配置i就是将csrf给关闭掉
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated()
.and().httpBasic()
.and()
.csrf()
.disable();
}
}
启动类
@EnableEurekaServer
@SpringBootApplication
public class SpringBootApplicationEurekaServer {
public static void main(String[] args) {
SpringApplication.run(SpringBootApplicationEurekaServer.class);
}
}
创建sunny-admin-server-service
这是SpringBootAdmin Server端。
pom文件
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--如果spring-boot-starter-web 排除掉tomcat,则可以引入jetty-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
<!-- admin server -->
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-server</artifactId>
<version>2.2.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- eureka客户端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- 健康监控 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
application.yml
server:
port: 8889
spring:
application:
name: sunny-admin-server-service
security:
user:
name: admin
password: 123456
eureka:
instance:
#服务注册中心实例的主机名
hostname: localhost
health-check-url-path: /actuator/health
status-page-url-path: /actuator/info
metadata-map:
user.name: ${spring.security.user.name}
user.password: ${spring.security.user.password}
client:
serviceUrl:
defaultZone: http://${spring.security.user.name}:${spring.security.user.password}@${eureka.instance.hostname}:8888/eureka/
management:
endpoints:
web:
exposure:
include: "*"
endpoint:
health:
show-details: ALWAYS #显示详细信息
config类
@Configuration
public class SecuritySecureConfig extends WebSecurityConfigurerAdapter {
private final String adminContextPath;
public SecuritySecureConfig(AdminServerProperties adminServerProperties) {
this.adminContextPath = adminServerProperties.getContextPath();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
successHandler.setTargetUrlParameter( "redirectTo" );
http.authorizeRequests()
.antMatchers( adminContextPath + "/assets/**" ).permitAll()
.antMatchers( adminContextPath + "/login" ).permitAll()
.anyRequest().authenticated()
.and()
.formLogin().loginPage( adminContextPath + "/login" ).successHandler( successHandler ).and()
.logout().logoutUrl( adminContextPath + "/logout" ).and()
.httpBasic().and()
.csrf().disable();
// @formatter:on
}
}
启动类
@EnableAdminServer
@EnableDiscoveryClient
@SpringBootApplication
public class SpringBootApplicationMainAdminServer {
public static void main(String[] args) {
SpringApplication.run(SpringBootApplicationMainAdminServer.class);
}
}
创建sunny-admin-client-service
这是SpringBootAdmin Server端。
pom文件
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
application.yml
server:
port: 8183
spring:
application:
name: sunny-admin-client-service
security:
user:
name: admin
password: 123456
eureka:
instance:
#服务注册中心实例的主机名
hostname: localhost
health-check-url-path: /actuator/health
status-page-url-path: /actuator/info
metadata-map:
user.name: ${spring.security.user.name}
user.password: ${spring.security.user.password}
client:
serviceUrl:
defaultZone: http://${spring.security.user.name}:${spring.security.user.password}@${eureka.instance.hostname}:8888/eureka/
management:
endpoints:
web:
exposure:
include: "*"
endpoint:
health:
show-details: ALWAYS #显示详细信息
启动类
@EnableDiscoveryClient
@SpringBootApplication
public class SpringBootApplicationMainAdminClient {
public static void main(String[] args) {
SpringApplication.run(SpringBootApplicationMainAdminClient.class);
}
}
SpringBootAdmin集成邮箱服务
邮件通知
在 Spring Boot Admin 中 当注册的应用程序状态更改为DOWN、UNKNOWN、OFFLINE 都可以指定触发通知,下面讲解配置邮件通知。
在sunny-admin-server-service工程的pom文件中,加上email的依赖,如下
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
在配置文件application.yml文件中,配置收发邮件的配置:
spring:
application:
name: sunny-admin-server-service
boot:
admin:
ui:
title: sunny-admin-server-service
notify:
mail:
from: xxxx@163.com #发件人
to: xxxx@163.com,xxxx@163.com #逗号分隔的收件人列表
cc: xxxx@163.com,xxxx@163.com #逗号分隔的抄送收件人列表
enabled: true # 开启邮箱通知
mail:
host: smtp.163.com
username: xxxx@163.com #自己的邮箱
password: xxx #授权码
properties:
mail:
smtp:
auth: true
starttls:
enable: true
required: true
default-encoding: utf-8
配置后,重启sunny-admin-server-service工程,之后若出现注册的客户端的状态从 UP 变为 OFFLINE 或其他状态,服务端就会自动将电子邮件发送到上面配置的收件地址。
注意 : 配置了邮件通知后,会出现 反复通知 service offline / up。这个问题的原因在于 查询应用程序的状态和信息超时,下面给出两种解决方案:
#方法一:增加超时时间(单位:ms)
spring.boot.admin.monitor.read-timeout=20000
#方法二:关闭闭未使用或不重要的检查点
management.health.db.enabled=false
management.health.mail.enabled=false
management.health.redis.enabled=false
management.health.mongo.enabled=false
自定义通知
可以通过添加实现Notifier接口的Spring Beans来添加您自己的通知程序,最好通过扩展 AbstractEventNotifier或AbstractStatusChangeNotifier。在sunny-admin-server-service工程中编写一个自定义的通知器:
@Component
public class CustomNotifier extends AbstractStatusChangeNotifier {
private static final Logger LOGGER = LoggerFactory.getLogger( LoggingNotifier.class);
public CustomNotifier(InstanceRepository repository) {
super(repository);
}
@Override
protected Mono<Void> doNotify(InstanceEvent event, Instance instance) {
return Mono.fromRunnable(() -> {
if (event instanceof InstanceStatusChangedEvent) {
LOGGER.info("Instance {} ({}) is {}", instance.getRegistration().getName(), event.getInstance(),
((InstanceStatusChangedEvent) event).getStatusInfo().getStatus());
String status = ((InstanceStatusChangedEvent) event).getStatusInfo().getStatus();
switch (status) {
// 健康检查没通过
case "DOWN":
System.out.println("发送 健康检查没通过 的通知!");
break;
// 服务离线
case "OFFLINE":
System.out.println("发送 服务离线 的通知!");
break;
//服务上线
case "UP":
System.out.println("发送 服务上线 的通知!");
break;
// 服务未知异常
case "UNKNOWN":
System.out.println("发送 服务未知异常 的通知!");
break;
default:
break;
}
} else {
LOGGER.info("Instance {} ({}) {}", instance.getRegistration().getName(), event.getInstance(),
event.getType());
}
});
}
}
效果图,邮件通知
我的博客即将同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=12tesopfl6i5t