Add counter and gauge services based on in-memory buffers

This seems pretty efficient (approx 12M write/s as opposed to 2M with
the DefaultCounterService). N.B. there is no need to change most of
the rest of the metrics stuff because metrics are write-often, read-
seldom, so we don't need high performance reads as much.

The Spring Integration configuration and Dropwizard support has changed
a bit. Functionally very similar and probably opaque to users, but now
the messaging operates as an Exporter on a @Scheduled method, and
Dropwizard is a replacement [Gauge,Counter]Service.

Metrics are all
collected live in-memory (and can be very fast with Java 8), buffered
there and shipped out to a MessageChannel (if one exists with id
"metricsChannel") in a background thread.

We can still use Java 8 library APIs (like LongAdder) but to compile
to java 7 compatible byte code we have to forgo the use of lambdas :-(
and shorthand generics (<>).

Fixes gh-2682, fixes gh-2513 (for Java 8 and Dropwizard users).
This commit is contained in:
Dave Syer
2015-03-30 10:39:41 +01:00
committed by Andy Wilkinson
parent 1b3efd41f5
commit 53a61474aa
31 changed files with 2016 additions and 291 deletions

View File

@@ -889,30 +889,77 @@ beans are gathered by the endpoint. You can easily change that by defining your
`MetricsEndpoint`.
[[production-ready-metric-repositories]]
=== Metric repositories
Metric service implementations are usually bound to a
{sc-spring-boot-actuator}/metrics/repository/MetricRepository.{sc-ext}[`MetricRepository`].
A `MetricRepository` is responsible for storing and retrieving metric information. Spring
Boot provides an `InMemoryMetricRepository` and a `RedisMetricRepository` out of the
box (the in-memory repository is the default) but you can also write your own. The
`MetricRepository` interface is actually composed of higher level `MetricReader` and
`MetricWriter` interfaces. For full details refer to the
{dc-spring-boot-actuator}/metrics/repository/MetricRepository.{dc-ext}[Javadoc].
=== Performance
There's nothing to stop you hooking a `MetricRepository` with back-end storage directly
into your app, but we recommend using the default `InMemoryMetricRepository`
(possibly with a custom `Map` instance if you are worried about heap usage) and
populating a back-end repository through a scheduled export job. In that way you get
some buffering in memory of the metric values and you can reduce the network
chatter by exporting less frequently or in batches. Spring Boot provides
an `Exporter` interface and a few basic implementations for you to get started with that.
The default implementation of `GaugeService` and `CounterService` provided by Spring Boot
depends on the version of Java that you are using. With Java 8 (or better) the
implementation switches to a high-performance version optimized for fast writes, backed by
atomic in-memory buffers, rather than by the immutable but relatively expensive
`Metric<?>` type (counters are approximately 5 times faster and gauges approximately twice
as fast as the repository-based implementations). The Dropwizard metrics services (see
below) are also very efficient even for Java 7 (they have backports of some of the Java 8
concurrency libraries), but they do not record timestamps for metric values. If
performance of metric gathering is a concern then it is always advisable to use one of the
high-performance options, and also to only read metrics infrequently, so that the writes
are buffered locally and only read when needed.
NOTE: The old `MetricRepository` and its `InMemoryMetricRepository` implementation are not
used by default if you are on Java 8 or if you are using Dropwizard metrics.
[[production-ready-code-hale-metrics]]
[[production-ready-metric-writers]]
=== Metric writers and aggregation
Spring Boot provides a couple of implementations of a marker interface called `Exporter`
which can be used to copy metric readings from the in-memory buffers to a place where they
can be analysed and displayed. Indeed, if you provide a `@Bean` that implements the
`MetricWriter` interface, then it will automatically be hooked up to an `Exporter` and fed
metric updates every 5 seconds (configured via `spring.metrics.export.delayMillis`) via a
`@Scheduled` annotation in `MetricRepositoryAutoConfiguration`.
The default exporter is a `MetricCopyExporter` which tries to optimize itself by not
copying values that haven't changed since it was last called. The optimization can be
switched off using a flag (`spring.metrics.export.ignoreTimestamps`). Note also that the
`MetricRegistry` has no support for timestamps, so the optimization is not available if
you are using Dropwizard metrics (all metrics will be copied on every tick).
[[production-ready-metric-writers-export-to-redis]]
==== Example: Export to Redis
If you provide a `@Bean` of type `RedisMetricRepository` the metrics are exported to a
Redis cache for aggregation. The `RedisMetricRepository` has 2 important parameters to
configure it for this purpose: `prefix` and `key` (passed into its constructor). It is
best to use a prefix that is unique to the application instance (e.g. using a random value
and maybe the logical name of the application to make it possible to correlate with other
instances of the same application). The "key" is used to keep a global index of all
metric names, so it should be unique "globally", whatever that means for your system (e.g.
2 instances of the same system could share a Redis cache if they have distinct keys).
Example:
[source,java,indent=0]
----
@Value("${spring.application.name:application}.${random.value:0000}")
private String prefix = "metrics;
@Value("${metrics.key:METRICSKEY}")
private String key = "KEY;
@Bean
MetricWriter metricWriter() {
return new RedisMetricRepository(connectionFactory, prefix, key);
}
----
[[production-ready-dropwizard-metrics]]
=== Dropwizard Metrics
User of the https://dropwizard.github.io/metrics/[Dropwizard '`Metrics`' library] will
Users of the https://dropwizard.github.io/metrics/[Dropwizard '`Metrics`' library] will
automatically find that Spring Boot metrics are published to
`com.codahale.metrics.MetricRegistry`. A default `com.codahale.metrics.MetricRegistry`
Spring bean will be created when you declare a dependency to the
@@ -920,17 +967,21 @@ Spring bean will be created when you declare a dependency to the
instance if you need customizations. Metrics from the `MetricRegistry` are also
automatically exposed via the `/metrics` endpoint.
Users can create Dropwizard metrics by prefixing their metric names with the appropriate
type (e.g. `+histogram.*+`, `+meter.*+`).
When Dropwizard metrics are in use, the default `CounterService` and `GaugeService` are
replaced with a `DropwizardMetricServices`, which is a wrapper around the `MetricRegistry`
(so you can `@Autowired` one of those services and use it as normal). You can also create
"special" Dropwizard metrics by prefixing your metric names with the appropriate type
(i.e. `+timer.*+`, `+histogram.*+` for gauges, and `+meter.*+` for counters).
[[production-ready-metrics-message-channel-integration]]
=== Message channel integration
If the '`Spring Messaging`' jar is on your classpath a `MessageChannel` called
`metricsChannel` is automatically created (unless one already exists). All metric update
events are additionally published as '`messages`' on that channel. Additional analysis or
actions can be taken by clients subscribing to that channel.
If a `MessageChannel` bean called `metricsChannel` exists, then a `MetricWriter` will be
created that writes metrics to that channel. The writer is automatically hooked up to an
exporter (as for all writers), so all metric values will appear on the channel, and
additional analysis or actions can be taken by subscribers (it's up to you to provide the
channel and any subscribers you need).