天天看點

Spring Cloud之——SpringBootAdmin監控程式簡介

簡介

Spring Boot Admin是一個開源社群項目,用于管理和監控SpringBoot應用程式。 應用程式作為Spring Boot Admin Client向為Spring Boot Admin Server注冊(通過HTTP)或使用SpringCloud注冊中心(例如Eureka,Consul)發現。 UI是的AngularJs應用程式,展示Spring Boot Admin Client的Actuator端點上的一些監控。常見的功能或者監控如下:

  • 顯示健康狀況
  • 顯示詳細資訊,例如

    JVM和記憶體名額

    micrometer.io名額

    資料源名額

    緩存名額

  • 顯示建構資訊編号
  • 關注并下載下傳日志檔案
  • 檢視jvm系統和環境屬性
  • 檢視Spring Boot配置屬性
  • 支援Spring Cloud的postable / env-和/ refresh-endpoint
  • 輕松的日志級管理
  • 與JMX-beans互動
  • 檢視線程轉儲
  • 檢視http跟蹤
  • 檢視auditevents
  • 檢視http-endpoints
  • 檢視計劃任務
  • 檢視和删除活動會話(使用spring-session)
  • 檢視Flyway / Liquibase資料庫遷移
  • 下載下傳heapdump
  • 狀态變更通知(通過電子郵件,Slack,Hipchat,…)
  • 狀态更改的事件日志(非持久性)

引用Pom

<!--    發送郵件    -->
		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-mail</artifactId>
        </dependency>

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

        <!--暴露各種名額-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

		<!--    SpringBootAdmin    -->
        <dependency>
            <groupId>de.codecentric</groupId>
            <artifactId>spring-boot-admin-starter-server</artifactId>
            <version>2.2.3</version>
        </dependency>

		<!--    服務注冊    -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-consul-discovery</artifactId>
            <version>2.2.3.RELEASE</version>
        </dependency>
        
           

Yml檔案

spring:
  application:
    name: spring-boot-admin
  main:
    allow-bean-definition-overriding: true

  boot:
    admin:
      discovery:
        ignored-services: consul
      #監控通知郵箱
      notify:
        mail:
          from: 郵箱賬号
          to: 接收郵箱賬号

  #配置郵箱
  mail:
    host: smtp.163.com
    username: 郵箱賬号
    password: 授權碼
    properties:
      mail:
        smtp:
          auth: true
          starttls:
            enable: true
            required: true
          #QQ郵箱必須加
          ssl:
            enable: true

  cloud:
    consul:
      #配置consul伺服器的host
      host: localhost
      #配置端口
      port: 8500
      discovery:
        #配置健康檢查時間間隔
        health-check-interval: 15s
        #配置執行個體id
        instance-id: ${spring.application.name}:${vcap.application.instance_id:${spring.application.instance_id:${random.value}}}
        #配置服務注冊
        register: true
        #表示注冊時使用ip而不是hostname
        prefer-ip-address: true
        #健康檢查失敗多長時間取消注冊
        health-check-critical-timeout: 30s

           

全局配置

建立全局配置類:MonkeyConfiguration

package com.admin.admin;

import de.codecentric.boot.admin.server.domain.entities.InstanceRepository;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.mail.javamail.JavaMailSender;

/**
 * @Author: LailaiMonkey
 * @Description:
 * @Date:Created in 2020-09-29 15:03
 * @Modified By:
 */
@Configuration
public class MonkeyConfiguration {

    @Bean
    public MonkeyCompositeNotifier monkeyCompositeNotifier(InstanceRepository instanceRepository,
                                                           JavaMailSender mailSender) {
        return new MonkeyCompositeNotifier(instanceRepository, mailSender);
    }

}

建立消息模型:MessageBody

