Spring Boot Admin是一個開源社區項目,用於管理和監控SpringBoot應用程序。 應用程序做爲Spring Boot Admin Client向爲Spring Boot Admin Server註冊(經過HTTP)或使用SpringCloud註冊中心(例如Eureka,Consul)發現。 UI是的Vue.js應用程序,展現Spring Boot Admin Client的Actuator端點上的一些監控。服務端採用Spring WebFlux + Netty的方式。Spring Boot Admin爲註冊的應用程序提供如下功能:html
顯示健康情況java
顯示詳細信息,例如react
JVM和內存指標web
micrometer.io指標spring
數據源指標數據庫
緩存指標apache
顯示構建信息編號c#
關注並下載日誌文件api
查看jvm system-和environment-properties緩存
查看Spring Boot配置屬性
支持Spring Cloud的postable / env-和/ refresh-endpoint
輕鬆的日誌級管理
與JMX-beans交互
查看線程轉儲
查看http-traces
查看auditevents
查看http-endpoints
查看計劃任務
查看和刪除活動會話(使用spring-session)
查看Flyway / Liquibase數據庫遷移
下載heapdump
狀態變動通知(經過電子郵件,Slack,Hipchat,......)
狀態更改的事件日誌(非持久性)
這是一個 Spring Boot Admin Server端。
<!--?xml version="1.0" encoding="UTF-8"?--> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemalocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelversion>4.0.0</modelversion> <parent> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-parent</artifactid> <version>2.1.6.RELEASE</version> <relativepath /> <!-- lookup parent from repository --> </parent> <packaging>jar</packaging> <artifactid>spring-boot-admin</artifactid> <name>spring-boot-admin</name> <description>Spring Boot Admin Server端</description> <properties> <java.version>1.8</java.version> <spring-boot-admin.version>2.1.6</spring-boot-admin.version> <spring-cloud.version>Greenwich.RELEASE</spring-cloud.version> </properties> <dependencies> <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.cloud</groupid> <artifactid>spring-cloud-starter-netflix-eureka-client</artifactid> </dependency> <dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-test</artifactid> <scope>test</scope> </dependency> <dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-security</artifactid> </dependency> <dependency> <groupid>org.jolokia</groupid> <artifactid>jolokia-core</artifactid> </dependency> </dependencies> <dependencymanagement> <dependencies> <dependency> <groupid>org.springframework.cloud</groupid> <artifactid>spring-cloud-dependencies</artifactid> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupid>de.codecentric</groupid> <artifactid>spring-boot-admin-dependencies</artifactid> <version>${spring-boot-admin.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencymanagement> <build> <plugins> <plugin> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-maven-plugin</artifactid> </plugin> </plugins> </build> </project>
spring: application: name: admin-server server: port: 1300 eureka: client: registryFetchIntervalSeconds: 5 service-url: defaultZone: ${EUREKA_SERVICE_URL:http://localhost:8761}/eureka/ instance: leaseRenewalIntervalInSeconds: 10 health-check-url-path: /actuator/health management: endpoints: web: exposure: include: "*" endpoint: health: show-details: ALWAYS
@SpringBootApplication @EnableAdminServer @EnableEurekaClient public class ScAdminServerApplication { public static void main(String[] args) { SpringApplication.run( ScAdminServerApplication.class, args ); } }
被監控端須要放開端點
spring: application: name: admin-client eureka: instance: leaseRenewalIntervalInSeconds: 10 health-check-url-path: /actuator/health client: registryFetchIntervalSeconds: 5 service-url: defaultZone: ${EUREKA_SERVICE_URL:http://localhost:8761}/eureka/ management: endpoints: web: exposure: include: "*" endpoint: health: show-details: ALWAYS server: port: 8762
admin 會本身拉取 Eureka 上註冊的信息,主動去註冊。
Web應用程序中的身份驗證和受權有多種方法,所以Spring Boot Admin不提供默認方法。默認狀況下,spring-boot-admin-server-ui提供登陸頁面和註銷按鈕。咱們結合 Spring Security 實現須要用戶名和密碼登陸的安全認證。
springboot-admin工程的pom文件須要增長如下的依賴:
<dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-security</artifactid> </dependency>
在 spirngboot-admin工的配置文件 application.yml 中配置 spring security 的用戶名和密碼,這時須要在服務註冊時帶上 metadata-map 的信息,以下:
spring: security: user: name: "admin" password: "admin" eureka: instance: metadata-map: user.name: ${spring.security.user.name} user.password: ${spring.security.user.password} startup: ${random.int} #needed to trigger info and endpoint update after restart
寫一個配置類SecuritySecureConfig繼承WebSecurityConfigurerAdapter,配置以下:
/** * security配置 * @author wangjiafang * @date 2019/10/10 */ @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"); 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() .httpBasic().and() .csrf() .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) .ignoringRequestMatchers( new AntPathRequestMatcher(adminContextPath + "/instances", HttpMethod.POST.toString()), new AntPathRequestMatcher(adminContextPath + "/instances/*", HttpMethod.DELETE.toString()), new AntPathRequestMatcher(adminContextPath + "/actuator/**") ); // @formatter:on } }
從新訪問 http:localhost:1300 會出現登陸界面,密碼是配置文件中配置好的,帳號 admin 密碼 admin
釘釘官方提供了統一的SDK,使用SDK能夠便捷的調用服務端API,但沒有放到公共maven倉庫中,須要自行下載後導入到項目,或者上傳到本身的搭建的nexus私服中
經過擴展 AbstractEventNotifier或AbstractStatusChangeNotifier。在springboot-admin-server工程中編寫一個自定義的通知器:
import com.dingtalk.api.DefaultDingTalkClient; import com.dingtalk.api.DingTalkClient; import com.dingtalk.api.request.OapiRobotSendRequest; import com.taobao.api.ApiException; 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 lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import reactor.core.publisher.Mono; /** * 釘釘通知 * @author wangjiafang * @date 2019/10/10 */ @Component @Slf4j public class CustomNotifier extends AbstractEventNotifier { /** * 消息模板 */ private static final String template = "服務名:%s(%s) \n狀態:%s(%s) \n服務ip:%s"; @Value("${spring.admin.ding-talk-token}") private String dingTalkToken; public CustomNotifier(InstanceRepository repository) { super(repository); } @Override protected Mono<void> doNotify(InstanceEvent event, Instance instance) { return Mono.fromRunnable(() -> { if (event instanceof InstanceStatusChangedEvent) { log.info("Instance {} ({}) is {}", instance.getRegistration().getName(), event.getInstance(), ((InstanceStatusChangedEvent) event).getStatusInfo().getStatus()); String status = ((InstanceStatusChangedEvent) event).getStatusInfo().getStatus(); String messageText = null; switch (status) { // 健康檢查沒經過 case "DOWN": log.info("發送 健康檢查沒經過 的通知!"); messageText = String.format(template, instance.getRegistration().getName(),event.getInstance(), ((InstanceStatusChangedEvent) event).getStatusInfo().getStatus(),"健康檢查沒經過",instance.getRegistration().getServiceUrl()); this.sendMessage(messageText); break; // 服務離線 case "OFFLINE": log.info("發送 服務離線 的通知!"); messageText = String.format(template, instance.getRegistration().getName(),event.getInstance(), ((InstanceStatusChangedEvent) event).getStatusInfo().getStatus(),"服務離線",instance.getRegistration().getServiceUrl()); this.sendMessage(messageText); break; //服務上線 case "UP": log.info("發送 服務上線 的通知!"); messageText = String.format(template, instance.getRegistration().getName(),event.getInstance(), ((InstanceStatusChangedEvent) event).getStatusInfo().getStatus(),"服務上線",instance.getRegistration().getServiceUrl()); this.sendMessage(messageText); break; // 服務未知異常 case "UNKNOWN": log.info("發送 服務未知異常 的通知!"); messageText = String.format(template, instance.getRegistration().getName(),event.getInstance(), ((InstanceStatusChangedEvent) event).getStatusInfo().getStatus(),"服務未知異常",instance.getRegistration().getServiceUrl()); this.sendMessage(messageText); break; default: break; } } else { log.info("Instance {} ({}) {}", instance.getRegistration().getName(), event.getInstance(), event.getType()); } }); } /** * 發送消息 * @param messageText */ private void sendMessage(String messageText){ DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/robot/send?access_token="+dingTalkToken); OapiRobotSendRequest request = new OapiRobotSendRequest(); request.setMsgtype("text"); OapiRobotSendRequest.Text text = new OapiRobotSendRequest.Text(); text.setContent(messageText); request.setText(text); try { client.execute(request); } catch (ApiException e) { log.info("[ERROR] sendMessage", e); } } }
要在springbootadmin面板中查看實時日誌,須要指定項目中日誌輸出的地址,好比個人日誌是在/logs/項目名稱/項目名稱-info.log下
logging: file: /logs/${spring.application.name}/${spring.application.name}-info.log