redis之Springboot源码解析

Jedis在实现上是直接连接的redis server,如果在多线程环境下是非线程安全的,这个时候只有使用连接池,为每个Jedis实例增加物理连接。

Lettuce的连接是基于Netty的,连接实例(StatefulRedisConnection)可以在多个线程间并发访问,应为StatefulRedisConnection是线程安全的,所以一个连接实例(StatefulRedisConnection)就可以满足多线程环境下的并发访问,当然这个也是可伸缩的设计,一个连接实例不够的情况也可以按需增加连接实例。lettuce主要利用netty实现与redis的同步和异步通信。

@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {

	@Bean
	@ConditionalOnMissingBean(name = "redisTemplate")
	public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){
		RedisTemplate<Object, Object> template = new RedisTemplate<>();
		template.setConnectionFactory(redisConnectionFactory);
		return template;
	}

	@Bean
	@ConditionalOnMissingBean
	public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory){
		StringRedisTemplate template = new StringRedisTemplate();
		template.setConnectionFactory(redisConnectionFactory);
		return template;
	}
}

任何时候Spring IOC容器中同时管理StringRedisTemplate、RedisTemplate两个实例。

LettuceConnectionConfiguration、JedisConnectionConfiguration有个公共功能是分别创建Redis连接工厂接口RedisConnectionFactory的实现子类:LettuceConnectionFactory、jedis连接工厂类之JedisConnectionFactory。两个实现子类均在接口InitializingBean功能下初始化连接池相关功能。

由于先解析LettuceConnectionConfiguration类,所以两者配置类都满足的前提下,优先通过LettuceConnectionConfiguration创建JedisConnectionFactory。

1.JedisConnectionConfiguration

@ConditionalOnClass({ GenericObjectPool.class, JedisConnection.class, Jedis.class })
class JedisConnectionConfiguration extends RedisConnectionConfiguration {
	@ConditionalOnMissingBean(RedisConnectionFactory.class)
	public JedisConnectionFactory redisConnectionFactory() throws UnknownHostException {
		return createJedisConnectionFactory();
	}
}
public class JedisConnectionFactory implements InitializingBean, RedisConnectionFactory {
	public void afterPropertiesSet() {

		clientConfig = createClientConfig(getDatabase(), getRedisUsername(), getRedisPassword());
		...
		if (getUsePool() && !isRedisClusterAware()) {
			//初始化连接池 -> JedisPool -> JedisPool构造器中初始化GenericObjectPool
			this.pool = createPool();
		}
		...
		this.initialized = true;
	}
}

JedisConnectionConfiguration发挥作用的前提条件之一是存在Jedis类,该类是通过以下依赖引入的:

 <dependency>
    <groupId>redis.clients</groupId>
    // 该依赖中默认存在 commons-pool2, 实现连接池的管理
    <artifactId>jedis</artifactId>
</dependency>
<!-- redis依赖commons-pool -->
<dependency>
   <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>

2.LettuceConnectionConfiguration

@Configuration
@ConditionalOnClass(RedisClient.class)
class LettuceConnectionConfiguration extends RedisConnectionConfiguration {

	@Bean
	@ConditionalOnMissingBean(RedisConnectionFactory.class)
	public LettuceConnectionFactory redisConnectionFactory(ClientResources clientResources)
			throws UnknownHostException {
		LettuceClientConfiguration clientConfig = getLettuceClientConfiguration(clientResources,
				this.properties.getLettuce().getPool());
		return createLettuceConnectionFactory(clientConfig);
	}
}

LettuceConnectionConfiguration配置类发挥作用的前提条件之一是存在Jedis类,该类是通过以下依赖引入的:

 <dependency>
   <groupId>io.lettuce</groupId>
   <artifactId>lettuce-core</artifactId>
   <version>5.1.7.RELEASE</version>
   <scope>compile</scope>
 </dependency>

在SpringBoot项目中spring-boot-starter-data-redis包下默认存在lettuce-core

3.获取客户端连接

class LettucePoolingConnectionProvider{
	
	private final Map<Class<?>, GenericObjectPool<StatefulConnection<?, ?>>> pools = new ConcurrentHashMap<>(32);
	
	public <T extends StatefulConnection<?, ?>> T getConnection(Class<T> connectionType) {

		GenericObjectPool<StatefulConnection<?, ?>> pool = pools.computeIfAbsent(connectionType, poolType -> {
			return ConnectionPoolSupport.createGenericObjectPool(() -> 
				  // connectionProvider:StandaloneConnectionProvider。工厂类PooledObjectFactory通过lambda表达式回调创建连接
				   connectionProvider.getConnection(connectionType),
				   poolConfig, false);
		});
		// pool:返回GenericObjectPool  调用ConnectionPoolSupport#createGenericObjectPool#borrowObject
		StatefulConnection<?, ?> connection = pool.borrowObject();
		poolRef.put(connection, pool);
		return connectionType.cast(connection);
	}
}

GenericObjectPool:管理连接核心类。也是实现池化技术常用手段。

public abstract class ConnectionPoolSupport {

	public static <T extends StatefulConnection<?, ?>> GenericObjectPool<T> createGenericObjectPool(
	        Supplier<T> connectionSupplier, GenericObjectPoolConfig config, boolean wrapConnections) {
	    
	    AtomicReference<Origin<T>> poolRef = new AtomicReference<>();
	    GenericObjectPool<T> pool = new GenericObjectPool<T>(new RedisPooledObjectFactory<T>(connectionSupplier), 	
	    				config) {
	        @Override
	        public T borrowObject() throws Exception {
	            return wrapConnections ? 
	            	   ConnectionWrapping.wrapConnection(super.borrowObject(), poolRef.get()) 
	            	   :super.borrowObject();//调用GenericObjectPool#borrowObject,最后是通过工厂类PooledObjectFactory的实现类RedisPooledObjectFactory创建连接
	        }
	
	        @Override
	        public void returnObject(T obj) {
	
	            if (wrapConnections && obj instanceof HasTargetConnection) {
	                super.returnObject((T) ((HasTargetConnection) obj).getTargetConnection());
	                return;
	            }
	            super.returnObject(obj);
	        }
	    };
	    poolRef.set(new ObjectPoolWrapper<>(pool));
	    return pool;
	}
}
class StandaloneConnectionProvider implements LettuceConnectionProvider, TargetAware {
	private final RedisClient client;
	private final RedisCodec<?, ?> codec;
	private final Optional<ReadFrom> readFrom;
	private final Supplier<RedisURI> redisURISupplier;
	
	public <T extends StatefulConnection<?, ?>> T getConnection(Class<T> connectionType) {

		if (StatefulConnection.class.isAssignableFrom(connectionType)) {
			return connectionType.cast(readFrom.map(it -> this.masterReplicaConnection(redisURISupplier.get(), it))
					.orElseGet(() -> client.connect(codec)));//RedisClient#connect 章节4内容
		}
		throw new UnsupportedOperationException("Connection type " + connectionType + " not supported!");
	}
}

4.Netty建立连接的过程

public class RedisClient extends AbstractRedisClient {
	public <K, V> StatefulRedisConnection<K, V> connect(RedisCodec<K, V> codec) {
		return getConnection(connectStandaloneAsync(codec, this.redisURI, timeout));
    }
	
