深入SpringBoot:自定义Endpoint

简介: 前言 上一篇文章介绍了SpringBoot的PropertySourceLoader,自定义了Json格式的配置文件加载。这里再介绍下EndPoint,并通过自定EndPoint来介绍实现原理。 Endpoint SpringBoot的Endpoint主要是用来监控应用服务的运行状况,并集成在Mvc中提供查看接口。

前言

上一篇文章介绍了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
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。

 

相关文章
|
前端开发 Java fastjson
Netty系列(一):Springboot整合Netty,自定义协议实现
Netty是由JBOSS提供的一个java开源框架,现为 [Github](https://github.com/netty/netty)上的独立项目。Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。
983 0
Netty系列(一):Springboot整合Netty,自定义协议实现
|
Java Maven 开发工具
《SpringBoot系列七》:SprinBoot自定义自动装配类与xxx-spring-boot-starter
《SpringBoot系列七》:SprinBoot自定义自动装配类与xxx-spring-boot-starter
271 0
《SpringBoot系列七》:SprinBoot自定义自动装配类与xxx-spring-boot-starter
|
前端开发 Java Nacos
《SpringBoot系列三》:自定义配置时@Value和@ConfigurationProperties孰优孰劣?
《SpringBoot系列三》:自定义配置时@Value和@ConfigurationProperties孰优孰劣?
734 1
《SpringBoot系列三》:自定义配置时@Value和@ConfigurationProperties孰优孰劣?
|
Java Spring
Spring Boot 系列三:如何自定义一个SpringBoot Srarter
​ 目录 如何自定义一个SpringBoot Srarter? 首先创建一个项目,命名为demo-spring-boot-starter,引入SpringBoot相关依赖         <dependency>             <groupId>org.springframework.boot</groupId>             <artifactId>spring-boot-starter</artifactId>         </dependency>         <dependency>             <groupId>org.spr
317 2
Spring Boot 系列三:如何自定义一个SpringBoot Srarter
|
Java Spring
自定义SpringBoot项目的启动Banner
``Banner``是``SpringBoot``框架一个特色的部分,其设计的目的无非就是一个框架的标识,其中包含了版本号、框架名称等内容,既然``SpringBoot``为我们提供了这个模块,它肯定也是可以更换的这也是``Spring``开源框架的设计理念。
|
Java Spring 容器
《SpringBoot系列十二》:如何自定义条件装配(由@ConditionalOnClass推导)
《SpringBoot系列十二》:如何自定义条件装配(由@ConditionalOnClass推导)
397 0
《SpringBoot系列十二》:如何自定义条件装配(由@ConditionalOnClass推导)
|
Java Spring
《SpringBoot系列十》:SpringBoot自定义Banner
《SpringBoot系列十》:SpringBoot自定义Banner
317 0
《SpringBoot系列十》:SpringBoot自定义Banner
|
NoSQL Java Redis
如何自定义一个SpringBoot中的starter
如何自定义一个SpringBoot中的starter
212 0
如何自定义一个SpringBoot中的starter
|
Java 数据库连接 Maven
SpringBoot——自定义一个spring-boot-starter包
SpringBoot——自定义一个spring-boot-starter包
568 0
SpringBoot——自定义一个spring-boot-starter包
|
监控 IDE Java
使用函数计算自定义运行时快速部署一个 SpringBoot 项目 | 文末有礼
本次体验,使用函数计算部署了一个 springboot 项目,总体来说过程很流畅,只需要在控制台点点就可以生成并部署好一个项目,对于新手来说非常的友好,省去一大堆传统部署项目时的环境搭建,安装依赖等等,可以真正的让我们做到只关心业务逻辑的开发!
使用函数计算自定义运行时快速部署一个 SpringBoot 项目 | 文末有礼