DATAREDIS-667 - Align Lettuce connection-pooling with LettuceClientConfiguration.
We now support Lettuce connection pooling by configuring a specialized client configuration via LettucePoolingClientConfiguration.builder(). Pooling settings can be configured through the builder and used with LettuceConnectionFactory. Connection pooling is supported with Standalone and Sentinel-managed connections. LettucePoolingClientConfiguration.builder() .poolConfig(poolConfig) .and() // allows configuration of further settings. Original Pull Request: #262
This commit is contained in:
committed by
Christoph Strobl
parent
5ac7f8eb19
commit
d731dfcf81
@@ -57,6 +57,14 @@ class DefaultLettuceClientConfiguration implements LettuceClientConfiguration {
|
||||
return useSsl;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration#isUsePooling()
|
||||
*/
|
||||
@Override
|
||||
public boolean isUsePooling() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration#isVerifyPeer()
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2017 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.data.redis.connection.lettuce;
|
||||
|
||||
import io.lettuce.core.ClientOptions;
|
||||
import io.lettuce.core.resource.ClientResources;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
|
||||
|
||||
/**
|
||||
* Default implementation of {@literal LettucePoolingClientConfiguration}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 2.0
|
||||
*/
|
||||
class DefaultLettucePoolingClientConfiguration extends DefaultLettuceClientConfiguration
|
||||
implements LettucePoolingClientConfiguration {
|
||||
|
||||
private final GenericObjectPoolConfig poolConfig;
|
||||
|
||||
DefaultLettucePoolingClientConfiguration(boolean useSsl, boolean verifyPeer, boolean startTls,
|
||||
ClientResources clientResources, ClientOptions clientOptions, Duration timeout, Duration shutdownTimeout,
|
||||
GenericObjectPoolConfig poolConfig) {
|
||||
|
||||
super(useSsl, verifyPeer, startTls, clientResources, clientOptions, timeout, shutdownTimeout);
|
||||
|
||||
this.poolConfig = poolConfig;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration#isUsePooling()
|
||||
*/
|
||||
@Override
|
||||
public boolean isUsePooling() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration#getPoolConfig()
|
||||
*/
|
||||
@Override
|
||||
public GenericObjectPoolConfig getPoolConfig() {
|
||||
return poolConfig;
|
||||
}
|
||||
}
|
||||
@@ -54,6 +54,11 @@ public interface LettuceClientConfiguration {
|
||||
*/
|
||||
boolean isUseSsl();
|
||||
|
||||
/**
|
||||
* @return {@literal true} to use connection-pooling.
|
||||
*/
|
||||
boolean isUsePooling();
|
||||
|
||||
/**
|
||||
* @return {@literal true} to verify peers when using {@link #isUseSsl() SSL}.
|
||||
*/
|
||||
@@ -217,15 +222,15 @@ public interface LettuceClientConfiguration {
|
||||
class DefaultLettuceClientConfigurationBuilder
|
||||
implements LettuceClientConfigurationBuilder, LettuceSslClientConfigurationBuilder {
|
||||
|
||||
private boolean useSsl;
|
||||
private boolean verifyPeer = true;
|
||||
private boolean startTls;
|
||||
private ClientResources clientResources;
|
||||
private ClientOptions clientOptions;
|
||||
private Duration timeout = Duration.ofSeconds(RedisURI.DEFAULT_TIMEOUT);
|
||||
private Duration shutdownTimeout = Duration.ofMillis(100);
|
||||
boolean useSsl;
|
||||
boolean verifyPeer = true;
|
||||
boolean startTls;
|
||||
ClientResources clientResources;
|
||||
ClientOptions clientOptions;
|
||||
Duration timeout = Duration.ofSeconds(RedisURI.DEFAULT_TIMEOUT);
|
||||
Duration shutdownTimeout = Duration.ofMillis(100);
|
||||
|
||||
private DefaultLettuceClientConfigurationBuilder() {}
|
||||
DefaultLettuceClientConfigurationBuilder() {}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
|
||||
@@ -38,7 +38,6 @@ import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.dao.InvalidDataAccessResourceUsageException;
|
||||
import org.springframework.data.redis.ExceptionTranslationStrategy;
|
||||
import org.springframework.data.redis.PassThroughExceptionTranslationStrategy;
|
||||
import org.springframework.data.redis.RedisConnectionFailureException;
|
||||
@@ -281,8 +280,8 @@ public class LettuceConnectionFactory
|
||||
}
|
||||
|
||||
LettuceConnection connection;
|
||||
if (pool != null) {
|
||||
|
||||
if (pool != null) {
|
||||
connection = new LettuceConnection(getSharedConnection(), getTimeout(), null, pool, getDatabase());
|
||||
} else {
|
||||
connection = new LettuceConnection(getSharedConnection(), connectionProvider, getTimeout(), getDatabase());
|
||||
@@ -779,6 +778,16 @@ public class LettuceConnectionFactory
|
||||
|
||||
private LettuceConnectionProvider createConnectionProvider(AbstractRedisClient client, RedisCodec<?, ?> codec) {
|
||||
|
||||
LettuceConnectionProvider connectionProvider = doConnectionProvider(client, codec);
|
||||
|
||||
if (this.clientConfiguration.isUsePooling()) {
|
||||
return new LettucePoolingConnectionProvider(connectionProvider, this.clientConfiguration);
|
||||
}
|
||||
|
||||
return connectionProvider;
|
||||
}
|
||||
|
||||
private LettuceConnectionProvider doConnectionProvider(AbstractRedisClient client, RedisCodec<?, ?> codec) {
|
||||
if (isClusterAware()) {
|
||||
return new ClusterConnectionProvider((RedisClusterClient) client, codec);
|
||||
}
|
||||
@@ -852,11 +861,6 @@ public class LettuceConnectionFactory
|
||||
|
||||
@Override
|
||||
public RedisSentinelConnection getSentinelConnection() {
|
||||
|
||||
if (!(connectionProvider instanceof StandaloneConnectionProvider)) {
|
||||
throw new InvalidDataAccessResourceUsageException(
|
||||
"Unable to connect to sentinels using " + connectionProvider.getClass());
|
||||
}
|
||||
return new LettuceSentinelConnection(connectionProvider);
|
||||
}
|
||||
|
||||
@@ -895,6 +899,14 @@ public class LettuceConnectionFactory
|
||||
return useSsl;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration#isUsePooling()
|
||||
*/
|
||||
@Override
|
||||
public boolean isUsePooling() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setUseSsl(boolean useSsl) {
|
||||
this.useSsl = useSsl;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright 2017 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.data.redis.connection.lettuce;
|
||||
|
||||
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
|
||||
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public interface LettucePoolingClientConfiguration extends LettuceClientConfiguration {
|
||||
|
||||
/**
|
||||
* Creates a new {@link LettucePoolingClientConfigurationBuilder} to build {@link LettucePoolingClientConfiguration}
|
||||
* to be used with the jedis client.
|
||||
*
|
||||
* @return a new {@link LettucePoolingClientConfigurationBuilder} to build {@link LettucePoolingClientConfiguration}.
|
||||
*/
|
||||
static LettucePoolingClientConfigurationBuilder builder() {
|
||||
return new DefaultLettucePoolingClientConfigurationBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a default {@link LettucePoolingClientConfiguration} with:
|
||||
* <dl>
|
||||
* <dt>SSL</dt>
|
||||
* <dd>no</dd>
|
||||
* <dt>Pooling</dt>
|
||||
* <dd>yes</dd>
|
||||
* <dt>Peer Verification</dt>
|
||||
* <dd>yes</dd>
|
||||
* <dt>Start TLS</dt>
|
||||
* <dd>no</dd>
|
||||
* <dt>Client Options</dt>
|
||||
* <dd>none</dd>
|
||||
* <dt>Client Resources</dt>
|
||||
* <dd>none</dd>
|
||||
* <dt>Connect Timeout</dt>
|
||||
* <dd>60 Seconds</dd>
|
||||
* <dt>Shutdown Timeout</dt>
|
||||
* <dd>2 Seconds</dd>
|
||||
* </dl>
|
||||
*
|
||||
* @return a {@link LettucePoolingClientConfiguration} with defaults.
|
||||
*/
|
||||
static LettucePoolingClientConfiguration defaultConfiguration() {
|
||||
return builder().build();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the optional {@link GenericObjectPoolConfig}.
|
||||
*/
|
||||
GenericObjectPoolConfig getPoolConfig();
|
||||
|
||||
/**
|
||||
* Builder for Pooling-related {@link LettuceClientConfiguration}.
|
||||
*/
|
||||
interface LettucePoolingClientConfigurationBuilder {
|
||||
|
||||
/**
|
||||
* @param poolConfig must not be {@literal null}.
|
||||
* @return {@literal this} builder.
|
||||
* @throws IllegalArgumentException if poolConfig is {@literal null}.
|
||||
*/
|
||||
LettucePoolingClientConfigurationBuilder poolConfig(GenericObjectPoolConfig poolConfig);
|
||||
|
||||
/**
|
||||
* Return to {@link LettuceClientConfigurationBuilder}.
|
||||
*
|
||||
* @return {@link LettuceClientConfigurationBuilder}.
|
||||
*/
|
||||
LettuceClientConfigurationBuilder and();
|
||||
|
||||
/**
|
||||
* Build the {@link JedisClientConfiguration} with the configuration applied from this builder.
|
||||
*
|
||||
* @return a new {@link JedisClientConfiguration} object.
|
||||
*/
|
||||
LettucePoolingClientConfiguration build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Default {@link LettuceClientConfigurationBuilder} implementation to build an immutable
|
||||
* {@link LettuceClientConfiguration}.
|
||||
*/
|
||||
class DefaultLettucePoolingClientConfigurationBuilder extends DefaultLettuceClientConfigurationBuilder
|
||||
implements LettucePoolingClientConfigurationBuilder {
|
||||
|
||||
GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
|
||||
|
||||
@Override
|
||||
public LettucePoolingClientConfigurationBuilder poolConfig(GenericObjectPoolConfig poolConfig) {
|
||||
|
||||
Assert.notNull(poolConfig, "GenericObjectPoolConfig must not be null!");
|
||||
|
||||
this.poolConfig = poolConfig;
|
||||
return this;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration.LettuceClientConfigurationBuilder#build()
|
||||
*/
|
||||
@Override
|
||||
public LettucePoolingClientConfiguration build() {
|
||||
return new DefaultLettucePoolingClientConfiguration(useSsl, verifyPeer, startTls, clientResources, clientOptions,
|
||||
timeout, shutdownTimeout, poolConfig);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright 2017 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.data.redis.connection.lettuce;
|
||||
|
||||
import io.lettuce.core.api.StatefulConnection;
|
||||
import io.lettuce.core.support.ConnectionPoolSupport;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.commons.pool2.impl.GenericObjectPool;
|
||||
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.data.redis.connection.PoolException;
|
||||
|
||||
/**
|
||||
* {@link LettuceConnectionProvider} with connection pooling support. This connection provider holds multiple pools (one
|
||||
* per connection type) for contextualized connection allocation.
|
||||
* <p />
|
||||
* Each allocated connection is tracked and to be returned into the pool which created the connection. Instances of this
|
||||
* class require {@link #destroy() disposal} to de-allocate lingering connections that were not returned to the pool and
|
||||
* to close the pools.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 2.0
|
||||
* @see #getConnection(Class)
|
||||
*/
|
||||
class LettucePoolingConnectionProvider implements LettuceConnectionProvider, DisposableBean {
|
||||
|
||||
private final static Log log = LogFactory.getLog(LettucePoolingConnectionProvider.class);
|
||||
|
||||
private final LettuceConnectionProvider connectionProvider;
|
||||
private final GenericObjectPoolConfig poolConfig;
|
||||
private final Map<StatefulConnection<?, ?>, GenericObjectPool<StatefulConnection<?, ?>>> poolRef = new ConcurrentHashMap<>(
|
||||
32);
|
||||
private final Map<Class<?>, GenericObjectPool<StatefulConnection<?, ?>>> pools = new ConcurrentHashMap<>(32);
|
||||
|
||||
LettucePoolingConnectionProvider(LettuceConnectionProvider connectionProvider,
|
||||
LettuceClientConfiguration clientConfiguration) {
|
||||
|
||||
this.connectionProvider = connectionProvider;
|
||||
this.poolConfig = ((LettucePoolingClientConfiguration) clientConfiguration).getPoolConfig();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.lettuce.LettuceConnectionProvider#getConnection(java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("rawtypes")
|
||||
public StatefulConnection<?, ?> getConnection(Class<? extends StatefulConnection> connectionType) {
|
||||
|
||||
GenericObjectPool<StatefulConnection<?, ?>> pool = pools.computeIfAbsent(connectionType, poolType -> {
|
||||
return ConnectionPoolSupport.createGenericObjectPool(() -> connectionProvider.getConnection(connectionType),
|
||||
poolConfig, false);
|
||||
});
|
||||
|
||||
try {
|
||||
StatefulConnection<?, ?> connection = pool.borrowObject();
|
||||
|
||||
poolRef.put(connection, pool);
|
||||
|
||||
return connection;
|
||||
} catch (Exception e) {
|
||||
throw new PoolException("Could not get a resource from the pool", e);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.lettuce.LettuceConnectionProvider#release(io.lettuce.core.api.StatefulConnection)
|
||||
*/
|
||||
@Override
|
||||
public void release(StatefulConnection<?, ?> connection) {
|
||||
|
||||
GenericObjectPool<StatefulConnection<?, ?>> pool = poolRef.remove(connection);
|
||||
|
||||
if (pool == null) {
|
||||
throw new PoolException("Returned connection " + connection
|
||||
+ " was either previously returned or does not belong to this connection provider");
|
||||
}
|
||||
|
||||
pool.returnObject(connection);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.DisposableBean#destroy()
|
||||
*/
|
||||
@Override
|
||||
public void destroy() throws Exception {
|
||||
|
||||
if (!poolRef.isEmpty()) {
|
||||
|
||||
log.warn("LettucePoolingConnectionProvider contains unreleased connections");
|
||||
|
||||
poolRef.forEach((connection, pool) -> pool.returnObject(connection));
|
||||
poolRef.clear();
|
||||
}
|
||||
|
||||
pools.forEach((type, pool) -> pool.close());
|
||||
pools.clear();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user