SpringBoot入門建站全系列(二十九)Actuator服務監控及SpringBootMonitor單機監控頁面

SpringBoot入門建站全系列(二十九)Actuator服務監控及SpringBootMonitor單機監控頁面

一、概述

Actuaotr是spring boot項目中非常強大的一個功能,有助於對應用程序進行監控和管理,通過restful api請求來監管、審計、收集應用的運行情況,針對微服務而言它是必不可少的一個環節。

spring-boot-actuator中已經內置了非常多的Endpoints(health、info、beans、httptrace、shutdown)等等,同時也允許我們自己擴展自己的端點.

本篇簡單介紹Spring Boot Actuaotr的使用方法,及自定義使用方法、安全控制方法,並介紹單機監控頁面SpringBootMonitor的使用。

代碼可以在SpringBoot組件化構建https://www.pomit.cn/java/spring/springboot.html中的Actuator組件中查看,並下載。

首發地址:
品茗IT-同步發佈

如果大家正在尋找一個java的學習環境,或者在開發中遇到困難,可以加入我們的java學習圈,點擊即可加入,共同學習,節約學習時間,減少很多在學習中遇到的難題。

二、配置

本文假設你已經引入spring-boot-starter-web。已經是個SpringBoot項目了,如果不會搭建,可以打開這篇文章看一看《SpringBoot入門建站全系列(一)項目建立》

2.1 Maven依賴

使用actuator可以使用spring-boot-starter-actuator,方便快捷,一般springboot對大多數開源項目都做了整合,提供了專用的stater。

同時,本篇使用了spring-boot-monitor做Spring boot actuator的監控頁面,spring-boot-monitor是一個對Spring boot Admin監控工具做簡化的單機版監控工具,單機版使用spring-boot-monitor足夠了,方便快捷。

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

<dependency>
	<groupId>cn.pomit</groupId>
	<artifactId>spring-boot-monitor</artifactId>
	<version>0.0.1</version>
</dependency>

2.2 配置文件

在application.properties 中需要配置actuator的信息,如:

management.endpoints.web.exposure.include=*

actuator.filter.switch=false

