• Lettuce异步读取过程分析


     通过走读Lettuce异步读取源码,针对Lettuce连接建立过程进行源码走读

    总体展示一个Lettuce异步get时序

    通过时序图可以发现MasterSlaveChannelWriter主要提供一个负载分配的功能,并不是真正的命令发送服务

    下面通过源码分析实现过程

    public static <K, V> StatefulRedisMasterSlaveConnection<K, V> connect(RedisClient redisClient, RedisCodec<K, V> codec,
                Iterable<RedisURI> redisURIs) {
    
            LettuceAssert.notNull(redisClient, "RedisClient must not be null");
            LettuceAssert.notNull(codec, "RedisCodec must not be null");
            LettuceAssert.notNull(redisURIs, "RedisURIs must not be null");
    
            List<RedisURI> uriList = LettuceLists.newList(redisURIs);
            LettuceAssert.isTrue(!uriList.isEmpty(), "RedisURIs must not be empty");
    
            if (isSentinel(uriList.get(0))) {
                return connectSentinel(redisClient, codec, uriList.get(0));
            } else {
                return connectStaticMasterSlave(redisClient, codec, uriList);
            }
        }
    

     

    private static <K, V> StatefulRedisMasterSlaveConnection<K, V> connectSentinel(RedisClient redisClient,
                RedisCodec<K, V> codec, RedisURI redisURI) {
            //创建拓扑提供者为哨兵拓扑
            TopologyProvider topologyProvider = new SentinelTopologyProvider(redisURI.getSentinelMasterId(), redisClient, redisURI);
    
            //创建哨兵拓扑刷新服务
            SentinelTopologyRefresh sentinelTopologyRefresh = new SentinelTopologyRefresh(redisClient,
                    redisURI.getSentinelMasterId(), redisURI.getSentinels());
    
            //利用拓扑提供者和redisClient创建主备拓扑刷新服务
            MasterSlaveTopologyRefresh refresh = new MasterSlaveTopologyRefresh(redisClient, topologyProvider);
            
            //创建主备连接提供者
            MasterSlaveConnectionProvider<K, V> connectionProvider = new MasterSlaveConnectionProvider<>(redisClient, codec,
                    redisURI, Collections.emptyMap());
            //使用主备拓扑刷新服务获取所有节点将其设置到连接提供者中
            connectionProvider.setKnownNodes(refresh.getNodes(redisURI));
            
            //使用连接提供者创建主备通道写入器
            MasterSlaveChannelWriter<K, V> channelWriter = new MasterSlaveChannelWriter<>(connectionProvider);
            
            //创建连接
            StatefulRedisMasterSlaveConnectionImpl<K, V> connection = new StatefulRedisMasterSlaveConnectionImpl<>(channelWriter,
                    codec, redisURI.getTimeout());
    
            connection.setOptions(redisClient.getOptions());
    
            Runnable runnable = () -> {
                try {
    
                    LOG.debug("Refreshing topology");
                    List<RedisNodeDescription> nodes = refresh.getNodes(redisURI);
    
                    if (nodes.isEmpty()) {
                        LOG.warn("Topology refresh returned no nodes from {}", redisURI);
                    }
    
                    LOG.debug("New topology: {}", nodes);
                    connectionProvider.setKnownNodes(nodes);
                } catch (Exception e) {
                    LOG.error("Error during background refresh", e);
                }
            };
    
            try {
                //向连接注册可关闭服务
                connection.registerCloseables(new ArrayList<>(), sentinelTopologyRefresh);
                //绑定哨兵拓扑结构变化执行逻辑
                sentinelTopologyRefresh.bind(runnable);
            } catch (RuntimeException e) {
    
                connection.close();
                throw e;
            }
    
            return connection;
        }
    

      

     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();
        }
    

      

     protected RedisAsyncCommandsImpl<K, V> newRedisAsyncCommandsImpl() {
            //使用装饰器模式对当前实例进行增强
            return new RedisAsyncCommandsImpl<>(this, codec);
        }
    

      

        public RedisAsyncCommandsImpl(StatefulRedisConnection<K, V> connection, RedisCodec<K, V> codec) {
            super(connection, codec);
        }
    

      

        public AbstractRedisAsyncCommands(StatefulConnection<K, V> connection, RedisCodec<K, V> codec) {
            this.connection = connection;
            this.codec = codec;
            this.commandBuilder = new RedisCommandBuilder<>(codec);
        }
    

      StatefulRedisConnectionImpl

      @Override
        public <T> RedisCommand<K, V, T> dispatch(RedisCommand<K, V, T> command) {
            //前置处理
            RedisCommand<K, V, T> toSend = preProcessCommand(command);
    
            try {
                //通过父类进行派发,父类中对writer为当前类对构造方法对入参
                return super.dispatch(toSend);
            } finally {
                if (command.getType().name().equals(MULTI.name())) {
                    multi = (multi == null ? new MultiOutput<>(codec) : multi);
                }
            }
        }
    

      

     protected <T> RedisCommand<K, V, T> dispatch(RedisCommand<K, V, T> cmd) {
    
            if (debugEnabled) {
                logger.debug("dispatching command {}", cmd);
            }
            //将发送命令对处理委派给channelWriter处理
            return channelWriter.write(cmd);
        }
    

      MasterSlaveChannelWriter

      @Override
        public <K, V, T> RedisCommand<K, V, T> write(RedisCommand<K, V, T> command) {
    
            LettuceAssert.notNull(command, "Command must not be null");
    
            if (closed) {
                throw new RedisException("Connection is closed");
            }
            //获取命令意图
            Intent intent = getIntent(command.getType());
            //根据读写意图获取连接
            StatefulRedisConnection<K, V> connection = (StatefulRedisConnection) masterSlaveConnectionProvider
                    .getConnection(intent);
            //通过这个connection派发命令
            return connection.dispatch(command);
        }
    

      

    //根据意图获取连接
        public StatefulRedisConnection<K, V> getConnection(Intent intent) {
    
            if (debugEnabled) {
                logger.debug("getConnection(" + intent + ")");
            }
            //如果readFrom不为null且是READ
            if (readFrom != null && intent == Intent.READ) {
                //根据readFrom配置从已知节点中选择可用节点描述
                List<RedisNodeDescription> selection = readFrom.select(new ReadFrom.Nodes() {
                    @Override
                    public List<RedisNodeDescription> getNodes() {
                        return knownNodes;
                    }
    
                    @Override
                    public Iterator<RedisNodeDescription> iterator() {
                        return knownNodes.iterator();
                    }
                });
                //如果可选择节点集合为空则抛出异常
                if (selection.isEmpty()) {
                    throw new RedisException(String.format("Cannot determine a node to read (Known nodes: %s) with setting %s",
                            knownNodes, readFrom));
                }
                try {
                    //遍历所有可用节点
                    for (RedisNodeDescription redisNodeDescription : selection) {
                        //获取节点连接
                        StatefulRedisConnection<K, V> readerCandidate = getConnection(redisNodeDescription);
                        //如果节点连接不是打开到连接则继续查找下一个连接
                        if (!readerCandidate.isOpen()) {
                            continue;
                        }
                        //返回可用连接
                        return readerCandidate;
                    }
                    //如果没有找到可用连接,默认返回第一个
                    return getConnection(selection.get(0));
                } catch (RuntimeException e) {
                    throw new RedisException(e);
                }
            }
            //如果没有配置readFrom或者不是READ 则返回master连接
            return getConnection(getMaster());
        }
    

      

     protected StatefulRedisConnection<K, V> getConnection(RedisNodeDescription redisNodeDescription) {
           //如果没有则创建新节点,并添加到缓存中
            return connections.computeIfAbsent(
                    new ConnectionKey(redisNodeDescription.getUri().getHost(), redisNodeDescription.getUri().getPort()),
                    connectionFactory);
        }
    

      创建实际的connectio

      @Override
            public StatefulRedisConnection<K, V> apply(ConnectionKey key) {
                //构建URI
                RedisURI.Builder builder = RedisURI.Builder
                        .redis(key.host, key.port)
                        .withSsl(initialRedisUri.isSsl())
                        .withVerifyPeer(initialRedisUri.isVerifyPeer())
                        .withStartTls(initialRedisUri.isStartTls());
    
                if (initialRedisUri.getPassword() != null && initialRedisUri.getPassword().length != 0) {
                    builder.withPassword(initialRedisUri.getPassword());
                }
    
                if (initialRedisUri.getClientName() != null) {
                    builder.withClientName(initialRedisUri.getClientName());
                }
                builder.withDatabase(initialRedisUri.getDatabase());
                
                //创建连接
                StatefulRedisConnection<K, V> connection = redisClient.connect(redisCodec, builder.build());
                
                //设置是否自动提交
                synchronized (stateLock) {
                    connection.setAutoFlushCommands(autoFlushCommands);
                }
    
                return connection;
            }
    

      

    public <K, V> StatefulRedisConnection<K, V> connect(RedisCodec<K, V> codec, RedisURI redisURI) {
    
            assertNotNull(redisURI);
            return connectStandalone(codec, redisURI, redisURI.getTimeout());
        }
    

      

     private <K, V> StatefulRedisConnection<K, V> connectStandalone(RedisCodec<K, V> codec, RedisURI redisURI, Duration timeout) {
            //单机异步
            ConnectionFuture<StatefulRedisConnection<K, V>> future = connectStandaloneAsync(codec, redisURI, timeout);
            //获取连接
            return getConnection(future);
        }
    

      

        private <K, V> ConnectionFuture<StatefulRedisConnection<K, V>> connectStandaloneAsync(RedisCodec<K, V> codec,
                RedisURI redisURI, Duration timeout) {
    
            assertNotNull(codec);
            //检查URI是否有效
            checkValidRedisURI(redisURI);
    
            logger.debug("Trying to get a Redis connection for: " + redisURI);
            //创建DefaultEndpoint
            DefaultEndpoint endpoint = new DefaultEndpoint(clientOptions);
            //创建connection
            StatefulRedisConnectionImpl<K, V> connection = newStatefulRedisConnection(endpoint, codec, timeout);
            //创建连接
            ConnectionFuture<StatefulRedisConnection<K, V>> future = connectStatefulAsync(connection, endpoint, 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,
                DefaultEndpoint endpoint, RedisURI redisURI, Supplier<CommandHandler> commandHandlerSupplier) {
            //connetion构造器
            ConnectionBuilder connectionBuilder;
            if (redisURI.isSsl()) {
                SslConnectionBuilder sslConnectionBuilder = SslConnectionBuilder.sslConnectionBuilder();
                sslConnectionBuilder.ssl(redisURI);
                connectionBuilder = sslConnectionBuilder;
            } else {
                connectionBuilder = ConnectionBuilder.connectionBuilder();
            }
            //设置connection
            connectionBuilder.connection(connection);
            //设置客户端选项
            connectionBuilder.clientOptions(clientOptions);
            //设置客户端资源
            connectionBuilder.clientResources(clientResources);
            //设置命令处理器以及endpoint
            connectionBuilder.commandHandler(commandHandlerSupplier).endpoint(endpoint);
            //填充连接构造器
            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);
            
            //如果客户端选项配置了pingBeforeActivateConnection同时有密码
            if (!clientOptions.isPingBeforeActivateConnection() && hasPassword(redisURI)) {
    
                future = future.thenApplyAsync(channelHandler -> {
    
                    connection.async().auth(new String(redisURI.getPassword()));
    
                    return channelHandler;
                }, clientResources.eventExecutorGroup());
            }
    
            if (LettuceStrings.isNotEmpty(redisURI.getClientName())) {
                future.thenApply(channelHandler -> {
                    connection.setClientName(redisURI.getClientName());
                    return channelHandler;
                });
    
            }
    
            if (redisURI.getDatabase() != 0) {
    
                future = future.thenApplyAsync(channelHandler -> {
    
                    connection.async().select(redisURI.getDatabase());
    
                    return channelHandler;
                }, clientResources.eventExecutorGroup());
            }
    
            return future.thenApply(channelHandler -> (S) connection);
        }
    

      

    /**
         *  连接同时通过connectionBuilder初始化一个通道
         */
        @SuppressWarnings("unchecked")
        protected <K, V, T extends RedisChannelHandler<K, V>> ConnectionFuture<T> initializeChannelAsync(
                ConnectionBuilder connectionBuilder) {
            //获取socketAddress
            SocketAddress redisAddress = connectionBuilder.socketAddress();
    
            //如果线程池关闭则抛出异常
            if (clientResources.eventExecutorGroup().isShuttingDown()) {
                throw new IllegalStateException("Cannot connect, Event executor group is terminated.");
            }
    
            logger.debug("Connecting to Redis at {}", redisAddress);
    
            CompletableFuture<Channel> channelReadyFuture = new CompletableFuture<>();
    
            //获取bootstrap
            Bootstrap redisBootstrap = connectionBuilder.bootstrap();
            //创建redis通道初始化器
            RedisChannelInitializer initializer = connectionBuilder.build();
            //设置netty的处理器
            redisBootstrap.handler(initializer);
            //netty自定设置处理
            clientResources.nettyCustomizer().afterBootstrapInitialized(redisBootstrap);
            CompletableFuture<Boolean> initFuture = initializer.channelInitialized();
            ChannelFuture connectFuture = redisBootstrap.connect(redisAddress);
            //增加监听器
            connectFuture.addListener(future -> {
                    
                if (!future.isSuccess()) {
    
                    logger.debug("Connecting to Redis at {}: {}", redisAddress, future.cause());
                    connectionBuilder.endpoint().initialState();
                    channelReadyFuture.completeExceptionally(future.cause());
                    return;
                }
    
                initFuture.whenComplete((success, throwable) -> {
    
                    if (throwable == null) {
                        logger.debug("Connecting to Redis at {}: Success", redisAddress);
                        RedisChannelHandler<?, ?> connection = connectionBuilder.connection();
                        connection.registerCloseables(closeableResources, connection);
                        channelReadyFuture.complete(connectFuture.channel());
                        return;
                    }
    
                    logger.debug("Connecting to Redis at {}, initialization: {}", redisAddress, throwable);
                    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);
    
                    CompletableFuture<Boolean> response = new CompletableFuture<>();
                    response.completeExceptionally(failure);
    
                });
            });
    
            return new DefaultConnectionFuture<T>(redisAddress, channelReadyFuture.thenApply(channel -> (T) connectionBuilder
                    .connection()));
        }
    

      connectionBuilder.build()

     public RedisChannelInitializer build() {
            return new PlainChannelInitializer(pingCommandSupplier, this::buildHandlers, clientResources, timeout);
        }
    

      

    protected List<ChannelHandler> buildHandlers() {
    
            LettuceAssert.assertState(channelGroup != null, "ChannelGroup must be set");
            LettuceAssert.assertState(connectionEvents != null, "ConnectionEvents must be set");
            LettuceAssert.assertState(connection != null, "Connection must be set");
            LettuceAssert.assertState(clientResources != null, "ClientResources must be set");
            LettuceAssert.assertState(endpoint != null, "Endpoint must be set");
    
            List<ChannelHandler> handlers = new ArrayList<>();
            //设置clientOptions
            connection.setOptions(clientOptions);
            //添加channel监听器
            handlers.add(new ChannelGroupListener(channelGroup));
            //添加命令编码器
            handlers.add(new CommandEncoder());
            //添加commandHander
            handlers.add(commandHandlerSupplier.get());
            //如果设置自动重连,则设置看门狗处理器
            if (clientOptions.isAutoReconnect()) {
                handlers.add(createConnectionWatchdog());
            }
            //设置connectionEvenTrigger
            handlers.add(new ConnectionEventTrigger(connectionEvents, connection, clientResources.eventBus()));
    
            if (clientOptions.isAutoReconnect()) {
                handlers.add(createConnectionWatchdog());
            }
    
            return handlers;
        }
    

      看门狗处理器到作用就是在通道断开是进行重连

     protected ConnectionWatchdog createConnectionWatchdog() {
             //如果看门狗不为null直接返回
            if (connectionWatchdog != null) {
                return connectionWatchdog;
            }
    
            LettuceAssert.assertState(bootstrap != null, "Bootstrap must be set for autoReconnect=true");
            LettuceAssert.assertState(timer != null, "Timer must be set for autoReconnect=true");
            LettuceAssert.assertState(socketAddressSupplier != null, "SocketAddressSupplier must be set for autoReconnect=true");
            //创建连接看门狗
            ConnectionWatchdog watchdog = new ConnectionWatchdog(clientResources.reconnectDelay(), clientOptions, bootstrap, timer,
                    clientResources.eventExecutorGroup(), socketAddressSupplier, reconnectionListener, connection);
            //向endpoint注册看门狗
            endpoint.registerConnectionWatchdog(watchdog);
    
            connectionWatchdog = watchdog;
            return watchdog;
        }
    

      

    lass PlainChannelInitializer extends io.netty.channel.ChannelInitializer<Channel> implements RedisChannelInitializer {
    
        //不ping
        final static Supplier<AsyncCommand<?, ?, ?>> NO_PING = () -> null;
        //处理器提供器
        private final Supplier<List<ChannelHandler>> handlers;
        //ping命令提供器
        private final Supplier<AsyncCommand<?, ?, ?>> pingCommandSupplier;
        private final ClientResources clientResources;
        //超时时间
        private final Duration timeout;
    
        private volatile CompletableFuture<Boolean> initializedFuture = new CompletableFuture<>();
    
        PlainChannelInitializer(Supplier<AsyncCommand<?, ?, ?>> pingCommandSupplier, Supplier<List<ChannelHandler>> handlers,
                ClientResources clientResources, Duration timeout) {
            this.pingCommandSupplier = pingCommandSupplier;
            this.handlers = handlers;
            this.clientResources = clientResources;
            this.timeout = timeout;
        }
    
        @Override
        protected void initChannel(Channel channel) throws Exception {
    
            //如果pipeline中没有配置channelActivator则需要添加channelActivator处理器
            if (channel.pipeline().get("channelActivator") == null) {
    
                channel.pipeline().addLast("channelActivator", new RedisChannelInitializerImpl() {
    
                    private AsyncCommand<?, ?, ?> pingCommand;
    
                    @Override
                    public CompletableFuture<Boolean> channelInitialized() {
                        return initializedFuture;
                    }
    
                    @Override
                    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
                        //如果通道断开连接
                        clientResources.eventBus().publish(new DisconnectedEvent(local(ctx), remote(ctx)));
                        //如果初始化没有完成则抛出异常
                        if (!initializedFuture.isDone()) {
                            initializedFuture.completeExceptionally(new RedisConnectionException("Connection closed prematurely"));
                        }
    
                        initializedFuture = new CompletableFuture<>();
                        pingCommand = null;
                        super.channelInactive(ctx);
                    }
    
                    @Override
                    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
    
                        if (evt instanceof ConnectionEvents.Activated) {
                            if (!initializedFuture.isDone()) {
                                initializedFuture.complete(true);
                                clientResources.eventBus().publish(new ConnectionActivatedEvent(local(ctx), remote(ctx)));
                            }
                        }
                        super.userEventTriggered(ctx, evt);
                    }
    
                    @Override
                    public void channelActive(final ChannelHandlerContext ctx) throws Exception {
                        //通过事件总线发送连接事件
                        clientResources.eventBus().publish(new ConnectedEvent(local(ctx), remote(ctx)));
                        //如果ping命令提供器不是NO_PING则发送执行ping
                        if (pingCommandSupplier != NO_PING) {
                            pingCommand = pingCommandSupplier.get();
                            pingBeforeActivate(pingCommand, initializedFuture, ctx, clientResources, timeout);
                        } else {
                            super.channelActive(ctx);
                        }
                    }
    
                    @Override
                    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
                        if (!initializedFuture.isDone()) {
                            initializedFuture.completeExceptionally(cause);
                        }
                        super.exceptionCaught(ctx, cause);
                    }
                });
            }
            //将hanler提供器提供的处理器添加到channel中
            for (ChannelHandler handler : handlers.get()) {
                channel.pipeline().addLast(handler);
            }
    
            clientResources.nettyCustomizer().afterChannelInitialized(channel);
        }
    
        static void pingBeforeActivate(AsyncCommand<?, ?, ?> cmd, CompletableFuture<Boolean> initializedFuture,
                ChannelHandlerContext ctx, ClientResources clientResources, Duration timeout) throws Exception {
    
            ctx.fireUserEventTriggered(new PingBeforeActivate(cmd));
    
            Runnable timeoutGuard = () -> {
    
                if (cmd.isDone() || initializedFuture.isDone()) {
                    return;
                }
    
                initializedFuture.completeExceptionally(new RedisCommandTimeoutException(String.format(
                        "Cannot initialize channel (PING before activate) within %s", timeout)));
            };
    
            Timeout timeoutHandle = clientResources.timer().newTimeout(t -> {
    
                if (clientResources.eventExecutorGroup().isShuttingDown()) {
                    timeoutGuard.run();
                    return;
                }
    
                clientResources.eventExecutorGroup().submit(timeoutGuard);
            }, timeout.toNanos(), TimeUnit.NANOSECONDS);
    
            cmd.whenComplete((o, throwable) -> {
    
                timeoutHandle.cancel();
    
                if (throwable == null) {
                    ctx.fireChannelActive();
                    initializedFuture.complete(true);
                } else {
                    initializedFuture.completeExceptionally(throwable);
                }
            });
    
        }
    
        @Override
        public CompletableFuture<Boolean> channelInitialized() {
            return initializedFuture;
        }
    
    }
    

      

  • 相关阅读:
    关于Tomcat启动时报The APR based Apache Tomcat Native library which allows optimal performanc e in production environments was not found on the java.library.path
    Java线程池的实现
    搜索引擎关键字智能提示的一种实现
    python简单的爬虫,网页图片
    HDU2065 指数型母函数
    HDU2063 二分图最大匹配问题
    HDU2067 卡特兰数
    HDU2068 错排
    HDU2082 普通型母函数
    ZOJ3798 Abs Problem
  • 原文地址:https://www.cnblogs.com/wei-zw/p/9157618.html
Copyright © 2020-2023  润新知