Talk about Lettuce OolingConnectionProvider

  lettuce

Order

This article mainly studies Lettuce OolingConnectionProvider.

LettucePoolingConnectionProvider

spring-data-redis-2.0.10.RELEASE-sources.jar! /org/springframework/data/redis/connection/lettuce/LettucePoolingConnectionProvider.java

/**
 * {@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
 * @author Christoph Strobl
 * @since 2.0
 * @see #getConnection(Class)
 */
class LettucePoolingConnectionProvider implements LettuceConnectionProvider, RedisClientProvider, 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,
            LettucePoolingClientConfiguration clientConfiguration) {

        Assert.notNull(connectionProvider, "ConnectionProvider must not be null!");
        Assert.notNull(clientConfiguration, "ClientConfiguration must not be null!");

        this.connectionProvider = connectionProvider;
        this.poolConfig = clientConfiguration.getPoolConfig();
    }

    /*
     * (non-Javadoc)
     * @see org.springframework.data.redis.connection.lettuce.LettuceConnectionProvider#getConnection(java.lang.Class)
     */
    @Override
    public <T extends StatefulConnection<?, ?>> T getConnection(Class<T> 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 connectionType.cast(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.RedisClientProvider#getRedisClient()
     */
    @Override
    public AbstractRedisClient getRedisClient() {

        if (connectionProvider instanceof RedisClientProvider) {
            return ((RedisClientProvider) connectionProvider).getRedisClient();
        }

        throw new IllegalStateException(
                String.format("Underlying connection provider %s does not implement RedisClientProvider!",
                        connectionProvider.getClass().getName()));
    }

    /*
     * (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();
    }
}
  • This provider is different from ordinary connections, it distinguishes different types of connection pools, so pools are map-structured, and there is also a map called poolRef to maintain the connection pool where each connection is located.
  • Pools is mainly used to obtain corresponding connection pools according to connection types, and is used for borrowing scenarios of connections, while poolRef is mainly used for returning scenarios of connections
  • Here, the computeIfAbsent method of ConcurrentHashMap is adopted, which triggers creation and return if the key does not exist, and directly returns if the key exists.

Summary

  • Lettuce version 4.0 introduces StatefulConnection, which maintains login, transaction, selected database, read and write status, etc.
  • There are several types of StatefulConnection, namely StatefulRedisConnection (StatefulRedisPubSubConnection、StatefulRedisMasterSlaveConnection)、StatefulRedisSentinelConnection、StatefulRedisClusterConnection
  • Lettuce OOLINGConnectionProvider maintains connection pools for multiple types of connections, so it adopts the data structure of map. Otherwise, a single type of connection can directly use GenericObjectPool.

doc