1. 程式人生 > >Springboot 系列(十七)迅速使用 Spring Boot Admin 監控你的 Spring Boot 程式

Springboot 系列(十七)迅速使用 Spring Boot Admin 監控你的 Spring Boot 程式

1. Spring Boot Admin 是什麼

Spring Boot Admin 是由 codecentric 組織開發的開源專案,使用 Spring Boot Admin 可以管理和監控你的 Spring Boot 專案。它分為客戶端和服務端兩部分,客戶端新增到你的 Spring Boot 應用增加暴漏相關資訊的 HTTP 介面,然後註冊到 Spring Boot Admin 服務端,這一步驟可以直接向服務端註冊,也可以通過 Eureka 或者 Consul 進行註冊。而 Spring Boot Admin Server 通過 Vue.js 程式監控資訊進行視覺化呈現。並且支援多種事件通知操作。

2. Spring Boot Admin 服務端

Spring Boot Admin 服務端是基於 Spring Boot 專案的,如何建立一個 Spring Boot 專案這裡不提,你可以參考之前文章或者從 https://start.spring.io/ 直接獲得一個 Spring Boot 專案。

2.1. 新增依賴

只需要新增 web 依賴和 Spring-boot-admin-starter-server 依賴。

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

<dependency>
    <groupId>de.codecentric</groupId>
    <artifactId>spring-boot-admin-starter-server</artifactId>
</dependency>

2.2. 啟動配置

為了和下面的客戶端埠不衝突,先修改埠號為 9090。

server:
  port: 9090

新增 @EnableAdminServer 註解啟用 Spring Boot Admin Server 功能。

@EnableAdminServer
@SpringBootApplication
public class SpringbootAdminServerApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootAdminServerApplication.class, args);
    }
}

服務端已經配置完成,啟動專案進行訪問就可以看到 Spring Boot Admin Server 的頁面了。

3. Spring Boot Admin 客戶端

建立 Spring Boot 專案依舊不提,這裡只需要新增 Spring Boot Admin 客戶端需要的依賴,在專案啟動時就會增加相關的獲取資訊的 API 介面。然後在 Spring Boot 配置檔案中配置 Spring Boot Admin 服務端,就可以進行監控了。

3.1 客戶端依賴

pom.xml

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>de.codecentric</groupId>
    <artifactId>spring-boot-admin-starter-client</artifactId>
</dependency>
<!-- Lombok 工具,與 admin client 無關 -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>

3.2 客戶端配置

客戶端配置主要為了讓客戶端可以成功向服務端註冊,所以需要配置客戶端所在應用相關資訊以及 Spring Boot Admin Server 服務端的 url。

server:
  port: 8080

spring:
  application:
    # 應用名稱
    name: sjfx-api-search
  jmx:
    enabled: true
  boot:
    admin:
      client:
        # 服務端 url
        url: http://127.0.0.1:9090
        instance:
          # 客戶端例項 url
          service-url: http://127.0.0.1:8080
          prefer-ip: true
          # 客戶端例項名稱
          name: sjfx-api-search

management:
  endpoints:
    web:
      exposure:
        # 暴漏的介面 - 所有介面
        include: "*"

配置中的 include: "*" 公開了所有的埠,對於生產環境,應該自信的選擇要公開的介面。

Spring Boot Admin 可以獲取應用中的定時任務,所以在程式碼中增加一個定時任務計劃,每 20 秒輸出一次當前時間,日誌級別為 INFO,用於下面的定時任務和日誌監控測試。

@Slf4j
@SpringBootApplication
@EnableScheduling
public class SpringbootAdminClientApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootAdminClientApplication.class, args);
    }

    @Scheduled(cron = "0/20 * * * * ?")
    public void run20s() {
        log.info("定時任務:{}", LocalDateTime.now());
    }
}

3.3. 客戶端執行

啟動客戶端會暴漏相關的執行狀態介面,並且自動向配置的服務端傳送註冊資訊。

下面是客戶端的啟動日誌:

