Actuator帮助我们监控和管理Spring Boot 应用。

一. SpringBoot Actuator

1. 集成Actuator

1.1 build.gradle

implementation group: 'org.springframework.boot', name: 'spring-boot-starter-actuator', version: springBootVersion
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-security', version: springBootVersion

1.2  applicaiton.properties

management.security.enabled=false
management.endpoints.web.exposure.include=*
management.endpoint.health.showDetails=always

1.3 代码

1.3.1 actuator代码

import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest;
import org.springframework.boot.actuate.health.HealthEndpoint;
import org.springframework.boot.actuate.info.InfoEndpoint;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
@Order(1)
class ActuatorWebSecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.antMatcher("/actuator/**")
                .authorizeRequests() // NOSONAR
                .requestMatchers(EndpointRequest.to(HealthEndpoint.class, InfoEndpoint.class))
                .permitAll()
                .anyRequest()
                .authenticated()
                .and()
                .formLogin();
    }
//以下时需要账号密码登录方式,
//另外需要额外在applicaiton.properties中配置登录账号密码:spring.security.user.name=actuator spring.security.user.password=actuator
//        http
// .authorizeRequests()
// .requestMatchers(EndpointRequest.toAnyEndpoint())
// .hasRole("ADMIN")
// .antMatchers("/")
// .permitAll()
// .antMatchers("/**")
// .authenticated()
// .and()
// .httpBasic();
// }

// }
}

 

1.3.2 actuator集成mongo监控代码(监控多个mongo clusters)

自定义健康监控:CustomHealthIndicator
package com.test.ark.mls.actuator;

import com.test.ark.mls.config.PrimaryMongoConfig;
import com.test.ark.mls.config.SecondaryMongoConfig;
import com.mongodb.client.MongoDatabase;
import lombok.extern.slf4j.Slf4j;
import org.bson.Document;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.actuate.health.AbstractHealthIndicator;
import org.springframework.boot.actuate.health.Health;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.stereotype.Component;

@Component
@Slf4j
public class CustomHealthIndicator extends AbstractHealthIndicator {
  @Autowired private PrimaryMongoConfig primaryMongoConfig;
  @Autowired private SecondaryMongoConfig secondaryMongoConfig;
  @Autowired
  @Qualifier(value="primaryMongoTemplate")
  private MongoTemplate primaryMongoTemplate;
  @Autowired
  @Qualifier(value="secondMongoTemplate")
  private MongoTemplate secondMongoTemplate;

  public CustomHealthIndicator(PrimaryMongoConfig primaryMongoConfig) {
    this.primaryMongoConfig = primaryMongoConfig;
  }

  @Override
  protected void doHealthCheck(Health.Builder builder) throws Exception {
    MongoDatabase database = primaryMongoTemplate.getDb();
    Document doc = new Document();
    doc.append("dbStats", 1);
    doc.append("scale", 1);
    Document collStatsResults = database.runCommand(doc);
    JSONObject jsonValue = new JSONObject(collStatsResults.toJson());
    MongoDatabase database2 = secondMongoTemplate.getDb();
    Document doc2 = new Document();
    doc2.append("dbStats", 1);
    doc2.append("scale", 1);
    Document collStatsResults2 = database2.runCommand(doc2);
    JSONObject jsonValue2 = new JSONObject(collStatsResults2.toJson());
    builder
        .up()
        .withDetail(
            "PrimaryMongoAddress",
            primaryMongoConfig.getUri())
        .withDetail("PrimaryMongoStats", jsonValue.toString())
        .withDetail(
            "SecondMongoAddress",
                secondaryMongoConfig.getUri())
            .withDetail("SecondMongoStats", jsonValue2.toString());
  }
}

 

