mirror of
https://github.com/spring-projects/spring-boot.git
synced 2024-09-03 04:26:12 +08:00
Polish DataSourceMetrics code
This commit is contained in:
parent
169a46b1aa
commit
e17769fc2f
@ -43,8 +43,8 @@ public class DataSourceMetricsAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(DataSourceMetadataProvider.class)
|
||||
@ConditionalOnMissingBean(DataSourcePublicMetrics.class)
|
||||
DataSourcePublicMetrics dataSourcePublicMetrics() {
|
||||
@ConditionalOnMissingBean
|
||||
public DataSourcePublicMetrics dataSourcePublicMetrics() {
|
||||
return new DataSourcePublicMetrics();
|
||||
}
|
||||
|
||||
|
@ -20,6 +20,7 @@ import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.sql.DataSource;
|
||||
@ -47,88 +48,63 @@ public class DataSourcePublicMetrics implements PublicMetrics {
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
@Autowired
|
||||
private Collection<DataSourceMetadataProvider> dataSourceMetadataProviders;
|
||||
private Collection<DataSourceMetadataProvider> providers;
|
||||
|
||||
private final Map<String, DataSourceMetadata> dataSourceMetadataByPrefix = new HashMap<String, DataSourceMetadata>();
|
||||
private final Map<String, DataSourceMetadata> metadataByPrefix = new HashMap<String, DataSourceMetadata>();
|
||||
|
||||
@PostConstruct
|
||||
public void initialize() {
|
||||
Map<String, DataSource> dataSources = this.applicationContext
|
||||
.getBeansOfType(DataSource.class);
|
||||
DataSource primaryDataSource = getPrimaryDataSource();
|
||||
|
||||
DataSourceMetadataProvider provider = new CompositeDataSourceMetadataProvider(
|
||||
this.dataSourceMetadataProviders);
|
||||
for (Map.Entry<String, DataSource> entry : dataSources.entrySet()) {
|
||||
String prefix = createPrefix(entry.getKey(), entry.getValue(), entry
|
||||
.getValue().equals(primaryDataSource));
|
||||
DataSourceMetadata dataSourceMetadata = provider.getDataSourceMetadata(entry
|
||||
.getValue());
|
||||
this.providers);
|
||||
for (Map.Entry<String, DataSource> entry : this.applicationContext
|
||||
.getBeansOfType(DataSource.class).entrySet()) {
|
||||
String beanName = entry.getKey();
|
||||
DataSource bean = entry.getValue();
|
||||
String prefix = createPrefix(beanName, bean, bean.equals(primaryDataSource));
|
||||
DataSourceMetadata dataSourceMetadata = provider.getDataSourceMetadata(bean);
|
||||
if (dataSourceMetadata != null) {
|
||||
this.dataSourceMetadataByPrefix.put(prefix, dataSourceMetadata);
|
||||
this.metadataByPrefix.put(prefix, dataSourceMetadata);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Metric<?>> metrics() {
|
||||
Collection<Metric<?>> result = new LinkedHashSet<Metric<?>>();
|
||||
for (Map.Entry<String, DataSourceMetadata> entry : this.dataSourceMetadataByPrefix
|
||||
Set<Metric<?>> metrics = new LinkedHashSet<Metric<?>>();
|
||||
for (Map.Entry<String, DataSourceMetadata> entry : this.metadataByPrefix
|
||||
.entrySet()) {
|
||||
String prefix = entry.getKey();
|
||||
// Make sure the prefix ends with a dot
|
||||
if (!prefix.endsWith(".")) {
|
||||
prefix = prefix + ".";
|
||||
}
|
||||
prefix = (prefix.endsWith(".") ? prefix : prefix + ".");
|
||||
DataSourceMetadata dataSourceMetadata = entry.getValue();
|
||||
Integer poolSize = dataSourceMetadata.getPoolSize();
|
||||
if (poolSize != null) {
|
||||
result.add(new Metric<Integer>(prefix + "active", poolSize));
|
||||
}
|
||||
Float poolUsage = dataSourceMetadata.getPoolUsage();
|
||||
if (poolUsage != null) {
|
||||
result.add(new Metric<Float>(prefix + "usage", poolUsage));
|
||||
}
|
||||
addMetric(metrics, prefix + "active", dataSourceMetadata.getPoolSize());
|
||||
addMetric(metrics, prefix + "usage", dataSourceMetadata.getPoolUsage());
|
||||
}
|
||||
return metrics;
|
||||
}
|
||||
|
||||
private <T extends Number> void addMetric(Set<Metric<?>> metrics, String name, T value) {
|
||||
if (value != null) {
|
||||
metrics.add(new Metric<T>(name, value));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the prefix to use for the metrics to associate with the given
|
||||
* {@link DataSource}.
|
||||
* @param dataSourceName the name of the data source bean
|
||||
* @param name the name of the data source bean
|
||||
* @param dataSource the data source to configure
|
||||
* @param primary if this data source is the primary data source
|
||||
* @return a prefix for the given data source
|
||||
*/
|
||||
protected String createPrefix(String dataSourceName, DataSource dataSource,
|
||||
boolean primary) {
|
||||
StringBuilder sb = new StringBuilder("datasource.");
|
||||
protected String createPrefix(String name, DataSource dataSource, boolean primary) {
|
||||
if (primary) {
|
||||
sb.append("primary");
|
||||
return "datasource.primary";
|
||||
}
|
||||
else if (endWithDataSource(dataSourceName)) { // Strip the data source part out of
|
||||
// the name
|
||||
sb.append(dataSourceName.substring(0, dataSourceName.length()
|
||||
- DATASOURCE_SUFFIX.length()));
|
||||
if (name.toLowerCase().endsWith(DATASOURCE_SUFFIX.toLowerCase())) {
|
||||
name = name.substring(0, name.length() - DATASOURCE_SUFFIX.length());
|
||||
}
|
||||
else {
|
||||
sb.append(dataSourceName);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify if the given value ends with {@code dataSource}.
|
||||
*/
|
||||
protected boolean endWithDataSource(String value) {
|
||||
int suffixLength = DATASOURCE_SUFFIX.length();
|
||||
int valueLength = value.length();
|
||||
if (valueLength > suffixLength) {
|
||||
String suffix = value.substring(valueLength - suffixLength, valueLength);
|
||||
return suffix.equalsIgnoreCase(DATASOURCE_SUFFIX);
|
||||
}
|
||||
return false;
|
||||
return "datasource." + name;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -140,7 +116,7 @@ public class DataSourcePublicMetrics implements PublicMetrics {
|
||||
try {
|
||||
return this.applicationContext.getBean(DataSource.class);
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException e) {
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -24,38 +24,35 @@ import javax.sql.DataSource;
|
||||
* @author Stephane Nicoll
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public abstract class AbstractDataSourceMetadata<D extends DataSource> implements
|
||||
public abstract class AbstractDataSourceMetadata<T extends DataSource> implements
|
||||
DataSourceMetadata {
|
||||
|
||||
private final D dataSource;
|
||||
private final T dataSource;
|
||||
|
||||
/**
|
||||
* Create an instance with the data source to use.
|
||||
*/
|
||||
protected AbstractDataSourceMetadata(D dataSource) {
|
||||
protected AbstractDataSourceMetadata(T dataSource) {
|
||||
this.dataSource = dataSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Float getPoolUsage() {
|
||||
Integer max = getMaxPoolSize();
|
||||
if (max == null) {
|
||||
Integer maxSize = getMaxPoolSize();
|
||||
Integer currentSize = getPoolSize();
|
||||
if (maxSize == null || currentSize == null) {
|
||||
return null;
|
||||
}
|
||||
if (max < 0) {
|
||||
if (maxSize < 0) {
|
||||
return -1F;
|
||||
}
|
||||
Integer current = getPoolSize();
|
||||
if (current == null) {
|
||||
return null;
|
||||
}
|
||||
if (current == 0) {
|
||||
if (currentSize == 0) {
|
||||
return 0F;
|
||||
}
|
||||
return (float) current / max; // something like that
|
||||
return (float) currentSize / (float) maxSize;
|
||||
}
|
||||
|
||||
protected final D getDataSource() {
|
||||
protected final T getDataSource() {
|
||||
return this.dataSource;
|
||||
}
|
||||
|
||||
|
@ -16,10 +16,12 @@
|
||||
|
||||
package org.springframework.boot.autoconfigure.jdbc;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.apache.commons.dbcp.BasicDataSource;
|
||||
|
||||
/**
|
||||
* A {@link DataSourceMetadata} implementation for the commons dbcp data source.
|
||||
* {@link DataSourceMetadata} for a Apache Commons DBCP {@link DataSource}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 1.2.0
|
||||
|
@ -18,6 +18,7 @@ package org.springframework.boot.autoconfigure.jdbc;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
@ -30,30 +31,30 @@ import javax.sql.DataSource;
|
||||
*/
|
||||
public class CompositeDataSourceMetadataProvider implements DataSourceMetadataProvider {
|
||||
|
||||
private final Collection<DataSourceMetadataProvider> providers;
|
||||
private final List<DataSourceMetadataProvider> providers;
|
||||
|
||||
/**
|
||||
* Create an instance with an initial collection of delegates to use.
|
||||
*/
|
||||
public CompositeDataSourceMetadataProvider(
|
||||
Collection<DataSourceMetadataProvider> providers) {
|
||||
this.providers = providers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance with no delegate.
|
||||
* Create a {@link CompositeDataSourceMetadataProvider} instance with no delegate.
|
||||
*/
|
||||
public CompositeDataSourceMetadataProvider() {
|
||||
this(new ArrayList<DataSourceMetadataProvider>());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link CompositeDataSourceMetadataProvider} instance with an initial
|
||||
* collection of delegates to use.
|
||||
*/
|
||||
public CompositeDataSourceMetadataProvider(
|
||||
Collection<? extends DataSourceMetadataProvider> providers) {
|
||||
this.providers = new ArrayList<DataSourceMetadataProvider>(providers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataSourceMetadata getDataSourceMetadata(DataSource dataSource) {
|
||||
for (DataSourceMetadataProvider provider : this.providers) {
|
||||
DataSourceMetadata dataSourceMetadata = provider
|
||||
.getDataSourceMetadata(dataSource);
|
||||
if (dataSourceMetadata != null) {
|
||||
return dataSourceMetadata;
|
||||
DataSourceMetadata metadata = provider.getDataSourceMetadata(dataSource);
|
||||
if (metadata != null) {
|
||||
return metadata;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
@ -19,8 +19,8 @@ package org.springframework.boot.autoconfigure.jdbc;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
/**
|
||||
* Provide various metadata regarding a {@link DataSource} that are shared by most data
|
||||
* source types but not accessible in a standard manner.
|
||||
* Provides access meta-data that is commonly available from most {@link DataSource}
|
||||
* implementations.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 1.2.0
|
||||
@ -28,7 +28,8 @@ import javax.sql.DataSource;
|
||||
public interface DataSourceMetadata {
|
||||
|
||||
/**
|
||||
* Return the usage of the pool as a double value between 0 and 1.
|
||||
* Return the usage of the pool as value between 0 and 1 (or -1 if the pool is not
|
||||
* limited).
|
||||
* <ul>
|
||||
* <li>1 means that the maximum number of connections have been allocated</li>
|
||||
* <li>0 means that no connection is currently active</li>
|
||||
|
@ -16,14 +16,15 @@
|
||||
|
||||
package org.springframework.boot.autoconfigure.jdbc;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.beans.DirectFieldAccessor;
|
||||
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
import com.zaxxer.hikari.pool.HikariPool;
|
||||
|
||||
/**
|
||||
* A {@link DataSourceMetadata} implementation for the hikari data source.
|
||||
* {@link DataSourceMetadata} for a Hikari {@link DataSource}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 1.2.0
|
||||
@ -31,20 +32,23 @@ import com.zaxxer.hikari.pool.HikariPool;
|
||||
public class HikariDataSourceMetadata extends
|
||||
AbstractDataSourceMetadata<HikariDataSource> {
|
||||
|
||||
private final HikariPoolProvider hikariPoolProvider;
|
||||
|
||||
public HikariDataSourceMetadata(HikariDataSource dataSource) {
|
||||
super(dataSource);
|
||||
this.hikariPoolProvider = new HikariPoolProvider(dataSource);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getPoolSize() {
|
||||
HikariPool hikariPool = this.hikariPoolProvider.getHikariPool();
|
||||
if (hikariPool != null) {
|
||||
return hikariPool.getActiveConnections();
|
||||
try {
|
||||
return getHikariPool().getActiveConnections();
|
||||
}
|
||||
return null;
|
||||
catch (Exception ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private HikariPool getHikariPool() {
|
||||
return (HikariPool) new DirectFieldAccessor(getDataSource())
|
||||
.getPropertyValue("pool");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -62,50 +66,4 @@ public class HikariDataSourceMetadata extends
|
||||
return getDataSource().getConnectionTestQuery();
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide the {@link HikariPool} instance managed internally by the
|
||||
* {@link HikariDataSource} as there is no other way to retrieve that information
|
||||
* except JMX access.
|
||||
*/
|
||||
private static class HikariPoolProvider {
|
||||
private final HikariDataSource dataSource;
|
||||
|
||||
private boolean poolAvailable;
|
||||
|
||||
private HikariPoolProvider(HikariDataSource dataSource) {
|
||||
this.dataSource = dataSource;
|
||||
this.poolAvailable = isHikariPoolAvailable();
|
||||
}
|
||||
|
||||
public HikariPool getHikariPool() {
|
||||
if (!this.poolAvailable) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Object value = doGetValue();
|
||||
if (value instanceof HikariPool) {
|
||||
return (HikariPool) value;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean isHikariPoolAvailable() {
|
||||
try {
|
||||
doGetValue();
|
||||
return true;
|
||||
}
|
||||
catch (BeansException e) { // No such field
|
||||
return false;
|
||||
}
|
||||
catch (SecurityException e) { // Security manager prevents to read the value
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private Object doGetValue() {
|
||||
DirectFieldAccessor accessor = new DirectFieldAccessor(this.dataSource);
|
||||
return accessor.getPropertyValue("pool");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -20,8 +20,7 @@ import org.apache.tomcat.jdbc.pool.ConnectionPool;
|
||||
import org.apache.tomcat.jdbc.pool.DataSource;
|
||||
|
||||
/**
|
||||
*
|
||||
* A {@link DataSourceMetadata} implementation for the tomcat data source.
|
||||
* {@link DataSourceMetadata} for a Tomcat {@link DataSource}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
|
@ -27,9 +27,11 @@ import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* Abstract base class for {@link DataSourceMetadata} tests.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public abstract class AbstractDataSourceMetadataTests<D extends AbstractDataSourceMetadata> {
|
||||
public abstract class AbstractDataSourceMetadataTests<D extends AbstractDataSourceMetadata<?>> {
|
||||
|
||||
/**
|
||||
* Return a data source metadata instance with a min size of 0 and max size of 2.
|
||||
|
@ -24,6 +24,8 @@ import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
/**
|
||||
* Tests for {@link CommonsDbcpDataSourceMetadata}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class CommonsDbcpDataSourceMetadataTests extends
|
||||
|
@ -30,6 +30,8 @@ import static org.junit.Assert.assertSame;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
|
||||
/**
|
||||
* Tests for {@link CompositeDataSourceMetadataProvider}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class CompositeDataSourceMetadataProviderTests {
|
||||
|
@ -23,6 +23,8 @@ import com.zaxxer.hikari.HikariDataSource;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* Tests for {@link HikariDataSourceMetadata}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class HikariDataSourceMetadataTests extends
|
||||
|
@ -22,6 +22,8 @@ import org.junit.Before;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* Tests for {@link TomcatDataSourceMetadata}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class TomcatDataSourceMetadataTests extends
|
||||
|
Loading…
Reference in New Issue
Block a user