https://blog.csdn.net/u010139373/article/details/107600758
1.简介
1.1 概述
Spring Boot Admin is a community project to manage and monitor your Spring Boot ® applications. The applications register with our Spring Boot Admin Client (via HTTP) or are discovered using Spring Cloud ® (e.g. Eureka, Consul). The UI is just a Vue.js application on top of the Spring Boot Actuator endpoints.
Spring Boot Admin是一个社区项目,用于管理和监视Spring Boot® 应用程序。这些应用程序在我们的Spring Boot Admin Client中注册(通过HTTP),或者使用Spring Cloud®(例如,Eureka,Consul)发现。 UI只是Spring Boot Actuator端点之上的 Vue.js 应用程序。
1.2 特点
-
Show health status:显示健康状况
-
Show details, like:显示详细信息,例如
-
JVM & memory metrics:JVM 和 内存指标
-
micrometer.io metrics:micrometer io指标
-
Datasource metrics:数据源指标
-
Cache metrics:缓存指标
-
-
Show build-info number:显示构建信息编号
-
Follow and download logfile:追踪并下载日志文件
-
View jvm system- & environment-properties:查看 jvm 系统属性和环境属性
-
View Spring Boot Configuration Properties:查看 Spring Boot 配置属性
-
Support for Spring Cloud’s postable /env- &/refresh-endpoint:支持Spring Cloud的可发布/ env-和// refresh-endpoint
-
Easy loglevel management:轻松的日志级别管理
-
Interact with JMX-beans:与JMX-beans交互
-
View thread dump:查看线程转储
-
View http-traces:查看http跟踪
-
View auditevents:查看审计事件
-
View http-endpoints:查看http端点
-
View scheduled tasks:查看定时任务
-
View and delete active sessions (using spring-session):查看和删除活动会话(使用spring-session)
-
View Flyway / Liquibase database migrations:查看Flyway / Liquibase数据库迁移
-
Download heapdump:下载 heapdump
-
Notification on status change (via e-mail, Slack, Hipchat, …):状态更改通知(通过电子邮件,Slack,Hipchat等)
-
Event journal of status changes (non persistent):状态更改的事件日志(非持久性)
2.演示环境
-
JDK 1.8.0_201
-
Spring Boot 2.2.0.RELEASE
-
构建工具(apache maven 3.6.3)
-
开发工具(IntelliJ IDEA )
3.演示代码
3.1 代码说明
使用 spring boot admin 需要两个项目:一个是 admin 项目,一个是 client 项目。admin 项目开启 SpringBoot Admin 功能,client 项目注册到 admin 项目上。
3.2 代码结构
3.3 maven 依赖
spring-boot-admin --> pom.xml
<modules> <module>spring-boot-admin-server</module> <module>spring-boot-admin-client</module> </modules> 1234
spring-boot-admin-server --> pom.xml
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>de.codecentric</groupId> <artifactId>spring-boot-admin-starter-server</artifactId> </dependency> </dependencies> 12345678910
spring-boot-admin-client --> pom.xml
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>de.codecentric</groupId> <artifactId>spring-boot-admin-starter-client</artifactId> </dependency> </dependencies> 12345678910
3.4 配置文件
spring-boot-admin-server --> application.properties
spring.application.name=spring-boot-admin-server server.port=8080 123
spring-boot-admin-client --> application.properties
spring.application.name=spring-boot-admin-client server.port=9090 # 指定admin-server地址 spring.boot.admin.client.url=http://localhost:8080 # 监控所有的endpoint management.endpoints.web.exposure.include=* 1234567
3.5 java代码
SpringBootAdminServerApplication.java
@EnableAdminServer @SpringBootApplication public class SpringBootAdminServerApplication { public static void main(String[] args) { SpringApplication.run(SpringBootAdminServerApplication.class, args); } } 12345678
SpringBootAdminClientApplication.java
@SpringBootApplication public class SpringBootAdminClientApplication { public static void main(String[] args) { SpringApplication.run(SpringBootAdminClientApplication.class, args); } } 1234567
3.6 git 地址
spring-boot/spring-boot-05-basis/spring-boot-admin
4.效果展示
先启动 SpringBootAdminServerApplication.main 方法,再启动 SpringBootAdminClientApplication.main 方法。
访问 http://localhost:8080/,会自动跳转到 Spring Boot Admin 界面,可以看到 spring-boot-admin-client 已经注册上来了
点击 spring-boot-admin-client,可以查看各项监控指标
5.源码分析
5.1 admin server 的请求原理
Spring Boot Admin 组件中,通过 @EnableAdminServer
标识一个 admin server 组件,并启动。EnableAdminServer 的内容如下.
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(AdminServerMarkerConfiguration.class) public @interface EnableAdminServer { } 1234567
EnableAdminServer 通过 @Import 导入一个 AdminServerMarkerConfiguration 配置类。
@Configuration(proxyBeanMethods = false) public class AdminServerMarkerConfiguration { @Bean public Marker adminServerMarker() { return new Marker(); } public static class Marker { } } 123456789101112
AdminServerMarkerConfiguration 中定义了一个静态内部类 Marker,并将它声明为 Bean。那么这个 admin server 又是如何生效的呢?实际上,admin server 有一个自动装配类 AdminServerAutoConfiguration,这个类的内容如下。
@Configuration(proxyBeanMethods = false) @ConditionalOnBean(AdminServerMarkerConfiguration.Marker.class) @EnableConfigurationProperties(AdminServerProperties.class) @ImportAutoConfiguration({ AdminServerInstanceWebClientConfiguration.class, AdminServerWebConfiguration.class }) @AutoConfigureAfter({ WebClientAutoConfiguration.class }) public class AdminServerAutoConfiguration { private final AdminServerProperties adminServerProperties; public AdminServerAutoConfiguration(AdminServerProperties adminServerProperties) { this.adminServerProperties = adminServerProperties; } @Bean @ConditionalOnMissingBean public InstanceRegistry instanceRegistry(InstanceRepository instanceRepository, InstanceIdGenerator instanceIdGenerator) { return new InstanceRegistry(instanceRepository, instanceIdGenerator); } @Bean @ConditionalOnMissingBean public ApplicationRegistry applicationRegistry(InstanceRegistry instanceRegistry, InstanceEventPublisher instanceEventPublisher) { return new ApplicationRegistry(instanceRegistry, instanceEventPublisher); } @Bean @ConditionalOnMissingBean public InstanceIdGenerator instanceIdGenerator() { return new HashingInstanceUrlIdGenerator(); } @Bean @ConditionalOnMissingBean public StatusUpdater statusUpdater(InstanceRepository instanceRepository, InstanceWebClient.Builder instanceWebClientBulder) { return new StatusUpdater(instanceRepository, instanceWebClientBulder.build()); } @Bean(initMethod = "start", destroyMethod = "stop") @ConditionalOnMissingBean public StatusUpdateTrigger statusUpdateTrigger(StatusUpdater statusUpdater, Publisher<InstanceEvent> events) { StatusUpdateTrigger trigger = new StatusUpdateTrigger(statusUpdater, events); trigger.setInterval(this.adminServerProperties.getMonitor().getStatusInterval()); trigger.setLifetime(this.adminServerProperties.getMonitor().getStatusLifetime()); return trigger; } @Bean @ConditionalOnMissingBean public EndpointDetector endpointDetector(InstanceRepository instanceRepository, InstanceWebClient.Builder instanceWebClientBuilder) { InstanceWebClient instanceWebClient = instanceWebClientBuilder.build(); ChainingStrategy strategy = new ChainingStrategy(new QueryIndexEndpointStrategy(instanceWebClient), new ProbeEndpointsStrategy(instanceWebClient, this.adminServerProperties.getProbedEndpoints())); return new EndpointDetector(instanceRepository, strategy); } @Bean(initMethod = "start", destroyMethod = "stop") @ConditionalOnMissingBean public EndpointDetectionTrigger endpointDetectionTrigger(EndpointDetector endpointDetector, Publisher<InstanceEvent> events) { return new EndpointDetectionTrigger(endpointDetector, events); } @Bean @ConditionalOnMissingBean public InfoUpdater infoUpdater(InstanceRepository instanceRepository, InstanceWebClient.Builder instanceWebClientBuilder) { return new InfoUpdater(instanceRepository, instanceWebClientBuilder.build()); } @Bean(initMethod = "start", destroyMethod = "stop") @ConditionalOnMissingBean public InfoUpdateTrigger infoUpdateTrigger(InfoUpdater infoUpdater, Publisher<InstanceEvent> events) { InfoUpdateTrigger trigger = new InfoUpdateTrigger(infoUpdater, events); trigger.setInterval(this.adminServerProperties.getMonitor().getInfoInterval()); trigger.setLifetime(this.adminServerProperties.getMonitor().getInfoLifetime()); return trigger; } @Bean @ConditionalOnMissingBean(InstanceEventStore.class) public InMemoryEventStore eventStore() { return new InMemoryEventStore(); } @Bean(initMethod = "start", destroyMethod = "stop") @ConditionalOnMissingBean(InstanceRepository.class) public SnapshottingInstanceRepository instanceRepository(InstanceEventStore eventStore) { return new SnapshottingInstanceRepository(eventStore); } } 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
AdminServerAutoConfiguration 通过 @ImportAutoConfiguration 导入了 AdminServerWebConfiguration 和
AdminServerInstanceWebClientConfiguration。查看 AdminServerWebConfiguration 内容
@Configuration(proxyBeanMethods = false) public class AdminServerWebConfiguration { private final AdminServerProperties adminServerProperties; public AdminServerWebConfiguration(AdminServerProperties adminServerProperties) { this.adminServerProperties = adminServerProperties; } @Bean public SimpleModule adminJacksonModule() { SimpleModule module = new SimpleModule(); module.addDeserializer(Registration.class, new RegistrationDeserializer()); module.setSerializerModifier(new RegistrationBeanSerializerModifier( new SanitizingMapSerializer(this.adminServerProperties.getMetadataKeysToSanitize()))); return module; } @Bean @ConditionalOnMissingBean public InstancesController instancesController(InstanceRegistry instanceRegistry, InstanceEventStore eventStore) { return new InstancesController(instanceRegistry, eventStore); } @Bean @ConditionalOnMissingBean public ApplicationsController applicationsController(ApplicationRegistry applicationRegistry) { return new ApplicationsController(applicationRegistry); } // reactive web configuration ... @Configuration(proxyBeanMethods = false) @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) @AutoConfigureAfter(WebMvcAutoConfiguration.class) public static class ServletRestApiConfirguation { private final AdminServerProperties adminServerProperties; public ServletRestApiConfirguation(AdminServerProperties adminServerProperties) { this.adminServerProperties = adminServerProperties; } @Bean @ConditionalOnMissingBean public de.codecentric.boot.admin.server.web.servlet.InstancesProxyController instancesProxyController( InstanceRegistry instanceRegistry, InstanceWebClient.Builder instanceWebClientBuilder) { return new de.codecentric.boot.admin.server.web.servlet.InstancesProxyController( this.adminServerProperties.getContextPath(), this.adminServerProperties.getInstanceProxy().getIgnoredHeaders(), instanceRegistry, instanceWebClientBuilder.build()); } @Bean public org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping adminHandlerMapping( ContentNegotiationManager contentNegotiationManager) { org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping mapping = new de.codecentric.boot.admin.server.web.servlet.AdminControllerHandlerMapping( this.adminServerProperties.getContextPath()); mapping.setOrder(0); mapping.setContentNegotiationManager(contentNegotiationManager); return mapping; } } } 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364
默认 servlet 情况下,会使用 ServletRestApiConfirguation 配置,生成 web mvc 的服务。在 ServletRestApiConfirguation 声明了 AdminControllerHandlerMapping 的 RequestMappingHandlerMapping 实例对象。
这个 AdminControllerHandlerMapping 用来处理被 @AdminController 修饰的类
@Override protected boolean isHandler(Class<?> beanType) { return AnnotatedElementUtils.hasAnnotation(beanType, AdminController.class); } 1234
spring-boot-admin-server.jar 中提供了一些被 @AdminController 修饰的类,比如:ApplicationsController、NotificationFilterController、InstancesController、InstancesProxyController 等。ApplicationsController 内容如下。
@AdminController @ResponseBody public class ApplicationsController { private static final Logger log = LoggerFactory.getLogger(ApplicationsController.class); private static final ServerSentEvent<?> PING = ServerSentEvent.builder().comment("ping").build(); private static final Flux<ServerSentEvent<?>> PING_FLUX = Flux.interval(Duration.ZERO, Duration.ofSeconds(10L)) .map((tick) -> PING); private final ApplicationRegistry registry; public ApplicationsController(ApplicationRegistry registry) { this.registry = registry; } @GetMapping(path = "/applications", produces = MediaType.APPLICATION_JSON_VALUE) public Flux<Application> applications() { return registry.getApplications(); } @GetMapping(path = "/applications/{name}", produces = MediaType.APPLICATION_JSON_VALUE) public Mono<ResponseEntity<Application>> application(@PathVariable("name") String name) { return registry.getApplication(name).map(ResponseEntity::ok).defaultIfEmpty(ResponseEntity.notFound().build()); } @GetMapping(path = "/applications", produces = MediaType.TEXT_EVENT_STREAM_VALUE) public Flux<ServerSentEvent<Application>> applicationsStream() { return registry.getApplicationStream().map((application) -> ServerSentEvent.builder(application).build()) .mergeWith(ping()); } @DeleteMapping(path = "/applications/{name}") public Mono<ResponseEntity<Void>> unregister(@PathVariable("name") String name) { log.debug("Unregister application with name '{}'", name); return registry.deregister(name).collectList().map((deregistered) -> !deregistered.isEmpty() ? ResponseEntity.noContent().build() : ResponseEntity.notFound().build()); } @SuppressWarnings("unchecked") private static <T> Flux<ServerSentEvent<T>> ping() { return (Flux<ServerSentEvent<T>>) (Flux) PING_FLUX; } } 123456789101112131415161718192021222324252627282930313233343536373839404142434445
其中 applications() 方法刚好处理 http://localhost:8080/ 跳转后的 http://localhost:8080/applications 请求。
5.2 server 和 client 之间通信
在 spring-boot-admin-server 中有自动装配类 AdminServerAutoConfiguration,相应地,在 spring-boot-admin-client 也有一个自动装配类。它是 SpringBootAdminClientAutoConfiguration。
这个 SpringBootAdminClientAutoConfiguration 声明了如下主要类型
@Configuration(proxyBeanMethods = false) @ConditionalOnWebApplication @Conditional(SpringBootAdminClientEnabledCondition.class) @AutoConfigureAfter({ WebEndpointAutoConfiguration.class, RestTemplateAutoConfiguration.class, WebClientAutoConfiguration.class }) @EnableConfigurationProperties({ ClientProperties.class, InstanceProperties.class, ServerProperties.class, ManagementServerProperties.class }) public class SpringBootAdminClientAutoConfiguration { @Bean @ConditionalOnMissingBean public ApplicationRegistrator registrator(RegistrationClient registrationClient, ClientProperties client, ApplicationFactory applicationFactory) { return new ApplicationRegistrator(applicationFactory, registrationClient, client.getAdminUrl(), client.isRegisterOnce()); } @Bean @ConditionalOnMissingBean public RegistrationApplicationListener registrationListener(ClientProperties client, ApplicationRegistrator registrator, Environment environment) { RegistrationApplicationListener listener = new RegistrationApplicationListener(registrator); listener.setAutoRegister(client.isAutoRegistration()); listener.setAutoDeregister(client.isAutoDeregistration(environment)); listener.setRegisterPeriod(client.getPeriod()); return listener; } @Bean @ConditionalOnMissingBean public StartupDateMetadataContributor startupDateMetadataContributor() { return new StartupDateMetadataContributor(); } @Configuration(proxyBeanMethods = false) @ConditionalOnWebApplication(type = Type.SERVLET) @AutoConfigureAfter(DispatcherServletAutoConfiguration.class) public static class ServletConfiguration { @Bean @ConditionalOnMissingBean public ApplicationFactory applicationFactory(InstanceProperties instance, ManagementServerProperties management, ServerProperties server, ServletContext servletContext, PathMappedEndpoints pathMappedEndpoints, WebEndpointProperties webEndpoint, ObjectProvider<List<MetadataContributor>> metadataContributors, DispatcherServletPath dispatcherServletPath) { return new ServletApplicationFactory(instance, management, server, servletContext, pathMappedEndpoints, webEndpoint, new CompositeMetadataContributor( metadataContributors.getIfAvailable(Collections::emptyList)), dispatcherServletPath); } } @Configuration(proxyBeanMethods = false) @ConditionalOnBean(RestTemplateBuilder.class) public static class BlockingRegistrationClientConfig { @Bean @ConditionalOnMissingBean public BlockingRegistrationClient registrationClient(ClientProperties client) { RestTemplateBuilder builder = new RestTemplateBuilder().setConnectTimeout(client.getConnectTimeout()) .setReadTimeout(client.getReadTimeout()); if (client.getUsername() != null && client.getPassword() != null) { builder = builder.basicAuthentication(client.getUsername(), client.getPassword()); } return new BlockingRegistrationClient(builder.build()); } } // ... } 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263
ApplicationRegistrator 用来注册到 admin-server,其中定义了 register 和 deregister 方法
/** * Registers the client application at spring-boot-admin-server */ public class ApplicationRegistrator { private static final Logger LOGGER = LoggerFactory.getLogger(ApplicationRegistrator.class); private final ConcurrentHashMap<String, LongAdder> attempts = new ConcurrentHashMap<>(); private final AtomicReference<String> registeredId = new AtomicReference<>(); private final ApplicationFactory applicationFactory; private final String[] adminUrls; private final boolean registerOnce; private final RegistrationClient registrationClient; public ApplicationRegistrator(ApplicationFactory applicationFactory, RegistrationClient registrationClient, String[] adminUrls, boolean registerOnce) { this.applicationFactory = applicationFactory; this.adminUrls = adminUrls; this.registerOnce = registerOnce; this.registrationClient = registrationClient; } /** * Registers the client application at spring-boot-admin-server. * @return true if successful registration on at least one admin server */ public boolean register() { Application application = this.applicationFactory.createApplication(); boolean isRegistrationSuccessful = false; for (String adminUrl : this.adminUrls) { LongAdder attempt = this.attempts.computeIfAbsent(adminUrl, (k) -> new LongAdder()); boolean successful = register(application, adminUrl, attempt.intValue() == 0); if (!successful) { attempt.increment(); } else { attempt.reset(); isRegistrationSuccessful = true; if (this.registerOnce) { break; } } } return isRegistrationSuccessful; } protected boolean register(Application application, String adminUrl, boolean firstAttempt) { try { String id = this.registrationClient.register(adminUrl, application); if (this.registeredId.compareAndSet(null, id)) { LOGGER.info("Application registered itself as {}", id); } else { LOGGER.debug("Application refreshed itself as {}", id); } return true; } catch (Exception ex) { if (firstAttempt) { LOGGER.warn( "Failed to register application as {} at spring-boot-admin ({}): {}. Further attempts are logged on DEBUG level", application, this.adminUrls, ex.getMessage()); } else { LOGGER.debug("Failed to register application as {} at spring-boot-admin ({}): {}", application, this.adminUrls, ex.getMessage()); } return false; } } public void deregister() { String id = this.registeredId.get(); if (id == null) { return; } for (String adminUrl : this.adminUrls) { try { this.registrationClient.deregister(adminUrl, id); this.registeredId.compareAndSet(id, null); if (this.registerOnce) { break; } } catch (Exception ex) { LOGGER.warn("Failed to deregister application (id={}) at spring-boot-admin ({}): {}", id, adminUrl, ex.getMessage()); } } } /** * @return the id of this client as given by the admin server. Returns null if the * client has not registered against the admin server yet. */ public String getRegisteredId() { return this.registeredId.get(); } } 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
RegistrationApplicationListener 则是用来触发开始注册和停止注册任务,它实现了 InitializingBean 和 DisposableBean 接口,分别定定义了初始化完成后和销毁前的处理方式。
private final ThreadPoolTaskScheduler taskScheduler; public RegistrationApplicationListener(ApplicationRegistrator registrator) { this(registrator, registrationTaskScheduler()); } // 声明线程池 private static ThreadPoolTaskScheduler registrationTaskScheduler() { ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler(); taskScheduler.setPoolSize(1); taskScheduler.setRemoveOnCancelPolicy(true); taskScheduler.setThreadNamePrefix("registrationTask"); return taskScheduler; } RegistrationApplicationListener(ApplicationRegistrator registrator, ThreadPoolTaskScheduler taskScheduler) { this.registrator = registrator; this.taskScheduler = taskScheduler; } // 实现 InitializingBean 中 afterPropertiesSet 方法 @Override public void afterPropertiesSet() { taskScheduler.afterPropertiesSet(); } // 实现 DisposableBean 中 destroy 方法 @Override public void destroy() { taskScheduler.destroy(); } 12345678910111213141516171819202122232425262728293031
同时,它还接收 ApplicationReadyEvent 事件和 ContextClosedEvent 事件
// 接收 ApplicationReadyEvent 事件 @EventListener @Order(Ordered.LOWEST_PRECEDENCE) public void onApplicationReady(ApplicationReadyEvent event) { if (autoRegister) { // 启动开始注册任务 startRegisterTask(); } } // 接收 ContextClosedEvent 事件 @EventListener @Order(Ordered.LOWEST_PRECEDENCE) public void onClosedContext(ContextClosedEvent event) { if (event.getApplicationContext().getParent() == null || "bootstrap".equals(event.getApplicationContext().getParent().getId())) { // 停止注册任务 stopRegisterTask(); if (autoDeregister) { // 删除注册内容 registrator.deregister(); } } } // 启动注册任务 public void startRegisterTask() { if (scheduledTask != null && !scheduledTask.isDone()) { return; } // 开启定时任务,默认执行周期 10s,定时任务调用 registrator::register 方法 scheduledTask = taskScheduler.scheduleAtFixedRate(registrator::register, registerPeriod); LOGGER.debug("Scheduled registration task for every {}ms", registerPeriod); } // 停止注册任务 public void stopRegisterTask() { if (scheduledTask != null && !scheduledTask.isDone()) { // 取消任务 scheduledTask.cancel(true); LOGGER.debug("Canceled registration task"); } } 123456789101112131415161718192021222324252627282930313233343536373839404142434445
其中 registrator.register 实现如下
public boolean register() { Application application = this.applicationFactory.createApplication(); boolean isRegistrationSuccessful = false; for (String adminUrl : this.adminUrls) { LongAdder attempt = this.attempts.computeIfAbsent(adminUrl, (k) -> new LongAdder()); // 注册 boolean successful = register(application, adminUrl, attempt.intValue() == 0); if (!successful) { attempt.increment(); } else { attempt.reset(); isRegistrationSuccessful = true; if (this.registerOnce) { break; } } } return isRegistrationSuccessful; } protected boolean register(Application application, String adminUrl, boolean firstAttempt) { try { // 调用 registrationClient 的 register 方法 String id = this.registrationClient.register(adminUrl, application); if (this.registeredId.compareAndSet(null, id)) { LOGGER.info("Application registered itself as {}", id); } else { LOGGER.debug("Application refreshed itself as {}", id); } return true; } catch (Exception ex) { if (firstAttempt) { LOGGER.warn( "Failed to register application as {} at spring-boot-admin ({}): {}. Further attempts are logged on DEBUG level", application, this.adminUrls, ex.getMessage()); } else { LOGGER.debug("Failed to register application as {} at spring-boot-admin ({}): {}", application, this.adminUrls, ex.getMessage()); } return false; } } 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849
最终调用 registrationClient.register 方法,它的实现如下
public class BlockingRegistrationClient implements RegistrationClient { private static final ParameterizedTypeReference<Map<String, Object>> RESPONSE_TYPE = new ParameterizedTypeReference<Map<String, Object>>() { }; private final RestTemplate restTemplate; public BlockingRegistrationClient(RestTemplate restTemplate) { this.restTemplate = restTemplate; } // 向指定的 adminUrl 方式 Post 请求 @Override public String register(String adminUrl, Application application) { ResponseEntity<Map<String, Object>> response = this.restTemplate.exchange(adminUrl, HttpMethod.POST, new HttpEntity<>(application, this.createRequestHeaders()), RESPONSE_TYPE); return response.getBody().get("id").toString(); } protected HttpHeaders createRequestHeaders() { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); return HttpHeaders.readOnlyHttpHeaders(headers); } } 12345678910111213141516171819202122232425
registrator.deregister
public void deregister() { String id = this.registeredId.get(); if (id == null) { return; } for (String adminUrl : this.adminUrls) { try { // 调用 registrationClient 的 deregister 方法 this.registrationClient.deregister(adminUrl, id); this.registeredId.compareAndSet(id, null); if (this.registerOnce) { break; } } catch (Exception ex) { LOGGER.warn("Failed to deregister application (id={}) at spring-boot-admin ({}): {}", id, adminUrl, ex.getMessage()); } } } 1234567891011121314151617181920
最终调用 registrationClient.deregister 方法,它的实现如下
@Override public void deregister(String adminUrl, String id) { // 调用 Delete 方法 this.restTemplate.delete(adminUrl + '/' + id); } 12345