SpringBoot系列——admin服務監控

qch 發表於 2021-06-17
Spring

  前言

  springboot專案部署起來後,如何實時監控專案的執行狀況呢?本文記錄使用springboot-admin對服務進行監控。

 

  springboot-admin介紹:https://codecentric.github.io/spring-boot-admin/current/#_what_is_spring_boot_admin

 

  工程結構

  服務端

  server服務端

SpringBoot系列——admin服務監控

 

 

  客戶端

  client客戶端

SpringBoot系列——admin服務監控

 

   服務端、客戶端都是獨立的web專案,服務端是監控程式,客戶端是被監控的程式,本文只測試了一個客戶端接入

 

  程式碼編寫

  服務端

  server服務端引入相關依賴

  2.2.0後admin的管理頁面支援中文,因此我們引入此版本(parent不再是引入我們的父工程pom了,直接引入springboot的2.2.0)

        <!-- 引入admin相關依賴 2.2.0頁面支援中文顯示,需要springboot 2.2.0 -->
        <dependency>
            <groupId>de.codecentric</groupId>
            <artifactId>spring-boot-admin-starter-server</artifactId>
            <version>2.2.0</version>
        </dependency>

  為了安全性,引入security

       <!--springboot security 安全相關-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

  解決控制檯報錯,移除tomcat,改用jetty

        <!--
            報錯:java.lang.IllegalStateException: Calling [asyncError()] is not valid for a request with Async state [MUST_DISPATCH]
            解決:移除tomcat,換成jetty
        -->
        <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>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jetty</artifactId>
        </dependency>

 

  監控系統,直接配置賬號、密碼,不用搞那麼麻煩接入資料庫

#配置一個賬號和密碼
spring.security.user.name=admin
spring.security.user.password=123456

  做好security配置

/**
 * Security安全配置
 */
@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 {
        SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
        successHandler.setTargetUrlParameter("redirectTo");
        successHandler.setDefaultTargetUrl(adminContextPath + "/");

        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()

                //開啟http basic支援,admin-client註冊時需要使用
                .httpBasic().and()
                .csrf()

                //開啟基於cookie的csrf保護
                .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
                //忽略這些路徑的csrf保護以便admin-client註冊
                .ignoringAntMatchers(
                        adminContextPath + "/instances",
                        adminContextPath + "/actuator/**"
                );
    }
}

  客戶端是要暴露actuator的web埠的,為了安全,客戶端只允許服務端請求actuator的web介面,為了方便客戶端區分請求來源,我們在請求頭注入自定義引數

/**
 * 注入額外的請求頭,方便客戶端區分請求來源
 */
@Component
public class HttpHeadersProviderConfig implements HttpHeadersProvider {
    @Value("${server.port}")
    private String port;

    @Override
    public HttpHeaders getHeaders(Instance instance) {
        HttpHeaders httpHeaders = new HttpHeaders();
        //設定約定好的請求頭引數
        httpHeaders.add("spring-boot-admin-service", port);
        return httpHeaders;
    }
}

  我們不可能整天上系統看監控資料,做好自定義通知,當例項狀態發生改變,及時通知(發郵件、企業微信、釘釘都可以,自己實現)

/**
 * 自定義通知
 * 繼承 AbstractStatusChangeNotifier 類,實現了 doNotify 方法,
 * 當應用狀態改變的時候會回撥 doNotify 方法。
 */
@Component
public class CustomNotifierConfig extends AbstractStatusChangeNotifier {

    public CustomNotifierConfig(InstanceRepository repository) {
        super(repository);
    }

    @Override
    protected Mono<Void> doNotify(InstanceEvent event, Instance instance) {
        return Mono.fromRunnable(() -> {
            if (event instanceof InstanceStatusChangedEvent) {
                System.out.println("例項名稱:"+instance.getRegistration().getName());
                System.out.println("例項服務地址:"+instance.getRegistration().getServiceUrl());
                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:
                        System.out.println(status);
                        break;
                }

            }
        });
    }
}

  最後在啟動打上@EnableAdminServer註解,開啟服務監控

@EnableAdminServer//開啟AdminServer功能
@SpringBootApplication
public class SpringBootAdminServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringBootAdminServerApplication.class, args);
    }

    /**
     * 啟動成功
     */
    @Bean
    public ApplicationRunner applicationRunner() {
        return applicationArguments -> {
            System.out.println("啟動成功!");
        };
    }
}

 

  客戶端

  服務端引入了2.2.0版本的依賴,因此客戶端也要引入2.2.0依賴

        <!-- 引入admin相關依賴 -->
        <dependency>
            <groupId>de.codecentric</groupId>
            <artifactId>spring-boot-admin-starter-client</artifactId>
            <version>2.2.0</version>
        </dependency>

  在配置檔案中,開啟埠、配置admin的server地址,以及賬號、密碼

