深入SpringBoot:自定义Endpoint

本文介绍Spring Boot的Endpoint机制及其实现原理,演示如何自定义监控端点以收集应用程序的内存状态。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

上一篇文章介绍了SpringBoot的PropertySourceLoader,自定义了Json格式的配置文件加载。这里再介绍下EndPoint,并通过自定EndPoint来介绍实现原理。

Endpoint

SpringBoot的Endpoint主要是用来监控应用服务的运行状况,并集成在Mvc中提供查看接口。内置的Endpoint比如HealthEndpoint会监控dist和db的状况,MetricsEndpoint则会监控内存和gc的状况。
Endpoint的接口如下,其中invoke()是主要的方法,用于返回监控的内容,isSensitive()用于权限控制。

    public interface Endpoint<T> { String getId(); boolean isEnabled(); boolean isSensitive(); T invoke(); }

Endpoint的加载还是依靠spring.factories实现的。spring-boot-actuator包下的META-INF/spring.factories配置了EndpointAutoConfiguration

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
...
org.springframework.boot.actuate.autoconfigure.EndpointAutoConfiguration,\
...

EndpointAutoConfiguration就会注入必要的Endpoint。有些Endpoint需要外部的收集类,比如TraceEndpoint

    @Bean
    @ConditionalOnMissingBean
    public TraceEndpoint traceEndpoint() { return new TraceEndpoint(this.traceRepository); }

TraceEndpoint会记录每次请求的Request和Response的状态,需要嵌入到Request的流程中,这里就主要用到了3个类。

  1. TraceRepository用于保存和获取Request和Response的状态。
     public interface TraceRepository {
         List<Trace> findAll(); void add(Map<String, Object> traceInfo); }
  2. WebRequestTraceFilter用于嵌入web request,收集请求的状态并保存在TraceRepository中。
  3. TraceEndpointinvoke()方法直接调用TraceRepository保存的数据。
     public class TraceEndpoint extends AbstractEndpoint<List<Trace>> { private final TraceRepository repository; public TraceEndpoint(TraceRepository repository) { super("trace"); Assert.notNull(repository, "Repository must not be null"); this.repository = repository; } public List<Trace> invoke() { return this.repository.findAll(); } }

Endpoint的Mvc接口主要是通过EndpointWebMvcManagementContextConfiguration实现的,这个类的配置也放在spring.factories中。

...
org.springframework.boot.actuate.autoconfigure.ManagementContextConfiguration=\
org.springframework.boot.actuate.autoconfigure.EndpointWebMvcManagementContextConfiguration,\
org.springframework.boot.actuate.autoconfigure.EndpointWebMvcHypermediaManagementContextConfiguration

EndpointWebMvcManagementContextConfiguration注入EndpointHandlerMapping来实现Endpoint的Mvc接口。

    @Bean
    @ConditionalOnMissingBean
    public EndpointHandlerMapping endpointHandlerMapping() { Set<? extends MvcEndpoint> endpoints = mvcEndpoints().getEndpoints(); CorsConfiguration corsConfiguration = getCorsConfiguration(this.corsProperties); EndpointHandlerMapping mapping = new EndpointHandlerMapping(endpoints,corsConfiguration); boolean disabled = this.managementServerProperties.getPort() != null && this.managementServerProperties.getPort() == -1; mapping.setDisabled(disabled); if (!disabled) { mapping.setPrefix(this.managementServerProperties.getContextPath()); } if (this.mappingCustomizers != null) { for (EndpointHandlerMappingCustomizer customizer : this.mappingCustomizers) { customizer.customize(mapping); } } return mapping; }

自定义Endpoint

自定义Endpoint也是类似的原理。这里自定义Endpoint实现应用内存的定时收集。完整的代码放在Github上了。

  1. 收集内存,MemStatus是内存的存储结构,MemCollector是内存的收集类,使用Spring内置的定时功能,每5秒收集当前内存。
     public static class MemStatus {
         public MemStatus(Date date, Map<String, Object> status) { this.date = date; this.status = status; } private Date date; private Map<String, Object> status; public Date getDate() { return date; } public Map<String, Object> getStatus() { return status; } }
     public static class MemCollector {
         private int maxSize = 5; private List<MemStatus> status; public MemCollector(List<MemStatus> status) { this.status = status; } @Scheduled(cron = "0/5 * * * * ? ") public void collect() { Runtime runtime = Runtime.getRuntime(); Long maxMemory = runtime.maxMemory(); Long totalMemory = runtime.totalMemory(); Map<String, Object> memoryMap = new HashMap<String, Object>(2, 1); Date date = Calendar.getInstance().getTime(); memoryMap.put("maxMemory", maxMemory); memoryMap.put("totalMemory", totalMemory); if (status.size() > maxSize) { status.remove(0); status.add(new MemStatus(date, memoryMap)); } else { status.add(new MemStatus(date, memoryMap)); } } }
  2. 自定义Endpoint,getIdEndPoint的唯一标识,也是Mvc接口对外暴露的路径。invoke方法,取出maxMemorytotalMemory和对应的时间。
     public static class MyEndPoint implements Endpoint { private List<MemStatus> status; public MyEndPoint(List<MemStatus> status) { this.status = status; } public String getId() { return "my"; } public boolean isEnabled() { return true; } public boolean isSensitive() { return false; } public Object invoke() { if (status == null || status.isEmpty()) { return "hello world"; } Map<String, List<Map<String, Object>>> result = new HashMap<String, List<Map<String, Object>>>(); for (MemStatus memStatus : status) { for (Map.Entry<String, Object> entry : memStatus.status.entrySet()) { List<Map<String, Object>> collectList = result.get(entry.getKey()); if (collectList == null) { collectList = new LinkedList<Map<String, Object>>(); result.put(entry.getKey(), collectList); } Map<String, Object> soloCollect = new HashMap<String, Object>(); soloCollect.put("date", memStatus.getDate()); soloCollect.put(entry.getKey(), entry.getValue()); collectList.add(soloCollect); } } return result; } }
  3. AutoConfig,注入了MyEndPoint,和MemCollector
     public static class EndPointAutoConfig { private List<MemStatus> status = new ArrayList<MemStatus>(); @Bean public MyEndPoint myEndPoint() { return new MyEndPoint(status); } @Bean public MemCollector memCollector() { return new MemCollector(status); } }
  4. 程序入口,运行后访问http://localhost:8080/my 就可以看到了。

     @Configuration
     @EnableAutoConfiguration
     public class CustomizeEndPoint { public static void main(String[] args) { SpringApplication application = new SpringApplication(CustomizeEndPoint.class); application.run(args); } }

结语

Endpoint也是通过spring.factories实现扩展功能,注入了对应的Bean来实现应用监控的功能。



文/wcong(简书作者)
原文链接:http://www.jianshu.com/p/9fab4e81d7bb
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值