2019-12-21 22:45:32.878  INFO 13204 --- [           main] n.c.b.SpringbootAdminClientApplication   : Starting SpringbootAdminClientApplication on DESKTOP-8SCFV4M with PID 13204 (D:\IdeaProjectMy\springboot-git\springboot-admin\springboot-admin-client\target\classes started by 83981 in D:\IdeaProjectMy\springboot-git\springboot-admin)
2019-12-21 22:45:32.881  INFO 13204 --- [           main] n.c.b.SpringbootAdminClientApplication   : No active profile set, falling back to default profiles: default
2019-12-21 22:45:33.627  INFO 13204 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2019-12-21 22:45:33.634  INFO 13204 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2019-12-21 22:45:33.634  INFO 13204 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.29]
2019-12-21 22:45:33.706  INFO 13204 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2019-12-21 22:45:33.706  INFO 13204 --- [           main] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 797 ms
2019-12-21 22:45:33.850  INFO 13204 --- [           main] o.s.b.a.e.web.ServletEndpointRegistrar   : Registered '/actuator/jolokia' to jolokia-actuator-endpoint
2019-12-21 22:45:33.954  INFO 13204 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2019-12-21 22:45:34.089  INFO 13204 --- [           main] o.s.s.c.ThreadPoolTaskScheduler          : Initializing ExecutorService
2019-12-21 22:45:34.117  INFO 13204 --- [           main] o.s.s.c.ThreadPoolTaskScheduler          : Initializing ExecutorService 'taskScheduler'
2019-12-21 22:45:34.120  INFO 13204 --- [           main] o.s.b.a.e.web.EndpointLinksResolver      : Exposing 15 endpoint(s) beneath base path '/actuator'
2019-12-21 22:45:34.162  INFO 13204 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2019-12-21 22:45:34.163  INFO 13204 --- [           main] n.c.b.SpringbootAdminClientApplication   : Started SpringbootAdminClientApplication in 1.563 seconds (JVM running for 2.131)
2019-12-21 22:45:34.271  INFO 13204 --- [gistrationTask1] d.c.b.a.c.r.ApplicationRegistrator       : Application registered itself as 6bcf19a6bf8c
2019-12-21 22:45:34.293  INFO 13204 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
2019-12-21 22:45:34.294  INFO 13204 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
2019-12-21 22:45:34.300  INFO 13204 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 6 ms

從啟動日誌裡的 Exposing 15 endpoint(s) beneath base path '/actuator' 這段,可以看到暴漏了 15 個 /actuator 的 API 介面,可以直接訪問檢視響應結果。

從日誌 Application registered itself as 6bcf19a6bf8c 可以看到客戶端已經註冊成功了。再看服務端可以看到註冊上來的一個應用例項。

4. Spring Boot Admin 功能

點選監控頁面上的線上的應用例項,可以跳轉到應用例項詳細的監控管理頁面,也就是 Vue.js 實現的 web 展示。

Spring Boot Admin Server 可以監控的功能很多,使用起來沒有難度,下面描述下可以監測的部分內容:

  • 應用執行狀態,如時間、垃圾回收次數,執行緒數量,記憶體使用走勢。
  • 應用效能監測,通過選擇 JVM 或者 Tomcat 引數,檢視當前數值。
  • 應用環境監測,檢視系統環境變數,應用配置引數,自動配置引數。
  • 應用 bean 管理,檢視 Spring Bean ,並且可以檢視是否單例。
  • 應用計劃任務,檢視應用的計劃任務列表。
  • 應用日誌管理,動態更改日誌級別,檢視日誌。
  • 應用 JVM 管理,檢視當前執行緒執行情況,dump 記憶體堆疊資訊。
  • 應用對映管理,檢視應用介面呼叫方法、返回型別、處理類等資訊。

上面提到的日誌管理,可以動態的更改日誌級別,以及檢視日誌。預設配置下是隻可以動態更改日誌級別的,如果要線上檢視日誌,就需要手動配置日誌路徑了。

