Last active
March 14, 2016 17:03
-
-
Save fastnsilver/ed6948e0f2e464b52d5f to your computer and use it in GitHub Desktop.
A possible way to bring together AWS CloudWatch and ServoMetrics observers in a Jetty Container
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@Configuration | |
public class JettyConfig { | |
@Autowired | |
private MetricRegistry metricRegistry; | |
public Server jettyServer(int maxThreads, int minThreads, int idleTimeout) { | |
QueuedThreadPool threadPool = new InstrumentedQueuedThreadPool(metricRegistry); | |
threadPool.setMaxThreads(maxThreads); | |
threadPool.setMinThreads(minThreads); | |
threadPool.setIdleTimeout(idleTimeout); | |
return new Server(threadPool); | |
} | |
// @see http://wiki.eclipse.org/Jetty/Howto/High_Load | |
// @see http://jdpgrailsdev.github.io/blog/2014/10/07/spring_boot_jetty_thread_pool.html | |
@Bean | |
public JettyEmbeddedServletContainerFactory jettyEmbeddedServletContainerFactory( | |
@Value("${server.port:8080}") final int port, | |
@Value("${jetty.threadPool.maxThreads:1000}") final int maxThreads, | |
@Value("${jetty.threadPool.minThreads:100}") final int minThreads, | |
@Value("${jetty.threadPool.idleTimeout:60000}") final int idleTimeout) { | |
final JettyEmbeddedServletContainerFactory factory = new JettyEmbeddedServletContainerFactory(port); | |
factory.addServerCustomizers(customizer -> { jettyServer(maxThreads, minThreads, idleTimeout); }); | |
return factory; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* {@link MetricObserver} to convert Servo metrics into Spring Boot {@link Metric} | |
* instances. | |
*/ | |
public final class ServoMetricObserver extends BaseMetricObserver { | |
private final MetricWriter metrics; | |
public ServoMetricObserver(MetricWriter metrics) { | |
super("spring-boot"); | |
this.metrics = metrics; | |
} | |
@Override | |
public void updateImpl(List<com.netflix.servo.Metric> servoMetrics) { | |
for (com.netflix.servo.Metric servoMetric : servoMetrics) { | |
MonitorConfig config = servoMetric.getConfig(); | |
String type = config.getTags().getValue("type"); | |
String key = new StringBuilder(type).append(".servo.") | |
.append(config.getName()).toString().toLowerCase(); | |
if (servoMetric.hasNumberValue()) { | |
this.metrics.set(new Metric<Number>(key, | |
servoMetric.getNumberValue(), new Date(servoMetric | |
.getTimestamp()))); | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Modeled after {@link org.springframework.cloud.netflix.servo.ServoMetricCollector} | |
* Scans for {@link com.netflix.servo.publish.MetricObserver} implementations on classpath and | |
* adds them to {@link com.netflix.servo.publish.PollRunnable} making this | |
* implementation open for extension | |
*/ | |
public class UnifiedMetricCollector { | |
private final Logger log = LoggerFactory.getLogger(getClass()); | |
@Value("${spring.aws.cloudwatch.metrics.polling.timeUnit:SECONDS}") | |
private String timeUnit; | |
@Value("${spring.aws.cloudwatch.metrics.polling.interval:5}") | |
private int pollingInterval; | |
@Autowired | |
private ListableBeanFactory beanFactory; | |
public UnifiedMetricCollector() { | |
PollRunnable task = new PollRunnable(new MonitorRegistryMetricPoller(), | |
BasicMetricFilter.MATCH_ALL, true, beanFactory.getBeansOfType(MetricObserver.class).values()); | |
if (!PollScheduler.getInstance().isStarted()) { | |
try { | |
PollScheduler.getInstance().start(); | |
} | |
catch (Exception e) { | |
// Can fail due to race condition with evil singletons (if more than one | |
// app in same JVM) | |
log.error("Could not start servo metrics poller", e); | |
return; | |
} | |
} | |
PollScheduler.getInstance().addPoller(task, pollingInterval, TimeUnit.valueOf(timeUnit.toUpperCase())); | |
} | |
@PreDestroy | |
public void destroy() throws Exception { | |
log.info("Stopping Servo PollScheduler"); | |
if (PollScheduler.getInstance().isStarted()) { | |
try { | |
PollScheduler.getInstance().stop(); | |
} | |
catch (Exception e) { | |
log.error("Could not stop servo metrics poller", e); | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@Configuration | |
@ConditionalOnMissingBean(value={ServoMetricsAutoConfiguration.class}) | |
@ConditionalOnWebApplication | |
@ConditionalOnClass({ Monitors.class, MetricReader.class }) | |
@ConditionalOnBean(MetricReader.class) | |
@AutoConfigureBefore(EndpointAutoConfiguration.class) | |
@AutoConfigureAfter({ MetricRepositoryAutoConfiguration.class }) | |
public class UnifiedMetricsConfig { | |
@Configuration | |
@ConditionalOnAwsCloudEnvironment | |
@EnableContextCredentials | |
static class AwsCloudWatchMetricsConfig { | |
@Value("${spring.application.name}") | |
private String applicationName; | |
@Autowired | |
private AWSCredentials awsCredentials; | |
@Bean | |
@ConditionalOnMissingBean | |
MetricObserver cloudWatchMetricObserver() { | |
return new CloudWatchMetricObserver(applicationName, "servo.cloudwatch", new AmazonCloudWatchAsyncClient(awsCredentials)); | |
} | |
} | |
@Bean | |
@ConditionalOnMissingBean | |
public MetricObserver servoMetricObserver(MetricWriter metrics) { | |
return new ServoMetricObserver(metrics); | |
} | |
@Bean | |
@ConditionalOnMissingBean | |
public UnifiedMetricCollector unifiedMetricCollector() { | |
return new UnifiedMetricCollector(); | |
} | |
} |
Note how UnifiedMetricsConfig
gets instantiated only when ServoMetricsAutoConfiguration
is not on classpath (i.e., not defined as bean).
UnifiedMetricCollector
is a drop-in replacement for ServoMetricCollector
when one or more MetricObserver
implementations are declared as Spring beans.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
InstrumentedQueuedThreadPool
inJettyConfig
above is fromwhich allows us to capture thread stats from Jetty.