【死磕 SpringBoot】— Actuator之Endpoint原理实战

1.1 Spring Boot Actuator 概述

image-2022.png

Spring Actuator 是 Spring Boot 给我提供的重要的 module,方便我们去构建我们的微服务架构,帮我们解决对微服务应用监控系统监控和运维。

1.2 Spring Boot Actuator 术语

Spring Boot 中关于监控有两个术语:EndpointActuator

Endpoint

中文含义为端点。在 Spring Boot 中 Endpoint 负责对外暴露服务,负责实现一个具体的业务监控指标,提供具体实现,类似于接口,其端点映射到一个 URL,可以采用 HTTP 或者 JMX 协议进行访问,从而实现监控与应用的交互。

Actuator

中文含义为执行器。在 Spring Boot 中 Actuator 负责对 Endpoint 进行自动装配、协议解析、服务调度,是 Endpoint 的功能集合。它集成了很多的监控功能于一体,由于做了大量的底层工作,使得对 Endpoint 的访问和扩展非常方便。

1.3 Spring Boot Actuator 内置Endpoint

HTTP方法 路径 描述 是否敏感信息
GET /autoconfig 查看自动配置的使用情况, 显示一个auto-configuration的报告,该报告展示所有auto-configuration候选者及它们被应用或未被应用的原因 true
GET /configprops 查看配置属性,包括默认配置, 显示一个所有@ConfigurationProperties的整理列表 true
GET /beans bean及其关系列表, 显示一个应用中所有Spring Beans的完整列表 true
GET /dump 打印线程栈 true
GET /env 查看所有环境变量 true
GET /env/{name} 查看具体变量值 true
GET /health 查看应用健康指标, 当使用一个未认证连接访问时显示一个简单的’status’,使用认证连接访问则显示全部信息详情 false
GET /info 查看应用信息 false
GET /mappings 查看所有url映射, 即所有@RequestMapping路径的整理列表 true
GET /metrics 查看应用基本指标 true
GET /metrics/{name} 查看具体指标 true
POST /shutdown 关闭应用,允许应用以优雅的方式关闭(默认情况下不启用) true
GET /trace 查看基本追踪信息,默认为最新的一些HTTP请求 true

实际上官方提供了 Endpoint 有很多,我们可以在 official documentation 上面看到完整的列表。

1.4 Spring Boot Actuator 快速开始

为了快速入手,建立一个 Hello Actuator 的 Web 工程。

建立模块

  • module 名称:springboot-endpoints
  • module 性质:Web 工程

引入依赖包

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

配置文件

  • application.yml
#spring配置
spring:
  #应用配置
  application:
    name: springboot-endpoints
  profiles:
    active: web,server,actuator
  #控制台配置
  output:
    ansi:
      enabled: always
  
 #配置应用信息 
 info:
  app:
    name: "springboot-endpoints"
    description: "springboot-endpoints"
    version: "0.0.1-SNAPSHOT"
     
  • application-server.yml
#服务配置
server:
  port: 8080
  servlet:
    context-path: /springboot-endpoints
  • application-actuator.yml
management:
  server:
    # 指定监端点暴露监听端口
    port: 10101
    
    
    

配置日志

<configuration>
    <!--引用 starter 中 Log.be 的配置-->
    <springProperty scope="context" name="APP_NAME" source="spring.application.name" defaultValue="mon-be"/>
    <conversionRule conversionWord="clr"
                    converterClass="org.springframework.boot.logging.logback.ColorConverter"/>

    <!-- log.be 输出格式 -->
    <property name="CONSOLE_LOG_PATTERN" value="%clr(%d{yyyy-MM-dd HH:mm:ss}]|[%-4level]|[%X{trace_id}]|[%X{biz_id}]|[%X{httpUri}]|[%thread]|[%logger{30}:\(%line\)]|[-- %msg %n)"/>
    <property name="FILE_LOG_PATTERN" value="${FILE_LOG_PATTERN:-%d{yyyy-MM-dd HH:mm:ss}]|[%-4level]|[%X{trace_id}]|[%X{biz_id}]|[%X{httpUri}]|[%thread]|[%logger{30} %line]|[-- %msg %ex%n}"/>

    <include resource="org/springframework/boot/logging/logback/base.xml" />

    <!--自定义配置-->
    <logger name="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping" level="Trace"/>
    <logger name="org.springframework.boot.actuate.endpoint.web.servlet.WebMvcEndpointHandlerMapping" level="Trace"/>
    <logger name="org.springframework.web.servlet.DispatcherServlet" level="Trace"/>
</configuration>

启动服务

启动成功之后我们可以看到控制台的日志如下
image-20220821092144908.png

通过Http访问

http://localhost:10101/actuator