客戶端上可以像下面這樣配置日誌路徑以及日誌高亮。

# 配置檔案:application.yml
logging:
  file:
    name: boot.log
  pattern:
#     日誌高亮
    file: '%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(%5p) %clr(${PID}){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n%wEx'

下面是在 Spring Boot Admin 監測頁面上檢視的客戶端應用日誌。

5. Spring Boot Admin 進階

5.1. 郵件通知

Spring Boot Admin Server 支援常見的通知方式,比如郵件通知、電報通知、PagerDuty 通知等,下面將會演示常見的通知方式 - 郵件通知,最後也會演示如何通過監聽時間進下設定自定義通知方式。

Spring Boot Admin Server 的郵件通知通過 Thymeleaf 模板傳送 HTML 格式的電子郵件。因此,想要使用郵件通知首先要引入 Thymeleaf 依賴以及 spring-boot-starter-mail 依賴,並配置郵件傳送者資訊和接受者資訊。

1. 新增依賴

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

<dependency>
    <groupId>de.codecentric</groupId>
    <artifactId>spring-boot-admin-starter-server</artifactId>
</dependency>

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

<!-- Thymeleaf 模版,用於傳送模版郵件 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

2. 配置郵件

主要設定傳送者資訊和接收者資訊。

spring:
  boot:
    admin:
      notify:
        mail:
          # 逗號分隔的郵件收件人列表
          to: [email protected]
          # 開啟郵箱通知
          enabled: true
          # 不需要傳送通知的狀態:從狀態A:到狀態B
          ignore-changes: {"UNKNOWN:UP"}
          # 逗號分隔的抄送收件人列表
          cc: [email protected]
          # 發件人
          from: Spring Boot Admin<[email protected]>
          
# 郵件傳送者資訊
  mail:
    host: smtp.126.com
    port: 25
    username: [email protected]
    default-encoding: utf-8
    password: xxxx

如果想了解更多關於 Spring Boot 郵件傳送資訊,可以參考 Spring Boot 系列文章第十三篇。

配置好郵件通知之後,重啟服務端和客戶端,等客戶端註冊到服務端之後直接終止客戶端的執行,稍等片刻就可以在配置的通知接收郵箱裡收到客戶端例項下線通知了。

郵件通知使用的模板存放在 server 依賴的 classpath:/META-INF/spring-boot-admin-server/mail/status-changed.html 路徑,如果想要自定義模板內容。可以拷貝這個檔案放到自己的 templates 目錄下,修改成自己想要的效果,然後在配置中指定自定義模板路徑。

spring:
  boot:
    admin:
      notify:
        mail:
          # 自定義郵件模版
          template: classpath:/templates/notify.html

5.2 自定義通知

自定義通知只需要自己實現 Spring Boot Admin Server 提供的監聽通知類即可,下面會演示如何在例項狀態改變時輸出例項相關資訊。

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import de.codecentric.boot.admin.server.domain.entities.Instance;
import de.codecentric.boot.admin.server.domain.entities.InstanceRepository;
import de.codecentric.boot.admin.server.domain.events.InstanceEvent;
import de.codecentric.boot.admin.server.domain.events.InstanceStatusChangedEvent;
import de.codecentric.boot.admin.server.notify.AbstractEventNotifier;
import de.codecentric.boot.admin.server.notify.LoggingNotifier;
import reactor.core.publisher.Mono;

@Component
public class CustomNotifier extends AbstractEventNotifier {

    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());
            } else {
                LOGGER.info("Instance {} ({}) {}", instance.getRegistration().getName(), event.getInstance(),
                    event.getType());
            }
        });
    }
}

5.2. 訪問限制

上面提到過,因為客戶端增加了暴漏執行資訊的相關介面,所以在生產環境中使用存在風險,而服務端沒有訪問限制,誰的可以訪問也是不合理的。

下面將會為客戶端和服務端分別增加訪問限制,客戶端主要是限制敏感介面的訪問許可權,服務端則是全域性的訪問限制。這些訪問限制都通過 spring 安全框架 security 來實現,所以首先要為客戶端和服務端都增加 maven 依賴。

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