PrimaryMongoConfig
@Configuration
@Data
@Immutable
@EqualsAndHashCode(callSuper = false)
@Slf4j
public class PrimaryMongoConfig extends AbstractMongoConfig {
  @Value("${spring.data.mongodb.database}")
  private String database;
  @Value("${spring.data.mongodb.uri}")
  private String uri;

  @Autowired
  private CyberarkUtil cyberarkUtil;
  @Primary
  @Override
  @Bean(name="primaryMongoTemplate")
  public MongoTemplate getMongoTemplate() throws Exception {
    String newUri = uri.replace("password", cyberarkUtil.retrievePassword());
    return new MongoTemplate(mongoDatabaseFactory(newUri, database));
  }

  public boolean pingPrimaryMongo(MongoTemplate mongoTemplate) {
    try {
      mongoTemplate.find(new Query(), Map.class);
      log.info("ping mongo success");
      return true;
    } catch (Exception e) {
      log.error("ping mongo with error: ", e);
      return false;
    }
  }

  @Bean
  public GridFsTemplate gridFsTemplate(MappingMongoConverter mappingMongoConverter)
          throws Exception {
    String newUri = uri.replace("password", cyberarkUtil.retrievePassword());
    return new GridFsTemplate(mongoDatabaseFactory(newUri, database), mappingMongoConverter);
  }
}
SecondMongoConfig
@Configuration
@Data
@Immutable
@EqualsAndHashCode(callSuper = false)
@Slf4j
public class SecondaryMongoConfig extends AbstractMongoConfig {

  @Value("${spring.data.mongodb.database2}")
  private String database;

  @Value("${spring.data.mongodb.uri2}")
  private String uri;

  @Value("${spring.data.mongodb.password2}")
  private String password;

  @Override
  @Bean(name = "secondMongoTemplate")
  public MongoTemplate getMongoTemplate() throws Exception {
    String newUri = uri.replace("password", password);
    return new MongoTemplate(mongoDatabaseFactory(newUri, database));
  }
}

 

 AbstractMongoConfig
import com.mongodb.ConnectionString;
import com.mongodb.client.MongoClients;
import org.springframework.data.mongodb.MongoDatabaseFactory;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.SimpleMongoClientDatabaseFactory;

public abstract class AbstractMongoConfig {

    public MongoDatabaseFactory mongoDatabaseFactory(String uri, String database) throws Exception {
        ConnectionString connectionString = new ConnectionString(uri);
        return new SimpleMongoClientDatabaseFactory(MongoClients.create(connectionString), database);
    }

    public abstract MongoTemplate getMongoTemplate() throws Exception;
}

MongoDB properties

spring.data.mongodb.uri=mongodb://FID1:password1@mongoHost1:port1/?replicaSet=MGORPST_3004&ssl=true&sslInvalidHostNameAllowed=true
spring.data.mongodb.uri2=mongodb://FID2:password2@mongoHost2:port2/marketdata?ssl=true&authSource=admin
spring.data.mongodb.database1=test1
spring.data.mongodb.database2=test2

management.security.enabled=false //不启用登录actuator页面验证

management.endpoints.web.exposure.include=*  //放开所有endpoint(不包含shutdown),如果management.endpoints.web.exposure.include=beans,mappings之放开actuator/beans和actuator/mappings

management.endpoint.health.showDetails=always

 

 

调用 http://localhost:8081/actuator/health结果

