前言
springboot項目部署起來后,如何實時監控項目的運行狀況呢?本文記錄使用springboot-admin對服務進行監控。
springboot-admin介紹:https://codecentric.github.io/spring-boot-admin/current/#_what_is_spring_boot_admin
工程結構
服務端
server服務端

客戶端
client客戶端

服務端、客戶端都是獨立的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緩存、異步任務,就是為了看服務端能否監控到

客戶端是要暴露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); } }
效果演示
安全配置生效
首先先看安全配置都生效了沒有
訪問服務端,需要登錄

登錄上去,客戶端已經注冊成功

正常監控客戶端中...

瀏覽器直接訪問客戶端的actuator接口,直接拋出異常
http://localhost:10011/actuator


其他接口正常訪問

自定義通知
注:客戶端首次在服務端注冊,並沒有觸發自定義通知
再看下自定義通知
停掉客戶端服務、重啟啟動客戶端,觸發服務端自定義通知


具體監控項
具體客戶端的監控首頁,有我們在客戶端寫的info信息、磁盤監控、堆、非堆內存監控、進程、線程監控、垃圾回收監控
#添加描述 info.describe=SpringBootAdmin,Test Client Service! info.author=huanzi-qch info.version=1.0.0


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

web映射可以看到所有的web接口

http跟蹤,可以查看具體請求的響應情況


緩存菜單,可以看到我們使用到的緩存空間

還可以下載jvm dump文件

其他就不一一列舉,自己把項目跑起來再看
另外,這個版本好像不能查看異步任務?我並沒有找到相關頁面
后記
SpringBoot-Admin監控Client有兩種模式:
一種是在Client端引入spring-boot-admin-starter-client依賴,配置好Server的相關信息。
另一種模式是將所有Client端注冊到服務發現(Eureka)組件中去,同時把Server端也注冊,這樣Server端就可以監控所有Client端了,不用對Client都添加依賴。
SpringBoot系列——admin服務監控暫時先記錄到這,后續有空再進行補充
更新
2021-08-12更新
springboot-admin,查看被監控的應用的服務日志
在client客戶端配置日志(logback),這里直接拿base-admin的日志配置過來
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
<!--日志文件主目錄:這里${user.home}為當前服務器用戶主目錄-->
<property name="LOG_HOME" value="${user.home}/log"/>
<!--日志文件名稱:這里spring.application.name表示工程名稱-->
<springProperty scope="context" name="APP_NAME" source="spring.application.name"/>
<!--引入默認配置-->
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<!--引入配置控制台(CONSOLE)-->
<include resource="org/springframework/boot/logging/logback/console-appender.xml"/>
<!--配置日志文件(File)-->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!--設置策略-->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--日志文件路徑:這里%d{yyyyMMdd}表示按天分類日志-->
<FileNamePattern>${LOG_HOME}/%d{yyyyMMdd}/${APP_NAME}.log</FileNamePattern>
<!--日志保留天數-->
<MaxHistory>15</MaxHistory>
</rollingPolicy>
<!--設置格式-->
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化輸出:%d表示日期,%thread表示線程名,%-5level:級別從左顯示5個字符寬度%msg:日志消息,%n是換行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<!-- 或者使用默認配置 -->
<!--<pattern>${FILE_LOG_PATTERN}</pattern>-->
<charset>utf8</charset>
</encoder>
</appender>
<!-- 將文件輸出設置成異步輸出 -->
<appender name="ASYNC-FILE" class="ch.qos.logback.classic.AsyncAppender">
<!-- 不丟失日志.默認的,如果隊列的80%已滿,則會丟棄TRACT、DEBUG、INFO級別的日志 -->
<discardingThreshold>0</discardingThreshold>
<!-- 更改默認的隊列的深度,該值會影響性能.默認值為256 -->
<queueSize>256</queueSize>
<!-- 添加附加的appender,最多只能添加一個 -->
<appender-ref ref="FILE"/>
</appender>
<!-- 將控制台輸出設置成異步輸出 -->
<appender name="ASYNC-CONSOLE" class="ch.qos.logback.classic.AsyncAppender">
<!-- 不丟失日志.默認的,如果隊列的80%已滿,則會丟棄TRACT、DEBUG、INFO級別的日志 -->
<discardingThreshold>0</discardingThreshold>
<!-- 更改默認的隊列的深度,該值會影響性能.默認值為256 -->
<queueSize>256</queueSize>
<!-- 添加附加的appender,最多只能添加一個 -->
<appender-ref ref="CONSOLE"/>
</appender>
<!-- 多環境配置 按照active profile選擇分支 -->
<springProfile name="dev">
<!--root節點 全局日志級別,用來指定最基礎的日志輸出級別-->
<root level="INFO">
<appender-ref ref="FILE"/>
<appender-ref ref="CONSOLE"/>
</root>
<!-- 子節點向上級傳遞 局部日志級別-->
<logger level="WARN" name="org.springframework"/>
<logger level="WARN" name="com.netflix"/>
</springProfile>
<springProfile name="prod">
</springProfile>
</configuration>
在配置文件中配置
#配置文件分支選擇 spring.profiles.active=dev #可在線查看日志,PS:好像不能按日期來存放了,直接指定一個死位置 log-file-path=${user.home}/log/20210812/${spring.application.name}.log management.endpoint.logfile.enabled=true #生成日志的路徑跟暴露給服務端的路徑要一致 management.endpoint.logfile.external-file=${log-file-path} logging.file=${log-file-path}
效果
實時刷新被監控的應用的服務日志,效果奈斯!

代碼開源
代碼已經開源、托管到我的GitHub、碼雲:
