<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.10.RELEASE</version>
</parent>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
spring:
redis:
host: 123.60.33.114
password: xCzLlC
port: 6399
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {
@Bean
@ConditionalOnMissingBean(name = "redisTemplate")
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
@ConditionalOnMissingBean
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}
我们可以看到RedisAutoConfiguration上有四个注解, 下面对这4个注解进行逐个分析.
proxyBeanMethods属性的默认值是true。
如果为true,则是Full模式。被@Bean标识的方法会做如下处理:
可以支持通过常规Java调用相同类的@Bean方法而保证是容器内的Bean,这有效规避了在“Lite模式”下操作时难以跟踪的细微错误。
如果为true,则是Lite模式。被@Bean标识的方法会做如下处理:
如果配置类中的@Bean标识的方法之间不存在依赖调用的话,可以设置为false,可以避免拦截方法进行代理操作,提升性能。
我们知道Springboot衍生了一系列的@Conditional注解, 而@ConditionalOnClass表示在RedisOperations类型存在的时候启用, 因为我们导入了spring-boot-starter-data-redis中包含了RedisOperations类型, 因此条件满足启用配置类.
@EnableConfigurationProperties注解的作用是:让使用 @ConfigurationProperties 注解的类生效。 这里是让RedisProperties生效, 而RedisProperties类中属性就是接收yml文件里的配置
@Import注解是向容器中注入bean, 首先注入的是LettuceConnectionConfiguration类型
protected RedisConnectionConfiguration(RedisProperties properties,
ObjectProvider<RedisSentinelConfiguration> sentinelConfigurationProvider,
ObjectProvider<RedisClusterConfiguration> clusterConfigurationProvider) {
this.properties = properties;
this.sentinelConfiguration = sentinelConfigurationProvider.getIfAvailable();
this.clusterConfiguration = clusterConfigurationProvider.getIfAvailable();
}
在构造器中将RedisProperties对象用properties属性接收.因为当前对象会被spring容器当做bean, 因此下面的源码将会执行.
@Bean
@ConditionalOnMissingBean(RedisConnectionFactory.class)
LettuceConnectionFactory redisConnectionFactory(
ObjectProvider<LettuceClientConfigurationBuilderCustomizer> builderCustomizers,
ClientResources clientResources) throws UnknownHostException {
//获取lettuce客户端配置
LettuceClientConfiguration clientConfig = getLettuceClientConfiguration(builderCustomizers, clientResources,
getProperties().getLettuce().getPool());
//创建connectionFactory对象
return createLettuceConnectionFactory(clientConfig);
}
@ConditionalOnMissingBean(RedisConnectionFactory.class), 表示RedisConnectionFactory类型不存在的时候当前方法执行, 而在项目启动的时候这个类型确实不存在, 因此当前方法会执行.
private LettuceConnectionFactory createLettuceConnectionFactory(LettuceClientConfiguration clientConfiguration) {
if (getSentinelConfig() != null) {
return new LettuceConnectionFactory(getSentinelConfig(), clientConfiguration);
}
if (getClusterConfiguration() != null) {
return new LettuceConnectionFactory(getClusterConfiguration(), clientConfiguration);
}
//单机redis
return new LettuceConnectionFactory(getStandaloneConfig(), clientConfiguration);
}
在实例化LettuceConnectionFactory对象的时候会先判断配置的redis模式, 分别是通过sentinel, cluster属性是否配置的了值来进行判断的, 说明一下,sentinel指的是哨兵模式, cluster指的是集群模式, 本公司用的是单机模式, 因此会走单机方式的实例化方法, 其实大部分公司用redis都是单机版的, 只有数据量大的才会采用其他方式.
protected final RedisStandaloneConfiguration getStandaloneConfig() {
RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();
if (StringUtils.hasText(this.properties.getUrl())) {
ConnectionInfo connectionInfo = parseUrl(this.properties.getUrl());
config.setHostName(connectionInfo.getHostName());
config.setPort(connectionInfo.getPort());
config.setPassword(RedisPassword.of(connectionInfo.getPassword()));
}
else {
config.setHostName(this.properties.getHost());
config.setPort(this.properties.getPort());
config.setPassword(RedisPassword.of(this.properties.getPassword()));
}
config.setDatabase(this.properties.getDatabase());
return config;
}
在yml文件中配置的redis参数其实都赋值给了RedisStandaloneConfiguration对象中的对应属性.这些配置信息最终都被包装在了LettuceConnectionFactory实例中.通过看LettuceConnectionFactory的源码我们会发现LettuceConnectionFactory实际上最终是实现了RedisConnectionFactory, 因此在RedisAutoConfiguration源码中的redisTemplate方法中注入的RedisConnectionFactory对象实际上是LettuceConnectionFactory
@Bean
@ConditionalOnMissingBean(name = "redisTemplate")
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
有些读者可能会有疑问了,不是还导入了JedisConnectionConfiguration了吗,为什么不会是JedisConnectionFactory, 仔细看redisTemplate()方法, 他里面将RedisTemplate交给了spring管理, 而方法上同时加了注解@ConditionalOnMissingBean(name = “redisTemplate”), 这就意味着当LettuceConnectionFactory注入后, RedisTemplate类型的bean也已经注入了容器中, redisTemplate方法不可能再次执行了, 因此我们说springboot集成redis默认用的是lettuce客户端方式
@Bean
@ConditionalOnMissingBean
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
当然springboot也默认将StringRedisTemplate 类型的bean交给了容器管理, 因此这两种bean都是自动装配的.
下面我们通过一个操作redis字符串类型的API来分析整个API的实现源码, 因为创建代理对象的源码调用链路很长, 所以关键的源码我会配上文字说明, 不是重点的源码我会贴出来,但不会进行文字说明了.
@RestController
@RequestMapping(value = "redis")
public class RedisController {
@Resource(name = "redisTemplate")
private ValueOperations valueOperations;
@GetMapping("/test")
public void test() throws IOException {
valueOperations.set("testname", "zhangsn");
}
}
这个图中我用@Resource(name = “redisTemplate”)将redisTemplate注入给了valueOperations, 这两个不是同一种类型,不会出现注入失败的情况吗, 这其实是spring集成redis的一种Editor机制, 有兴趣的可以自行研究, 这不是本次的重点.不深入为读者解析了.
@Override
public void set(K key, V value) {
//这一步是将value进行序列化, 默认用JdkSerializationRedisSerializer二进制流的方式
byte[] rawValue = rawValue(value);
execute(new ValueDeserializingRedisCallback(key) {
@Override
protected byte[] inRedis(byte[] rawKey, RedisConnection connection) {
connection.set(rawKey, rawValue);
return null;
}
}, true);
}
@Nullable
<T> T execute(RedisCallback<T> callback, boolean exposeConnection) {
return template.execute(callback, exposeConnection);
}
@Nullable
public <T> T execute(RedisCallback<T> action, boolean exposeConnection) {
return execute(action, exposeConnection, false);
}
@Nullable
public <T> T execute(RedisCallback<T> 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, enableTransactionSupport);
}
}
最终会调用RedisTemplate中的execute()方法, 这里有个enableTransactionSupport属性,表示是否开启了事务, 默认关闭, 这里提到了redis事务, 但不是本次的重点,我不做解析了, 我们往下看else中获取RedisConnection对象的方法,这里获取的RedisConnection类型的对象就是一个代理对象, 这就是第二部分的核心源码, 创建代理对象.
public static RedisConnection getConnection(RedisConnectionFactory factory) {
return getConnection(factory, false);
}
public static RedisConnection getConnection(RedisConnectionFactory factory, boolean transactionSupport) {
return doGetConnection(factory, true, false, transactionSupport);
}
public static RedisConnection doGetConnection(RedisConnectionFactory factory, boolean allowCreate, boolean bind,
boolean transactionSupport) {
Assert.notNull(factory, "No RedisConnectionFactory specified");
RedisConnectionHolder connHolder = (RedisConnectionHolder) TransactionSynchronizationManager.getResource(factory);
if (connHolder != null) {
if (transactionSupport) {
potentiallyRegisterTransactionSynchronisation(connHolder, factory);
}
return connHolder.getConnection();
}
if (!allowCreate) {
throw new IllegalArgumentException("No connection found and allowCreate = false");
}
if (log.isDebugEnabled()) {
log.debug("Opening RedisConnection");
}
//核心方法, 获取连接对象, 这里的连接对象实际上是LettuceConnection类型
RedisConnection conn = factory.getConnection();
if (bind) {
RedisConnection connectionToBind = conn;
if (transactionSupport && isActualNonReadonlyTransactionActive()) {
connectionToBind = createConnectionProxy(conn, factory);
}
connHolder = new RedisConnectionHolder(connectionToBind);
TransactionSynchronizationManager.bindResource(factory, connHolder);
if (transactionSupport) {
potentiallyRegisterTransactionSynchronisation(connHolder, factory);
}
return connHolder.getConnection();
}
return conn;
}
这里的核心是通过LettuceConnectionFactory获取LettuceConnection对象, 这个对象就相当于我们连接数据库的Connection对象,我们可以通过spring.redis.lettuce配置连接信息.
if (isClusterAware()) {
return getClusterConnection();
}
LettuceConnection connection;
//核心方法,创建连接redis的对象
connection = doCreateLettuceConnection(getSharedConnection(), connectionProvider, getTimeout(), getDatabase());
connection.setConvertPipelineAndTxResults(convertPipelineAndTxResults);
return connection;
}
因为在自动装配的时候实例化的是LettuceConnectionFactory, 因此这个factory就是LettuceConnectionFactory
@Nullable
protected StatefulRedisConnection<byte[], byte[]> getSharedConnection() {
return shareNativeConnection ? (StatefulRedisConnection) getOrCreateSharedConnection().getConnection() : null;
}
@Nullable
StatefulConnection<E, E> getConnection() {
synchronized (this.connectionMonitor) {
//获取StatefulRedisConnectionImpl类型的连接,这里比较重要的点是只有连接不存在的时候,才去获取, 所有整个过程只有一个connection对象.
if (this.connection == null) {
this.connection = getNativeConnection();
}
if (getValidateConnection()) {
validateConnection();
}
return this.connection;
}
}
private StatefulConnection<E, E> getNativeConnection() {
return connectionProvider.getConnection(StatefulConnection.class);
}
@Override
public <T extends StatefulConnection<?, ?>> T getConnection(Class<T> connectionType) {
if (connectionType.equals(StatefulRedisSentinelConnection.class)) {
return connectionType.cast(client.connectSentinel());
}
//通过RedisClient创建连接对象
if (connectionType.equals(StatefulRedisPubSubConnection.class)) {
return connectionType.cast(client.connectPubSub(codec));
}
if (StatefulConnection.class.isAssignableFrom(connectionType)) {
return connectionType.cast(readFrom.map(it -> this.masterReplicaConnection(redisURISupplier.get(), it))
.orElseGet(() -> client.connect(codec)));
}
throw new UnsupportedOperationException("Connection type " + connectionType + " not supported!");
}
public <K, V> StatefulRedisConnection<K, V> connect(RedisCodec<K, V> codec) {
checkForRedisURI();
//先创建连接对象, 通过ConnectionFuture的获取线程的返回值
return getConnection(connectStandaloneAsync(codec, this.redisURI, timeout));
}
private <K, V> ConnectionFuture<StatefulRedisConnection<K, V>> connectStandaloneAsync(RedisCodec<K, V> codec,
RedisURI redisURI, Duration timeout) {
assertNotNull(codec);
checkValidRedisURI(redisURI);
logger.debug("Trying to get a Redis connection for: " + redisURI);
DefaultEndpoint endpoint = new DefaultEndpoint(clientOptions, clientResources);
RedisChannelWriter writer = endpoint;
if (CommandExpiryWriter.isSupported(clientOptions)) {
writer = new CommandExpiryWriter(writer, clientOptions, clientResources);
}
//实例化StatefulRedisConnectionImpl对象, 这个对象包装了代理对象
StatefulRedisConnectionImpl<K, V> connection = newStatefulRedisConnection(writer, codec, timeout);
ConnectionFuture<StatefulRedisConnection<K, V>> future = connectStatefulAsync(connection, codec, endpoint, redisURI,
() -> new CommandHandler(clientOptions, clientResources, endpoint));
future.whenComplete((channelHandler, throwable) -> {
if (throwable != null) {
connection.close();
}
});
return future;
}
先创建连接对象, 通过ConnectionFuture的获取线程的返回值,实例化StatefulRedisConnectionImpl对象, 这个对象包装了代理对象.
protected <K, V> StatefulRedisConnectionImpl<K, V> newStatefulRedisConnection(RedisChannelWriter channelWriter,
RedisCodec<K, V> codec, Duration timeout) {
return new StatefulRedisConnectionImpl<>(channelWriter, codec, timeout);
}
public StatefulRedisConnectionImpl(RedisChannelWriter writer, RedisCodec<K, V> codec, Duration timeout) {
super(writer, timeout);
this.codec = codec;
this.async = newRedisAsyncCommandsImpl();
this.sync = newRedisSyncCommandsImpl();
this.reactive = newRedisReactiveCommandsImpl();
}
在StatefulRedisConnectionImpl的构造器中会实例化3种类型的commands实例, RedisAsyncCommandsImpl是一个异步API, RedisSyncCommandsImpl是一个同步API, RedisTemplate默认用的就是这个api, 但底层其实还是用的异步, 这个等分析执行API的时候再说, RedisReactiveCommandsImpl是一个响应式API, 在使用ReactiveRedisTemplate时执行该API, 因为RedisTemplate用的是同步API, 因此我们特别分析RedisSyncCommandsImpl构造器.
protected RedisCommands<K, V> newRedisSyncCommandsImpl() {
return syncHandler(async(), RedisCommands.class, RedisClusterCommands.class);
}
//这个方法在newRedisSyncCommandsImpl方法中被调用, 这里返回一个异步对象, 而这个对象就是上面的RedisAsyncCommandsImpl
@Override
public RedisAsyncCommands<K, V> async() {
return async;
}
//创建RedisCommands,RedisClusterCommands接口的代理
protected <T> T syncHandler(Object asyncApi, Class<?>... interfaces) {
FutureSyncInvocationHandler h = new FutureSyncInvocationHandler((StatefulConnection<?, ?>) this, asyncApi, interfaces);
return (T) Proxy.newProxyInstance(AbstractRedisClient.class.getClassLoader(), interfaces, h);
}
async()方法在newRedisSyncCommandsImpl方法中被调用, 这里返回一个异步对象, 而这个对象就是上面的RedisAsyncCommandsImpl, 这点很重要, 这就是为什么说同步执行其实执行的是异步命令, 因为在执行同步命令的时候,会通过反射执行RedisAsyncCommandsImpl的method, syncHandler是创建RedisCommands,RedisClusterCommands接口的代理, 而对应的InvocationHandler是FutureSyncInvocationHandler, 所以他们的delegate最终都会执行FutureSyncInvocationHandler.到这里代理对象就被创建完成了.
我们回到 RedisConnection conn = factory.getConnection()方法, 这个RedisConnection实际上是LettuceConnection类型, 上面说到StatefulRedisConnectionImpl是单实例的, 但是lettuce每次创建的LettuceConnection是多实例的, 每个LettuceConnection包装同一个StatefulRedisConnectionImpl实例, 这点很重要,所以整个项目的代理也只创建了一个.
LettuceConnection(@Nullable StatefulConnection<byte[], byte[]> sharedConnection,
LettuceConnectionProvider connectionProvider, long timeout, int defaultDbIndex) {
Assert.notNull(connectionProvider, "LettuceConnectionProvider must not be null.");
this.asyncSharedConn = sharedConnection;
this.connectionProvider = connectionProvider;
this.timeout = timeout;
this.defaultDbIndex = defaultDbIndex;
this.dbIndex = this.defaultDbIndex;
}
public final V doInRedis(RedisConnection connection) {
byte[] result = inRedis(rawKey(key), connection);
return deserializeValue(result);
}
获取到连接对象后, 会new 一个LettuceConnection实例,同时将StatefulRedisConnectionImpl通过构造器赋值给asyncSharedConn属性, 这点很重要. 之后会执行T result = action.doInRedis(connToExpose); 因为这个action其实是ValueDeserializingRedisCallback, 我们可以看到会执行inRedis(),根据函数式编程的特性,实际上就是执行下面这个方法里的inRedis()方法
@Override
public void set(K key, V value) {
byte[] rawValue = rawValue(value);
execute(new ValueDeserializingRedisCallback(key) {
@Override
protected byte[] inRedis(byte[] rawKey, RedisConnection connection) {
//实际上会执行这个方法,这里的connection就是LettuceConnection对象
connection.set(rawKey, rawValue);
return null;
}
}, true);
}
实际会执行connection.set()方法,这里的connection就是LettuceConnection对象.
因为LettuceConnection继承了AbstractRedisConnection,而AbstractRedisConnection又实现了DefaultedRedisConnection, 因此这个set方法实现上执行的是DefaultedRedisConnection的set方法, 如果用的是StringRedisTemplate那么执行的就是DefaultedStringRedisConnection的set方法, 不管怎样,看到最后我们会发现,他们底层执行的方法是一样的, 这里我们以DefaultedRedisConnection为例分析
default Boolean set(byte[] key, byte[] value) {
return stringCommands().set(key, value);
}
//stringCommands方法调用的实际上是这个,获取LettuceStringCommands对象
@Override
public RedisStringCommands stringCommands() {
return new LettuceStringCommands(this);
}
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);
}
}
//这里的connection就是LettuceConnetion
public RedisClusterCommands<byte[], byte[]> getConnection() {
return connection.getConnection();
}
protected RedisClusterCommands<byte[], byte[]> getConnection() {
if (isQueueing()) {
return getDedicatedConnection();
}
if (asyncSharedConn != null) {
//在实例化LettuceConnection时已经将asyncSharedConn属性赋值StatefulRedisConnectionImpl,因此会执行当前方法, 默认获取的是同步对象,也就是代理对象
if (asyncSharedConn instanceof StatefulRedisConnection) {
return ((StatefulRedisConnection<byte[], byte[]>) asyncSharedConn).sync();
}
if (asyncSharedConn instanceof StatefulRedisClusterConnection) {
return ((StatefulRedisClusterConnection<byte[], byte[]>) asyncSharedConn).sync();
}
}
return getDedicatedConnection();
}
这里是获取LettuceStringCommands对象, 最终调用LettuceStringCommands的getConnect(), 因为在返回LettuceCommands实例的时候,在构造器中将asyncSharedConn 属性赋值StatefulRedisConnectionImpl, 因为此通过sync()方法获取就是第二部分创建的代理对象,因为实际上会执行FutureSyncInvocationHandler的handleInvocation方法.
FutureSyncInvocationHandler(StatefulConnection<?, ?> connection, Object asyncApi, Class<?>[] interfaces) {
this.connection = connection;
this.timeoutProvider = new TimeoutProvider(() -> connection.getOptions().getTimeoutOptions(), () -> connection
.getTimeout().toNanos());
this.asyncApi = asyncApi;
//translator内部维护了一个map, 这个map的key是RedisAsyncCommandsImpl的命令方法, value是AbstractRedisAsyncCommands的命令方法, 而RedisAsyncCommandsImpl又继承了AbstractRedisAsyncCommands, 因此所有操作redis的命令都能获取到对应的执行命令
this.translator = MethodTranslator.of(asyncApi.getClass(), interfaces);
}
protected Object handleInvocation(Object proxy, Method method, Object[] args) throws Throwable {
try {
//实际会执行AbstractRedisAsyncCommands中的命令方法
Method targetMethod = this.translator.get(method);
Object result = targetMethod.invoke(asyncApi, args);
if (result instanceof RedisFuture<?>) {
RedisFuture<?> command = (RedisFuture<?>) result;
if (isNonTxControlMethod(method.getName()) && isTransactionActive(connection)) {
return null;
}
long timeout = getTimeoutNs(command);
return LettuceFutures.awaitOrCancel(command, timeout, TimeUnit.NANOSECONDS);
}
return result;
} catch (InvocationTargetException e) {
throw e.getTargetException();
}
}
//AbstractRedisAsyncCommands中的set()方法
@Override
public RedisFuture<String> set(K key, V value) {
return dispatch(commandBuilder.set(key, value));
}
Command<K, V, String> set(K key, V value) {
notNullKey(key);
return createCommand(SET, new StatusOutput<>(codec), key, value);
}
protected <T> Command<K, V, T> createCommand(CommandType type, CommandOutput<K, V, T> output, K key, V value) {
CommandArgs<K, V> args = new CommandArgs<K, V>(codec).addKey(key).addValue(value);
return createCommand(type, output, args);
}
protected <T> Command<K, V, T> createCommand(CommandType type, CommandOutput<K, V, T> output, CommandArgs<K, V> args) {
return new Command<K, V, T>(type, output, args);
}
//命令每次都实例化一个
public Command(ProtocolKeyword type, CommandOutput<K, V, T> output, CommandArgs<K, V> args) {
LettuceAssert.notNull(type, "Command type must not be null");
this.type = type;
this.output = output;
this.args = args;
}
//这里的connection就是StatefulRedisConnectionImpl, 在底层维护一个 tcp 连接,多个线程共享一个连接对象。同时会有一个ConnectionWatchdog[ChannelInboundHandlerAdapter继承netty]来维护连接,实现断连重连。因此是一个线程安全的对象
public <T> AsyncCommand<K, V, T> dispatch(RedisCommand<K, V, T> cmd) {
AsyncCommand<K, V, T> asyncCommand = new AsyncCommand<>(cmd);
RedisCommand<K, V, T> dispatched = connection.dispatch(asyncCommand);
if (dispatched instanceof AsyncCommand) {
return (AsyncCommand<K, V, T>) dispatched;
}
return asyncCommand;
}
@Override
public <T> RedisCommand<K, V, T> dispatch(RedisCommand<K, V, T> command) {
RedisCommand<K, V, T> toSend = preProcessCommand(command);
try {
return super.dispatch(toSend);
} finally {
if (command.getType().name().equals(MULTI.name())) {
multi = (multi == null ? new MultiOutput<>(codec) : multi);
}
}
}
//底层是通过netty通信发送命令
protected <T> RedisCommand<K, V, T> dispatch(RedisCommand<K, V, T> cmd) {
if (debugEnabled) {
logger.debug("dispatching command {}", cmd);
}
if (tracingEnabled) {
RedisCommand<K, V, T> commandToSend = cmd;
TraceContextProvider provider = CommandWrapper.unwrap(cmd, TraceContextProvider.class);
if (provider == null) {
commandToSend = new TracedCommand<>(cmd, clientResources.tracing()
.initialTraceContextProvider().getTraceContext());
}
return channelWriter.write(commandToSend);
}
return channelWriter.write(cmd);
}