這裏將actuator的所有endpoints都開放出來了,這是一種很危險的配置,但是,開放出來的理由有

  • 如果是內網環境,可以開放出來,但不要開放到外網環境;
  • 如果使用了Spring Security,可以根據用戶權限配置,需要將所有endpoints開放出來;
  • 如果想簡單使用filter對endpoints做控制,可以全部開放出來,本篇就是使用filter對actuator/*做簡單的控制,actuator.filter.switch的配置就是爲了對filter做開關。

三、Actuator的使用

3.1 Actuator開放的端點

Actuator只需要加載依賴和配置即可使用,啓動時候會提示你Actuator暴漏了哪些接口,如:

...WebMvcEndpointHandlerMapping] Mapped "{[/actuator/super],methods=[POST],consumes=[application/vnd.spring-boot.actuator.v2+json || application/json],produces=[application/vnd.spring-boot.actuator.v2+json || application/json]}" 
...WebMvcEndpointHandlerMapping] Mapped "{[/actuator/super],methods=[GET],produces=[application/vnd.spring-boot.actuator.v2+json || application/json]}" 
...WebMvcEndpointHandlerMapping] Mapped "{[/actuator/super/{arg0}],methods=[GET],produces=[application/vnd.spring-boot.actuator.v2+json || application/json]}"
...WebMvcEndpointHandlerMapping] Mapped "{[/actuator/auditevents],methods=[GET],produces=[application/vnd.spring-boot.actuator.v2+json || application/json]}"
...WebMvcEndpointHandlerMapping] Mapped "{[/actuator/beans],methods=[GET],produces=[application/vnd.spring-boot.actuator.v2+json || application/json]}" 
...WebMvcEndpointHandlerMapping] Mapped "{[/actuator/health],methods=[GET],produces=[application/vnd.spring-boot.actuator.v2+json || application/json]}" 
...WebMvcEndpointHandlerMapping] Mapped "{[/actuator/conditions],methods=[GET],produces=[application/vnd.spring-boot.actuator.v2+json || application/json]}" 
...WebMvcEndpointHandlerMapping] Mapped "{[/actuator/configprops],methods=[GET],produces=[application/vnd.spring-boot.actuator.v2+json || application/json]}" 
...WebMvcEndpointHandlerMapping] Mapped "{[/actuator/env],methods=[GET],produces=[application/vnd.spring-boot.actuator.v2+json || application/json]}" 
...WebMvcEndpointHandlerMapping] Mapped "{[/actuator/env/{toMatch}],methods=[GET],produces=[application/vnd.spring-boot.actuator.v2+json || application/json]}" 
...WebMvcEndpointHandlerMapping] Mapped "{[/actuator/info],methods=[GET],produces=[application/vnd.spring-boot.actuator.v2+json || application/json]}" 
...WebMvcEndpointHandlerMapping] Mapped "{[/actuator/logfile],methods=[GET],produces=[application/octet-stream]}" 
...WebMvcEndpointHandlerMapping] Mapped "{[/actuator/loggers],methods=[GET],produces=[application/vnd.spring-boot.actuator.v2+json || application/json]}"
...WebMvcEndpointHandlerMapping] Mapped "{[/actuator/loggers/{name}],methods=[POST],consumes=[application/vnd.spring-boot.actuator.v2+json || application/json]}"
...WebMvcEndpointHandlerMapping] Mapped "{[/actuator/loggers/{name}],methods=[GET],produces=[application/vnd.spring-boot.actuator.v2+json || application/json]}" 
...WebMvcEndpointHandlerMapping] Mapped "{[/actuator/heapdump],methods=[GET],produces=[application/octet-stream]}" 
...WebMvcEndpointHandlerMapping] Mapped "{[/actuator/threaddump],methods=[GET],produces=[application/vnd.spring-boot.actuator.v2+json || application/json]}" 
...WebMvcEndpointHandlerMapping] Mapped "{[/actuator/metrics],methods=[GET],produces=[application/vnd.spring-boot.actuator.v2+json || application/json]}" 
...WebMvcEndpointHandlerMapping] Mapped "{[/actuator/metrics/{requiredMetricName}],methods=[GET],produces=[application/vnd.spring-boot.actuator.v2+json || application/json]}" 
...WebMvcEndpointHandlerMapping] Mapped "{[/actuator/scheduledtasks],methods=[GET],produces=[application/vnd.spring-boot.actuator.v2+json || application/json]}" 
...WebMvcEndpointHandlerMapping] Mapped "{[/actuator/httptrace],methods=[GET],produces=[application/vnd.spring-boot.actuator.v2+json || application/json]}" 
...WebMvcEndpointHandlerMapping] Mapped "{[/actuator/mappings],methods=[GET],produces=[application/vnd.spring-boot.actuator.v2+json || application/json]}" 
...WebMvcEndpointHandlerMapping] Mapped "{[/actuator],methods=[GET],produces=[application/vnd.spring-boot.actuator.v2+json || application/json]}" 

這裏面的/actuator/super是我自定義的,下面會說明,其他的都是actuator自己的endpoint。

大致有這些:

在這裏插入圖片描述

3.2 Actuator路徑

Actuator開放的端口都可以直接訪問的,根據提示的get/post請求即可:

  • get請求返回的都是json,有的可以帶路徑篩選;
  • post請求是json數據。返回也是json。

下面請求下127.0.0.1:8080/actuator:

{
    "_links": {
        "self": {
            "href": "http://127.0.0.1:8080/actuator",
            "templated": false
        },
        "super": {
            "href": "http://127.0.0.1:8080/actuator/super",
            "templated": false
        },
        "super-arg0": {
            "href": "http://127.0.0.1:8080/actuator/super/{arg0}",
            "templated": true
        },
        "auditevents": {
            "href": "http://127.0.0.1:8080/actuator/auditevents",
            "templated": false
        },
        "beans": {
            "href": "http://127.0.0.1:8080/actuator/beans",
            "templated": false
        },
        "health": {
            "href": "http://127.0.0.1:8080/actuator/health",
            "templated": false
        },
        "conditions": {
            "href": "http://127.0.0.1:8080/actuator/conditions",
            "templated": false
        },
        "configprops": {
            "href": "http://127.0.0.1:8080/actuator/configprops",
            "templated": false
        },
        "env": {
            "href": "http://127.0.0.1:8080/actuator/env",
            "templated": false
        },
        "env-toMatch": {
            "href": "http://127.0.0.1:8080/actuator/env/{toMatch}",
            "templated": true
        },
        "info": {
            "href": "http://127.0.0.1:8080/actuator/info",
            "templated": false
        },
        "logfile": {
            "href": "http://127.0.0.1:8080/actuator/logfile",
            "templated": false
        },
        "loggers": {
            "href": "http://127.0.0.1:8080/actuator/loggers",
            "templated": false
        },
        "loggers-name": {
            "href": "http://127.0.0.1:8080/actuator/loggers/{name}",
            "templated": true
        },
        "heapdump": {
            "href": "http://127.0.0.1:8080/actuator/heapdump",
            "templated": false
        },
        "threaddump": {
            "href": "http://127.0.0.1:8080/actuator/threaddump",
            "templated": false
        },
        "metrics": {
            "href": "http://127.0.0.1:8080/actuator/metrics",
            "templated": false
        },
        "metrics-requiredMetricName": {
            "href": "http://127.0.0.1:8080/actuator/metrics/{requiredMetricName}",
            "templated": true
        },
        "scheduledtasks": {
            "href": "http://127.0.0.1:8080/actuator/scheduledtasks",
            "templated": false
        },
        "httptrace": {
            "href": "http://127.0.0.1:8080/actuator/httptrace",
            "templated": false
        },
        "mappings": {
            "href": "http://127.0.0.1:8080/actuator/mappings",
            "templated": false
        }
    }
}

3.3 自定義Endpoint

一般情況下,是沒必要自定義Endpoint的,但是也不排除特殊情況,我這裏自定義一個Endpoint,用來往request裏放一個user對象,這個user是用來做測試的,用於下面突破filter用的(下面再說),這裏先說怎麼增查這個user。

過程如下:

  • 使用@Endpoint註解相應的類,作爲Actuator的一個endpoint。註解要指定id,這個id作爲訪問路徑,比如這裏是/actuator/super;
  • @ReadOperation來註解查詢接口,如果要根據路徑做查詢,要用@Selector註解方法參數;注意這地方是@Selector String arg0,這個arg0不能改變,改成其他的,開放出去的接口還是/{arg0},這就導致你的方法無法正常獲取參數值。
  • @WriteOperation 來註解修改接口,注意請求數據必須是json,而且參數不像controller中那麼靈活,不能將實體作爲參數,要把實體中相應的屬性拿出來做參數。
  • 這裏在增加用戶時,往request裏放一個user對象。

SuperEndPoint :

package com.cff.springbootwork.actuator.endpoint;

import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import javax.servlet.http.HttpServletRequest;

import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
import org.springframework.boot.actuate.endpoint.annotation.Selector;
import org.springframework.boot.actuate.endpoint.annotation.WriteOperation;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

@Endpoint(id = "super")
public class SuperEndPoint {
	private Map<String, SuperUser> users = new ConcurrentHashMap<>();

	@ReadOperation
	public Set<String> users() {
		return users.keySet();
	}

	@ReadOperation
	public SuperUser usersIdentify(@Selector String arg0) {
		return users.get(arg0);
	}

	@WriteOperation
	public Set<String> set(String userName, String passwd) {
		HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
				.getRequest();
		if (request != null) {
			SuperUser superUser = new SuperUser();
			superUser.setUserName(userName);
			superUser.setPasswd(passwd);
			request.getSession().setAttribute("superUser", superUser);

			users.put(superUser.getUserName(), superUser);
		}

		return users.keySet();
	}

	public static class SuperUser {
		private String userName;
		private String passwd;

		public String getUserName() {
			return userName;
		}

		public void setUserName(String userName) {
			this.userName = userName;
		}

		public String getPasswd() {
			return passwd;
		}

		public void setPasswd(String passwd) {
			this.passwd = passwd;
		}
	}
}

還要將Endpoint註冊爲bean

MvcEndPointConfig:

package com.cff.springbootwork.actuator;

import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnEnabledEndpoint;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.cff.springbootwork.actuator.endpoint.SuperEndPoint;

@Configuration
@ServletComponentScan 
public class MvcEndPointConfig {

	@Bean
	@ConditionalOnEnabledEndpoint
	public SuperEndPoint superEndPoint() {
		return new SuperEndPoint();
	}
}

四、使用Filter對訪問actuator做限制

上面已經說了,actuator的接口要做保護,我這裏就用filter對接口做最簡單的保護。

  • 對/actuator/*下所有路徑做過濾,並用actuator.filter.switch屬性對filter做開關;
  • 如果時/actuator/super路徑的post操作,放行它,它將會往request中放一個對象;
  • 其他/actuator/*下路徑要判斷request中有沒有user對象,沒有就返回錯誤提示。

ActuatorPermissionFilter :


package com.cff.springbootwork.actuator.filter;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;

import com.fasterxml.jackson.databind.ObjectMapper;

@WebFilter(urlPatterns = "/actuator/*", filterName = "actuatorPermissionFilter")
@Order(1) // 指定過濾器的執行順序,值越大越靠後執行
public class ActuatorPermissionFilter implements Filter {
	private String excludePath = "actuator/super";
	@Value("${actuator.filter.switch}")
	Boolean actuatorSwitch;

	@Override
	public void init(FilterConfig filterConfig) {

	}

	@Override
	public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
			throws IOException, ServletException {
		HttpServletRequest request = (HttpServletRequest) servletRequest;
		HttpServletResponse response = (HttpServletResponse) servletResponse;
		if (actuatorSwitch && !(request.getRequestURI().endsWith(excludePath)
				&& request.getMethod().equals(HttpMethod.POST.toString()))) {
			Object user = request.getSession().getAttribute("superUser");
			if (user == null) {
				// 未登錄,返回數據
				ObjectMapper mapper = new ObjectMapper();
				response.setStatus(HttpStatus.OK.value());
				response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
				mapper.writeValue(response.getWriter(), "您沒有權限訪問該接口,請使用自定義的登錄接口設置superUser後使用!");
				return;
			}
		}
		filterChain.doFilter(servletRequest, servletResponse);

	}

	@Override
	public void destroy() {
	}

}

五、Spring Boot Monitor做監控頁面

Spring Boot Monitor是一個對Spring boot admin監控工具做修改並適配單機的監控工具,完美繼承了Spring boot admin的風格,直接使用actuator的指標進行顯示。

Spring Boot Monitor官網:https://www.pomit.cn/SpringBootMonitor

前面maven依賴中,已經說明依賴spring-boot-monitor,這時,無需其他配置.

訪問http://127.0.0.1:8080/monitor, 自動跳轉到Spring Boot Monitor的監控頁面。

在這裏插入圖片描述

Spring Boot Monitor的監控頁面和Spring boot admin的一模一樣,前端的功能也一模一樣。

在這裏插入圖片描述

可以對Spring boot的各項指標一目瞭然,還可以進行簡單的操作。

當然,如果Spring boot actuator的指標被限制了,它也拿不到相應的指標了,因爲它是直接請求actuator接口的。

品茗IT-博客專題:https://www.pomit.cn/lecture.html彙總了Spring專題Springboot專題SpringCloud專題web基礎配置專題。

快速構建項目

Spring項目快速開發工具:

一鍵快速構建Spring項目工具

一鍵快速構建SpringBoot項目工具

一鍵快速構建SpringCloud項目工具

一站式Springboot項目生成

Mysql一鍵生成Mybatis註解Mapper

Spring組件化構建

SpringBoot組件化構建

SpringCloud服務化構建

喜歡這篇文章麼,喜歡就加入我們一起討論SpringBoot使用吧!
品茗IT交流羣

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