{
    _links: {
        self: {
        href: "http://localhost:10101/actuator",
        templated: false
        },
        health-path: {
        href: "http://localhost:10101/actuator/health/{*path}",
        templated: true
        },
        health: {
        href: "http://localhost:10101/actuator/health",
        templated: false
        },
        info: {
        href: "http://localhost:10101/actuator/info",
        templated: false
        }
    }
}

通过JMX访问

image-20220821092835962.png

访问应用 info端点

http://localhost:10101/actuator/info

{
    app: {
    name: "springboot-endpoints",
    description: "springboot-endpoints",
    version: "0.0.1-SNAPSHOT"
    }
}

访问应用 health端点

http://localhost:10101/actuator/health

{
   status: "UP"
}

访问应用指标端点

http://localhost:10101/actuator/metrics

默认配置下,Spring Boot Actuator Http 服务只暴露了监控检测,应用信息两个端点,info端点 health端点 ,其他端点需要配置

{
    timestamp: "2022-08-21T01:52:57.026+00:00",
    status: 404,
    error: "Not Found",
    message: "",
    path: "/actuator/metrics"
}

小结

  • 依托 Spring Boot 实现监控需求只需要引入 spring-boot-starter-actuator 模块,如果不进行端点功能扩展,Actuator 模块本身就内置了很多关于监控端点的实现,在使用的时候某些端点需要稍加配置,有的则直接使用;
  • 采用 HTTP 协议访问监控端点,其访问根路径是 /actuator,由于监控端点和业务功能是集成在一个微服务里面,建议业务访问的 REST 接口不要采用 Actuator 作为 URL 的前缀;
  • Spring Boot 内置的监控端点其输出内容是可以扩展的,比如 /actuator/info 这个端点就可以在配置文件中进一步配置丰富其输出内容。

1.5 Spring Boot Actuator 端点全局配置

Spring Boot 2.0 默认没有开启全部端点。通过修改 application-actuator.yml 配置

management:
  server:
    # 指定监端点暴露监听端口
    port: 10101
  #配置不同类型(web)
  endpoints:
    # 配置web方式暴露端点
    web:
      # 配置通过http方式暴露端点的根路径
      base-path: "/actuator"
      # 配置http方式暴露端点
      exposure:
        #配置暴露的端点,配置"*"暴露全部
        include: '*'
        #配置不暴露的端点
        exclude:
    # 配置jms方式暴露端点
    jmx:
      # 配置jms方式暴露端点
      exposure:
        #配置暴露的端点,配置"*"暴露全部
        include: '*'
        #配置不暴露的端点
        exclude:

启动服务

启动成功之后我们可以看到控制台的日志如下

image-20220821101412007.png

通过Http访问

http://localhost:10101/actuator

{
    _links: {
        self: {
        href: "http://localhost:10101/actuator",
        templated: false
        },
        beans: {
        href: "http://localhost:10101/actuator/beans",
        templated: false
        },
        caches-cache: {
        href: "http://localhost:10101/actuator/caches/{cache}",
        templated: true
        },
        caches: {
        href: "http://localhost:10101/actuator/caches",
        templated: false
        },
        health: {
        href: "http://localhost:10101/actuator/health",
        templated: false
        },
        health-path: {
        href: "http://localhost:10101/actuator/health/{*path}",
        templated: true
        },
        info: {
        href: "http://localhost:10101/actuator/info",
        templated: false
        },
        conditions: {
        href: "http://localhost:10101/actuator/conditions",
        templated: false
        },
        configprops: {
        href: "http://localhost:10101/actuator/configprops",
        templated: false
        },
        env: {
        href: "http://localhost:10101/actuator/env",
        templated: false
        },
        env-toMatch: {
        href: "http://localhost:10101/actuator/env/{toMatch}",
        templated: true
        },
        logfile: {
        href: "http://localhost:10101/actuator/logfile",
        templated: false
        },
        loggers: {
        href: "http://localhost:10101/actuator/loggers",
        templated: false
        },
        loggers-name: {
        href: "http://localhost:10101/actuator/loggers/{name}",
        templated: true
        },
        heapdump: {
        href: "http://localhost:10101/actuator/heapdump",
        templated: false
        },
        threaddump: {
        href: "http://localhost:10101/actuator/threaddump",
        templated: false
        },
        metrics-requiredMetricName: {
        href: "http://localhost:10101/actuator/metrics/{requiredMetricName}",
        templated: true
        },
        metrics: {
        href: "http://localhost:10101/actuator/metrics",
        templated: false
        },
        scheduledtasks: {
        href: "http://localhost:10101/actuator/scheduledtasks",
        templated: false
        },
        mappings: {
        href: "http://localhost:10101/actuator/mappings",
        templated: false
        }
    }
}

