Spring Boot构建系统监控层(六)

本文为博主自学笔记整理,内容来源于互联网,如有侵权,请联系删除。

个人笔记:https://github.com/dbses/TechNotes

01 | 使用 Actuator 组件实现系统监控

引入 Spring Boot Actuator 组件

引入依赖:

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

服务启动后,访问 http://localhost:8080/actuator 可以看到如下结果:

{
  "_links":{
    "self":{
      "href":"http://localhost:8080/actuator",
      "templated":false
    },
    "health-path":{
      "href":"http://localhost:8080/actuator/health/{*path}",
      "templated":true
    },
    "health":{
      "href":"http://localhost:8080/actuator/health",
      "templated":false
    },
    "info":{
      "href":"http://localhost:8080/actuator/info",
      "templated":false
    },
    
    // 上面是默认暴露信息,加上下面就是所有的信息
    
    "beans":{
      "href":"http://localhost:8080/actuator/beans",
      "templated":false
    },
    "conditions":{
      "href":"http://localhost:8080/actuator/conditions",
      "templated":false
    },
    "configprops":{
      "href":"http://localhost:8080/actuator/configprops",
      "templated":false
    },
    "env":{
      "href":"http://localhost:8080/actuator/env",
      "templated":false
    },
    "env-toMatch":{
      "href":"http://localhost:8080/actuator/env/{toMatch}",
      "templated":true
    },
    "loggers":{
      "href":"http://localhost:8080/actuator/loggers",
      "templated":false
    },
    "loggers-name":{
      "href":"http://localhost:8080/actuator/loggers/{name}",
      "templated":true
    },
    "heapdump":{
      "href":"http://localhost:8080/actuator/heapdump",
      "templated":false
    },
    "threaddump":{
      "href":"http://localhost:8080/actuator/threaddump",
      "templated":false
    },
    "metrics-requiredMetricName":{
      "href":"http://localhost:8080/actuator/metrics/{requiredMetricName}",
      "templated":true
    },
    "metrics":{
      "href":"http://localhost:8080/actuator/metrics",
      "templated":false
    },
    "scheduledtasks":{
      "href":"http://localhost:8080/actuator/scheduledtasks",
      "templated":false
    },
    "mappings":{
      "href":"http://localhost:8080/actuator/mappings",
      "templated":false
    }
  }
}

如果我们想看到所有端点,可以在配置文件中配置:

management:
  endpoints:
    web:
      exposure:
        include: "*"

常见端点梳理如下:

image-20210419231958588

通过访问上表中的各个端点,我们就可以获取自己感兴趣的监控信息了。

health 端点

访问 http://localhost:8082/actuator/health 端点,就可以得到服务的基本状态:

{
  "status":"UP",
  // 下面是更详细的信息
  "components":{
    "diskSpace":{
      "status":"UP",
      "details":{
        "total":201649549312,
        "free":3434250240,
        "threshold":10485760
      }
    },
    "ping":{
      "status":"UP"
    }
  }
}

如果想获取更详细的状态信息,可在配置文件中配置:

management: 
  endpoint:
    health:
      show-details: always

扩展 Actuator 端点

Spring Boot 默认暴露了日常开发中最常见的两个端点:Info 端点和 Health 端点。接下来,我们讨论下如何对这两个端点进行扩展。

  • 扩展 Info 端点

Info 端点用于暴露 Spring Boot 应用的自身信息。Spring Boot 中常见的 InfoContributor 如下表所示:

InfoContributor名称描述
EnvironmentInfoContributor暴露 Environment 中 key 为 info 的所有 key
GitInfoContributor暴露 git 信息,如果存在 git.properties 文件
BuildInfoContributor暴露构建信息,如果存在 META-INF/build-info.properties 文件

EnvironmentInfoContributor 会收集 info 下的所有key,例如在 application.yml 中:

info:
  app:
	  encoding: UTF-8
	  java:
	    source: 1.8.0_31
	    target: 1.8.0_31

我们也可以扩展 info 端点的信息:

@Component
public class CustomBuildInfoContributor implements InfoContributor {

    @Override
    public void contribute(Builder builder) {
        builder.withDetail("build", Collections.singletonMap("timestamp", new Date())); 
    }
}

最终效果如下:

{
  "app":{
    "encoding":"UTF-8",
    "java":{
      "source":"1.8.0_31",
      "target":"1.8.0_31"
    }
  },
  "build":{
    "timestamp":1604307503710
  }
}
  • 扩展 Health 端点

Health 端点用于检查正在运行的应用程序健康状态,健康状态信息由 HealthIndicator 对象从 Spring 的 ApplicationContext 中获取。

常见的 HealthIndicator 如下表所示:

HealthIndicator 名称描述
DiskSpaceHealthIndicator检查磁盘空间是否足够
DataSourceHealthIndicator检查是否可以获得连接 DataSource
ElasticsearchHealthIndicator检查 Elasticsearch 集群是否启动
JmsHealthIndicator检查 JMS 代理是否启动
MailHealthIndicator检查邮件服务器是否启动
MongoHealthIndicator检查 Mongo 数据库是否启动
RabbitHealthIndicator检查 RabbitMQ 服务器是否启动
RedisHealthIndicator检查 Redis 服务器是否启动
SolrHealthIndicator检查 Solr 服务器是否已启动

为了明确某个服务的状态,我们可以自定义一个端点来展示这个服务的状态信息:

@Component
public class CustomerServiceHealthIndicator implements HealthIndicator {

    @Override
    public Health health() {
        try {
            URL url = new URL("http://localhost:8083/health/");
            HttpURLConnection conn = (HttpURLConnection) 
            url.openConnection();
            int statusCode = conn.getResponseCode();
            if (statusCode >= 200 && statusCode < 300) {
                return Health.up().build();
            } else {
                return Health.down().withDetail("HTTP Status Code", statusCode).build();
            }
        } catch (IOException e) {
            return Health.down(e).build();
        }
    }
}

效果如下:

{
  "status": "UP",
  "details": {
    "customerservice": {
      "status": "UP"
    }}
}
{
  "status": "DOWN",
  "details": {
    "customerservice": {
      "status": "DOWN",
      "details": {
        "HTTP Status Code": "404"
      }
    },}
}
{
  "status": "DOWN",
  "details": {
    "customerservice": {
      "status": "DOWN",
      "details": {
        "error": "java.net.ConnectException: Connection refused: connect"
      }
    },}
}

02 | 实现自定义的度量指标和 Actuator 端点

在 Spring Boot 中,它为我们提供了一个 Metrics 端点用于实现生产级的度量工具。访问 actuator/metrics 端点后,我们将得到如下所示的一系列度量指标。

{
  "names":[
    "jvm.memory.max",
    "jvm.threads.states",
    "jdbc.connections.active",
    "jvm.gc.memory.promoted",
    "jvm.memory.used",
    "jvm.gc.max.data.size",
    "jdbc.connections.max",
    "jdbc.connections.min",
    "jvm.memory.committed",
    "system.cpu.count",
    "logback.events",
    "http.server.requests",
    "jvm.buffer.memory.used",
    "tomcat.sessions.created",
    "jvm.threads.daemon",
    "system.cpu.usage",
    "jvm.gc.memory.allocated",
    "hikaricp.connections.idle",
    "hikaricp.connections.pending",
    "jdbc.connections.idle",
    "tomcat.sessions.expired",
    "hikaricp.connections",
    "jvm.threads.live",
    "jvm.threads.peak",
    "hikaricp.connections.active",
    "hikaricp.connections.creation",
    "process.uptime",
    "tomcat.sessions.rejected",
    "process.cpu.usage",
    "jvm.classes.loaded",
    "hikaricp.connections.max",
    "hikaricp.connections.min",
    "jvm.gc.pause",
    "jvm.classes.unloaded",
    "tomcat.sessions.active.current",
    "tomcat.sessions.alive.max",
    "jvm.gc.live.data.size",
    "hikaricp.connections.usage",
    "hikaricp.connections.timeout",
    "jvm.buffer.count",
    "jvm.buffer.total.capacity",
    "tomcat.sessions.active.max",
    "hikaricp.connections.acquire",
    "process.start.time"
  ]
}

例如我们想了解当前内存的使用情况,就可以通过 actuator/metrics/jvm.memory.used 端点进行获取,如下代码所示。

{
  "name":"jvm.memory.used",
  "description":"The amount of used memory",
  "baseUnit":"bytes",
  "measurements":[
    {
      "statistic":"VALUE",
      "value":115520544
    }
  ],
  "availableTags":[
    {
      "tag":"area",
      "values":[
        "heap",
        "nonheap"
      ]
    },
    {
      "tag":"id",
      "values":[
        "Compressed Class Space",
        "PS Survivor Space",
        "PS Old Gen",
        "Metaspace",
        "PS Eden Space",
        "Code Cache"
      ]
    }
  ]
}

扩展 Metrics 端点

Metrics 指标体系中包含支持 Counter 和 Gauge 这两种级别的度量指标。通过将 Counter 或 Gauge 注入业务代码中,我们就可以记录自己想要的度量指标。其中,Counter 用来暴露 increment() 方法,而 Gauge 用来提供一个 value() 方法。

下面我们以 Counter 为例介绍在业务代码中嵌入自定义 Metrics 指标的方法,如下代码所示:

@Component
public class CounterService {

    public CounterService() {
        Metrics.addRegistry(new SimpleMeterRegistry());
    }
 
    public void counter(String name, String... tags) {
        Counter counter = Metrics.counter(name, tags);
        counter.increment();
    }
}

也可以使用 MeterRegistry 进行计数。比如我们希望系统每创建一个客服工单,就对所创建的工单进行计数:

@Service
public class CustomerTicketService {
    
    @Autowired
    private MeterRegistry meterRegistry;

    public CustomerTicket generateCustomerTicket(Long accountId, String orderNumber) {

        CustomerTicket customerTicket = new CustomerTicket();
        …
        meterRegistry.summary("customertickets.generated.count").record(1);

        return customerTicket;
    }   
}

现在访问 actuator/metrics/customertickets.generated.count 端点,就能看到如下信息:

