redission收发命令流程分析

一、示例,我们从最简单的GET命令开始。

RBucket<Object> t = redissonClient.getBucket("syncTradeUid_idOff");
int idOff = (int)t.get();

二、springboot的Redission自动配置

@Order(value = 4001)
@ConditionalOnProperty("redisson.password")
@Configuration
@EnableConfigurationProperties({RedissonProperties.class})
public class RedissonAutoConfiguration {

    public RedissonAutoConfiguration() {
       System.out.println("==========================redis 初始化成功=======================");
    }

    @Autowired
    private RedissonProperties redissonProperties;

    @Bean(name = "redissonClient")
    @ConditionalOnProperty(name="redisson.address")
    RedissonClient redissonSingle() {
        Config config = new Config();
        config.setCodec(new FastJsonCodec());
        SingleServerConfig serverConfig = config.useSingleServer()
                .setAddress(redissonProperties.getAddress())
                .setTimeout(redissonProperties.getTimeout())
                .setConnectionPoolSize(redissonProperties.getConnectionPoolSize())
                .setConnectionMinimumIdleSize(redissonProperties.getConnectionMinimumIdleSize());

        if(!StrUtil.isEmpty(redissonProperties.getPassword())) {
            serverConfig.setPassword(redissonProperties.getPassword());
        }

        return Redisson.create(config);
    }

    /**
     * 哨兵模式自动装配
     * @return
     */
    @Bean(name = "redissonClient")
    @ConditionalOnProperty(name="redisson.masterName")
    RedissonClient redissonSentinel() {
        Config config = new Config();
        config.setCodec(new FastJsonCodec());
        SentinelServersConfig serverConfig = config.useSentinelServers().addSentinelAddress(redissonProperties.getSentinelAddresses())
                .setMasterName(redissonProperties.getMasterName())
                .setTimeout(redissonProperties.getTimeout())
                .setMasterConnectionPoolSize(redissonProperties.getMasterConnectionPoolSize())
                .setSlaveConnectionPoolSize(redissonProperties.getSlaveConnectionPoolSize())
                .setReadMode(ReadMode.SLAVE);

        if(!StrUtil.isEmpty(redissonProperties.getPassword())) {
            serverConfig.setPassword(redissonProperties.getPassword());
        }
        return Redisson.create(config);
    }


}

application.properites

#单机
redisson.address = redis://127.0.0.1:6379
redisson.password =

#哨兵
#redisson.masterName=BF-20190319DBXF
#redisson.schema=redis://
#redisson.sentinelAddresses=redis://127.0.0.1:26379,redis://127.0.0.1:26479,redis://127.0.0.1:26579
#redisson.password=

三、REDISSION自动配置初始化流程

1.从Redisson.create(config)创建redission对象开始。Redission继承于

RedissonClient

2. 创建连接管理器对象

org.redisson.config.ConfigSupport
  
public static ConnectionManager createConnectionManager(Config configCopy) {
        UUID id = UUID.randomUUID();
        
        if (configCopy.getMasterSlaveServersConfig() != null) {
            validate(configCopy.getMasterSlaveServersConfig());
            return new MasterSlaveConnectionManager(configCopy.getMasterSlaveServersConfig(), configCopy, id);
        } else if (configCopy.getSingleServerConfig() != null) {
            validate(configCopy.getSingleServerConfig());
            return new SingleConnectionManager(configCopy.getSingleServerConfig(), configCopy, id);
        } else if (configCopy.getSentinelServersConfig() != null) {
            validate(configCopy.getSentinelServersConfig());
            return new SentinelConnectionManager(configCopy.getSentinelServersConfig(), configCopy, id);
        } else if (configCopy.getClusterServersConfig() != null) {
            validate(configCopy.getClusterServersConfig());
            return new ClusterConnectionManager(configCopy.getClusterServersConfig(), configCopy, id);
        } else if (configCopy.getReplicatedServersConfig() != null) {
            validate(configCopy.getReplicatedServersConfig());
            return new ReplicatedConnectionManager(configCopy.getReplicatedServersConfig(), configCopy, id);
        } else if (configCopy.getConnectionManager() != null) {
            return configCopy.getConnectionManager();
        }else {
            throw new IllegalArgumentException("server(s) address(es) not defined!");
        }
    }

