Skip to content

Commit

Permalink
Add option for fallback servers to register at
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
joshiste committed Jan 10, 2016
1 parent 9250b26 commit 25b3e50
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 51 deletions.
18 changes: 9 additions & 9 deletions spring-boot-admin-docs/src/main/asciidoc/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -236,15 +236,15 @@ 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
| Time interval in ms to update the status of applications with expired status-informations.
| 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
|===

Expand Down Expand Up @@ -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
|===
Expand Down Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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) {
Expand All @@ -66,8 +66,12 @@ public String getApiPath() {
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() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,31 +69,33 @@ private static HttpHeaders createHttpHeaders() {
* @return true if successful
*/
public boolean register() {
Application self = null;
try {
self = createApplication();

@SuppressWarnings("rawtypes")
ResponseEntity<Map> response = template.postForEntity(admin.getAdminUrl(),
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())) {
LOGGER.info("Application registered itself as {}", response.getBody());
return true;
Application self = createApplication();

for (String adminUrl : admin.getAdminUrl()) {
try {
@SuppressWarnings("rawtypes")
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())) {
LOGGER.info("Application registered itself as {}", response.getBody());
return true;

This comment has been minimized.

Copy link
@mikexliu

mikexliu Jul 14, 2016

@joshiste
Is there any reason it can only register to only one server?

This comment has been minimized.

Copy link
@joshiste

joshiste Jul 15, 2016

Author Collaborator

@mikexliu see discussion here: #225

This comment has been minimized.

Copy link
@mikexliu

mikexliu Jul 15, 2016

@joshiste
Thanks. I don't see it as being super chatty since I don't imagine people having so many "shared" centralized servers (ones that would received from all services).

That said, I can definitely modify my pull request so it's a separate option instead. For example, adding another option called "spring.boot.admin.centralized.urls" (or something similar), so registration against those always occur and the normal "spring.boot.admin.url" will work like a queue as it is now.

Edit: Thinking about it more, it might more sense to add a flag specifying whether or not to ping all urls or go with the current model instead of having two url lists. Having two lists will add confusion but introducing a flag will give the necessary controls for the developers to do what they want to do.

}
}
}

LOGGER.debug("Application refreshed itself as {}", response.getBody());
return true;
} else {
LOGGER.warn("Application failed to registered itself as {}. Response: {}", self,
response.toString());
LOGGER.debug("Application refreshed itself as {}", response.getBody());
return true;
} else {
LOGGER.warn("Application failed to registered itself as {}. Response: {}", self,
response.toString());
}
} catch (Exception ex) {
LOGGER.warn("Failed to register application as {} at spring-boot-admin ({}): {}",
self, admin.getAdminUrl(), ex.getMessage());
}
} catch (Exception ex) {
LOGGER.warn("Failed to register application as {} at spring-boot-admin ({}): {}", self,
admin.getAdminUrl(), ex.getMessage());
}

return false;
Expand All @@ -102,13 +104,16 @@ public boolean register() {
public void deregister() {
String id = registeredId.get();
if (id != null) {
try {
template.delete(admin.getAdminUrl() + "/" + id);
registeredId.set(null);
} catch (Exception ex) {
LOGGER.warn(
"Failed to deregister application (id={}) at spring-boot-admin ({}): {}",
id, admin.getAdminUrl(), ex.getMessage());
for (String adminUrl : admin.getAdminUrl()) {
try {
template.delete(adminUrl + "/" + id);
registeredId.set(null);
return;
} catch (Exception ex) {
LOGGER.warn(
"Failed to deregister application (id={}) at spring-boot-admin ({}): {}",
id, adminUrl, ex.getMessage());
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public void setup() {
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");
Expand All @@ -73,9 +73,7 @@ public void register_successful() {
.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")
Expand All @@ -90,9 +88,19 @@ public void register_failed() {
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")
Expand Down

0 comments on commit 25b3e50

Please sign in to comment.