{
  "name":"customertickets.generated.count",
  "measurements":[
    {
      "statistic":"Count",
      "value":1
    },
    {
      "statistic":"Total",
      "value":19
    }
  ]
}

同样可以使用 AbstractRepositoryEventListener 实现上述的效果:

@Component
public class CustomerTicketMetrics extends AbstractRepositoryEventListener<CustomerTicket> {

    private MeterRegistry meterRegistry;

    public CustomerTicketMetrics(MeterRegistry meterRegistry) {
        this.meterRegistry = meterRegistry;
    }

    @Override
    protected void onAfterCreate(CustomerTicket customerTicket) {
        meterRegistry.counter("customerticket.created.count").increment();
    }
}

自定义 Actuator 端点

假设我们需要提供一个监控端点以获取当前系统的用户信息和计算机名称,就可以通过一个独立的 MySystemEndPoint 进行实现,如下代码所示:

@Configuration
@Endpoint(id = "mysystem", enableByDefault=true)
public class MySystemEndpoint { 
 
    @ReadOperation
    public Map<String, Object> getMySystemInfo() {
        Map<String,Object> result= new HashMap<>();
        Map<String, String> map = System.getenv();
        result.put("username",map.get("USERNAME"));
        result.put("computername",map.get("COMPUTERNAME"));
        return result;
    }
}

@ReadOperation 注解用于标识读取数据操作。在 Actuator 中,还提供 @WriteOperation 和 @DeleteOperation 注解。

现在,通过访问 http://localhost:8080/actuator/mysystem,我们就能获取如下所示监控信息。

{
  "computername":"LAPTOP-EQB59J5P",
  "username":"user"
}

有时为了获取特定的度量信息,我们需要对某个端点传递参数,而 Actuator 专门提供了一个 @Selector 注解标识输入参数,示例代码如下所示:

@Configuration
@Endpoint(id = "account", enableByDefault = true)
public class AccountEndpoint {
    
    @Autowired
    private AccountRepository accountRepository;    
 
    @ReadOperation
    public Map<String, Object> getMySystemInfo(@Selector String arg0) {
        Map<String, Object> result = new HashMap<>();
        result.put(accountName, accountRepository.findAccountByAccountName(arg0));
        return result;
    }
}

接着,使用 http://localhost:8080/actuator/ account/account1 这样的端口地址就可以触发度量操作了。

03 | 使用 Admin Server 管理 Spring 应用程序

Spring Boot Admin 是一个用于监控 Spring Boot 的应用程序,它的基本原理是通过统计、集成 Spring Boot Actuator 中提供的各种 HTTP 端点,从而提供简洁的可视化 WEB UI。

Spring Boot Admin 的整体架构中存在两大角色,服务器端组件 Admin Server 和客户端组件 Admin Client。Admin Client 实际上是一个普通的 Spring Boot 应用程序,而 Admin Server 则是一个独立服务,需要进行专门构建。如下图所示:

image-20210421231437846

构建 Admin Server 的两种实现方式:一种是简单的基于独立的 Admin 服务;另一种需要依赖服务注册中心的服务注册和发现机制。

基于独立服务构建 Admin Server

创建 Spring Boot 应用程序并引入依赖:

<dependency>
  <groupId>de.codecentric</groupId>
  <artifactId>spring-boot-admin-server</artifactId>
</dependency>

<dependency>
  <groupId>de.codecentric</groupId>
  <artifactId>spring-boot-admin-server-ui</artifactId>
</dependency>

在 Bootstrap 类上添加 @EnableAdminServer 注解:

@SpringBootApplication
@EnableAdminServer
public class AdminApplication {

    public static void main(String[] args) {
        SpringApplication.run(AdminApplication.class, args);
    }
}

接下来我们将应用程序关联到 Admin Server。

引入依赖:

<dependency>
  <groupId>de.codecentric</groupId>
  <artifactId>spring-boot-admin-starter-client</artifactId>
</dependency>

然后,我们在配置文件中添加 Admin Server 的配置信息。

spring:
  boot:
    admin:
      client:
        url: http://localhost:9000

启动应用程序和 Admin Server 后,可以看到如下效果:

image-20210421233714305

基于注册中心构建 Admin Server

基于注册中心,Admin Server 与各个 Admin Client 之间的交互方式如下图所示:

image-20210421232903258

引入注册中心的目的是降低 Admin Client 与 Admin Server 之间的耦合度。

Admin Client 和 Admin Server 都需要使用如下所示的 Eureka 客户端组件。

<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

接着在 Admin Server 的配置文件中配置如下:

eureka:
  client:
    registerWithEureka: true
    fetchRegistry: true
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/

控制访问安全性

有些监控功能不应该暴露给所有的开发人员。因此,我们需要控制 Admin Server 的访问安全性。

引入依赖:

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

然后,我们在配置文件中添加如下配置项:

spring:
  security:
    user:
      name: "springcss_admin"
      password: "springcss_password"

重启 Admin Server 后,再次访问 Web 界面时,就需要我们输入用户名和密码了,如下图所示:

image-20210421234949967
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值