Commit 25b3e509 by Johannes Edmeier

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.
parent 9250b263
......@@ -187,7 +187,7 @@ The Spring Boot Admin Client registers the application at the admin server. This
| Property name |Description |Default value
| spring.boot.admin.url
| URL of the spring-boot-admin application to register at. This triggers the AutoConfiguration. *Mandatory*.
| List of URLs of the Spring Boot Admin server to register at. This triggers the AutoConfiguration. *Mandatory*.
|
| spring.boot.admin.api-path
......@@ -204,19 +204,19 @@ spring.boot.admin.password
| `10.000`
| spring.boot.admin.auto-deregistration
| Swtich to enable auto-deregistration at admin when context is closed.
| Switch to enable auto-deregistration at Spring Boot Admin server when context is closed.
| `false`
| spring.boot.admin.client.health-url
| Client-health-url to register with. Can be overriden in case the reachable URL is different (e.g. Docker). Must be unique in registry.
| Client-health-url to register with. Can be overridden in case the reachable URL is different (e.g. Docker). Must be unique in registry.
| Guessed based on management-url and `endpoints.health.id`.
| spring.boot.admin.client.management-url
| Client-management-url to register with. Can be overriden in case the reachable url is different (e.g. Docker).
| Client-management-url to register with. Can be overridden in case the reachable url is different (e.g. Docker).
| Guessed based on service-url, `server.servlet-path`, `management.port` and `management.context-path`.
| spring.boot.admin.client.service-url
| Client-service-url to register with. Can be overriden in case the reachable url is different (e.g. Docker).
| Client-service-url to register with. Can be overridden in case the reachable url is different (e.g. Docker).
| Guessed based on hostname, `server.port` and `server.context-path`.
| spring.boot.admin.client.name
......@@ -236,7 +236,7 @@ spring.boot.admin.password
| Property name |Description |Default value
| spring.boot.admin.context-path
| The context-path prefixes the path where the Admin Servers statics assets and api should be served. Relative to the Dispatcher-Servlet.
| The context-path prefixes the path where the Admin Servers statics assets and API should be served. Relative to the Dispatcher-Servlet.
|
| spring.boot.admin.monitor.period
......@@ -244,7 +244,7 @@ spring.boot.admin.password
| 10.000
| spring.boot.admin.monitor.status-lifetime
| Lifetime of iapplication statuses in ms. The applications /health-endpoint will not be queried until the lifetime has expired.
| Lifetime of application statuses in ms. The applications /health-endpoint will not be queried until the lifetime has expired.
| 10.000
|===
......@@ -384,7 +384,7 @@ spring.boot.admin.notify.mail.to=admin@example.com
[[pagerduty-notifications]]
=== Pagerduty notifications ===
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.
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.
.Pagerduty notifications configuration options
|===
......@@ -424,7 +424,7 @@ To enable pagerduty notifications you just have to add a generic service to your
[qanda]
Can I include spring-boot-admin into my business application?::
*tl;dr* You can, but you shouldn't. +
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.
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.
How do I disable Spring Boot Admin Client for my unit tests?::
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.
......
......@@ -23,7 +23,7 @@ public class AdminProperties {
/**
* The admin servers url to register at
*/
private String url;
private String[] url;
/**
* The admin rest-apis path.
......@@ -50,12 +50,12 @@ public class AdminProperties {
*/
private boolean autoDeregistration;
public void setUrl(String url) {
this.url = url;
public void setUrl(String[] url) {
this.url = url.clone();
}
public String getUrl() {
return url;
public String[] getUrl() {
return url.clone();
}
public void setApiPath(String apiPath) {
......@@ -66,8 +66,12 @@ public class AdminProperties {
return apiPath;
}
public String getAdminUrl() {
return url + "/" + apiPath;
public String[] getAdminUrl() {
String adminUrls[] = url.clone();
for (int i = 0; i < adminUrls.length; i++) {
adminUrls[i] += "/" + apiPath;
}
return adminUrls;
}
public int getPeriod() {
......
......@@ -69,17 +69,18 @@ public class ApplicationRegistrator {
* @return true if successful
*/
public boolean register() {
Application self = null;
try {
self = createApplication();
Application self = createApplication();
for (String adminUrl : admin.getAdminUrl()) {
try {
@SuppressWarnings("rawtypes")
ResponseEntity<Map> response = template.postForEntity(admin.getAdminUrl(),
ResponseEntity<Map> response = template.postForEntity(adminUrl,
new HttpEntity<Application>(self, HTTP_HEADERS), Map.class);
if (response.getStatusCode().equals(HttpStatus.CREATED)) {
if (registeredId.get() == null) {
if (registeredId.compareAndSet(null, response.getBody().get("id").toString())) {
if (registeredId.compareAndSet(null,
response.getBody().get("id").toString())) {
LOGGER.info("Application registered itself as {}", response.getBody());
return true;
}
......@@ -92,8 +93,9 @@ public class ApplicationRegistrator {
response.toString());
}
} catch (Exception ex) {
LOGGER.warn("Failed to register application as {} at spring-boot-admin ({}): {}", self,
admin.getAdminUrl(), ex.getMessage());
LOGGER.warn("Failed to register application as {} at spring-boot-admin ({}): {}",
self, admin.getAdminUrl(), ex.getMessage());
}
}
return false;
......@@ -102,13 +104,16 @@ public class ApplicationRegistrator {
public void deregister() {
String id = registeredId.get();
if (id != null) {
for (String adminUrl : admin.getAdminUrl()) {
try {
template.delete(admin.getAdminUrl() + "/" + id);
template.delete(adminUrl + "/" + id);
registeredId.set(null);
return;
} catch (Exception ex) {
LOGGER.warn(
"Failed to deregister application (id={}) at spring-boot-admin ({}): {}",
id, admin.getAdminUrl(), ex.getMessage());
id, adminUrl, ex.getMessage());
}
}
}
}
......
......@@ -51,7 +51,7 @@ public class ApplicationRegistratorTest {
restTemplate = mock(RestTemplate.class);
AdminProperties adminProps = new AdminProperties();
adminProps.setUrl("http://sba:8080");
adminProps.setUrl(new String[] { "http://sba:8080", "http://sba2:8080" });
AdminClientProperties clientProps = new AdminClientProperties();
clientProps.setManagementUrl("http://localhost:8080/mgmt");
......@@ -73,9 +73,7 @@ public class ApplicationRegistratorTest {
.thenReturn(new ResponseEntity<Map>(Collections.singletonMap("id", "-id-"),
HttpStatus.CREATED));
boolean result = registrator.register();
assertTrue(result);
assertTrue(registrator.register());
verify(restTemplate)
.postForEntity("http://sba:8080/api/applications",
new HttpEntity<Application>(Application.create("AppName")
......@@ -90,9 +88,19 @@ public class ApplicationRegistratorTest {
when(restTemplate.postForEntity(isA(String.class), isA(HttpEntity.class),
eq(Application.class))).thenThrow(new RestClientException("Error"));
boolean result = registrator.register();
assertFalse(registrator.register());
}
@SuppressWarnings("rawtypes")
@Test
public void register_retry() {
when(restTemplate.postForEntity(isA(String.class), isA(HttpEntity.class),
eq(Application.class))).thenThrow(new RestClientException("Error"));
when(restTemplate.postForEntity(isA(String.class), isA(HttpEntity.class), eq(Map.class)))
.thenReturn(new ResponseEntity<Map>(Collections.singletonMap("id", "-id-"),
HttpStatus.CREATED));
assertFalse(result);
assertTrue(registrator.register());
}
@SuppressWarnings("rawtypes")
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment