Skip to content

Commit 25b3e50

Browse files
committed
Add option for fallback servers to register at
In case you have clustered admin servers, you can now specify multiple urls for the client to register at. So that in the case the first server is down it will register at the next one specified in the list. This allows you to do cluster- ing without the need for a load-balanced host.
1 parent 9250b26 commit 25b3e50

File tree

4 files changed

+68
-51
lines changed

4 files changed

+68
-51
lines changed

spring-boot-admin-docs/src/main/asciidoc/index.adoc

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ The Spring Boot Admin Client registers the application at the admin server. This
187187
| Property name |Description |Default value
188188

189189
| spring.boot.admin.url
190-
| URL of the spring-boot-admin application to register at. This triggers the AutoConfiguration. *Mandatory*.
190+
| List of URLs of the Spring Boot Admin server to register at. This triggers the AutoConfiguration. *Mandatory*.
191191
|
192192

193193
| spring.boot.admin.api-path
@@ -204,19 +204,19 @@ spring.boot.admin.password
204204
| `10.000`
205205

206206
| spring.boot.admin.auto-deregistration
207-
| Swtich to enable auto-deregistration at admin when context is closed.
207+
| Switch to enable auto-deregistration at Spring Boot Admin server when context is closed.
208208
| `false`
209209

210210
| spring.boot.admin.client.health-url
211-
| Client-health-url to register with. Can be overriden in case the reachable URL is different (e.g. Docker). Must be unique in registry.
211+
| Client-health-url to register with. Can be overridden in case the reachable URL is different (e.g. Docker). Must be unique in registry.
212212
| Guessed based on management-url and `endpoints.health.id`.
213213

214214
| spring.boot.admin.client.management-url
215-
| Client-management-url to register with. Can be overriden in case the reachable url is different (e.g. Docker).
215+
| Client-management-url to register with. Can be overridden in case the reachable url is different (e.g. Docker).
216216
| Guessed based on service-url, `server.servlet-path`, `management.port` and `management.context-path`.
217217

218218
| spring.boot.admin.client.service-url
219-
| Client-service-url to register with. Can be overriden in case the reachable url is different (e.g. Docker).
219+
| Client-service-url to register with. Can be overridden in case the reachable url is different (e.g. Docker).
220220
| Guessed based on hostname, `server.port` and `server.context-path`.
221221

222222
| spring.boot.admin.client.name
@@ -236,15 +236,15 @@ spring.boot.admin.password
236236
| Property name |Description |Default value
237237

238238
| spring.boot.admin.context-path
239-
| The context-path prefixes the path where the Admin Servers statics assets and api should be served. Relative to the Dispatcher-Servlet.
239+
| The context-path prefixes the path where the Admin Servers statics assets and API should be served. Relative to the Dispatcher-Servlet.
240240
|
241241

242242
| spring.boot.admin.monitor.period
243243
| Time interval in ms to update the status of applications with expired status-informations.
244244
| 10.000
245245

246246
| spring.boot.admin.monitor.status-lifetime
247-
| Lifetime of iapplication statuses in ms. The applications /health-endpoint will not be queried until the lifetime has expired.
247+
| Lifetime of application statuses in ms. The applications /health-endpoint will not be queried until the lifetime has expired.
248248
| 10.000
249249
|===
250250

@@ -384,7 +384,7 @@ spring.boot.admin.notify.mail.to=admin@example.com
384384

385385
[[pagerduty-notifications]]
386386
=== Pagerduty notifications ===
387-
To enable pagerduty notifications you just have to add a generic service to your pagerduty-account and set `spring.boot.admin.notify.pagerduty.service-key` to the service-key you recieved.
387+
To enable pagerduty notifications you just have to add a generic service to your pagerduty-account and set `spring.boot.admin.notify.pagerduty.service-key` to the service-key you received.
388388