	private <K, V> ConnectionFuture<StatefulRedisConnection<K, V>> connectStandaloneAsync(RedisCodec<K, V> codec,
            RedisURI redisURI, Duration timeout) {
		// 该类是Redis通过通道channel写数据的核心类,内部notifyChannelActive方法是通过Netty建立连接后在ChannelActive方法中回调赋值channel的过程
        DefaultEndpoint endpoint = new DefaultEndpoint(clientOptions, clientResources);
        RedisChannelWriter writer = endpoint;
        if (CommandExpiryWriter.isSupported(clientOptions)) {
        	//CommandExpiryWriter对DefaultEndpoint包装了一层
            writer = new CommandExpiryWriter(writer, clientOptions, clientResources);
        }
		// 返回存在状态的连接 StatefulRedisConnectionImpl,其实是对DefaultEndpoint的抽象
        StatefulRedisConnectionImpl<K, V> connection = newStatefulRedisConnection(writer, codec, timeout);
        ConnectionFuture<StatefulRedisConnection<K, V>> future = connectStatefulAsync(connection, codec, endpoint, 
        // lambda表达式是为了初始化Netty涉及的ChannelHandler之CommandHandler,既是出栈也是入栈Handler。也就是在该Handler内部的ChannelActive方法负责对DefaultEndpoint赋值channel的操作
        redisURI,() -> new CommandHandler(clientOptions, clientResources, endpoint));
        future.whenComplete((channelHandler, throwable) -> {
            if (throwable != null) {
                connection.close();
            }
        });
        return future;
    }
	
	private <K, V, S> ConnectionFuture<S> connectStatefulAsync(StatefulRedisConnectionImpl<K, V> connection,
            RedisCodec<K, V> codec, Endpoint endpoint,RedisURI redisURI, Supplier<CommandHandler> 	
            		commandHandlerSupplier) {

        ConnectionBuilder connectionBuilder = ConnectionBuilder.connectionBuilder();
 		connectionBuilder.connection(connection);//StatefulRedisConnectionImpl
        connectionBuilder.clientOptions(clientOptions);
        connectionBuilder.clientResources(clientResources);
        connectionBuilder.commandHandler(commandHandlerSupplier).endpoint(endpoint);//DefaultEndpoint
        // 核心创建Netty 客户端的启动器之Bootstrap
        connectionBuilder(getSocketAddressSupplier(redisURI), connectionBuilder, redisURI);
        channelType(connectionBuilder, redisURI);

        if (clientOptions.isPingBeforeActivateConnection()) {
            if (hasPassword(redisURI)) {
                connectionBuilder.enableAuthPingBeforeConnect();
            } else {
                connectionBuilder.enablePingBeforeConnect();
            }
        }
        ConnectionFuture<RedisChannelHandler<K, V>> future = initializeChannelAsync(connectionBuilder);//调用父类
        ConnectionFuture<?> sync = future;
        if (!clientOptions.isPingBeforeActivateConnection() && hasPassword(redisURI)) {

            sync = sync.thenCompose(channelHandler -> {
                CommandArgs<K, V> args = new CommandArgs<>(codec).add(redisURI.getPassword());
                return connection.async().dispatch(CommandType.AUTH, new StatusOutput<>(codec), args);
            });
        }

        if (LettuceStrings.isNotEmpty(redisURI.getClientName())) {
            sync = sync.thenApply(channelHandler -> {
                connection.setClientName(redisURI.getClientName());
                return channelHandler;
            });
        }

        if (redisURI.getDatabase() != 0) {
            sync = sync.thenCompose(channelHandler -> {
                CommandArgs<K, V> args = new CommandArgs<>(codec).add(redisURI.getDatabase());
                return connection.async().dispatch(CommandType.SELECT, new StatusOutput<>(codec), args);
            });
        }
        return sync.thenApply(channelHandler -> (S) connection);
    }
}
public abstract class AbstractRedisClient {