3.先看下MasterSlaveEntry->setupMasterEntry,这里会创建RedisClient,以及连接REDIS服务器。

org.redisson.connection.MasterSlaveEntry    

public RFuture<RedisClient> setupMasterEntry(RedisURI address) {
        RedisClient client = connectionManager.createClient(NodeType.MASTER, address, sslHostname);
        return setupMasterEntry(client);
    }

4.创建RedisClient,这里面单机也是使用主从管理器,即是只有主没有从。统一起来。

org.redisson.connection.MasterSlaveConnectionManager
    
@Override
    public RedisClient createClient(NodeType type, RedisURI address, int timeout, int commandTimeout, String sslHostname) {
        RedisClientConfig redisConfig = createRedisConfig(type, address, timeout, commandTimeout, sslHostname);
        return RedisClient.create(redisConfig);
    }

5.在RedisClient会创建NEETY的bootstrap,channel,handler.

org.redisson.client.RedisClient
    
private Bootstrap createBootstrap(RedisClientConfig config, Type type) {
        Bootstrap bootstrap = new Bootstrap()
                        .resolver(config.getResolverGroup())
                        .channel(config.getSocketChannelClass())
                        .group(config.getGroup());

        bootstrap.handler(new RedisChannelInitializer(bootstrap, config, this, channels, type));
        bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, config.getConnectTimeout());
        bootstrap.option(ChannelOption.SO_KEEPALIVE, config.isKeepAlive());
        bootstrap.option(ChannelOption.TCP_NODELAY, config.isTcpNoDelay());
        config.getNettyHook().afterBoostrapInitialization(bootstrap);
        return bootstrap;
    }

 6.我们再看下RedisChannelInitializer,有添加哪些inBounder,outBounder

org.redisson.client.handler.RedisChannelInitializer   
 @Override
    protected void initChannel(Channel ch) throws Exception {
        initSsl(config, ch);
        
        if (type == Type.PLAIN) {
            ch.pipeline().addLast(new RedisConnectionHandler(redisClient));
        } else {
            ch.pipeline().addLast(new RedisPubSubConnectionHandler(redisClient));
        }

        ch.pipeline().addLast(
            connectionWatchdog,
            CommandEncoder.INSTANCE,
            CommandBatchEncoder.INSTANCE,
            new CommandsQueue());

        if (pingConnectionHandler != null) {
            ch.pipeline().addLast(pingConnectionHandler);
        }
        
        if (type == Type.PLAIN) {
            ch.pipeline().addLast(new CommandDecoder(config.getAddress().getScheme()));
        } else {
            ch.pipeline().addLast(new CommandPubSubDecoder(config));
        }

        ch.pipeline().addLast(new ErrorsLoggingHandler());

        config.getNettyHook().afterChannelInitialization(ch);
    }

 

 7.创建好RedisClient后,开始连接REDIS服务器。这里首先异步解析地址,解析成功后,在添加到写连接池时会创建和添加连接,在创建连接时会去连接REDIS服务器。

 org.redisson.connection. MasterSlaveEntry
 private RFuture<RedisClient> setupMasterEntry(RedisClient client) {
        RPromise<RedisClient> result = new RedissonPromise<RedisClient>();
        result.onComplete((res, e) -> {
            if (e != null) {
                client.shutdownAsync();
            }
        });
        RFuture<InetSocketAddress> addrFuture = client.resolveAddr();
        addrFuture.onComplete((res, e) -> {
            if (e != null) {
                result.tryFailure(e);
                return;
            }
            
            masterEntry = new ClientConnectionsEntry(
                    client, 
                    config.getMasterConnectionMinimumIdleSize(), 
                    config.getMasterConnectionPoolSize(),
                    config.getSubscriptionConnectionMinimumIdleSize(),
                    config.getSubscriptionConnectionPoolSize(), 
                    connectionManager,
                    NodeType.MASTER);
    
            int counter = 1;
            if (config.getSubscriptionMode() == SubscriptionMode.MASTER) {
                counter++;
            }
            
            CountableListener<RedisClient> listener = new CountableListener<>(result, client, counter);
            RFuture<Void> writeFuture = writeConnectionPool.add(masterEntry);
            writeFuture.onComplete(listener);
            
        });
        
        return result;
    }

