mirror of
https://github.com/spring-projects/spring-boot.git
synced 2024-09-03 04:26:12 +08:00
Improve thread-safety of MetricRegistryMetricReader
ee567fa
boldy claimed that it had made MetricRegistryMetricReader
thread-safe. It had not. This commit should actually make it thread
safe. I hope.
One notable improvement is that MetricRegistryMetricReader.findAll()
will no longer contain null values if a metric is removed on another
thread during iteration.
names is now a ConcurrentHashMap to allow it to be safely read and
written without holding a lock.
reverse is a LinkedMultiValueMap which is not thread-safe. This could
lead to values being lost when concurrent add calls were made. Access
to reverse is now protected by synchronizing on an internal monitor
object.
Calls to containsKey(key) followed by get(key) have been reworked to
only call get(key), this avoids the possibility of the key being
removed after the contains check but before the get.
Closes gh-2590
This commit is contained in:
parent
f761916b51
commit
4487823ff9
@ -13,12 +13,13 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.metrics.reader;
|
||||
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
@ -54,12 +55,14 @@ public class MetricRegistryMetricReader implements MetricReader, MetricRegistryL
|
||||
|
||||
private static final Map<Class<?>, Set<String>> NUMBER_KEYS = new ConcurrentHashMap<Class<?>, Set<String>>();
|
||||
|
||||
private final MetricRegistry registry;
|
||||
private final Object monitor = new Object();
|
||||
|
||||
private final Map<String, String> names = new HashMap<String, String>();
|
||||
private final Map<String, String> names = new ConcurrentHashMap<String, String>();
|
||||
|
||||
private final MultiValueMap<String, String> reverse = new LinkedMultiValueMap<String, String>();
|
||||
|
||||
private final MetricRegistry registry;
|
||||
|
||||
public MetricRegistryMetricReader(MetricRegistry registry) {
|
||||
this.registry = registry;
|
||||
registry.addListener(this);
|
||||
@ -67,11 +70,14 @@ public class MetricRegistryMetricReader implements MetricReader, MetricRegistryL
|
||||
|
||||
@Override
|
||||
public Metric<?> findOne(String metricName) {
|
||||
if (!this.names.containsKey(metricName)) {
|
||||
String name = this.names.get(metricName);
|
||||
if (name == null) {
|
||||
return null;
|
||||
}
|
||||
com.codahale.metrics.Metric metric = this.registry.getMetrics().get(name);
|
||||
if (metric == null) {
|
||||
return null;
|
||||
}
|
||||
com.codahale.metrics.Metric metric = this.registry.getMetrics().get(
|
||||
this.names.get(metricName));
|
||||
if (metric instanceof Counter) {
|
||||
Counter counter = (Counter) metric;
|
||||
return new Metric<Number>(metricName, counter.getCount());
|
||||
@ -100,7 +106,14 @@ public class MetricRegistryMetricReader implements MetricReader, MetricRegistryL
|
||||
return new Iterable<Metric<?>>() {
|
||||
@Override
|
||||
public Iterator<Metric<?>> iterator() {
|
||||
return new MetricRegistryIterator();
|
||||
Set<Metric<?>> metrics = new HashSet<Metric<?>>();
|
||||
for (String name : MetricRegistryMetricReader.this.names.keySet()) {
|
||||
Metric<?> metric = findOne(name);
|
||||
if (metric != null) {
|
||||
metrics.add(metric);
|
||||
}
|
||||
}
|
||||
return metrics.iterator();
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -113,8 +126,10 @@ public class MetricRegistryMetricReader implements MetricReader, MetricRegistryL
|
||||
@Override
|
||||
public void onGaugeAdded(String name, Gauge<?> gauge) {
|
||||
this.names.put(name, name);
|
||||
synchronized (this.monitor) {
|
||||
this.reverse.add(name, name);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGaugeRemoved(String name) {
|
||||
@ -124,8 +139,10 @@ public class MetricRegistryMetricReader implements MetricReader, MetricRegistryL
|
||||
@Override
|
||||
public void onCounterAdded(String name, Counter counter) {
|
||||
this.names.put(name, name);
|
||||
synchronized (this.monitor) {
|
||||
this.reverse.add(name, name);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCounterRemoved(String name) {
|
||||
@ -137,14 +154,18 @@ public class MetricRegistryMetricReader implements MetricReader, MetricRegistryL
|
||||
for (String key : getNumberKeys(histogram)) {
|
||||
String metricName = name + "." + key;
|
||||
this.names.put(metricName, name);
|
||||
synchronized (this.monitor) {
|
||||
this.reverse.add(name, metricName);
|
||||
}
|
||||
}
|
||||
for (String key : getNumberKeys(histogram.getSnapshot())) {
|
||||
String metricName = name + ".snapshot." + key;
|
||||
this.names.put(metricName, name);
|
||||
synchronized (this.monitor) {
|
||||
this.reverse.add(name, metricName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHistogramRemoved(String name) {
|
||||
@ -156,9 +177,11 @@ public class MetricRegistryMetricReader implements MetricReader, MetricRegistryL
|
||||
for (String key : getNumberKeys(meter)) {
|
||||
String metricName = name + "." + key;
|
||||
this.names.put(metricName, name);
|
||||
synchronized (this.monitor) {
|
||||
this.reverse.add(name, metricName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMeterRemoved(String name) {
|
||||
@ -170,14 +193,18 @@ public class MetricRegistryMetricReader implements MetricReader, MetricRegistryL
|
||||
for (String key : getNumberKeys(timer)) {
|
||||
String metricName = name + "." + key;
|
||||
this.names.put(metricName, name);
|
||||
synchronized (this.monitor) {
|
||||
this.reverse.add(name, metricName);
|
||||
}
|
||||
}
|
||||
for (String key : getNumberKeys(timer.getSnapshot())) {
|
||||
String metricName = name + ".snapshot." + key;
|
||||
this.names.put(metricName, name);
|
||||
synchronized (this.monitor) {
|
||||
this.reverse.add(name, metricName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTimerRemoved(String name) {
|
||||
@ -185,43 +212,22 @@ public class MetricRegistryMetricReader implements MetricReader, MetricRegistryL
|
||||
}
|
||||
|
||||
private void remove(String name) {
|
||||
for (String key : this.reverse.get(name)) {
|
||||
List<String> keys;
|
||||
|
||||
synchronized (this.monitor) {
|
||||
keys = this.reverse.remove(name);
|
||||
}
|
||||
|
||||
for (String key : keys) {
|
||||
this.names.remove(name + "." + key);
|
||||
}
|
||||
this.reverse.remove(name);
|
||||
}
|
||||
|
||||
private class MetricRegistryIterator implements Iterator<Metric<?>> {
|
||||
|
||||
private Iterator<String> iterator;
|
||||
|
||||
public MetricRegistryIterator() {
|
||||
this.iterator = new HashSet<String>(
|
||||
MetricRegistryMetricReader.this.names.keySet()).iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return this.iterator.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Metric<?> next() {
|
||||
String name = this.iterator.next();
|
||||
return MetricRegistryMetricReader.this.findOne(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException(
|
||||
"You cannot remove from this iterator.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static Set<String> getNumberKeys(Object metric) {
|
||||
Set<String> result = NUMBER_KEYS.containsKey(metric.getClass()) ? NUMBER_KEYS
|
||||
.get(metric.getClass()) : new HashSet<String>();
|
||||
Set<String> result = NUMBER_KEYS.get(metric.getClass());
|
||||
if (result == null) {
|
||||
result = new HashSet<String>();
|
||||
}
|
||||
if (result.isEmpty()) {
|
||||
for (PropertyDescriptor descriptor : BeanUtils.getPropertyDescriptors(metric
|
||||
.getClass())) {
|
||||
|
Loading…
Reference in New Issue
Block a user