服务监控与健康检查

监控维度
  • 第一,提供健康检测接口。传统采用 ping 的方式对应用进行探活检测并不准确。有的时候,应用的关键内部或外部依赖已经离线,导致其根本无法正常工作,但其对外的 Web 端口或管理端口是可以 ping 通的。我们应该提供一个专有的监控检测接口,并尽可能触达一些内部组件。
  • 第二,暴露应用内部信息。应用内部诸如线程池、内存队列等组件,往往在应用内部扮演了重要的角色,如果应用或应用框架可以对外暴露这些重要信息,并加以监控,那么就有可能在诸如 OOM 等重大问题暴露之前发现蛛丝马迹,避免出现更大的问题。
  • 第三,建立应用指标 Metrics 监控。Metrics 可以翻译为度量或者指标,指的是对于一些关键信息以可聚合的、数值的形式做定期统计,并绘制出各种趋势图表。这里的指标监控,包括两个方面:一是,应用内部重要组件的指标监控,比如 JVM 的一些指标、接口的 QPS等;二是,应用的业务数据的监控,比如电商订单量、游戏在线人数等。
Spring Actuator监控
  • 配置如下:
management.server.port=8081
management.endpoints.web.exposure.include=*
management.endpoints.web.base-path=/admin
 
<dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

在这里插入图片描述

大部分端点提供的是只读信息,比如查询 Spring 的 Bean、ConfigurableEnvironment、定时任务、SpringBoot 自动配置、Spring MVC 映射等;少部分端点还提供了修改功能,比如优雅关闭程序、下载线程 Dump、下载堆 Dump、修改日志级别等。

Spring Boot Admin,它把大部分 Actuator 端点提供的功能封装为了 Web UI。

在这里插入图片描述

Spring Boot Actuator 帮我们预先实现了诸如数据库、InfluxDB、Elasticsearch、Redis、RabbitMQ 等三方系统的健康检测指示器 HealthIndicator。通过 Spring Boot 的自动配置,这些指示器会自动生效。当这些组件有问题的时候,HealthIndicator 会返回 DOWN 或 OUT_OF_SERVICE 状态,health 端点 HTTP 响应状态码也会变为 503,我们可以以此来配置程序健康状态监控报警。

1、实现一个单接口健康检查
@Component
@Slf4j
public class UserServiceHealthIndicator implements HealthIndicator {

    @Resource
    private RestTemplate restTemplate;

    @Override
    public Health health() {
        long begin = System.currentTimeMillis();
        long userId = 1L;
        User user = null;
        try {
            user = restTemplate.getForObject("http://localhost:8080//v1/api/user/1", User.class);
            if (user != null) {
                return Health.up()
                        .withDetail("user", user)
                        .withDetail("time", System.currentTimeMillis() - begin)
                        .build();
            } else {
                return Health.down()
                        .withDetail("time", System.currentTimeMillis() - begin)
                        .build();
            }

        }  catch (Exception ex) {
            log.warn("health check failed!", ex);
            return Health.down(ex).withDetail("time", System.currentTimeMillis() - begin).build();
        }
    }
}

可以看到接口耗时及响应的结果:
在这里插入图片描述

2、线程池聚合监控
  • 定义线程池:
public class ThreadPoolProvider {

    private static ThreadPoolExecutor demoThreadPool = new ThreadPoolExecutor(
            1,
            1,
            2,
            TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(10),
            new ThreadFactoryBuilder().setNameFormat("demo-threadpool-%d").build()
    );

    private static ThreadPoolExecutor ioThreadPool = new ThreadPoolExecutor(
            10,
            50,
            2,
            TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(100),
            new ThreadFactoryBuilder().setNameFormat("io-threadpool-%d").build()
    );

    public static ThreadPoolExecutor getDemoThreadPool(){
        return demoThreadPool;
    }
    public static ThreadPoolExecutor getIOThreadPool() {
        return ioThreadPool;
    }
}

  • 配置需要监控指标
public class ThreadPoolHealthIndicator implements HealthIndicator {


    private ThreadPoolExecutor threadPool;

    public ThreadPoolHealthIndicator(ThreadPoolExecutor threadPoolExecutor) {
        this.threadPool = threadPoolExecutor;
    }

    @Override
    public Health health() {
        Map<String, Integer> detail = new HashMap<>();
        detail.put("queue_size", threadPool.getQueue().size());
        detail.put("queue_remaining", threadPool.getQueue().remainingCapacity());
        if (threadPool.getQueue().remainingCapacity() > 0) {
            return Health.up().withDetails(detail).build();
        } else {
            return Health.down().withDetails(detail).build();
        }
    }
}

@Component
public class ThreadPoolsHealthContributor implements CompositeHealthContributor {

    private Map<String, HealthContributor> contributors = new HashMap<>();

    ThreadPoolsHealthContributor() {
        contributors.put("demoThreadPool", new ThreadPoolHealthIndicator(ThreadPoolProvider.getDemoThreadPool()));
        contributors.put("ioThreadPool", new ThreadPoolHealthIndicator(ThreadPoolProvider.getDemoThreadPool()));
    }

    @Override
    public HealthContributor getContributor(String name) {
        return  contributors.get(name);
    }

    @Override
    public Iterator<NamedContributor<HealthContributor>> iterator() {
        return contributors.entrySet().stream().map((entry) ->
                NamedContributor.of(entry.getKey(), entry.getValue())).iterator();
    }
}

在这里插入图片描述

3、向外暴露数据
  • 使用 info 的 HTTP 端点

以上面线程池为例, 可以对外展示当前线程池状态:

@Component
public class ThreadPoolInfoContributor implements InfoContributor {

    private static Map threadPoolInfo(ThreadPoolExecutor threadPool) {
        java.util.Map<String, Object> info = new HashMap<>();
        info.put("poolSize", threadPool.getPoolSize());//当前池大小
        info.put("corePoolSize", threadPool.getCorePoolSize());//设置的核心池大小
        info.put("largestPoolSize", threadPool.getLargestPoolSize());//最大达到过
        info.put("maximumPoolSize", threadPool.getMaximumPoolSize());//设置的最大
        info.put("completedTaskCount", threadPool.getCompletedTaskCount());//总
        return info;
    }

    @Override
    public void contribute(Info.Builder builder) {
        builder.withDetail("demoThreadPool", threadPoolInfo(ThreadPoolProvider.getDemoThreadPool()));
        builder.withDetail("ioThreadPool", threadPoolInfo(ThreadPoolProvider.getIOThreadPool()));
    }
}

在这里插入图片描述

  • JMX MBean

注:开启JMX[spring.jmx.enabled=true]
在这里插入图片描述

其他监控
  • 日志 Logging
  • 指标 Metrics 和追
  • 全链路追踪 Tracing
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值