	protected <K, V, T extends RedisChannelHandler<K, V>> ConnectionFuture<T> initializeChannelAsync(
	        ConnectionBuilder connectionBuilder) {
	
	    Mono<SocketAddress> socketAddressSupplier = connectionBuilder.socketAddress();//Redis配置的url地址
	    CompletableFuture<SocketAddress> socketAddressFuture = new CompletableFuture<>();
	    CompletableFuture<Channel> channelReadyFuture = new CompletableFuture<>();
		// 这种方式涉及 rtjava中响应式编程,
		socketAddressSupplier.doOnError(socketAddressFuture::completeExceptionally)
				.doOnNext(socketAddressFuture::complete)
	            .subscribe(redisAddress -> {// 启动发布者Mono的流式处理
	                if (channelReadyFuture.isCancelled()) {
	                    return;
	                }
	                initializeChannelAsync0(connectionBuilder, channelReadyFuture, redisAddress);
	            }, channelReadyFuture::completeExceptionally);
		// thenApply:当channelReadyFuture执行完complete方法触发。表明客户单连接正常建立。
		// DefaultConnectionFuture也是RedisClient返回连接的最终形式。其中connection是对DefaultEndpoint抽象化的 
		//StatefulRedisConnectionImpl
	    return new DefaultConnectionFuture<>(socketAddressFuture, channelReadyFuture.thenApply(channel -> (T) connectionBuilder.connection()));
	}
	
	private void initializeChannelAsync0(ConnectionBuilder connectionBuilder, CompletableFuture<Channel> 	
							channelReadyFuture,SocketAddress redisAddress) {
	    Bootstrap redisBootstrap = connectionBuilder.bootstrap();
		//ChannelInitializer的子类 PlainChannelInitializer,包裹客户端全部的channelHandler,其中就包括上述CommandHandler
	    RedisChannelInitializer initializer = connectionBuilder.build();
	    //初始化ChannelInitializer,最终会将全部channelHandler绑定到NioSocketChannel的管道pipeline中
	    redisBootstrap.handler(initializer);
	    clientResources.nettyCustomizer().afterBootstrapInitialized(redisBootstrap);
	    // 异步控制Redis客户端连接创建的结果
	    CompletableFuture<Boolean> initFuture = initializer.channelInitialized();
	    // 真正驱动Netty组件开始创建客户端连接
	    ChannelFuture connectFuture = redisBootstrap.connect(redisAddress);
	
	    channelReadyFuture.whenComplete((c, t) -> {
	
	        if (t instanceof CancellationException) {
	            connectFuture.cancel(true);
	            initFuture.cancel(true);
	        }
	    });
		// 此处的监听器涉及一个任务future。添加监听器的过程其实是将任务future添加到Netty普通队列中
	    connectFuture.addListener(future -> {
	        if (!future.isSuccess()) {
	            connectionBuilder.endpoint().initialState();
	            channelReadyFuture.completeExceptionally(future.cause());
	            return;
	        }
			//当Netty逐步处理普通任务队列中所有任务时,会涉及当前任务的执行。
			//RedisChannelInitializer中所有handler之channelInactive、userEventTriggered、channelActive、exceptionCaught方法执行都会涉及回调initFuture。执行完毕后继续执行initFuture回调方法whenComplete,根据handler执行结果判断连接建立是否顺利
	        initFuture.whenComplete((success, throwable) -> {
	            if (throwable == null) {// Netty建立连接过程中没有异常产生,说明连接建立顺利
	            	// StatefulRedisConnectionImpl
	                RedisChannelHandler<?, ?> connection = connectionBuilder.connection();
	                connection.registerCloseables(closeableResources, connection);
	                channelReadyFuture.complete(connectFuture.channel());//此处触发异步执行任务channelReadyFuture的执行
	                return;
	            }
	            connectionBuilder.endpoint().initialState();
	            Throwable failure;
	
	            if (throwable instanceof RedisConnectionException) {
	                failure = throwable;
	            } else if (throwable instanceof TimeoutException) {
	                failure = new RedisConnectionException("Could not initialize channel within "
	                        + connectionBuilder.getTimeout(), throwable);
	            } else {
	                failure = throwable;
	            }
	            channelReadyFuture.completeExceptionally(failure);
	        });
	    });
	}
}

返回的客户端连接实例为DefaultConnectionFuture,其实内部已经抽象化DefaultEndpoint。能获取到DefaultEndpoint就能获取到Netty从缓存区ByteBuf刷数据到通道channel对应的NioSocketChannel。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值