注意:通过 HTTP 协议,可以使用 * 打开所有的端点,但在具体访问某些端点的时候,由于安全和敏感性等原因,还需要增加额外的配置才行。比如用来关闭应用的端点 /actuator/shutdown,还需要增加如下额外配置,在实际访问的时候,还必须使用 post 方法才可以。

通过修改 application-actuator.yml 配置

management:
  server:
    # 指定监端点暴露监听端口
    port: 10101
  #配置不同类型(web)
  endpoints:
    # 配置web方式暴露端点
    web:
      # 配置通过http方式暴露端点的根路径
      base-path: "/actuator"
      # 配置http方式暴露端点
      exposure:
        #配置暴露的端点,配置"*"暴露全部
        include: '*'
        #配置不暴露的端点
        exclude:
    # 配置jms方式暴露端点
    jmx:
      # 配置jms方式暴露端点
      exposure:
        #配置暴露的端点,配置"*"暴露全部
        include: '*'
        #配置不暴露的端点
        exclude:
  #配置指定端点
  endpoint:
    # 配置shutdown 端点
    shutdown:
      #启用shutdown
      enabled: true

1.6 Spring Boot Actuator 原生端端点

应用 info端点

配置应用信息

 #配置应用信息 
 info:
  app:
    name: "springboot-endpoints"
    description: "springboot-endpoints"
    version: "0.0.1-SNAPSHOT"

访问应用 info端点

http://localhost:10101/actuator/info

{
    app: {
    name: "springboot-endpoints",
    description: "springboot-endpoints",
    version: "0.0.1-SNAPSHOT"
    }
}

应用日志端点

查询应用所有日志

http://localhost:10101/actuator/loggers

{
    levels: [
        "OFF",
        "ERROR",
        "WARN",
        "INFO",
        "DEBUG",
        "TRACE"
    ],
    loggers: {
        ROOT: {
        configuredLevel: "INFO",
        effectiveLevel: "INFO"
        },
        ...
        org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping: {
        configuredLevel: "TRACE",
        effectiveLevel: "TRACE"
        },
        org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping: {
        configuredLevel: null,
        effectiveLevel: "INFO"
        },
        org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping$WebMvcEndpointHandlerMethod: {
        configuredLevel: null,
        effectiveLevel: "INFO"
        },
        org.springframework.boot.actuate.endpoint.web.servlet.ControllerEndpointHandlerMapping: {
        configuredLevel: null,
        effectiveLevel: "INFO"
        },
        ....
    }
}

查询应用指定日志

http://localhost:10101/actuator/loggers/org.springframework.boot.actuate.endpoint.web.servlet.WebMvcEndpointHandlerMapping

{
    configuredLevel: "TRACE",
    effectiveLevel: "TRACE"
}

修改应用日志级别

  • 发送一个Post请求,这里请求路径中填写日志名称,参数configuredLevel添加日志修改后日志级别
image-20220821111235341.png
  • 通过请求重新查看日志修改后日志级别

http://localhost:10101/actuator/loggers/org.springframework.boot.actuate.endpoint.web.servlet.WebMvcEndpointHandlerMapping

{
    configuredLevel: "INFO",
    effectiveLevel: "INFO"
}

为什么是 configuredLevel ? 这里留个悬念,具体可以查看 LoggersEndpoint

应用健康检测端点

1.7 Spring Boot Actuator 自定义端点

参考官方文档说明,如果要实现一个端点,需要用 @Endpoint 注解进行标注,在对象内部定义方法,实现端点的具体功能。

阅读官方文档

打开官方文档《自定义端点 Endpoint-“53.7 Implementing Custom Endpoints”》,关键部分摘录如下:

[^]: If you add a @Bean annotated with@Endpoint, any methods annotated with@ReadOperation,@WriteOperation, or@DeleteOperationare automatically exposed over JMX and, in a web application, over HTTP as well. Endpoints can be exposed over HTTP using Jersey, Spring MVC, or Spring WebFlux.You can also write technology-specific endpoints by using @JmxEndpointor @WebEndpoint. These endpoints are restricted to their respective technologies. For example,@WebEndpointis exposed only over HTTP and not over JMX. … …

翻译

[^]: 如果添加用 @Endpoint 注解的 @Bean,那么使用 @ReadOperation、@WriteOperation 或 @DeleteOperation 注解来定义的方法会自动通过 JMX 协议对外公开。当然在 Web 应用中,最好通过 HTTP 协议暴露服务,我们可以使用 Jersey、Spring MVC 或 Spring WebFlux 通过 HTTP 公开端点,使其具备 HTTP 访问能力。您也可以直接使用 @JmxEndpoint 或 @WebEndpoint 注解定义端点,那么这些端点就具备对应协议的访问能力,这些端点仅限于各自的技术。例如,@WebEndpoint 只通过 HTTP 协议访问而不通过 JMX 访问。

自定义应用运行时间的 Endpoint

