序
本文主要研究一下redisTemplate对lettuce的封装
RedisTemplate
spring-data-redis-2.0.10.RELEASE-sources.jar!/org/springframework/data/redis/core/RedisTemplate.java
public class RedisTemplate extends RedisAccessor implements RedisOperations, BeanClassLoaderAware {
//......
/**
* Executes the given action object within a connection that can be exposed or not. Additionally, the connection can
* be pipelined. Note the results of the pipeline are discarded (making it suitable for write-only scenarios).
*
* @param return type
* @param action callback object to execute
* @param exposeConnection whether to enforce exposure of the native Redis Connection to callback code
* @param pipeline whether to pipeline or not the connection for the execution
* @return object returned by the action
*/
@Nullable
public T execute(RedisCallback action, boolean exposeConnection, boolean pipeline) {
Assert.isTrue(initialized, "template not initialized; call afterPropertiesSet() before using it");
Assert.notNull(action, "Callback object must not be null");
RedisConnectionFactory factory = getRequiredConnectionFactory();
RedisConnection conn = null;
try {
if (enableTransactionSupport) {
// only bind resources in case of potential transaction synchronization
conn = RedisConnectionUtils.bindConnection(factory, enableTransactionSupport);
} else {
conn = RedisConnectionUtils.getConnection(factory);
}
boolean existingConnection = TransactionSynchronizationManager.hasResource(factory);
RedisConnection connToUse = preProcessConnection(conn, existingConnection);
boolean pipelineStatus = connToUse.isPipelined();
if (pipeline && !pipelineStatus) {
connToUse.openPipeline();
}
RedisConnection connToExpose = (exposeConnection ? connToUse : createRedisConnectionProxy(connToUse));
T result = action.doInRedis(connToExpose);
// close pipeline
if (pipeline && !pipelineStatus) {
connToUse.closePipeline();
}
// TODO: any other connection processing?
return postProcessResult(result, connToUse, existingConnection);
} finally {
RedisConnectionUtils.releaseConnection(conn, factory);
}
}
//......
}
- redisTemplate内部有诸多方法,这里以execute(RedisCallback
action, boolean exposeConnection, boolean pipeline)为例 - 该方法内部是先获取RedisConnection,然后调用action.doInRedis方法
- 默认这里的connection是DefaultStringRedisConnection
- 最后这里还会调用RedisConnectionUtils.releaseConnection(conn, factory)来释放连接
RedisConnectionUtils.getConnection
spring-data-redis-2.0.10.RELEASE-sources.jar!/org/springframework/data/redis/core/RedisConnectionUtils.java
/**
* Gets a Redis connection from the given factory. Is aware of and will return any existing corresponding connections
* bound to the current thread, for example when using a transaction manager. Will always create a new connection
* otherwise.
*
* @param factory connection factory for creating the connection
* @return an active Redis connection without transaction management.
*/
public static RedisConnection getConnection(RedisConnectionFactory factory) {
return getConnection(factory, false);
}
/**
* Gets a Redis connection. Is aware of and will return any existing corresponding connections bound to the current
* thread, for example when using a transaction manager. Will create a new Connection otherwise, if
* {@code allowCreate} is true.
*
* @param factory connection factory for creating the connection
* @param allowCreate whether a new (unbound) connection should be created when no connection can be found for the
* current thread
* @param bind binds the connection to the thread, in case one was created
* @param enableTransactionSupport
* @return an active Redis connection
*/
public static RedisConnection doGetConnection(RedisConnectionFactory factory, boolean allowCreate, boolean bind,
boolean enableTransactionSupport) {
Assert.notNull(factory, "No RedisConnectionFactory specified");
RedisConnectionHolder connHolder = (RedisConnectionHolder) TransactionSynchronizationManager.getResource(factory);
if (connHolder != null) {
if (enableTransactionSupport) {
potentiallyRegisterTransactionSynchronisation(connHolder, factory);
}
return connHolder.getConnection();
}
if (!allowCreate) {
throw new IllegalArgumentException("No connection found and allowCreate = false");
}
if (log.isDebugEnabled()) {
log.debug("Opening RedisConnection");
}
RedisConnection conn = factory.getConnection();
if (bind) {
RedisConnection connectionToBind = conn;
if (enableTransactionSupport && isActualNonReadonlyTransactionActive()) {
connectionToBind = createConnectionProxy(conn, factory);
}
connHolder = new RedisConnectionHolder(connectionToBind);
TransactionSynchronizationManager.bindResource(factory, connHolder);
if (enableTransactionSupport) {
potentiallyRegisterTransactionSynchronisation(connHolder, factory);
}
return connHolder.getConnection();
}
return conn;
}
- 通过factory.getConnection()获取conn
DefaultStringRedisConnection
spring-data-redis-2.0.10.RELEASE-sources.jar!/org/springframework/data/redis/connection/DefaultStringRedisConnection.java
public class DefaultStringRedisConnection implements StringRedisConnection, DecoratedRedisConnection {
private static final byte[][] EMPTY_2D_BYTE_ARRAY = new byte[0][];
private final Log log = LogFactory.getLog(DefaultStringRedisConnection.class);
private final RedisConnection delegate;
private final RedisSerializer serializer;
private Converter bytesToString = new DeserializingConverter();
private SetConverter tupleToStringTuple = new SetConverter<>(new TupleConverter());
private SetConverter stringTupleToTuple = new SetConverter<>(new StringTupleConverter());
private ListConverter byteListToStringList = new ListConverter<>(bytesToString);
private MapConverter byteMapToStringMap = new MapConverter<>(bytesToString);
private SetConverter byteSetToStringSet = new SetConverter<>(bytesToString);
private Converter>, GeoResults>> byteGeoResultsToStringGeoResults;
@SuppressWarnings("rawtypes") private Queue pipelineConverters = new LinkedList<>();
@SuppressWarnings("rawtypes") private Queue txConverters = new LinkedList<>();
private boolean deserializePipelineAndTxResults = false;
private IdentityConverter
- 该类实际是委托给RedisConnection delegate来执行
- 对于lettuce的类库来说,这个delegate是LettuceConnection
LettuceConnection
spring-data-redis-2.0.10.RELEASE-sources.jar!/org/springframework/data/redis/connection/lettuce/LettuceConnection.java
public class LettuceConnection extends AbstractRedisConnection {
//......
@Override
@Deprecated
default Boolean set(byte[] key, byte[] value) {
return stringCommands().set(key, value);
}
@Override
public RedisStringCommands stringCommands() {
return new LettuceStringCommands(this);
}
//......
}
- 以set(byte[] key, byte[] value)方法为例,这是调用stringCommands().set方法
LettuceStringCommands
spring-data-redis-2.0.10.RELEASE-sources.jar!/org/springframework/data/redis/connection/lettuce/LettuceStringCommands.java
@RequiredArgsConstructor
class LettuceStringCommands implements RedisStringCommands {
//......
@Override
public Boolean set(byte[] key, byte[] value) {
Assert.notNull(key, "Key must not be null!");
Assert.notNull(value, "Value must not be null!");
try {
if (isPipelined()) {
pipeline(
connection.newLettuceResult(getAsyncConnection().set(key, value), Converters.stringToBooleanConverter()));
return null;
}
if (isQueueing()) {
transaction(
connection.newLettuceResult(getAsyncConnection().set(key, value), Converters.stringToBooleanConverter()));
return null;
}
return Converters.stringToBoolean(getConnection().set(key, value));
} catch (Exception ex) {
throw convertLettuceAccessException(ex);
}
}
public RedisClusterCommands getConnection() {
return connection.getConnection();
}
//......
}
- 这里直接调用getConnection().set方法,这里的getConnection(),实际还是调用LettuceConnection的getConnection方法
LettuceConnection.getConnection
spring-data-redis-2.0.10.RELEASE-sources.jar!/org/springframework/data/redis/connection/lettuce/LettuceConnection.java
public class LettuceConnection extends AbstractRedisConnection {
//......
protected RedisClusterCommands getConnection() {
if (isQueueing()) {
return getDedicatedConnection();
}
if (asyncSharedConn != null) {
if (asyncSharedConn instanceof StatefulRedisConnection) {
return ((StatefulRedisConnection) asyncSharedConn).sync();
}
if (asyncSharedConn instanceof StatefulRedisClusterConnection) {
return ((StatefulRedisClusterConnection) asyncSharedConn).sync();
}
}
return getDedicatedConnection();
}
RedisClusterCommands getDedicatedConnection() {
if (asyncDedicatedConn == null) {
asyncDedicatedConn = doGetAsyncDedicatedConnection();
if (asyncDedicatedConn instanceof StatefulRedisConnection) {
((StatefulRedisConnection) asyncDedicatedConn).sync().select(dbIndex);
}
}
if (asyncDedicatedConn instanceof StatefulRedisConnection) {
return ((StatefulRedisConnection) asyncDedicatedConn).sync();
}
if (asyncDedicatedConn instanceof StatefulRedisClusterConnection) {
return ((StatefulRedisClusterConnection) asyncDedicatedConn).sync();
}
throw new IllegalStateException(
String.format("%s is not a supported connection type.", asyncDedicatedConn.getClass().getName()));
}
protected StatefulConnection doGetAsyncDedicatedConnection() {
return connectionProvider.getConnection(StatefulConnection.class);
}
//......
}
- asyncDedicatedConn为null的话,会调用doGetAsyncDedicatedConnection来获取StatefulConnection
- connectionProvider.getConnection里头则封装了连接池相关的操作
- 可以看到无论是StatefulRedisConnection还是StatefulRedisClusterConnection,都是调用async连接的sync方法转换为同步方法
RedisConnectionUtils.releaseConnection
spring-data-redis-2.0.10.RELEASE-sources.jar!/org/springframework/data/redis/core/RedisConnectionUtils.java
/**
* Closes the given connection, created via the given factory if not managed externally (i.e. not bound to the
* thread).
*
* @param conn the Redis connection to close.
* @param factory the Redis factory that the connection was created with.
*/
public static void releaseConnection(@Nullable RedisConnection conn, RedisConnectionFactory factory) {
if (conn == null) {
return;
}
RedisConnectionHolder connHolder = (RedisConnectionHolder) TransactionSynchronizationManager.getResource(factory);
if (connHolder != null && connHolder.isTransactionSyncronisationActive()) {
if (log.isDebugEnabled()) {
log.debug("Redis Connection will be closed when transaction finished.");
}
return;
}
// release transactional/read-only and non-transactional/non-bound connections.
// transactional connections for read-only transactions get no synchronizer registered
if (isConnectionTransactional(conn, factory) && TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
unbindConnection(factory);
} else if (!isConnectionTransactional(conn, factory)) {
if (log.isDebugEnabled()) {
log.debug("Closing Redis Connection");
}
conn.close();
}
}
- 这里调用的是lettuceConnection的close方法
LettuceConnection.close
spring-data-redis-2.0.10.RELEASE-sources.jar!/org/springframework/data/redis/connection/lettuce/LettuceConnection.java
@Override
public void close() throws DataAccessException {
super.close();
if (isClosed) {
return;
}
isClosed = true;
if (asyncDedicatedConn != null) {
try {
connectionProvider.release(asyncDedicatedConn);
} catch (RuntimeException ex) {
throw convertLettuceAccessException(ex);
}
}
if (subscription != null) {
if (subscription.isAlive()) {
subscription.doClose();
}
subscription = null;
}
this.dbIndex = defaultDbIndex;
}
- 可以看到这里仅仅是对于asyncDedicatedConn进行release操作,对于asyncSharedConn则不做任何操作
小结
- lettuce是reactive的异步类库,spring-date-redis的redisTemplate对lettuce的封装底层是调用asynConn的sync方法来转换为同步方法的
- 另外redisTemplate封装了connection的获取及释放,获取是调用RedisConnectionUtils.getConnection,释放是调用RedisConnectionUtils.releaseConnection
- RedisConnectionUtils.getConnection底层是调用factory.getConnection()获取conn
- RedisConnectionUtils.releaseConnection底层是调用LettuceConnection.close,这里仅仅对asyncDedicatedConn进行release操作