Extract common metric Buffer code

Extract common features from CounterBuffers and GuageBuffers into
a shared superclass. The new extracted types allows the service
implementations to be simplified.

Fixes gh-3257
pull/3314/merge
Phillip Webb 10 years ago
parent 291388affe
commit 134bc02404

@ -0,0 +1,48 @@
/*
* Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.metrics.buffer;
/**
* Base class for a mutable buffer containing a timestamp and a value.
*
* @author Dave Syer
* @author Phillip Webb
* @param <T> The value type
*/
abstract class Buffer<T extends Number> {
private volatile long timestamp;
public Buffer(long timestamp) {
this.timestamp = timestamp;
}
public long getTimestamp() {
return this.timestamp;
}
public void setTimestamp(long timestamp) {
this.timestamp = timestamp;
}
/**
* Returns the buffer value.
* @return the value of the buffer
*/
public abstract T getValue();
}

@ -32,29 +32,29 @@ public class BufferCounterService implements CounterService {
private final ConcurrentHashMap<String, String> names = new ConcurrentHashMap<String, String>();
private final CounterBuffers writer;
private final CounterBuffers buffers;
/**
* Create a {@link BufferCounterService} instance.
* @param writer the underlying writer used to manage metrics
* @param buffers the underlying buffers used to store metrics
*/
public BufferCounterService(CounterBuffers writer) {
this.writer = writer;
public BufferCounterService(CounterBuffers buffers) {
this.buffers = buffers;
}
@Override
public void increment(String metricName) {
this.writer.increment(wrap(metricName), 1L);
this.buffers.increment(wrap(metricName), 1L);
}
@Override
public void decrement(String metricName) {
this.writer.increment(wrap(metricName), -1L);
this.buffers.increment(wrap(metricName), -1L);
}
@Override
public void reset(String metricName) {
this.writer.reset(wrap(metricName));
this.buffers.reset(wrap(metricName));
}
private String wrap(String metricName) {

@ -32,19 +32,19 @@ public class BufferGaugeService implements GaugeService {
private final ConcurrentHashMap<String, String> names = new ConcurrentHashMap<String, String>();
private final GaugeBuffers writer;
private final GaugeBuffers buffers;
/**
* Create a {@link BufferGaugeService} instance.
* @param writer the underlying writer used to manage metrics
* @param buffers the underlying buffers used to store metrics
*/
public BufferGaugeService(GaugeBuffers writer) {
this.writer = writer;
public BufferGaugeService(GaugeBuffers buffers) {
this.buffers = buffers;
}
@Override
public void submit(String metricName, double value) {
this.writer.set(wrap(metricName), value);
this.buffers.set(wrap(metricName), value);
}
private String wrap(String metricName) {

@ -38,35 +38,29 @@ import org.springframework.lang.UsesJava8;
@UsesJava8
public class BufferMetricReader implements MetricReader, PrefixMetricReader {
private final CounterBuffers counters;
private static final Predicate<String> ALL = Pattern.compile(".*").asPredicate();
private final GaugeBuffers gauges;
private final CounterBuffers counterBuffers;
private final Predicate<String> all = Pattern.compile(".*").asPredicate();
private final GaugeBuffers gaugeBuffers;
public BufferMetricReader(CounterBuffers counters, GaugeBuffers gauges) {
this.counters = counters;
this.gauges = gauges;
public BufferMetricReader(CounterBuffers counterBuffers, GaugeBuffers gaugeBuffers) {
this.counterBuffers = counterBuffers;
this.gaugeBuffers = gaugeBuffers;
}
@Override
public Metric<?> findOne(final String name) {
LongBuffer buffer = this.counters.find(name);
if (buffer != null) {
return new Metric<Long>(name, buffer.getValue(), new Date(
buffer.getTimestamp()));
Buffer<?> buffer = this.counterBuffers.find(name);
if (buffer == null) {
buffer = this.gaugeBuffers.find(name);
}
DoubleBuffer doubleValue = this.gauges.find(name);
if (doubleValue != null) {
return new Metric<Double>(name, doubleValue.getValue(), new Date(
doubleValue.getTimestamp()));
}
return null;
return (buffer == null ? null : asMetric(name, buffer));
}
@Override
public Iterable<Metric<?>> findAll() {
return findAll(this.all);
return findAll(BufferMetricReader.ALL);
}
@Override
@ -76,30 +70,30 @@ public class BufferMetricReader implements MetricReader, PrefixMetricReader {
@Override
public long count() {
return this.counters.count() + this.gauges.count();
return this.counterBuffers.count() + this.gaugeBuffers.count();
}
private Iterable<Metric<?>> findAll(Predicate<String> predicate) {
final List<Metric<?>> metrics = new ArrayList<Metric<?>>();
this.counters.forEach(predicate, new BiConsumer<String, LongBuffer>() {
@Override
public void accept(String name, LongBuffer value) {
metrics.add(new Metric<Long>(name, value.getValue(), new Date(value
.getTimestamp())));
}
collectMetrics(this.gaugeBuffers, predicate, metrics);
collectMetrics(this.counterBuffers, predicate, metrics);
return metrics;
}
});
this.gauges.forEach(predicate, new BiConsumer<String, DoubleBuffer>() {
private <T extends Number, B extends Buffer<T>> void collectMetrics(
Buffers<B> buffers, Predicate<String> predicate, final List<Metric<?>> metrics) {
buffers.forEach(predicate, new BiConsumer<String, B>() {
@Override
public void accept(String name, DoubleBuffer value) {
metrics.add(new Metric<Double>(name, value.getValue(), new Date(value
.getTimestamp())));
public void accept(String name, B value) {
metrics.add(asMetric(name, value));
}
});
return metrics;
}
private <T extends Number> Metric<T> asMetric(final String name, Buffer<T> buffer) {
return new Metric<T>(name, buffer.getValue(), new Date(buffer.getTimestamp()));
}
}

@ -0,0 +1,76 @@
/*
* Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.metrics.buffer;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import org.springframework.lang.UsesJava8;
/**
* Base class used to manage a map of {@link Buffer} objects.
*
* @author Dave Syer
* @author Phillip Webb
* @param <B> The buffer type
*/
@UsesJava8
abstract class Buffers<B extends Buffer<?>> {
private final ConcurrentHashMap<String, B> buffers = new ConcurrentHashMap<String, B>();
public void forEach(final Predicate<String> predicate,
final BiConsumer<String, B> consumer) {
this.buffers.forEach(new BiConsumer<String, B>() {
@Override
public void accept(String name, B value) {
if (predicate.test(name)) {
consumer.accept(name, value);
}
}
});
}
public B find(final String name) {
return this.buffers.get(name);
}
public int count() {
return this.buffers.size();
}
protected final void doWith(final String name, final Consumer<B> consumer) {
B buffer = this.buffers.get(name);
if (buffer == null) {
buffer = this.buffers.computeIfAbsent(name, new Function<String, B>() {
@Override
public B apply(String name) {
return createBuffer();
}
});
}
consumer.accept(buffer);
}
protected abstract B createBuffer();
}

@ -27,35 +27,26 @@ import org.springframework.lang.UsesJava8;
* @since 1.3.0
*/
@UsesJava8
public class LongBuffer {
public class CounterBuffer extends Buffer<Long> {
private final LongAdder adder;
private volatile long timestamp;
public LongBuffer(long timestamp) {
public CounterBuffer(long timestamp) {
super(timestamp);
this.adder = new LongAdder();
this.timestamp = timestamp;
}
public void setTimestamp(long timestamp) {
this.timestamp = timestamp;
}
public long getValue() {
return this.adder.sum();
}
public long getTimestamp() {
return this.timestamp;
public void add(long delta) {
this.adder.add(delta);
}
public void reset() {
this.adder.reset();
}
public void add(long delta) {
this.adder.add(delta);
@Override
public Long getValue() {
return this.adder.sum();
}
}

@ -16,93 +16,46 @@
package org.springframework.boot.actuate.metrics.buffer;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import org.springframework.lang.UsesJava8;
/**
* Fast writes to in-memory metrics store using {@link LongBuffer}.
* Fast writes to in-memory metrics store using {@link CounterBuffer}.
*
* @author Dave Syer
* @since 1.3.0
*/
@UsesJava8
public class CounterBuffers {
public class CounterBuffers extends Buffers<CounterBuffer> {
private final ConcurrentHashMap<String, LongBuffer> metrics = new ConcurrentHashMap<String, LongBuffer>();
public void increment(final String name, final long delta) {
doWith(name, new Consumer<CounterBuffer>() {
public void forEach(final Predicate<String> predicate,
final BiConsumer<String, LongBuffer> consumer) {
this.metrics.forEach(new BiConsumer<String, LongBuffer>() {
@Override
public void accept(String name, LongBuffer value) {
if (predicate.test(name)) {
consumer.accept(name, value);
}
public void accept(CounterBuffer buffer) {
buffer.setTimestamp(System.currentTimeMillis());
buffer.add(delta);
}
});
}
public LongBuffer find(final String name) {
return this.metrics.get(name);
}
public void get(final String name, final Consumer<LongBuffer> consumer) {
read(name, consumer);
}
public void increment(final String name, final long delta) {
write(name, new Consumer<LongBuffer>() {
@Override
public void accept(LongBuffer adder) {
adder.add(delta);
}
});
}
public void reset(final String name) {
write(name, new Consumer<LongBuffer>() {
@Override
public void accept(LongBuffer adder) {
adder.reset();
}
});
}
doWith(name, new Consumer<CounterBuffer>() {
public int count() {
return this.metrics.size();
}
private void read(final String name, final Consumer<LongBuffer> consumer) {
acceptInternal(name, consumer);
}
private void write(final String name, final Consumer<LongBuffer> consumer) {
acceptInternal(name, new Consumer<LongBuffer>() {
@Override
public void accept(LongBuffer buffer) {
public void accept(CounterBuffer buffer) {
buffer.setTimestamp(System.currentTimeMillis());
consumer.accept(buffer);
buffer.reset();
}
});
}
private void acceptInternal(final String name, final Consumer<LongBuffer> consumer) {
LongBuffer adder;
if (null == (adder = this.metrics.get(name))) {
adder = this.metrics.computeIfAbsent(name,
new Function<String, LongBuffer>() {
@Override
public LongBuffer apply(String name) {
return new LongBuffer(0L);
}
});
}
consumer.accept(adder);
@Override
protected CounterBuffer createBuffer() {
return new CounterBuffer(0);
}
}

@ -22,22 +22,17 @@ package org.springframework.boot.actuate.metrics.buffer;
* @author Dave Syer
* @since 1.3.0
*/
public class DoubleBuffer {
public class GaugeBuffer extends Buffer<Double> {
private volatile double value;
private volatile long timestamp;
public DoubleBuffer(long timestamp) {
public GaugeBuffer(long timestamp) {
super(timestamp);
this.value = 0;
this.timestamp = timestamp;
}
public void setTimestamp(long timestamp) {
this.timestamp = timestamp;
}
public double getValue() {
@Override
public Double getValue() {
return this.value;
}
@ -45,8 +40,4 @@ public class DoubleBuffer {
this.value = value;
}
public long getTimestamp() {
return this.timestamp;
}
}

@ -16,79 +16,32 @@
package org.springframework.boot.actuate.metrics.buffer;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import org.springframework.lang.UsesJava8;
/**
* Fast writes to in-memory metrics store using {@link DoubleBuffer}.
* Fast writes to in-memory metrics store using {@link GaugeBuffer}.
*
* @author Dave Syer
* @since 1.3.0
*/
@UsesJava8
public class GaugeBuffers {
private final ConcurrentHashMap<String, DoubleBuffer> metrics = new ConcurrentHashMap<String, DoubleBuffer>();
public void forEach(final Predicate<String> predicate,
final BiConsumer<String, DoubleBuffer> consumer) {
this.metrics.forEach(new BiConsumer<String, DoubleBuffer>() {
@Override
public void accept(String name, DoubleBuffer value) {
if (predicate.test(name)) {
consumer.accept(name, value);
}
}
});
}
public DoubleBuffer find(final String name) {
return this.metrics.get(name);
}
public void get(final String name, final Consumer<DoubleBuffer> consumer) {
acceptInternal(name, consumer);
}
public class GaugeBuffers extends Buffers<GaugeBuffer> {
public void set(final String name, final double value) {
write(name, value);
}
public int count() {
return this.metrics.size();
}
private void write(final String name, final double value) {
acceptInternal(name, new Consumer<DoubleBuffer>() {
doWith(name, new Consumer<GaugeBuffer>() {
@Override
public void accept(DoubleBuffer buffer) {
public void accept(GaugeBuffer buffer) {
buffer.setTimestamp(System.currentTimeMillis());
buffer.setValue(value);
}
});
}
public void reset(String name) {
this.metrics.remove(name, this.metrics.get(name));
}
private void acceptInternal(final String name, final Consumer<DoubleBuffer> consumer) {
DoubleBuffer value;
if (null == (value = this.metrics.get(name))) {
value = this.metrics.computeIfAbsent(name,
new Function<String, DoubleBuffer>() {
@Override
public DoubleBuffer apply(String tag) {
return new DoubleBuffer(0L);
}
});
}
consumer.accept(value);
@Override
protected GaugeBuffer createBuffer() {
return new GaugeBuffer(0L);
}
}

@ -99,18 +99,18 @@ public class BufferGaugeServiceSpeedTests {
watch.start("readRaw" + count);
for (String name : names) {
this.gauges.forEach(Pattern.compile(name).asPredicate(),
new BiConsumer<String, DoubleBuffer>() {
new BiConsumer<String, GaugeBuffer>() {
@Override
public void accept(String name, DoubleBuffer value) {
public void accept(String name, GaugeBuffer value) {
err.println(name + "=" + value);
}
});
}
final DoubleAdder total = new DoubleAdder();
this.gauges.forEach(Pattern.compile(".*").asPredicate(),
new BiConsumer<String, DoubleBuffer>() {
new BiConsumer<String, GaugeBuffer>() {
@Override
public void accept(String name, DoubleBuffer value) {
public void accept(String name, GaugeBuffer value) {
total.add(value.getValue());
}
});

@ -37,9 +37,9 @@ public class CounterBuffersTests {
@Test
public void inAndOut() {
this.buffers.increment("foo", 2);
this.buffers.get("foo", new Consumer<LongBuffer>() {
this.buffers.doWith("foo", new Consumer<CounterBuffer>() {
@Override
public void accept(LongBuffer buffer) {
public void accept(CounterBuffer buffer) {
CounterBuffersTests.this.value = buffer.getValue();
}
});
@ -48,9 +48,9 @@ public class CounterBuffersTests {
@Test
public void getNonExistent() {
this.buffers.get("foo", new Consumer<LongBuffer>() {
this.buffers.doWith("foo", new Consumer<CounterBuffer>() {
@Override
public void accept(LongBuffer buffer) {
public void accept(CounterBuffer buffer) {
CounterBuffersTests.this.value = buffer.getValue();
}
});

@ -98,18 +98,18 @@ public class CounterServiceSpeedTests {
watch.start("readRaw" + count);
for (String name : names) {
this.counters.forEach(Pattern.compile(name).asPredicate(),
new BiConsumer<String, LongBuffer>() {
new BiConsumer<String, CounterBuffer>() {
@Override
public void accept(String name, LongBuffer value) {
public void accept(String name, CounterBuffer value) {
err.println(name + "=" + value);
}
});
}
final LongAdder total = new LongAdder();
this.counters.forEach(Pattern.compile(".*").asPredicate(),
new BiConsumer<String, LongBuffer>() {
new BiConsumer<String, CounterBuffer>() {
@Override
public void accept(String name, LongBuffer value) {
public void accept(String name, CounterBuffer value) {
total.add(value.getValue());
}
});

Loading…
Cancel
Save