#啟用端點,預設情況下,除shutdown以外的所有端點均已啟用
management.endpoint.shutdown.enabled=true

#顯示db、redis、rabbti連線情況等
management.endpoint.health.show-details=always

#公開所有端點web介面
management.endpoints.web.exposure.include=*

#admin-server地址,以及登入賬號、密碼
spring.boot.admin.client.port=10010
spring.boot.admin.client.url=http://localhost:${spring.boot.admin.client.port}
spring.boot.admin.client.username=admin
spring.boot.admin.client.password=123456

 

  為了方便測試其他東西

        <!--新增springdata-cache依賴 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>

        <!--新增MySQL驅動依賴 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

  同時建立測試介面、定時器、cache快取、非同步任務,就是為了看服務端能否監控到

SpringBoot系列——admin服務監控

 

   客戶端是要暴露actuator的web埠的,為了安全,客戶端只允許服務端請求actuator的web介面(通過約定好的請求頭來判斷)

/**
 * 針對actuator介面做安全限制,只允許服務端呼叫
 */
@WebFilter
@ServletComponentScan
@Component
public class ActuatorFilter implements Filter {
    @Value("${spring.boot.admin.client.port}")
    private String adminServicePort;

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;

        //判斷約定好的請求頭引數
        if (request.getRequestURI().contains("/actuator") && !adminServicePort.equals(request.getHeader("spring-boot-admin-service"))){
            throw new RuntimeException("抱歉,你無許可權訪問,Actuator埠受保護! Sorry, you have no permission to access it,Actuator port protected!");
        }

        filterChain.doFilter(servletRequest, servletResponse);
    }
}

 

  效果演示

  安全配置生效

  首先先看安全配置都生效了沒有

  訪問服務端,需要登入

SpringBoot系列——admin服務監控

 

   登入上去,客戶端已經註冊成功

SpringBoot系列——admin服務監控

 

   正常監控客戶端中...

SpringBoot系列——admin服務監控

 

  瀏覽器直接訪問客戶端的actuator介面,直接丟擲異常

  http://localhost:10011/actuator

SpringBoot系列——admin服務監控

 

 SpringBoot系列——admin服務監控

   其他介面正常訪問

SpringBoot系列——admin服務監控

 

 

 

  自定義通知

  注:客戶端首次在服務端註冊,並沒有觸發自定義通知

  再看下自定義通知

  停掉客戶端服務、重啟啟動客戶端,觸發服務端自定義通知

 

 SpringBoot系列——admin服務監控

 SpringBoot系列——admin服務監控

 

 

  具體監控項

  具體客戶端的監控首頁,有我們在客戶端寫的info資訊、磁碟監控、堆、非堆記憶體監控、程式、執行緒監控、垃圾回收監控

#新增描述
info.describe=SpringBootAdmin,Test Client Service!
info.author=huanzi-qch
info.version=1.0.0

SpringBoot系列——admin服務監控

 

 SpringBoot系列——admin服務監控

 

 

   計劃任務這裡可以看到我們配置的定時器

SpringBoot系列——admin服務監控

 

 

  web對映可以看到所有的web介面

SpringBoot系列——admin服務監控

 

   http跟蹤,可以檢視具體請求的響應情況

SpringBoot系列——admin服務監控

 

 

SpringBoot系列——admin服務監控

 

 

  快取選單,可以看到我們使用到的快取空間

SpringBoot系列——admin服務監控

 

   還可以下載jvm dump檔案

SpringBoot系列——admin服務監控

 

   其他就不一一列舉,自己把專案跑起來再看

 

  另外,這個版本好像不能檢視非同步任務?我並沒有找到相關頁面

 

  後記

  SpringBoot-Admin監控Client有兩種模式:

  一種是在Client端引入spring-boot-admin-starter-client依賴,配置好Server的相關資訊。

  另一種模式是將所有Client端註冊到服務發現(Eureka)元件中去,同時把Server端也註冊,這樣Server端就可以監控所有Client端了,不用對Client都新增依賴。

 

  SpringBoot系列——admin服務監控暫時先記錄到這,後續有空再進行補充

 

  程式碼開源

 

  程式碼已經開源、託管到我的GitHub、碼雲:

  GitHub:https://github.com/huanzi-qch/springBoot

  碼雲:https://gitee.com/huanzi-qch/springBoot