Luttuce源码一:启动并创建连接

最近在看Luttuce的源码,简单记录一下

1.LuttuceConnectionConfiguration

LettuceConnectionConfiguration类是lettuce初始化的起始类,这个类是spring的管理的配置类,它初始化了lettuce连接工厂类,见如下代码

  @Bean
    @ConditionalOnMissingBean({RedisConnectionFactory.class})
    LettuceConnectionFactory redisConnectionFactory(ObjectProvider<LettuceClientConfigurationBuilderCustomizer> builderCustomizers, ClientResources clientResources) throws UnknownHostException {
        LettuceClientConfiguration clientConfig = this.getLettuceClientConfiguration(builderCustomizers, clientResources, this.getProperties().getLettuce().getPool());
        return this.createLettuceConnectionFactory(clientConfig);
    }

下面会判断单点模式/集群模式/哨兵模式,来初始化连接工厂,本文以集群模式为例来讲解

  private LettuceConnectionFactory createLettuceConnectionFactory(LettuceClientConfiguration clientConfiguration) {
        if (this.getSentinelConfig() != null) {
            return new LettuceConnectionFactory(this.getSentinelConfig(), clientConfiguration);
        } else {
            return this.getClusterConfiguration() != null ? new LettuceConnectionFactory(this.getClusterConfiguration(), clientConfiguration) : new LettuceConnectionFactory(this.getStandaloneConfig(), clientConfiguration);
        }
    }

后面会进入LettuceConnectionFactory的构造方法:

	public LettuceConnectionFactory(RedisClusterConfiguration clusterConfiguration,
			LettuceClientConfiguration clientConfig) {

		this(clientConfig);

		Assert.notNull(clusterConfiguration, "RedisClusterConfiguration must not be null!");

		this.configuration = clusterConfiguration;
	}

2. LettuceConnectionFactory

这个是LettuceConnection工厂,看名字基本上知道是做什么的

由于它继承了InitializingBean,最终会走到afterPropertiesSet方法

	public void afterPropertiesSet() {

		this.client = createClient(); // todo redisClusterClient

		this.connectionProvider = new ExceptionTranslatingConnectionProvider(createConnectionProvider(client, CODEC)); // todo LettuceConnectionFactory$ExceptionTranslatingConnectionProvider
		this.reactiveConnectionProvider = new ExceptionTranslatingConnectionProvider(
				createConnectionProvider(client, LettuceReactiveRedisConnection.CODEC));

		if (isClusterAware()) {

			this.clusterCommandExecutor = new ClusterCommandExecutor(
					new LettuceClusterTopologyProvider((RedisClusterClient) client),
					new LettuceClusterConnection.LettuceClusterNodeResourceProvider(this.connectionProvider),
					EXCEPTION_TRANSLATION);
		}

		if (getEagerInitialization() && getShareNativeConnection()) {
			initConnection();
		}
	}

createConnectionProvider:
在这里插入图片描述
这里的ConnectionProvider是ClusterConnectionProvider,然后又包装了一层LettucePoolingConnectionProvider,也就是说最终connectionProvider和reactiveConnectionProvider都是LettucePoolingConnectionProvider外面再包一层ExceptionTranslatingConnectionProvider

3. ClusterConnectionProvider.getConnection

入口是:RedisConnectionUtils.getConnection -> LettuceConnectionFactory.getConnection ->ClusterConnectionProvider.getConnection
在这里插入图片描述

使用ExceptionTranslatingConnectionProvider.getConnection(这里的delegate就是LettucePoolingConnectionProvider)获取连接
在这里插入图片描述

这里的delegate就是LettucePoolingConnectionProvider,会执行到它的getConnection()
在这里插入图片描述
这里一共做了如下这些事:

  1. 查找或创建连接池:当需要一个新的连接时,首先在 pools 中查找是否已经存在对应类型的连接池。如果不存在,则创建一个新的连接池。
  2. 借用连接:从连接池中借用一个连接,连接池管理连接的生命周期。如果连接池为空且配置允许,连接池将创建一个新的连接并返回。
  3. 存储连接与池的映射:将借用到的连接和它所属的连接池记录下来,以便后续管理。
  4. 返回连接:将连接转换为请求的类型并返回给调用者。