**自定义应用运行时间的 Endpoint **

package com.wuhao.example.endpoint;


import org.springframework.boot.actuate.endpoint.annotation.DeleteOperation;
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.boot.actuate.endpoint.web.annotation.WebEndpoint;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;


/**
 * 自定义端点:返回程序已经持续运行的时间
 *
 * @Author: wuhao.w
 * @Date: 2020/8/17 20:47
 */
@Component
@WebEndpoint(id = "runtime")
public class RuntimeEndpoint {
    public long starttime = 0L;
    public Map runtimeMap = new HashMap<>();

    public RuntimeEndpoint() {
        starttime = System.currentTimeMillis();
        runtimeMap.put("startTime", starttime);
        runtimeMap.put("currentTime", System.currentTimeMillis());
        runtimeMap.put("interval", 0);
    }

    @ReadOperation
    public Map runtime() {
        long currentTime = System.currentTimeMillis();
        long interval = currentTime - starttime;

        runtimeMap.put("currentTime", currentTime);
        runtimeMap.put("interval", interval);

        return runtimeMap;
    }

    @ReadOperation
    public Map runtimeKey(@Selector String name) {
        Map runtimeKey = new HashMap<>();
        runtimeKey.put(name, runtimeMap.get(name));
        return runtimeKey;
    }

    @WriteOperation
    public void writeRuntimeKey(@Selector String name, String runtimepoint) {
        runtimeMap.put(name, runtimepoint);
    }

    @DeleteOperation
    public void deleteRuntimeKey(@Selector String name) {
        runtimeMap.remove(name);
    }
}
  • @WebEndpoint 注解添加到自定义端点类上包装成具备 HTTP 访问能力端点 (配置端点类)

  • @WebEndpoint 标注的类的方法中添加如下三个注解完成 端点配置 (配置端点类)

    • @ReadOperation : 对应HTTP GET请求,查询端点数据

    • @WriteOperation : 对应HTTP POST请求,修改端点数据

    • DeleteOperation : 对应HTTP DELETE请求,删除端点数据

  • @Selector 标注@ReadOperation方法中描述端点数据的子集 (配置端点类)

自定义端点装配类

package com.wuhao.springboot.endpoint;

import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 自定义端点装配类
 *
 * @Author: wuhao.w
 * @Date: 2022/8/21 12:48
 */
@Configuration
public class RuntimeEndpointAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean
    public RuntimeEndpoint runtimeEndpoint() {
        return new RuntimeEndpoint();
    }
}

建立 META-INF/spring.factories 配置文件

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.wuhao.springboot.endpoint.RuntimeEndpointAutoConfiguration

启动工程访问

http://localhost:10101/actuator/

{
    _links: {
        self: {
        href: "http://localhost:10101/actuator",
        templated: false
        },
        runtime-arg0: {
        href: "http://localhost:10101/actuator/runtime/{arg0}",
        templated: true
        },
        runtime: {
        href: "http://localhost:10101/actuator/runtime",
        templated: false
        },
        ....
    }
}

这里出现了两个新的端点分别对应

   @ReadOperation
    public Map runtime() {
        long currentTime = System.currentTimeMillis();
        long interval = currentTime - starttime;

        runtimeMap.put("currentTime", currentTime);
        runtimeMap.put("interval", interval);

        return runtimeMap;
    }

    @ReadOperation
    public Map runtimeKey(@Selector String name) {
        Map runtimeKey = new HashMap<>();
        runtimeKey.put(name, runtimeMap.get(name));
        return runtimeKey;
    }

@ReadOperation

访问 http://localhost:10101/actuator/runtime

  • 查看应用运行时间端点全部数据
{
    currentTime: 1661055951216,
    startTime: 1661054901799,
    interval: 1049417
}

访问 http://localhost:10101/actuator/runtime/runtimeKey?name=startTime

  • 查看应用运行时间端点指定数据
{
   startTime: 1661054901799
}

@WriteOperation

访问 http://localhost:10101/actuator/runtime/writeRuntimeKey

  • 修改应用运行时间端点数据
image-20220821123635029.png

http://localhost:10101/actuator/runtime/runtimeKey?name=lastCheckTime

{
  lastCheckTime: "2021-06-21 00:00:00"
}

@DeleteOperation

访问 http://localhost:10101/actuator/runtime/deleteRuntimeKey?name=lastCheckTime

image-20220821124406735.png

访问 http://localhost:10101/actuator/runtime/

{
    currentTime: 1661057004163,
    startTime: 1661054901799,
    interval: 2102364
}

1.8 Spring Boot Actuator 原理初探

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,372评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,368评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,415评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,157评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,171评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,125评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,028评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,887评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,310评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,533评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,690评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,411评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,004评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,812评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,693评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,577评论 2 353

推荐阅读更多精彩内容