SpringBoot特性之Actuator

SpringBoot自動配置的特性,很大程度上解放了我們繁瑣的配置的工作,但是也向我們屏蔽了很多內部運行
的細節,好在SpringBoot爲我們提供了Actuator,Actuator在應用程序裏提供了衆多的Web端點,通過它
們,我們可以瞭解應用程序運行時的內部狀況。我們可以瞭解Bean的組裝信息,獲取環境配置信息,等等
Actuator爲我們提供瞭如下的一些端口

HTTP方法 路徑 描述 Sensitive Default
GET /autoconfig 自動配置報告,記錄哪些自動配置條件通過了,哪些沒通過 true
GET /configprops 自動配置的屬性信息 true
GET /beans 應用程序上下文裏全部的Bean,以及它們的關係 true
GET /dump 獲取線程活動的快照 true
GET /env 獲取全部環境屬性,包含環境變量和配置屬性信息 true
GET /env/{name} 根據名稱獲取特定的環境屬性值 true
GET /health 報告應用程序的健康指標,這些值由HealthIndicator的實現類提供 false
GET /info 獲取應用程序的定製信息,這些信息由info打頭的屬性提供 false
GET /mappings 全部的URI路徑,以及它們和控制器(包含Actuator端點)的映射關係 true
GET /metrics 報告各種應用程序度量信息,比如內存用量和HTTP請求計數 true
GET /metrics/{name} 報告指定名稱的應用程序度量值 true
GET /shutdown 優雅的關閉應用程序(endpoints.shutdown.enabled設置爲true纔會生效) true
GET /trace 提供基本的HTTP請求跟蹤信息(時間戳、HTTP頭等) true
GET /loggers 展示應用程序中的日誌配置信息 true
GET /heapdump 當訪問這個請求的時候,會下載一個壓縮的hprof的堆棧信息文件 true

我們如果要使用Actuator的話也很簡單,我們首先在pom文件中引入Actuator的依賴就行了,如下:

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

然後再添加一個配置項即可:

management:
  security:
    enabled: false

下面我們訪問幾個來看一下功能(端口號換成自己應用的端口號):
請求:http://localhost:8003/beans
效果如下:
Beans Info
在上面的圖片中,Actuator爲我們展示了一個Bean的信息,Bean的名字(bean)、別名(aliases)、
scope(singleton or prototype)、類型(type)、位置(resource絕對路徑)、依賴(dependencies)
請求:http://localhost:8003/autoconfig
效果如下:
autoconfig
SpringBoot的另一個重要的特性是自動配置,當我們訪問/autoconfig的時候,Actuator爲我們輸出了
autoconfig的一些信息,自動配置這個bean需要什麼樣的條件。
其他的一些Actuator端點也很有意思,例如:/configprops、/mappings、/heapdump等。當然我們也可以自定義Actuator來滿足自己的功能需要,demo如下所示:

package com.zkn.springboot.exercise.endpoint;

import org.springframework.boot.actuate.endpoint.AbstractEndpoint;
import org.springframework.stereotype.Component;

import java.lang.management.*;
import java.util.HashMap;
import java.util.Map;

/**
 * @author zkn
 * @date 2017/11/15 22:50
 */
@Component
public class ThreadInfoEndpoint extends AbstractEndpoint<Map<String, String>> {

    public ThreadInfoEndpoint() {
        //id
        super("threadInfo");
    }

    /**
     * Called to invoke the endpoint.
     *
     * @return the results of the invocation
     */
    @Override
    public Map<String, String> invoke() {
        ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
        //獲取所有的線程信息
        ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(true, true);
        if (threadInfos != null && threadInfos.length > 0) {
            Map<String, String> map = new HashMap<>(threadInfos.length);
            for (ThreadInfo threadInfo : threadInfos) {
                map.put(threadInfo.getThreadName(), getThreadDumpString(threadInfo));
            }
            return map;
        }
        return null;
    }

    /**
     * 組裝線程信息
     *
     * @param threadInfo
     */
    private String getThreadDumpString(ThreadInfo threadInfo) {

        StringBuilder sb = new StringBuilder("threadName:" + threadInfo.getThreadName() + ",threadId:" + threadInfo.getThreadId() + ",threadStatus:" + threadInfo.getThreadState());
        //鎖的名字
        if (threadInfo.getLockName() != null) {
            sb.append(",lockName:" + threadInfo.getLockName());
        }
        //鎖的持有者
        if (threadInfo.getLockOwnerName() != null) {
            sb.append(",lockOwnerName:" + threadInfo.getLockOwnerName());
        }
        //線程中斷
        if (threadInfo.isSuspended()) {
            sb.append(",suspended:" + threadInfo.isSuspended());
        }
        if (threadInfo.isInNative()) {
            sb.append(",inNative:" + threadInfo.isInNative());
        }
        sb.append("\n");

        StackTraceElement[] stackTraceElementst = threadInfo.getStackTrace();
        MonitorInfo[] monitorInfos = threadInfo.getLockedMonitors();
        StackTraceElement stackTraceElement;
        if (stackTraceElementst != null) {
            int i;
            for (i = 0; i < stackTraceElementst.length; i++) {
                stackTraceElement = stackTraceElementst[i];
                sb.append(",stackTraceElement:" + i + ";" + stackTraceElement.toString());
                if (i == 0 && threadInfo.getLockInfo() != null) {
                    Thread.State ts = threadInfo.getThreadState();
                    switch (ts) {
                        case BLOCKED:
                            sb.append("\t-  blocked on " + threadInfo.getLockInfo());
                            sb.append('\n');
                            break;
                        case WAITING:
                            sb.append("\t-  waiting on " + threadInfo.getLockInfo());
                            sb.append('\n');
                            break;
                        case TIMED_WAITING:
                            sb.append("\t-  waiting on " + threadInfo.getLockInfo());
                            sb.append('\n');
                            break;
                        default:
                    }
                }
                for (MonitorInfo mi : monitorInfos) {
                    if (mi.getLockedStackDepth() == i) {
                        sb.append("\t-  locked " + mi);
                        sb.append('\n');
                    }
                }
            }
            if (i < stackTraceElementst.length) {
                sb.append("\t...");
                sb.append('\n');
            }

            LockInfo[] locks = threadInfo.getLockedSynchronizers();
            if (locks.length > 0) {
                sb.append("\n\tNumber of locked synchronizers = " + locks.length);
                sb.append('\n');
                for (LockInfo li : locks) {
                    sb.append("\t- " + li);
                    sb.append('\n');
                }
            }
            sb.append('\n');
        }
        return sb.toString();
    }
}

關鍵點是:一:繼承AbstractEndpoint這個類(注意泛型類型),二:寫一個無參的構造函數,調用父類的一
個有參構造函數,傳入一個id描述(id描述會映射爲響應的請求),三:重寫invoke方法。在
AbstractEndpoint中注入Environment,所以你可以通過Environment獲取系統環境變量中的值。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章