Spring Boot Admin 是一个很不错的仪表板,用于监控您的 Spring Boot 应用程序。然而,设置它并不是那么简单。
- 在您的启动应用程序中包含一个连接到管理应用程序的客户端库——这需要将管理应用程序部署在公共的某个地方或至少可以从您的应用程序访问,并让您的应用程序知道它正在被监控。
- 使用云发现,这意味着您的应用程序是服务发现基础设施的一部分,例如使用微服务
对于更简单的场景,例如在某些 IaaS 上运行的单体应用程序,以及将您的管理应用程序部署在本地机器或某些本地公司基础设施中,这两种方案都不是很好的选择。如果您还不需要云发现,则它是一种矫枉过正的做法,并且包含客户端库会引入使应用程序可访问管理服务器的复杂性,反之亦然。此外,这种双向依赖听起来是错误的。
幸运的是,有一个未记录但已实现的方法SimpleDiscoveryClient
,让您只需在任何机器上运行带有一些配置的 Spring Boot Admin,并将其连接到您的 Spring Boot 应用程序。
第一个要求是在您的启动应用程序中设置 spring boot actuator
。Actuator 公开了管理应用程序工作所需的所有端点。设置听起来很简单——您只需添加一堆依赖项并可能指定一些配置参数,仅此而已。事实上,在实际应用中,这并不容易——尤其是关于执行器端点的基本身份验证。您需要一个单独的 spring-security
(除了现有的 spring-security 配置之外),以便仅将基本身份验证应用于执行器端点。例如:
@Configuration
@Order(99)// the default security configuration has order 100
public class ActuatorSecurityConfigurationextends WebSecurityConfigurerAdapter {
@Value("${security.user.name}")
private String username;
@Value("${security.user.password}")
private String password;
@Override
protected void configure(HttpSecurity http)throws Exception {
InMemoryUserDetailsManager manager =new InMemoryUserDetailsManager();
manager.createUser(User.withUsername(username).password(password).roles("ACTUATOR","ADMIN").build());
http.antMatcher("/manage/**").authorizeRequests().anyRequest().hasRole("ACTUATOR").and().httpBasic()
.and().userDetailsService(manager);
}
}
这有点违反直觉,但它有效。不确定它是否是惯用的——使用 spring 安全和 spring 引导,你永远不知道什么是惯用的。注意 - 据称应该可以将security.user.name
(和 password
)自动包含在某个管理器中,但我没有找到,所以我只是在内存中实例化了一个。请注意/manage/**
路径——为了在该路径下拥有所有执行器端点,您需要在应用程序属性文件中指定management.context-path=/manage
。
现在已经设置了执行器端点,我们必须附加我们的 spring 管理应用程序。它看起来像这样:
@Configuration
@EnableAutoConfiguration
@PropertySource("classpath:/application.properties")
@EnableAdminServer
public class BootAdminApplication {
public static void main(String[] args) {
SpringApplication.run(BootAdminApplication.class, args);
}
@Autowired
private ApplicationDiscoveryListener listener;
@PostConstruct
public void init() {
// we have to fire this event in order to trigger the service registration
InstanceRegisteredEvent<?> event =new InstanceRegisteredEvent<>("prod",null);
// for some reason publising doesn't work, so we invoke directly
listener.onInstanceRegistered(event);
}
}
通常,应该将ApplicationEventPublisher
消息注入并推送到那里,而不是直接调用侦听器,如上所示。我没有设法让它轻松工作,所以我解决了这个问题。
提到的 application.properties
文件应该在 src/main/resources 中,看起来像这样:
spring.cloud.discovery.client.simple.instances.prod[0].uri=https://your-spring-boot-application-url.com
spring.cloud.discovery.client.simple.instances.prod[0].metadata.user.name=<basic-auth-username>
spring.cloud.discovery.client.simple.instances.prod[0].metadata.user.password=<basic-auth-password>
spring.boot.admin.discovery.converter.management-context-path=/manage
spring.boot.admin.discovery.services=*
那是在做什么?它正在使用SimpleDiscoveryClient由自动配置实例化的 。实际上,该客户端直到最新版本才工作——它抛出 NullPointerException
因为元数据(处理用户名和密码)始终为空。在 1.2.2 的 cloud-commons 中,他们修复了它:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-commons</artifactId>
<version>1.2.2.RELEASE</version>
</dependency>
简单的发现客户端就是这样——您指定启动应用程序的 URL,它会定期从执行器端点获取数据。为什么没有记录在案,为什么它直到最近才真正起作用——我不知道。另外,我不知道为什么您必须手动发送触发发现的事件。也许它不是惯用的,但它不会自动发生,这使它起作用。
像往常一样,“正常工作”和“简单设置”的东西——从来都不是那样的。如果你有比 hello world 稍微复杂一点的东西,你必须挖掘一些晦涩的类并“off-road”。幸运的是,在这种情况下,它确实有效,而不是需要不好的解决方法。