通过走读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;
}
}