1. 服務端

在引入安全框架依賴之後,需要配置訪問控制,比如靜態資源不需要限制,登入登出頁面指定等。

import java.util.UUID;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import de.codecentric.boot.admin.server.config.AdminServerProperties;
import io.netty.handler.codec.http.HttpMethod;

@Configuration(proxyBeanMethods = false)
public class SecuritySecureConfig extends WebSecurityConfigurerAdapter {

    private final AdminServerProperties adminServer;

    public SecuritySecureConfig(AdminServerProperties adminServer) {
        this.adminServer = adminServer;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // @formatter:off
        SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
        successHandler.setTargetUrlParameter("redirectTo");
        successHandler.setDefaultTargetUrl(this.adminServer.path("/"));

        http.authorizeRequests()
                .antMatchers(this.adminServer.path("/assets/**")).permitAll()
                .antMatchers(this.adminServer.path("/login")).permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin().loginPage(this.adminServer.path("/login")).successHandler(successHandler).and()
                .logout().logoutUrl(this.adminServer.path("/logout")).and()
                .httpBasic().and()
                .csrf()
                .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
                .ignoringRequestMatchers(
                        new AntPathRequestMatcher(this.adminServer.path("/instances"), HttpMethod.POST.toString()),
                        new AntPathRequestMatcher(this.adminServer.path("/instances/*"), HttpMethod.DELETE.toString()),
                        new AntPathRequestMatcher(this.adminServer.path("/actuator/**"))
                )
                .and()
                .rememberMe().key(UUID.randomUUID().toString()).tokenValiditySeconds(1209600);
        // @formatter:on
    }

    // 程式碼配置使用者名稱和密碼的方式
    // Required to provide UserDetailsService for "remember functionality"
    // @Override
    // protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    // auth.inMemoryAuthentication().withUser("user").password("{noop}password").roles("USER");
    // }
}

在 application.yml 配置檔案中配置使用者名稱和密碼。

spring:
  security:
    user:
      name: user
      password: 123

重啟服務端,再次訪問就需要使用者名稱和密碼進行登入了。

2. 客戶端

客戶端在引入安全框架之後,也需要配置訪問許可權,主要是配置哪些路徑可以訪問,哪些路徑訪問需要登入限制,預設所有介面都需要登入限制。

同樣的,客戶端應用也需要在配置中配置客戶端應用對於敏感介面的登入使用者和密碼,同時需要配置 Spring Boot Admin Server 的訪問使用者和密碼,還要把自身的使用者和密碼註冊時告訴服務端,不然服務端不能獲取到監測資料。

spring:
  security:
    user:
      # 客戶端敏感介面使用者和密碼
      name: client
      password: 123
  application:
    # 應用名稱
    name: sjfx-api-search
  jmx:
    enabled: true
  boot:
    admin:
      client:
        # 服務端 url
        url: http://127.0.0.1:9090
        instance:
          # 客戶端例項 url
          service-url: http://127.0.0.1:8080
          prefer-ip: true
          # 客戶端例項名稱
          name: sjfx-api-search
          metadata:
            # 客戶端自身的使用者和密碼告訴服務端
            user.name: client
            user.password: 123
        # 服務端使用者名稱密碼
        username: user
        password: 123

客戶端敏感介面訪問測試。

到這裡,客戶端的敏感介面訪問需要登入,服務端的管理頁面也需要登入,客戶端和服務端的訪問控制已經完成了。

文中程式碼已經上傳到:github.com/niumoo/springboot/tree/master/springboot-admin

參考資料:

https://github.com/codecentric/spring-boot-admin

https://codecentric.github.io/spring-boot-admin/current/

<完>
個人網站:https://www.codingme.net
如果你喜歡這篇文章,可以關注公眾號,一起成長。
關注公眾號回覆資源可以沒有套路的獲取全網最火的的 Java 核心知識整理&面試核心資料。