Spring Boot Admin 是一個很不錯的儀表板,用于監(jiān)控您的 Spring Boot 應用程序。然而,設置它并不是那么簡單。
- 在您的啟動應用程序中包含一個連接到管理應用程序的客戶端庫——這需要將管理應用程序部署在公共的某個地方或至少可以從您的應用程序訪問,并讓您的應用程序知道它正在被監(jiān)控。
- 使用云發(fā)現(xiàn),這意味著您的應用程序是服務發(fā)現(xiàn)基礎設施的一部分,例如使用微服務
對于更簡單的場景,例如在某些 IaaS 上運行的單體應用程序,以及將您的管理應用程序部署在本地機器或某些本地公司基礎設施中,這兩種方案都不是很好的選擇。如果您還不需要云發(fā)現(xiàn),則它是一種矯枉過正的做法,并且包含客戶端庫會引入使應用程序可訪問管理服務器的復雜性,反之亦然。此外,這種雙向依賴聽起來是錯誤的。
幸運的是,有一個未記錄但已實現(xiàn)的方法?SimpleDiscoveryClient
?,讓您只需在任何機器上運行帶有一些配置的 Spring Boot Admin,并將其連接到您的 Spring Boot 應用程序。
第一個要求是在您的啟動應用程序中設置 ?spring boot actuator
?。Actuator 公開了管理應用程序工作所需的所有端點。設置聽起來很簡單——您只需添加一堆依賴項并可能指定一些配置參數(shù),僅此而已。事實上,在實際應用中,這并不容易——尤其是關于執(zhí)行器端點的基本身份驗證。您需要一個單獨的 ?spring-security
?(除了現(xiàn)有的 spring-security 配置之外),以便僅將基本身份驗證應用于執(zhí)行器端點。例如:
@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 引導,你永遠不知道什么是慣用的。注意 - 據(jù)稱應該可以將?security.user.name
?(和 ?password
?)自動包含在某個管理器中,但我沒有找到,所以我只是在內(nèi)存中實例化了一個。請注意?/manage/**
?路徑——為了在該路徑下?lián)碛兴袌?zhí)行器端點,您需要在應用程序?qū)傩晕募兄付?management.context-path=/manage
?。
現(xiàn)在已經(jīng)設置了執(zhí)行器端點,我們必須附加我們的 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
?消息注入并推送到那里,而不是直接調(diào)用偵聽器,如上所示。我沒有設法讓它輕松工作,所以我解決了這個問題。
提到的 ?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
?因為元數(shù)據(jù)(處理用戶名和密碼)始終為空。在 1.2.2 的 cloud-commons 中,他們修復了它:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-commons</artifactId>
<version>1.2.2.RELEASE</version>
</dependency>
簡單的發(fā)現(xiàn)客戶端就是這樣——您指定啟動應用程序的 URL,它會定期從執(zhí)行器端點獲取數(shù)據(jù)。為什么沒有記錄在案,為什么它直到最近才真正起作用——我不知道。另外,我不知道為什么您必須手動發(fā)送觸發(fā)發(fā)現(xiàn)的事件。也許它不是慣用的,但它不會自動發(fā)生,這使它起作用。
像往常一樣,“正常工作”和“簡單設置”的東西——從來都不是那樣的。如果你有比 hello world 稍微復雜一點的東西,你必須挖掘一些晦澀的類并“off-road”。幸運的是,在這種情況下,它確實有效,而不是需要不好的解決方法。