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