赞
踩
前言
上一章说了Spring Boot Admin(SBA)的client端自定义management.server.servlet.context-path、management.endpoints.web.base-path来解决一个Tomcat多个实例的问题。但是这个配置eureka instance是SBA Admin端通过eureka server获取的配置不能识别的,SBA Admin从client的配置获取不到监控管理路径,会使用默认/actuator,这显然不然拿到我们定制的信息采集接口。
1. 模拟client
笔者就使用上一章的demo,分别配置management.server.servlet.context-path、management.endpoints.web.base-path来调试源码来确认Spring Cloud的这个坑
配置management.endpoints.web.base-path,management.server.servlet.context-path同理(上一章就是配置的这个)
- #server.port=8180
- spring.application.name=BootClient
-
-
-
- #spring.boot.admin.client.url=http://127.0.0.1:8082
- eureka.client.service-url.defaultZone=http://127.0.0.1:8088/eureka
- eureka.client.registry-fetch-interval-seconds=30
- eureka.instance.lease-renewal-interval-in-seconds=30
- eureka.instance.health-check-url-path=/bootClient/actuator/health
- #eureka.instance.metadata-map.management.context-path=ROOT2
-
-
-
- management.endpoints.web.exposure.include=*
- management.endpoint.health.show-details=always
-
- management.endpoints.web.base-path=/bootClient/actuator

这里把 eureka.instance.metadata-map.management.context-path注释了
2. SBA Admin源码分析
在admin 端打上断点,查看Spring Boot Admin的官方说明,实际上是client与admin保持心跳方式,我们看admin端监听器,可以看到使用了webflux技术
- /**
- * Listener for Heartbeats events to publish all services to the instance registry.
- *
- * @author Johannes Edmeier
- */
- public class InstanceDiscoveryListener {
跟踪registerInstance注册方法,这个方法是异步执行的定期心跳,可配置
- protected Mono<InstanceId> registerInstance(ServiceInstance instance) {
- try {
- Registration registration = converter.convert(instance).toBuilder().source(SOURCE).build();
- log.debug("Registering discovered instance {}", registration);
- return registry.register(registration);
- }
- catch (Exception ex) {
- log.error("Couldn't register instance for discovered instance ({})", toString(instance), ex);
- return Mono.empty();
- }
- }
跟踪注册方法
- @Override
- public Registration convert(ServiceInstance instance) {
- LOGGER.debug("Converting service '{}' running at '{}' with metadata {}", instance.getServiceId(),
- instance.getUri(), instance.getMetadata());
-
- return Registration.create(instance.getServiceId(), getHealthUrl(instance).toString())
- .managementUrl(getManagementUrl(instance).toString()).serviceUrl(getServiceUrl(instance).toString())
- .metadata(getMetadata(instance)).build();
- }
这里面有注册信息的拼接,getHealthUrl(instance)与getManagementUrl(instance)
getHealthUrl(instance),是心跳接口获取URL,也调用了getManagementUrl(instance)
- protected URI getHealthUrl(ServiceInstance instance) {
- return UriComponentsBuilder.fromUri(getManagementUrl(instance)).path("/").path(getHealthPath(instance)).build()
- .toUri();
- }
getManagementUrl(instance), 获取数据信息接口
这里很关键
- protected URI getManagementUrl(ServiceInstance instance) {
- return UriComponentsBuilder.newInstance().scheme(getManagementScheme(instance))
- .host(getManagementHost(instance)).port(getManagementPort(instance)).path("/")
- .path(getManagementPath(instance)).build().toUri();
- }
关键在于
.path(getManagementPath(instance)
前面是拼接http://hostname:port/;关键在于path,就是admin识别的client暴露的actuator接口信息URL
- protected String getManagementPath(ServiceInstance instance) {
- String managementPath = instance.getMetadata().get(DefaultServiceInstanceConverter.KEY_MANAGEMENT_PATH);
- if (!isEmpty(managementPath)) {
- return managementPath;
- }
- return this.managementContextPath;
- }
调试代码,是从client的注册中心的客户端配置读取的,为空就使用默认,eureka server,consul配置都是
management.context-path
只是配置的前缀不一样,这个配置是map里获取的,配置的metadata是map结构。
源码看出
可配置项如上。
默认路径,单独的应用肯定是没问题的
我们如果没有配置,就使用默认
见证本质的地方,这个URL是错误的
页面上也可以看到
3. 解决方法
源码看了,解决方法就很简单了,只要SBA识别的路径正确就可以了。需要配置
eureka.instance.metadata-map.management.context-path
使这个路径地址 = management.server.servlet.context-path + management.endpoints.web.base-path + "/actuator"
配置
management.server.servlet.context-path
要注意,这个是servlet 的content-path,不能和Spring boot的server.context-path同时配置,否则在心跳时会拼接两个contentPath,但是eureka instance都是配置eureka.instance.metadata-map.management.context-path
#这年头Spring Boot的基础配置也变了
server.context-path #Spring Boot 1.X
server.servlet.context-path #Spring Boot 2.X
比如我配置
- #server.port=8180
- spring.application.name=BootClient
-
-
-
- #spring.boot.admin.client.url=http://127.0.0.1:8082
- eureka.client.service-url.defaultZone=http://127.0.0.1:8088/eureka
- eureka.client.registry-fetch-interval-seconds=30
- eureka.instance.lease-renewal-interval-in-seconds=30
- eureka.instance.health-check-url-path=/bootClient/actuator/health
- eureka.instance.status-page-url-path=/bootClient/actuator/info
- eureka.instance.metadata-map.management.context-path=/bootClient/actuator
-
-
-
- management.endpoints.web.exposure.include=*
- management.endpoint.health.show-details=always
-
- management.endpoints.web.base-path=/bootClient/actuator
- #management.server.servlet.context-path=/bootClient

页面正常
比如
- #server.port=8180
- spring.application.name=BootClient
- server.servlet.context-path=/boot
-
-
-
- #spring.boot.admin.client.url=http://127.0.0.1:8082
- eureka.client.service-url.defaultZone=http://127.0.0.1:8088/eureka
- eureka.client.registry-fetch-interval-seconds=30
- eureka.instance.lease-renewal-interval-in-seconds=30
- eureka.instance.health-check-url-path=/bootClient/actuator/health
- eureka.instance.status-page-url-path=/bootClient/actuator/info
- eureka.instance.metadata-map.management.context-path=/boot/bootClient/actuator
-
-
-
- management.endpoints.web.exposure.include=*
- management.endpoint.health.show-details=always
-
- management.endpoints.web.base-path=/bootClient/actuator
- #management.server.servlet.context-path=/boot

笔者配置上
management.server.servlet.context-path=/boot
就会有两个contentpath了,心跳不通过,数据是可以获取到的,上一章这个参数在Tomcat多实例服务器发挥作用了。
总结
调试源码就可以解决这种奇特的问题,但是Spring Boot Admin的路径管理实在太麻烦了,各种拼接;当然使用默认不用配置,但必须 单应用单实例。不过一般微服务就是这样,所以估计Spring Boot Admin设计的时候就没深度的设计这块。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。