389389
.Pagerduty notifications configuration options
390390
|===
@@ -424,7 +424,7 @@ To enable pagerduty notifications you just have to add a generic service to your
424424
[qanda]
425425
Can I include spring-boot-admin into my business application?::
426426
*tl;dr* You can, but you shouldn't. +
427-
You can set `spring.boot.admin.context-path` to alter the path where the UI and REST-api is served, but depending on the complexity of your application you might get in trouble. On the other hand in my opinion it makes no sense for an application to monitor itself. In case your application goes down your monitioring tool also does.
427+
You can set `spring.boot.admin.context-path` to alter the path where the UI and REST-API is served, but depending on the complexity of your application you might get in trouble. On the other hand in my opinion it makes no sense for an application to monitor itself. In case your application goes down your monitoring tool also does.
428428

429429
How do I disable Spring Boot Admin Client for my unit tests?::
430430
The AutoConfiguration is triggered by the presence of the `spring.boot.admin.url`. So if you don't set this property for your tests, the Spring Boot Admin Client is not active.

spring-boot-admin-starter-client/src/main/java/de/codecentric/boot/admin/config/AdminProperties.java

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public class AdminProperties {
2323
/**
2424
* The admin servers url to register at
2525
*/
26-
private String url;
26+
private String[] url;
2727

2828
/**
2929
* The admin rest-apis path.
@@ -50,12 +50,12 @@ public class AdminProperties {
5050
*/
5151
private boolean autoDeregistration;
5252

53-
public void setUrl(String url) {
54-
this.url = url;
53+
public void setUrl(String[] url) {
54+
this.url = url.clone();
5555
}
5656

57-
public String getUrl() {
58-
return url;
57+
public String[] getUrl() {
58+
return url.clone();
5959
}
6060

6161
public void setApiPath(String apiPath) {
@@ -66,8 +66,12 @@ public String getApiPath() {
6666
return apiPath;
6767
}
6868

69-
public String getAdminUrl() {
70-
return url + "/" + apiPath;
69+
public String[] getAdminUrl() {
70+
String adminUrls[] = url.clone();
71+
for (int i = 0; i < adminUrls.length; i++) {
72+
adminUrls[i] += "/" + apiPath;
73+
}
74+
return adminUrls;
7175
}
7276

7377
public int getPeriod() {

spring-boot-admin-starter-client/src/main/java/de/codecentric/boot/admin/services/ApplicationRegistrator.java

Lines changed: 34 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -69,31 +69,33 @@ private static HttpHeaders createHttpHeaders() {
6969
* @return true if successful
7070
*/
7171
public boolean register() {
72-
Application self = null;
73-
try {
74-
self = createApplication();
75-
76-
@SuppressWarnings("rawtypes")
77-
ResponseEntity<Map> response = template.postForEntity(admin.getAdminUrl(),
78-
new HttpEntity<Application>(self, HTTP_HEADERS), Map.class);
79-
80-
if (response.getStatusCode().equals(HttpStatus.CREATED)) {
81-
if (registeredId.get() == null) {
82-
if (registeredId.compareAndSet(null, response.getBody().get("id").toString())) {
83-
LOGGER.info("Application registered itself as {}", response.getBody());
84-
return true;
72+
Application self = createApplication();
73+
74+
for (String adminUrl : admin.getAdminUrl()) {
75+
try {
76+
@SuppressWarnings("rawtypes")
77+
ResponseEntity<Map> response = template.postForEntity(adminUrl,
78+
new HttpEntity<Application>(self, HTTP_HEADERS), Map.class);
79+
80+
if (response.getStatusCode().equals(HttpStatus.CREATED)) {
81+
if (registeredId.get() == null) {
82+
if (registeredId.compareAndSet(null,
83+
response.getBody().get("id").toString())) {
84+
LOGGER.info("Application registered itself as {}", response.getBody());
85+
return true;
86+
}
8587
}
86-
}
8788

88-
LOGGER.debug("Application refreshed itself as {}", response.getBody());
89-
return true;
90-
} else {
91-
LOGGER.warn("Application failed to registered itself as {}. Response: {}", self,
92-
response.toString());
89+
LOGGER.debug("Application refreshed itself as {}", response.getBody());
90+
return true;
91+
} else {
92+
LOGGER.warn("Application failed to registered itself as {}. Response: {}", self,
93+
response.toString());
94+
}
95+
} catch (Exception ex) {
96+
LOGGER.warn("Failed to register application as {} at spring-boot-admin ({}): {}",
97+
self, admin.getAdminUrl(), ex.getMessage());
9398
}
94-
} catch (Exception ex) {
95-
LOGGER.warn("Failed to register application as {} at spring-boot-admin ({}): {}", self,
96-
admin.getAdminUrl(), ex.getMessage());
9799
}
98100

99101
return false;
@@ -102,13 +104,16 @@ public boolean register() {
102104
public void deregister() {
103105
String id = registeredId.get();
104106
if (id != null) {
105-
try {
106-
template.delete(admin.getAdminUrl() + "/" + id);
107-
registeredId.set(null);
108-
} catch (Exception ex) {
109-
LOGGER.warn(
110-
"Failed to deregister application (id={}) at spring-boot-admin ({}): {}",
111-
id, admin.getAdminUrl(), ex.getMessage());
107+
for (String adminUrl : admin.getAdminUrl()) {
108+
try {
109+
template.delete(adminUrl + "/" + id);
110+
registeredId.set(null);
111+
return;
112+
} catch (Exception ex) {
113+
LOGGER.warn(
114+
"Failed to deregister application (id={}) at spring-boot-admin ({}): {}",
115+
id, adminUrl, ex.getMessage());
116+
}
112117
}
113118
}
114119
}

spring-boot-admin-starter-client/src/test/java/de/codecentric/boot/admin/services/ApplicationRegistratorTest.java

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ public void setup() {
5151
restTemplate = mock(RestTemplate.class);
5252

5353
AdminProperties adminProps = new AdminProperties();
54-
adminProps.setUrl("http://sba:8080");
54+
adminProps.setUrl(new String[] { "http://sba:8080", "http://sba2:8080" });
5555

5656
AdminClientProperties clientProps = new AdminClientProperties();
5757
clientProps.setManagementUrl("http://localhost:8080/mgmt");
@@ -73,9 +73,7 @@ public void register_successful() {
7373
.thenReturn(new ResponseEntity<Map>(Collections.singletonMap("id", "-id-"),
7474
HttpStatus.CREATED));
7575

76-
boolean result = registrator.register();
77-
78-
assertTrue(result);
76+
assertTrue(registrator.register());
7977
verify(restTemplate)
8078
.postForEntity("http://sba:8080/api/applications",
8179
new HttpEntity<Application>(Application.create("AppName")
@@ -90,9 +88,19 @@ public void register_failed() {
9088
when(restTemplate.postForEntity(isA(String.class), isA(HttpEntity.class),
9189
eq(Application.class))).thenThrow(new RestClientException("Error"));
9290

93-
boolean result = registrator.register();
91+
assertFalse(registrator.register());
92+
}
93+
94+
@SuppressWarnings("rawtypes")
95+
@Test
96+
public void register_retry() {
97+
when(restTemplate.postForEntity(isA(String.class), isA(HttpEntity.class),
98+
eq(Application.class))).thenThrow(new RestClientException("Error"));
99+
when(restTemplate.postForEntity(isA(String.class), isA(HttpEntity.class), eq(Map.class)))
100+
.thenReturn(new ResponseEntity<Map>(Collections.singletonMap("id", "-id-"),
101+
HttpStatus.CREATED));
94102

95-
assertFalse(result);
103+
assertTrue(registrator.register());
96104
}
97105

98106
@SuppressWarnings("rawtypes")

0 commit comments

Comments
 (0)