{
    "status": "UP",
    "components": {
        "custom": {
            "status": "UP",
            "details": {
                "PrimaryMongoAddress": "mongodb://FID:password@host:port/?replicaSet=MGORPST_3004&ssl=true&sslInvalidHostNameAllowed=true",
                "PrimaryMongoStats": "{\"objects\":178082860,\"scaleFactor\":1,\"dataSize\":534560218484,\"indexSize\":13836587008,\"operationTime\":{\"$timestamp\":{\"t\":1671342061,\"i\":5}},\"totalSize\":378842005504,\"$clusterTime\":{\"clusterTime\":{\"$timestamp\":{\"t\":1671342061,\"i\":5}},\"signature\":{\"keyId\":7129165744469704705,\"hash\":{\"$binary\":{\"base64\":\"DlyyV0xsnvCfsDtJ0j\\/0zEdH9XI=\",\"subType\":\"00\"}}}},\"indexes\":388,\"collections\":102,\"storageSize\":365005418496,\"fsUsedSize\":716543451136,\"avgObjSize\":3001.7499633822144,\"ok\":1,\"db\":\"test\",\"views\":0,\"fsTotalSize\":951036878848}",
                "SecondMongoAddress": "mongodb://FID:password@host:port/marketdata?ssl=true&authSource=admin",
                "SecondMongoStats": "{\"objects\":131977,\"scaleFactor\":1,\"dataSize\":312007082,\"indexSize\":13578240,\"operationTime\":{\"$timestamp\":{\"t\":1671342063,\"i\":1}},\"totalSize\":81588224,\"$clusterTime\":{\"clusterTime\":{\"$timestamp\":{\"t\":1671342063,\"i\":1}},\"signature\":{\"keyId\":7151448915269648390,\"hash\":{\"$binary\":{\"base64\":\"uSUyLlJpD0NMq5fCMjcH5l1uKAk=\",\"subType\":\"00\"}}}},\"indexes\":136,\"collections\":40,\"storageSize\":68009984,\"fsUsedSize\":15365464064,\"avgObjSize\":2364.101942005046,\"ok\":1,\"db\":\"test\",\"views\":2,\"fsTotalSize\":48449372160}"
            }
        },
        "diskSpace": {
            "status": "UP",
            "details": {
                "total": 343595282432,
                "free": 98476376064,
                "threshold": 10485760,
                "exists": true
            }
        },
        "mongo": {
            "status": "UP",
            "components": {
                "getReactiveMongoTemplate": {
                    "status": "UP",
                    "details": {
                        "version": "4.4.11"
                    }
                },
                "reactiveMongoTemplate": {
                    "status": "UP",
                    "details": {
                        "version": "4.4.11"
                    }
                }
            }
        },
        "ping": {
            "status": "UP"
        }
    }
}
View Code

 

2.  Actuator详述

访问http://localhost:8099/actuator,查看暴露出来的端点:

注意如果在上例ActuatorWebSecurityConfiguration.java中设置为requestMatchers(EndpointRequest.toAnyEndpoint())则会暴露actuator下所有endpoint,如果设置为requestMatchers(EndpointRequest.to(HealthEndpoint.class, InfoEndpoint.class))则只会暴露health和info这两个端点。比如health提供应用健康情况基础信息;metrics提供一些有用应用程序指标(jvm内存使用、cpu使用等),比如访问 http://localhost:8081/actuator/metrics/jvm.memory.max得到

{"name":"jvm.memory.max","description":"The maximum amount of memory in bytes that can be used for memory management","baseUnit":"bytes","measurements":[{"statistic":"VALUE","value":6.671564799E9}],"availableTags":[{"tag":"area","values":["heap","nonheap"]},{"tag":"id","values":["Compressed Class Space","PS Old Gen","PS Survivor Space","Metaspace","PS Eden Space","Code Cache"]}]}

 

另外每个端点都可以通过配置来单独启停

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

 