在创建ConnectionPoolSupport.createGenericObjectPool传入一个表达式,在执行pool.borrowObject的时候会真正调用这里的connectionProvider.getConnection(connectionType),也就是最终会调用到ClusterConnectionProvider.getConnectionAsync方法
在这里插入图片描述
这里大致的逻辑是:

  1. 设置初始化
  2. 根据不同的连接类型创建,当 connectionType 是 StatefulRedisPubSubConnection 或 StatefulRedisClusterPubSubConnection 类型时,调用 client.connectPubSubAsync(codec) 获取发布订阅连接,并使用 thenApply 方法转换连接类型;当 connectionType 是 StatefulRedisClusterConnection 的子类或直接等于 StatefulConnection 时,调用 client.connectAsync(codec) 获取集群连接,并设置读取选项(如果存在

4. RedisClusterClient.connectAsync

在这里插入图片描述

  • 创建ClusterDistributionChannelWriter: Redis 客户端中一个专门设计用于管理和分发集群命令的类。它的主要作用是协调将 Redis 命令发送到适当的 Redis 集群节点,并确保这些命令能够正确地路由到对应的分片(shard)上。
  • 创建 PooledClusterConnectionProvider: 是 Redis 集群客户端中的一个关键组件,它的主要职责是管理 Redis 集群的连接池。它在集群环境中负责创建和管理连接实例,并根据需要将连接提供给客户端使用,以确保高效的资源管理和连接的复用。
  • 创建 StatefulRedisClusterConnectionImpl:是 Redis 客户端库中的一个实现类,它提供了与 Redis 集群建立和管理状态连接的具体实现。这个类实现了 StatefulRedisClusterConnection<K, V> 接口,封装了与 Redis 集群的交互逻辑,包括命令的发送、连接的管理、拓扑的更新等。
  • commandHandlerSupplier:命令处理handler,在后续构造netty客户端的时候会用到

这里的细节不用特别关注,大概知道这里是通过下面这段代码在创建连接:
在这里插入图片描述

5. RedisClusterClient.connectStatefulAsync

    private <K, V, T extends StatefulRedisClusterConnectionImpl<K, V>, S> ConnectionFuture<S> connectStatefulAsync(T connection,
            DefaultEndpoint endpoint, RedisURI connectionSettings, Mono<SocketAddress> socketAddressSupplier,
            Supplier<CommandHandler> commandHandlerSupplier) {
        // 创建连接构造器
        ConnectionBuilder connectionBuilder = createConnectionBuilder(connection, connection.getConnectionState(), endpoint,
                connectionSettings, socketAddressSupplier, commandHandlerSupplier);
        // 初始化channel
        ConnectionFuture<RedisChannelHandler<K, V>> future = initializeChannelAsync(connectionBuilder);
        return future.thenApply(channelHandler -> (S) connection);
    }

initializeChannelAsync方法最终调用io.lettuce.core.AbstractRedisClient#initializeChannelAsync0,关键代码如下:

    private void initializeChannelAsync0(ConnectionBuilder connectionBuilder, CompletableFuture<Channel> channelReadyFuture,
            SocketAddress redisAddress) {
        // netty客户端启动引导,在前文createConnectionBuilder流程中构造
        Bootstrap redisBootstrap = connectionBuilder.bootstrap();
        // 连接初始化器
        ChannelInitializer<Channel> initializer = connectionBuilder.build(redisAddress);
        redisBootstrap.handler(initializer);
        clientResources.nettyCustomizer().afterBootstrapInitialized(redisBootstrap);
        // 建链
        ChannelFuture connectFuture = redisBootstrap.connect(redisAddress);
        channelReadyFuture.whenComplete((c, t) -> {
            if (t instanceof CancellationException) {
                connectFuture.cancel(true);
            }
        });
        // 建链响应回调处理,连接初始化、握手结果处理
        connectFuture.addListener(future -> {
            // 省略。。。
        });
    }

channel初始化器构造:io.lettuce.core.ConnectionBuilder#build

    public ChannelInitializer<Channel> build(SocketAddress socketAddress) {
        return new PlainChannelInitializer(this::buildHandlers, clientResources);
    }

    protected List<ChannelHandler> buildHandlers() {
        List<ChannelHandler> handlers = new ArrayList<>();
        connection.setOptions(clientOptions);
        // channel连接、断链事件监听handler(tcp级别事件)
        handlers.add(new ChannelGroupListener(channelGroup, clientResources.eventBus()));
        // resp协议编码handler
        handlers.add(new CommandEncoder());
        // 连接建立后握手handler,主要执行AUTH、PING命令
        handlers.add(getHandshakeHandler());
        // 命令处理handler
        handlers.add(commandHandlerSupplier.get());
        // 连接事件发布handler(握手完成后,应用层级别的事件)
        handlers.add(new ConnectionEventTrigger(connectionEvents, connection, clientResources.eventBus()));
        // 自动重连处理handler
        if (clientOptions.isAutoReconnect()) {
            handlers.add(createConnectionWatchdog());
        }
        return handlers;
    }

其中初始化链接:

  • 当饥饿式初始化链接来自于LettuceConnectionFactory.afterPropertiesSet 里面的initConnection
  • 懒惰式建链由第一次命令执行时触发建链,建链调用触发点为org.springframework.data.redis.core.RedisTemplate#execute(org.springframework.data.redis.core.RedisCallback, boolean, boolean)

获取连接的核心逻辑如下:
在这里插入图片描述

释放连接的源码就不追了,大致过程如下:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值