一、概述
開始閱讀這篇文章之前,建議先閱讀下《SpringBoot 之Actuator》,該篇文章提到 Spring Boot Actuator 提供了對單個Spring Boot的監控,信息包含:應用狀態、內存、線程、堆棧等等,比較全面的監控了Spring Boot應用的整個生命周期。但是美中不足的是:
- 所有的監控都需要調用固定的接口來查看,如果全面查看應用狀態需要調用很多接口,並且接口返回的 Json 信息不方便運營人員理解;
- 如果Spring Boot 應用集群非常大,每個應用都需要調用不同的接口來查看監控信息,操作非常繁瑣低效。
在這樣的背景下,就誕生了另外一個開源軟件:Spring Boot Admin。那么什么是 Spring Boot Admin 呢?Spring Boot Admin 是一個針對 Spring Boot Actuator 進行UI美化封裝的監控工具。集群的每個應用都認為是一個客戶端(或者說實例),通過HTTP或者使用 Eureka 注冊到 Spring Boot Admin Server中進行展示,Spring Boot Admin UI 使用AngularJs將數據展示在前端。
下面將給大家介紹如何使用Spring Boot Admin對Spring Boot應用進行監控。
二、spring-boot-admin-starter-server
下面介紹 spring-boot-admin-server 的構建,要監控的每個客戶端(或者說實例),都可以把 Actuator 數據注冊到 server 中進行 UI 渲染展示。
1. pom.xml
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-server</artifactId>
<version>2.1.5</version>
</dependency>
2. application.yml
server:
port: 3333
spring:
application:
name: monitor
3. Application.java
@SpringBootApplication
@EnableAdminServer
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
}
做完以上動作,我們一個 spring-boot-admin-server 項目就搭建好了。以下是一些附加的功能:權限認證和郵件預警。
4. 配置 spring-security 權限驗證
- pom.xml
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-server-ui-login</artifactId>
<version>1.5.7</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
- application.yml
security:
user:
name: ${monitor.user}
password: ${monitor.password}
- SecurityConfig.java
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// Page with login form is served as /login.html and does a POST on /login
http.formLogin().loginPage("/login.html").loginProcessingUrl("/login").permitAll();
// The UI does a POST on /logout on logout
http.logout().logoutUrl("/logout");
// The ui currently doesn't support csrf
http.csrf().disable();
// Requests for the login page and the static assets are allowed
//允許登錄頁面和靜態資源的請求
http.authorizeRequests().antMatchers("/login.html", "/**/*.css", "/img/**", "/third-party/**")
.permitAll();
// ... and any other request needs to be authorized
//這點重要:所有請求都需要認證
http.authorizeRequests().antMatchers("/**").authenticated();
// Enable so that the clients can authenticate via HTTP basic for registering
http.httpBasic();
}
}
- 效果
5、郵件預警
- pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
<version>1.5.12.RELEASE</version>
</dependency>
- application.yml
spring:
mail:
host: smtp.exmail.qq.com
port: 465
username: xxx
password: xxx
properties:
mail:
smtp:
auth: true
debug: true
timeout: 0
socketFactory:
port: 465
class: javax.net.ssl.SSLSocketFactory
- NotifierConfig.java
/**
* 重新配置消息通知
*
* @author : cuixiuyin
*/
@Configuration
@EnableScheduling
public class NotifierConfig {
private static final Logger log = LoggerFactory.getLogger(NotifierConfig.class);
@Autowired
private JavaMailSender javaMailSender;
@Autowired
private RemindingNotifier remindingNotifier;
@Bean
@Primary
public RemindingNotifier remindingNotifier() {
RemindingNotifier remindingNotifier = new RemindingNotifier(new AbstractEventNotifier() {
@Override
protected void doNotify(ClientApplicationEvent event) throws Exception {
if (event instanceof ClientApplicationStatusChangedEvent) {
ClientApplicationStatusChangedEvent changedEvent = (ClientApplicationStatusChangedEvent) event;
log.info("Application {} ({}) is {}", event.getApplication().getName(), event.getApplication().getId(), changedEvent.getTo().getStatus());
String text = String.format("應用:%s 服務ID:%s,服務ip:%s 狀態改變為:[%s ---> %s],時間:%s"
, event.getApplication().getName()
, event.getApplication().getId()
, event.getApplication().getHealthUrl()
, changedEvent.getFrom().getStatus()
, changedEvent.getTo().getStatus()
, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(changedEvent.getTimestamp())));
log.warn(text);
SimpleMailMessage message = new SimpleMailMessage();
message.setFrom("xxx@qq.com");
message.setTo("xxx@163.com");
message.setSubject(event.getApplication().getName() + "服務狀態改變");
message.setText(text);
javaMailSender.send(message);
} else {
log.info("Application {} ({}) {}", event.getApplication().getName(), event.getApplication().getId(), event.getType());
}
}
});
// 每5分鍾就需要提醒一次,並不一定會提醒,有 RemindingNotifier 里面的狀態進行決定
remindingNotifier.setReminderPeriod(TimeUnit.MINUTES.toMillis(5));
return remindingNotifier;
}
/**
* 每隔一分鍾檢查還有那些需要進行提醒
*/
@Scheduled(fixedRate = 1_000L)
public void remind() {
remindingNotifier.sendReminders();
}
}
- 效果
三、spring-boot-admin-starter-client
我們已經有了一個 spring-boot-admin-server,現在要做的就是如何把客戶端(或者說實例)的 Actuator 數據注冊到 Server 中。
1. pom.xml
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
<version>2.1.5</version>
</dependency>
2. application.yml
spring:
application:
name: dubbo-provider
boot:
admin:
enabled: true
client:
instance:
name: ${spring.application.name}
prefer-ip: true
url: http://127.0.0.1:3333
management:
endpoints:
web:
exposure:
include: '*'
如此,我們就把客戶端(或者說實例)的 Actuator 數據注冊到 Server 中了。
附錄
1. 效果圖