8.查看连接REDIS服务器过程

org.redisson.connection.pool.ConnectionPool.    
private void initConnections(ClientConnectionsEntry entry, RPromise<Void> initPromise, boolean checkFreezed) {
        int minimumIdleSize = getMinimumIdleSize(entry);

        if (minimumIdleSize == 0 || (checkFreezed && entry.isFreezed())) {
            initPromise.trySuccess(null);
            return;
        }

        AtomicInteger initializedConnections = new AtomicInteger(minimumIdleSize);
        int startAmount = Math.min(10, minimumIdleSize);
        AtomicInteger requests = new AtomicInteger(startAmount);
        for (int i = 0; i < startAmount; i++) {
            createConnection(checkFreezed, requests, entry, initPromise, minimumIdleSize, initializedConnections);
        }
    }

在这里可以看到会初始化10个客户端连接到连接池。

9.从连接池去申请创建连接

ConnectionPool 
private void createConnection(boolean checkFreezed, AtomicInteger requests, ClientConnectionsEntry entry, RPromise<Void> initPromise,
            int minimumIdleSize, AtomicInteger initializedConnections) {
      
        acquireConnection(entry, new Runnable() {
            
            @Override
            public void run() {
                RPromise<T> promise = new RedissonPromise<T>();
                createConnection(entry, promise);
                promise.onComplete((conn, e) -> {


                           
                });
            }
        });

    }

 10.最终创建连接是在RedisClient.connectAsync这个异步连接方法中。

  public RFuture<RedisConnection> connectAsync() {
        final RPromise<RedisConnection> f = new RedissonPromise<RedisConnection>();
        
        RFuture<InetSocketAddress> addrFuture = resolveAddr();
        addrFuture.onComplete((res, e) -> {
            if (e != null) {
                f.tryFailure(e);
                return;
            }
            
            ChannelFuture channelFuture = bootstrap.connect(res);
        });
        
        return f;
    }

11.在连接成功后,RedisConnectionHandler.channelRegistered方法中创建连接对象。

    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        if (connection == null) {
            connection = createConnection(ctx);
        }
        super.channelRegistered(ctx);
    }

12.在这里对channel赋值,保存。这里每个channel里面会有一个RedisConnection的属性。

RedisConnection    
public void updateChannel(Channel channel) {
        this.channel = channel;
        channel.attr(CONNECTION).set(this);
    }

13.在连接成功后发送PING心跳命令

 BaseConnectionHandler
@Override
    public void channelActive(final ChannelHandlerContext ctx) {
        List<RFuture<Object>> futures = new ArrayList<RFuture<Object>>();

        RedisClientConfig config = redisClient.getConfig();
        if (config.getPassword() != null) {
            RFuture<Object> future;
            if (config.getUsername() != null) {
                future = connection.async(RedisCommands.AUTH, config.getUsername(), config.getPassword());
            } else {
                future = connection.async(RedisCommands.AUTH, config.getPassword());
            }
            futures.add(future);
        }futures.add(future);
        }
        if (config.getPingConnectionInterval() > 0) {
            RFuture<Object> future = connection.async(RedisCommands.PING);
            futures.add(future);
        }
        
  
        
        final AtomicBoolean retry = new AtomicBoolean();
        final AtomicInteger commandsCounter = new AtomicInteger(futures.size());
        for (RFuture<Object> future : futures) {
            future.onComplete((res, e) -> {
                if (e != null) {
                    if (e instanceof RedisLoadingException) {
                        if (retry.compareAndSet(false, true)) {
                            ctx.executor().schedule(() -> {
                                channelActive(ctx);
                            }, 1, TimeUnit.SECONDS);
                        }
                        return;
                    }
                    connection.closeAsync();
                    connectionPromise.tryFailure(e);
                    return;
                }
                if (commandsCounter.decrementAndGet() == 0) {
                    ctx.fireChannelActive();
                    connectionPromise.trySuccess(connection);
                }
            });
        }
    }

四、发送命令过程。

1.首先从RedissionBucket的set方法

,这里面的 commandExecutor来源于connectionManager中的命令执行器。

2.然后进行入到RedisExecutor中的execute方法,去异步执行命令。这里首先从连接池获取连接,然后在异步连接成功后,发送命令。

  public void execute() {
        codec = getCodec(codec);
        
        RFuture<RedisConnection> connectionFuture = getConnection();

        connectionFuture.onComplete((connection, e) -> {
         
            sendCommand(attemptPromise, connection);

            writeFuture.addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) throws Exception {
                    checkWriteFuture(writeFuture, attemptPromise, connection);
                }
            });

            releaseConnection(attemptPromise, connectionFuture);
        });

    }

3.获取连接是从连接池中获取。根据读写模式从连接管理器中选择可用连接返回。

RedisExecutor    
protected RFuture<RedisConnection> getConnection() {
        if (readOnlyMode) {
            connectionFuture = connectionManager.connectionReadOp(source, command);
        } else {
            connectionFuture = connectionManager.connectionWriteOp(source, command);
        }
        return connectionFuture;
    }

 3.接着调用RedisConnection的send向channel写入数据。

RedisConnection    
public <T, R> ChannelFuture send(CommandData<T, R> data) {
        return channel.writeAndFlush(data);
    }

4.netty的inBoundHandler中有一个CommandsQueue,为一个命令同步队列,同一时刻一个连接只有一个命令在执行,执行完后,再执行下一个命令。

 org.redisson.client.handler.CommandsQueue
private void sendData(Channel ch) {
        QueueCommandHolder command = queue.peek();
        if (command != null && command.trySend()) {
            QueueCommand data = command.getCommand();
            List<CommandData<Object, Object>> pubSubOps = data.getPubSubOperations();
            if (!pubSubOps.isEmpty()) {
                for (CommandData<Object, Object> cd : pubSubOps) {
                    for (Object channel : cd.getParams()) {
                        ch.pipeline().get(CommandPubSubDecoder.class).addPubSubCommand((ChannelName) channel, cd);
                    }
                }
            } else {
                ch.attr(CURRENT_COMMAND).set(data);
            }

            command.getChannelPromise().addListener(listener);
            ch.writeAndFlush(data, command.getChannelPromise());
        }
    }

 

 

五、接收数据回调过程。

1.接收inhandler, 在收到数据后,从attr中的current_command属性中取出数据。

 CommandDecoder
@Override
    protected final void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        QueueCommand data = ctx.channel().attr(CommandsQueue.CURRENT_COMMAND).get();

        if (state() == null) {
            state(new State());
        }
        
        if (data == null) {
            while (in.writerIndex() > in.readerIndex()) {
                int endIndex = skipCommand(in);

                try {
                    decode(ctx, in, data);
                } catch (Exception e) {
                    in.readerIndex(endIndex);
                    throw e;
                }
            }
        } else {
            int endIndex = 0;
            if (!(data instanceof CommandsData)) {
                endIndex = skipCommand(in);
            }
            
            try {
                decode(ctx, in, data);
            } catch (Exception e) {
                if (!(data instanceof CommandsData)) {
                    in.readerIndex(endIndex);
                }
                throw e;
            }
        }
    }

 

2.根据相应的PROMISE设置回调数据。

   CommandDecoder
 protected void completeResponse(CommandData<Object, Object> data, Object result) {
        if (data != null) {
            data.getPromise().trySuccess(result);
        }
    }

3.在等待异步PROMISE结果。

    CommandAsyncService
@Override
    public <V> V get(RFuture<V> future) {
        try {
            future.await();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        if (future.isSuccess()) {
            return future.getNow();
        }

        throw convertException(future);
    }

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值