```java
package com.admin.admin.sendMessage.model;

/**
 * @Author: LailaiMonkey
 * @Description:
 * @Date:Created in 2020-11-06 15:48
 * @Modified By:
 */
public class MessageBody {

    /**
     * 内容
     */
    private String text;

    /**
     * 标題
     */
    private String title;

    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

}

           

建立發送消息類:MonkeyCompositeNotifier

package com.admin.admin;

import com.admin.admin.sendMessage.SendAllMessage;
import com.admin.admin.sendMessage.model.MessageBody;
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.InstanceDeregisteredEvent;
import de.codecentric.boot.admin.server.domain.events.InstanceEvent;
import de.codecentric.boot.admin.server.domain.events.InstanceRegisteredEvent;
import de.codecentric.boot.admin.server.domain.events.InstanceStatusChangedEvent;
import de.codecentric.boot.admin.server.domain.values.Registration;
import de.codecentric.boot.admin.server.domain.values.StatusInfo;
import de.codecentric.boot.admin.server.notify.AbstractEventNotifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import reactor.core.publisher.Mono;

import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiFunction;

/**
 * @Author: LailaiMonkey
 * @Description:
 * @Date:Created in 2020-09-29 15:07
 * @Modified By:
 */
public class MonkeyCompositeNotifier extends AbstractEventNotifier {

    private static final Logger logger = LoggerFactory.getLogger(MonkeyCompositeNotifier.class);

    private JavaMailSender mailSender;

    @Value("${spring.boot.admin.notify.mail.from}")
    private String from;

    @Value("${spring.boot.admin.notify.mail.to}")
    private String to;

    private final static Map<Class<?>, BiFunction<InstanceEvent, Instance, MessageBody>> cacheMap = new HashMap<>();

    static {
        //注冊
        cacheMap.put(InstanceRegisteredEvent.class, MonkeyCompositeNotifier::register);
        //登出
        cacheMap.put(InstanceDeregisteredEvent.class, MonkeyCompositeNotifier::deRegistered);
        //狀态變更
        cacheMap.put(InstanceStatusChangedEvent.class, MonkeyCompositeNotifier::statusChanged);
    }

    public MonkeyCompositeNotifier(InstanceRepository repository, JavaMailSender mailSender) {
        super(repository);
        this.mailSender = mailSender;
    }

    @Override
    protected Mono<Void> doNotify(InstanceEvent event, Instance instance) {
        return Mono.fromRunnable(() -> {
            BiFunction<InstanceEvent, Instance, MessageBody> messageBiFunction = cacheMap.get(event.getClass());
            if (messageBiFunction != null) {
                MessageBody messageBody = messageBiFunction.apply(event, instance);

                MimeMessage mimeMessage = mailSender.createMimeMessage();
                MimeMessageHelper message = new MimeMessageHelper(mimeMessage, StandardCharsets.UTF_8.name());
                try {
                    message.setText(messageBody.getText());
                    message.setSubject(messageBody.getTitle());
                    message.setTo(to);
                    message.setFrom(from);
                    mailSender.send(mimeMessage);
                } catch (MessagingException e) {
                    e.printStackTrace();
                    logger.error("發送郵件異常!", e);
                }

            }
        });
    }

    /**
     * 注冊通知
     *
     * @param event
     * @param instance
     * @return
     */
    private static MessageBody register(InstanceEvent event, Instance instance) {
        Registration registration = instance.getRegistration();
        MessageBody body = new MessageBody();
        body.setText("服務名稱:" + registration.getName() + "  服務位址:" + registration.getServiceUrl());
        body.setTitle("服務上線通知!");
        return body;
    }

    /**
     * 登出通知
     *
     * @param event
     * @param instance
     * @return
     */
    private static MessageBody deRegistered(InstanceEvent event, Instance instance) {
        Registration registration = instance.getRegistration();
        MessageBody body = new MessageBody();
        body.setText("服務名稱:" + registration.getName() + "  服務位址:" + registration.getServiceUrl());
        body.setTitle("服務下線通知!");
        return body;
    }

    /**
     * 狀态變更通知
     *
     * @param event
     * @param instance
     * @return
     */
    private static MessageBody statusChanged(InstanceEvent event, Instance instance) {
        StatusInfo statusInfo = ((InstanceStatusChangedEvent) event).getStatusInfo();
        Registration registration = instance.getRegistration();
        MessageBody body = new MessageBody();
        body.setText("服務名稱:" + registration.getName() + "  服務位址:" + registration.getServiceUrl() + "  狀态:" + statusInfo.getStatus());
        body.setTitle("服務狀态變量通知!");
        return body;
    }

}

           

如果看過源碼的小夥伴知道,自帶發送郵件服務繼承

AbstractStatusChangeNotifier

,而我們需要重寫他的方法是無法收到消息,這是為什麼呢?

它有一個

shouldNotify

方法,把一些方法過濾了。是以我們繼承

AbstractEventNotifier

@Override
	protected boolean shouldNotify(InstanceEvent event, Instance instance) {
		if (event instanceof InstanceStatusChangedEvent) {
			InstanceStatusChangedEvent statusChange = (InstanceStatusChangedEvent) event;
			String from = getLastStatus(event.getInstance());
			String to = statusChange.getStatusInfo().getStatus();
			return Arrays.binarySearch(ignoreChanges, from + ":" + to) < 0
					&& Arrays.binarySearch(ignoreChanges, "*:" + to) < 0
					&& Arrays.binarySearch(ignoreChanges, from + ":*") < 0;
		}
		return false;
	}
           

而且他本身的郵件通知隻支援

InstanceStatusChangedEvent(狀态變更通知)

,沒有注冊、上下線通知,是以我們重寫一個。

通路:http://localhost:8080出現管理界面

權限通路配置

  • 添加Pom檔案
<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
           
  • 配置檔案
spring:
  security:
    user:
      name: "admin"
      password: "admin"
           

通路頁面時候會彈出登入界面。

繼續閱讀