Skip to content

Commit 3d4b2c5

Browse files
committed
Introduce strategy for running HealthIndicators
1 parent acbb4e6 commit 3d4b2c5

10 files changed

+363
-24
lines changed

spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointAutoConfiguration.java

+9-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2015 the original author or authors.
2+
* Copyright 2012-2016 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -46,8 +46,10 @@
4646
import org.springframework.boot.actuate.endpoint.RequestMappingEndpoint;
4747
import org.springframework.boot.actuate.endpoint.ShutdownEndpoint;
4848
import org.springframework.boot.actuate.endpoint.TraceEndpoint;
49+
import org.springframework.boot.actuate.health.DefaultHealthIndicatorRunner;
4950
import org.springframework.boot.actuate.health.HealthAggregator;
5051
import org.springframework.boot.actuate.health.HealthIndicator;
52+
import org.springframework.boot.actuate.health.HealthIndicatorRunner;
5153
import org.springframework.boot.actuate.health.OrderedHealthAggregator;
5254
import org.springframework.boot.actuate.trace.InMemoryTraceRepository;
5355
import org.springframework.boot.actuate.trace.TraceRepository;
@@ -81,6 +83,7 @@
8183
* @author Christian Dupuis
8284
* @author Stephane Nicoll
8385
* @author Eddú Meléndez
86+
* @author Vedran Pavic
8487
*/
8588
@Configuration
8689
@AutoConfigureAfter({ FlywayAutoConfiguration.class, LiquibaseAutoConfiguration.class })
@@ -96,6 +99,9 @@ public class EndpointAutoConfiguration {
9699
@Autowired(required = false)
97100
private Map<String, HealthIndicator> healthIndicators = new HashMap<String, HealthIndicator>();
98101

102+
@Autowired(required = false)
103+
private HealthIndicatorRunner healthIndicatorRunner = new DefaultHealthIndicatorRunner();
104+
99105
@Autowired(required = false)
100106
private Collection<PublicMetrics> publicMetrics;
101107

@@ -111,7 +117,8 @@ public EnvironmentEndpoint environmentEndpoint() {
111117
@Bean
112118
@ConditionalOnMissingBean
113119
public HealthEndpoint healthEndpoint() {
114-
return new HealthEndpoint(this.healthAggregator, this.healthIndicators);
120+
return new HealthEndpoint(this.healthAggregator, this.healthIndicators,
121+
this.healthIndicatorRunner);
115122
}
116123

117124
@Bean

spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/HealthIndicatorAutoConfiguration.java

+14-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2015 the original author or authors.
2+
* Copyright 2012-2016 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -33,12 +33,14 @@
3333
import org.springframework.boot.actuate.health.CassandraHealthIndicator;
3434
import org.springframework.boot.actuate.health.CompositeHealthIndicator;
3535
import org.springframework.boot.actuate.health.DataSourceHealthIndicator;
36+
import org.springframework.boot.actuate.health.DefaultHealthIndicatorRunner;
3637
import org.springframework.boot.actuate.health.DiskSpaceHealthIndicator;
3738
import org.springframework.boot.actuate.health.DiskSpaceHealthIndicatorProperties;
3839
import org.springframework.boot.actuate.health.ElasticsearchHealthIndicator;
3940
import org.springframework.boot.actuate.health.ElasticsearchHealthIndicatorProperties;
4041
import org.springframework.boot.actuate.health.HealthAggregator;
4142
import org.springframework.boot.actuate.health.HealthIndicator;
43+
import org.springframework.boot.actuate.health.HealthIndicatorRunner;
4244
import org.springframework.boot.actuate.health.JmsHealthIndicator;
4345
import org.springframework.boot.actuate.health.MailHealthIndicator;
4446
import org.springframework.boot.actuate.health.MongoHealthIndicator;
@@ -84,6 +86,7 @@
8486
* @author Stephane Nicoll
8587
* @author Phillip Webb
8688
* @author Tommy Ludwig
89+
* @author Vedran Pavic
8790
* @since 1.1.0
8891
*/
8992
@Configuration
@@ -110,6 +113,12 @@ public OrderedHealthAggregator healthAggregator() {
110113
return healthAggregator;
111114
}
112115

116+
@Bean
117+
@ConditionalOnMissingBean(HealthIndicatorRunner.class)
118+
public HealthIndicatorRunner healthIndicatorRunner() {
119+
return new DefaultHealthIndicatorRunner();
120+
}
121+
113122
@Bean
114123
@ConditionalOnMissingBean(HealthIndicator.class)
115124
public ApplicationHealthIndicator applicationHealthIndicator() {
@@ -127,12 +136,15 @@ protected static abstract class CompositeHealthIndicatorConfiguration<H extends
127136
@Autowired
128137
private HealthAggregator healthAggregator;
129138

139+
@Autowired
140+
private HealthIndicatorRunner healthIndicatorRunner;
141+
130142
protected HealthIndicator createHealthIndicator(Map<String, S> beans) {
131143
if (beans.size() == 1) {
132144
return createHealthIndicator(beans.values().iterator().next());
133145
}
134146
CompositeHealthIndicator composite = new CompositeHealthIndicator(
135-
this.healthAggregator);
147+
this.healthAggregator, this.healthIndicatorRunner);
136148
for (Map.Entry<String, S> entry : beans.entrySet()) {
137149
composite.addHealthIndicator(entry.getKey(),
138150
createHealthIndicator(entry.getValue()));

spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/HealthEndpoint.java

+8-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2015 the original author or authors.
2+
* Copyright 2012-2016 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -22,6 +22,7 @@
2222
import org.springframework.boot.actuate.health.Health;
2323
import org.springframework.boot.actuate.health.HealthAggregator;
2424
import org.springframework.boot.actuate.health.HealthIndicator;
25+
import org.springframework.boot.actuate.health.HealthIndicatorRunner;
2526
import org.springframework.boot.context.properties.ConfigurationProperties;
2627
import org.springframework.util.Assert;
2728

@@ -31,6 +32,7 @@
3132
* @author Dave Syer
3233
* @author Christian Dupuis
3334
* @author Andy Wilkinson
35+
* @author Vedran Pavic
3436
*/
3537
@ConfigurationProperties(prefix = "endpoints.health", ignoreUnknownFields = true)
3638
public class HealthEndpoint extends AbstractEndpoint<Health> {
@@ -46,14 +48,17 @@ public class HealthEndpoint extends AbstractEndpoint<Health> {
4648
* Create a new {@link HealthIndicator} instance.
4749
* @param healthAggregator the health aggregator
4850
* @param healthIndicators the health indicators
51+
* @param healthIndicatorRunner the health indicator runner
4952
*/
5053
public HealthEndpoint(HealthAggregator healthAggregator,
51-
Map<String, HealthIndicator> healthIndicators) {
54+
Map<String, HealthIndicator> healthIndicators,
55+
HealthIndicatorRunner healthIndicatorRunner) {
5256
super("health", false);
5357
Assert.notNull(healthAggregator, "HealthAggregator must not be null");
5458
Assert.notNull(healthIndicators, "HealthIndicators must not be null");
59+
Assert.notNull(healthIndicatorRunner, "HealthIndicatorRunner must not be null");
5560
CompositeHealthIndicator healthIndicator = new CompositeHealthIndicator(
56-
healthAggregator);
61+
healthAggregator, healthIndicatorRunner);
5762
for (Map.Entry<String, HealthIndicator> entry : healthIndicators.entrySet()) {
5863
healthIndicator.addHealthIndicator(getKey(entry.getKey()), entry.getValue());
5964
}

spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/CompositeHealthIndicator.java

+15-8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2015 the original author or authors.
2+
* Copyright 2012-2016 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -27,6 +27,7 @@
2727
* @author Tyler J. Frederick
2828
* @author Phillip Webb
2929
* @author Christian Dupuis
30+
* @author Vedran Pavic
3031
* @since 1.1.0
3132
*/
3233
public class CompositeHealthIndicator implements HealthIndicator {
@@ -35,26 +36,35 @@ public class CompositeHealthIndicator implements HealthIndicator {
3536

3637
private final HealthAggregator healthAggregator;
3738

39+
private final HealthIndicatorRunner healthIndicatorRunner;
40+
3841
/**
3942
* Create a new {@link CompositeHealthIndicator}.
4043
* @param healthAggregator the health aggregator
44+
* @param healthIndicatorRunner the health indicator runner
4145
*/
42-
public CompositeHealthIndicator(HealthAggregator healthAggregator) {
43-
this(healthAggregator, new LinkedHashMap<String, HealthIndicator>());
46+
public CompositeHealthIndicator(HealthAggregator healthAggregator,
47+
HealthIndicatorRunner healthIndicatorRunner) {
48+
this(healthAggregator, new LinkedHashMap<String, HealthIndicator>(),
49+
healthIndicatorRunner);
4450
}
4551

4652
/**
4753
* Create a new {@link CompositeHealthIndicator} from the specified indicators.
4854
* @param healthAggregator the health aggregator
4955
* @param indicators a map of {@link HealthIndicator}s with the key being used as an
5056
* indicator name.
57+
* @param healthIndicatorRunner the health indicator runner
5158
*/
5259
public CompositeHealthIndicator(HealthAggregator healthAggregator,
53-
Map<String, HealthIndicator> indicators) {
60+
Map<String, HealthIndicator> indicators,
61+
HealthIndicatorRunner healthIndicatorRunner) {
5462
Assert.notNull(healthAggregator, "HealthAggregator must not be null");
5563
Assert.notNull(indicators, "Indicators must not be null");
64+
Assert.notNull(healthIndicatorRunner, "HealthIndicatorRunner must not be null");
5665
this.indicators = new LinkedHashMap<String, HealthIndicator>(indicators);
5766
this.healthAggregator = healthAggregator;
67+
this.healthIndicatorRunner = healthIndicatorRunner;
5868
}
5969

6070
public void addHealthIndicator(String name, HealthIndicator indicator) {
@@ -63,10 +73,7 @@ public void addHealthIndicator(String name, HealthIndicator indicator) {
6373

6474
@Override
6575
public Health health() {
66-
Map<String, Health> healths = new LinkedHashMap<String, Health>();
67-
for (Map.Entry<String, HealthIndicator> entry : this.indicators.entrySet()) {
68-
healths.put(entry.getKey(), entry.getValue().health());
69-
}
76+
Map<String, Health> healths = this.healthIndicatorRunner.run(this.indicators);
7077
return this.healthAggregator.aggregate(healths);
7178
}
7279

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
/*
2+
* Copyright 2012-2016 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.actuate.health;
18+
19+
import java.util.HashMap;
20+
import java.util.Map;
21+
import java.util.concurrent.Callable;
22+
import java.util.concurrent.ExecutorService;
23+
import java.util.concurrent.Executors;
24+
import java.util.concurrent.Future;
25+
import java.util.concurrent.ThreadFactory;
26+
import java.util.concurrent.atomic.AtomicInteger;
27+
28+
import org.apache.commons.logging.Log;
29+
import org.apache.commons.logging.LogFactory;
30+
31+
import org.springframework.util.Assert;
32+
import org.springframework.util.ClassUtils;
33+
34+
/**
35+
* Default implementation of {@link HealthIndicatorRunner}.
36+
*
37+
* @author Vedran Pavic
38+
*/
39+
public class DefaultHealthIndicatorRunner implements HealthIndicatorRunner {
40+
41+
private static final Log logger = LogFactory.getLog(DefaultHealthIndicatorRunner.class);
42+
43+
private DefaultHealthIndicatorRunnerProperties properties;
44+
45+
private ExecutorService executor;
46+
47+
/**
48+
* Create a {@link DefaultHealthIndicatorRunner} instance.
49+
* @param properties the configuration properties
50+
*/
51+
public DefaultHealthIndicatorRunner(DefaultHealthIndicatorRunnerProperties properties) {
52+
Assert.notNull(properties, "Properties must not be null");
53+
this.properties = properties;
54+
if (this.properties.isRunInParallel()) {
55+
this.executor = Executors.newFixedThreadPool(
56+
this.properties.getThreadCount(), new WorkerThreadFactory());
57+
}
58+
}
59+
60+
/**
61+
* Create a {@link DefaultHealthIndicatorRunner} instance.
62+
*/
63+
public DefaultHealthIndicatorRunner() {
64+
this(new DefaultHealthIndicatorRunnerProperties());
65+
}
66+
67+
@Override
68+
public Map<String, Health> run(Map<String, HealthIndicator> healthIndicators) {
69+
Map<String, Health> healths = new HashMap<String, Health>();
70+
if (this.properties.isRunInParallel()) {
71+
Assert.notNull(this.executor, "Executor must not be null");
72+
Map<String, Future<Health>> futures = new HashMap<String, Future<Health>>();
73+
for (final Map.Entry<String, HealthIndicator> entry : healthIndicators.entrySet()) {
74+
Future<Health> future = this.executor.submit(new Callable<Health>() {
75+
@Override
76+
public Health call() throws Exception {
77+
return entry.getValue().health();
78+
}
79+
});
80+
futures.put(entry.getKey(), future);
81+
}
82+
for (Map.Entry<String, Future<Health>> entry : futures.entrySet()) {
83+
try {
84+
healths.put(entry.getKey(), entry.getValue().get());
85+
}
86+
catch (Exception e) {
87+
logger.warn("Error invoking health indicator '" + entry.getKey() + "'", e);
88+
healths.put(entry.getKey(), Health.down(e).build());
89+
}
90+
}
91+
}
92+
else {
93+
for (Map.Entry<String, HealthIndicator> entry : healthIndicators.entrySet()) {
94+
healths.put(entry.getKey(), entry.getValue().health());
95+
}
96+
}
97+
return healths;
98+
}
99+
100+
/**
101+
* {@link ThreadFactory} to create the worker threads.
102+
*/
103+
private static class WorkerThreadFactory implements ThreadFactory {
104+
105+
private final AtomicInteger threadNumber = new AtomicInteger(1);
106+
107+
@Override
108+
public Thread newThread(Runnable r) {
109+
Thread thread = new Thread(r);
110+
thread.setName(ClassUtils.getShortName(getClass()) +
111+
"-" + this.threadNumber.getAndIncrement());
112+
return thread;
113+
}
114+
115+
}
116+
117+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Copyright 2012-2016 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.actuate.health;
18+
19+
import org.springframework.boot.context.properties.ConfigurationProperties;
20+
21+
/**
22+
* External configuration properties for {@link DefaultHealthIndicatorRunner}.
23+
*
24+
* @author Vedran Pavic
25+
*/
26+
@ConfigurationProperties("management.health.runner")
27+
public class DefaultHealthIndicatorRunnerProperties {
28+
29+
/**
30+
* Flag to indicate the health indicators should run in parallel.
31+
*/
32+
private boolean runInParallel = false;
33+
34+
/**
35+
* Number of threads used to run health indicators in parallel.
36+
*/
37+
private int threadCount = 2;
38+
39+
public boolean isRunInParallel() {
40+
return this.runInParallel;
41+
}
42+
43+
public void setRunInParallel(boolean runInParallel) {
44+
this.runInParallel = runInParallel;
45+
}
46+
47+
public int getThreadCount() {
48+
return this.threadCount;
49+
}
50+
51+
public void setThreadCount(int threadCount) {
52+
this.threadCount = threadCount;
53+
}
54+
55+
}

0 commit comments

Comments
 (0)