HTTP方法Endpoint描述
GET /actuator 查看有哪些 Actuator endpoint 是開放的
GET /actuator/auditevent 查看 audit 的事件,例如認證進入、訂單失敗,需要搭配 Spring security 使用,sample code
GET /actuator/beans 查看運行當下裡面全部的 bean,以及他們的關係
GET /actuator/conditions 查看自動配置的結果,記錄哪些自動配置條件通過了,哪些沒通過
GET /actuator/configprops 查看注入帶有 @ConfigurationProperties 類的 properties 值為何(包含默認值)
GET /actuator/env (常用) 查看全部環境屬性,可以看到 SpringBoot 載入了哪些 properties,以及這些 properties 的值(但是會自動*掉帶有 key、password、secret 等關鍵字的 properties 的值,保護安全資訊)
GET /actuator/flyway 查看 flyway DB 的 migration 資訊
GET /actuator/health (常用) 查看當前 SpringBoot 運行的健康指標,值由 HealthIndicator 的實現類提供(所以可以自定義一些健康指標資訊,加到這裡面)
GET /actuator/heapdump 取得 JVM 當下的 heap dump,會下載一個檔案
GET /actuator/info 查看 properties 中 info 開頭的屬性的值,沒啥用
GET /actuator/mappings 查看全部的 endpoint(包含 Actuator 的),以及他們和 Controller 的關係
GET /actuator/metrics(常用) 查看有哪些指標可以看(ex: jvm.memory.max、system.cpu.usage),要再使用/actuator/metrics/{metric.name}分別查看各指標的詳細資訊
GET /actuator/scheduledtasks 查看定時任務的資訊
POST /actuator/shutdown 唯一一個需要 POST 請求的 endpoint,關閉這個 SpringBoot 程式

 

放开所有endpoint

management.endpoints.web.exposure.include=* 

 只放开beans和mappings

management.endpoints.web.exposure.include=beans,mappings

 exclude可以用来关闭某些endpoints,比如下边语句关闭beans这个endpoint

management.endpoints.web.exposure.exclude=beans

2.1 health 

2.2 metrics端口含义

端点用来返回当前应用的各类重要度量指标,比如:内存信息、线程信息、垃圾回收信息、tomcat、数据库连接池等。

序号参数参数说明是否监控监控手段重要度
JVM
1 jvm.memory.max JVM 最大内存      
2 jvm.memory.committed JVM 可用内存 展示并监控堆内存和 Metaspace 重要
3 jvm.memory.used JVM 已用内存 展示并监控堆内存和 Metaspace 重要
4 jvm.buffer.memory.used JVM 缓冲区已用内存      
5 jvm.buffer.count 当前缓冲区数      
6 jvm.threads.daemon JVM 守护线程数 显示在监控页面  
7 jvm.threads.live JVM 当前活跃线程数 显示在监控页面;监控达到阈值时报警 重要
8 jvm.threads.peak JVM 峰值线程数 显示在监控页面  
9 jvm.classes.loaded 加载 classes 数      
10 jvm.classes.unloaded 未加载的 classes 数      
11 jvm.gc.memory.allocated GC 时,年轻代分配的内存空间      
12 jvm.gc.memory.promoted GC 时,老年代分配的内存空间      
13 jvm.gc.max.data.size GC 时,老年代的最大内存空间      
14 jvm.gc.live.data.size FullGC 时,老年代的内存空间      
15 jvm.gc.pause GC 耗时 显示在监控页面  
TOMCAT
16 tomcat.sessions.created tomcat 已创建 session 数      
17 tomcat.sessions.expired tomcat 已过期 session 数      
18 tomcat.sessions.active.current tomcat 活跃 session 数      
19 tomcat.sessions.active.max tomcat 最多活跃 session 数 显示在监控页面,超过阈值可报警或者进行动态扩容 重要
20 tomcat.sessions.alive.max.second tomcat 最多活跃 session 数持续时间      
21 tomcat.sessions.rejected 超过 session 最大配置后,拒绝的 session 个数 显示在监控页面,方便分析问题  
22 tomcat.global.error 错误总数 显示在监控页面,方便分析问题  
23 tomcat.global.sent 发送的字节数      
24 tomcat.global.request.max request 最长时间      
25 tomcat.global.request 全局 request 次数和时间      
26 tomcat.global.received 全局 received 次数和时间      
27 tomcat.servlet.request servlet 的请求次数和时间      
28 tomcat.servlet.error servlet 发生错误总数      
29 tomcat.servlet.request.max servlet 请求最长时间      
30 tomcat.threads.busy tomcat 繁忙线程 显示在监控页面,据此检查是否有线程夯住  
31 tomcat.threads.current tomcat 当前线程数(包括守护线程) 显示在监控页面 重要
32 tomcat.threads.config.max tomcat 配置的线程最大数 显示在监控页面 重要
33 tomcat.cache.access tomcat 读取缓存次数      
34 tomcat.cache.hit tomcat 缓存命中次数      
CPU
35 system.cpu.count CPU 数量      
36 system.load.average.1m load average 超过阈值报警 重要
37 system.cpu.usage 系统 CPU 使用率      
38 process.cpu.usage 当前进程 CPU 使用率 超过阈值报警  
39 http.server.requests http 请求调用情况 显示 10 个请求量最大,耗时最长的 URL;统计非 200 的请求量 重要
40 process.uptime 应用已运行时间 显示在监控页面  
41 process.files.max 允许最大句柄数 配合当前打开句柄数使用  
42 process.start.time 应用启动时间点 显示在监控页面  
43 process.files.open 当前打开句柄数 监控文件句柄使用率,超过阈值后报警 重要

 2.3. threaddump

方便日常定位问题时候查看线程情况。主要展示线程名、线程id、线程状态、是否等待锁资源、线程堆栈等信息

 2.4. headdump

访问http://localhost:8081/actuator/headdump会自动生成一个jvm堆文件headdump,使用visualvm打开此文件可以查看内存快照

 

2.5. shutdown

这个端口默认不开启,如果想开启需要

management.endpoint.shutdown.enabled=true

二. SpringBoot Admin

springBoot Admin提供了springboot actuator监控的可视化界面

 

SpringBoot Admin是由两组applicaiton构成:applicaiton-server(提供监控可视化界面),application-client(被监控的服务注册到applicaiton-server上)

1. applicaiton server

1.1 依赖

依赖如下,注意点:①由于applicaiton servert提供监控可视化界面,是一个web应用,所以需要导入spring-boot-web

            ②spring-boot-admin-starter-server版本需要和springboot版本一致

implementation group: 'de.codecentric', name: 'spring-boot-admin-starter-server', version: springBootVersion
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: springBootVersion

1.2 Springboot 启动类添加注解@EnableAdminServer

@SpringBootApplication
@EnableAdminServer
public class Application { //NOSONAR

public static void main(String[] args) throws JobParametersInvalidException, JobExecutionAlreadyRunningException, JobRestartException, JobInstanceAlreadyCompleteException {
SpringApplication.run(Application.class, args); //NOSONAR
}
}

1.3 appliciton.properties配置文件

server.port=8082  这个端口号需要在applicaiton-client中指定

1.4 启动application-server服务

http://localhost:8082/applications

由于还没有服务注册上来此时应用数时0

2. application client

2.1 依赖

implementation group: 'de.codecentric', name: 'spring-boot-admin-starter-client', version: springBootVersion

2.2 配置文件

management.security.enabled=true
management.endpoints.web.exposure.include=*
management.endpoint.health.showDetails=always
server.port=8081
spring.applicaiton.name=admin
spring.boot.admin.client.url=http://localhost:8082 --指定applicaition-server地址:port
spring.boot.admin.client.instance.preferIp=true

appliction-client起起来后,再访问http://localhost:8082/applications,可见已经有服务注册上来了。

 

 

 

点进去可以将actuator监控内容通过可视化界面形式展示出来 

 

 

 

另外springboot admin可以配置账号密码登录、发送邮件等功能,这里不再 赘述

 

参考:

https://www.cnblogs.com/caoweixiong/p/15325382.html

https://blog.csdn.net/weixin_46666822/article/details/124483078

posted on 2022-12-18 15:00  colorfulworld  阅读(441)  评论(0编辑  收藏  举报