Redis:redission 源代码剖析1 连接建立

来源:互联网 发布:mm国家域名 编辑:程序博客网 时间:2024/06/02 05:51



redission作为redis  官方推荐的java客户端。 redission使用netty4.x作为网络层。 redission使用异步io方式操作。这与jedis 同步io操作方式完全不同。

但是redission也提供了同步操作方式。


在org.redisson.client 包下面,RedisClient 提供了对netty client端的包装。RedisClient 提供了同步或者异步连接redis服务器的方式,同时也提供了断线重连的机制。


ConnectionWatchdog   过滤器实现了断线重连的机制。


在redission中也提供了编码和解码器。


RedisClient 使用ChannelGroup来管理实际的socketchannel链接。


在netty中,实际输入输出操作都是通过Channel通道来进行。虽然,netty和apache  mina架构非常类似,这也加快了使用apache  mina的人迁移到netty上面。

但是在netty 中使用Channel 管理输入输出流。在apache  mina 是使用IoSession来管理输入输出流。


RedisClient 代码注释如下:

//redisclient  use  netty4.xpublic class RedisClient {    //构造客户端    private final Bootstrap bootstrap;    //连接地址    private final InetSocketAddress addr;    //管理客户端    private final ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);    private final long timeout;    public RedisClient(String host, int port) {        this(new NioEventLoopGroup(), NioSocketChannel.class, host, port, 60*1000);    }    public RedisClient(EventLoopGroup group, Class<? extends SocketChannel> socketChannelClass, String host, int port, int timeout) {    //连接端口        addr = new InetSocketAddress(host, port);        //构造客户端        bootstrap = new Bootstrap().channel(socketChannelClass).group(group).remoteAddress(addr);        //配置业务处理。这里实现了断线重连机制和编码解码功能        bootstrap.handler(new ChannelInitializer<Channel>() {            @Override            protected void initChannel(Channel ch) throws Exception {                            ch.pipeline().addFirst(new ConnectionWatchdog(bootstrap, channels),                                        new CommandEncoder(),                                        new CommandsListEncoder(),                                        new CommandsQueue(),                                        new CommandDecoder());            }        });         //设置链接超时时间        bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, timeout);        this.timeout = timeout;    }    public InetSocketAddress getAddr() {        return addr;    }    long getTimeout() {        return timeout;    }    public Bootstrap getBootstrap() {        return bootstrap;    }    //同步连接方法    public RedisConnection connect() {        try {            ChannelFuture future = bootstrap.connect();            future.syncUninterruptibly();            return new RedisConnection(this, future.channel());        } catch (Exception e) {            throw new RedisConnectionException("Unable to connect to " + addr, e);        }    }    //异步连接方式    public Future<RedisConnection> connectAsync() {        final Promise<RedisConnection> f = bootstrap.group().next().newPromise();        ChannelFuture channelFuture = bootstrap.connect();        channelFuture.addListener(new ChannelFutureListener() {            @Override            public void operationComplete(ChannelFuture future) throws Exception {                if (future.isSuccess()) {                    RedisConnection c = new RedisConnection(RedisClient.this, future.channel());                    f.setSuccess(c);                } else {                    f.setFailure(future.cause());                }            }        });        return f;    }    public RedisPubSubConnection connectPubSub() {        try {            ChannelFuture future = bootstrap.connect();            future.syncUninterruptibly();            return new RedisPubSubConnection(this, future.channel());        } catch (Exception e) {            throw new RedisConnectionException("Unable to connect to " + addr, e);        }    }    public Future<RedisPubSubConnection> connectPubSubAsync() {        final Promise<RedisPubSubConnection> f = bootstrap.group().next().newPromise();        ChannelFuture channelFuture = bootstrap.connect();        channelFuture.addListener(new ChannelFutureListener() {            @Override            public void operationComplete(ChannelFuture future) throws Exception {                if (future.isSuccess()) {                    RedisPubSubConnection c = new RedisPubSubConnection(RedisClient.this, future.channel());                    f.setSuccess(c);                } else {                    f.setFailure(future.cause());                }            }        });        return f;    }    //同步关闭客户端功能    public void shutdown() {        shutdownAsync().syncUninterruptibly();    }    //异步关闭客户端功能    public ChannelGroupFuture shutdownAsync() {        return channels.close();    }    @Override    public String toString() {        return "RedisClient [addr=" + addr + "]";    }}

在这里    private final ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);  来管理通道。


在RedisClient 实现了断线重连机制。ConnectionWatchdog


public class ConnectionWatchdog extends ChannelInboundHandlerAdapter  代码如下:

public class ConnectionWatchdog extends ChannelInboundHandlerAdapter {    private final Logger log = LoggerFactory.getLogger(getClass());    private final Bootstrap bootstrap;    private final ChannelGroup channels;    private static final int BACKOFF_CAP = 12;    public ConnectionWatchdog(Bootstrap bootstrap, ChannelGroup channels) {        this.bootstrap = bootstrap;        this.channels  = channels;    }    //当连接创建时,持有通道的引用    @Override    public void channelActive(ChannelHandlerContext ctx) throws Exception {        channels.add(ctx.channel());        ctx.fireChannelActive();    }    //当断开链接时,使用短线重连功能更    @Override    public void channelInactive(ChannelHandlerContext ctx) throws Exception {        RedisConnection connection = RedisConnection.getFrom(ctx.channel());        if (!connection.isClosed()) {        //获取连接线程池来做重新链接            EventLoopGroup group = ctx.channel().eventLoop().parent();            //开辟一个新线程,处理新连接任务            reconnect(group, connection);        }        ctx.fireChannelInactive();    }    //断线重连,执行一次任务    private void reconnect(final EventLoopGroup group, final RedisConnection connection){        group.schedule(new Runnable() {            @Override            public void run() {                tryReconnect(group, connection, 1);            }        }, 100, TimeUnit.MILLISECONDS);    }    private void tryReconnect(final EventLoopGroup group, final RedisConnection connection, final int attempts) {        if (connection.isClosed() || group.isShuttingDown()) {            return;        }        log.debug("reconnecting {} to {} ", connection, connection.getRedisClient().getAddr(), connection);        bootstrap.connect().addListener(new ChannelFutureListener() {            @Override            public void operationComplete(final ChannelFuture future) throws Exception {                if (connection.isClosed()) {                    return;                }                try {                    if (future.isSuccess()) {                        log.debug("{} connected to {}", connection, connection.getRedisClient().getAddr());                        reconnect(connection, future.channel());                        return;                    }                } catch (RedisException e) {                    log.warn("Can't connect " + connection + " to " + connection.getRedisClient().getAddr(), e);                }                //根据失败尝试次数,来决定尝试连接的次数                int timeout = 2 << attempts;                group.schedule(new Runnable() {                    @Override                    public void run() {                        tryReconnect(group, connection, Math.min(BACKOFF_CAP, attempts + 1));                    }                }, timeout, TimeUnit.MILLISECONDS);            }        });    }    private void reconnect(final RedisConnection connection, final Channel channel) {        if (connection.getReconnectListener() != null) {            // new connection used only for channel init            RedisConnection rc = new RedisConnection(connection.getRedisClient(), channel);            Promise<RedisConnection> connectionFuture = bootstrap.group().next().newPromise();            connection.getReconnectListener().onReconnect(rc, connectionFuture);            connectionFuture.addListener(new FutureListener<RedisConnection>() {                @Override                public void operationComplete(Future<RedisConnection> future) throws Exception {                    if (future.isSuccess()) {                        connection.updateChannel(channel);                        resubscribe(connection);                    }                }            });        } else {            connection.updateChannel(channel);            resubscribe(connection);        }    }    private void resubscribe(RedisConnection connection) {        if (connection instanceof RedisPubSubConnection) {            RedisPubSubConnection conn = (RedisPubSubConnection) connection;            for (Entry<String, Codec> entry : conn.getChannels().entrySet()) {                conn.subscribe(entry.getValue(), entry.getKey());            }            for (Entry<String, Codec> entry : conn.getPatternChannels().entrySet()) {                conn.psubscribe(entry.getValue(), entry.getKey());            }        }    }    @Override    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {        ctx.channel().close();    }}
在channelActive 时间触发时, 连接被添加到    private final ChannelGroup channels;  进行管理。

    //当连接创建时,持有通道的引用    @Override    public void channelActive(ChannelHandlerContext ctx) throws Exception {        channels.add(ctx.channel());        ctx.fireChannelActive();    }


当channelActive 链接断开时,触发断线重连。

    //当断开链接时,使用短线重连功能更    @Override    public void channelInactive(ChannelHandlerContext ctx) throws Exception {        RedisConnection connection = RedisConnection.getFrom(ctx.channel());        if (!connection.isClosed()) {        //获取连接线程池来做重新链接            EventLoopGroup group = ctx.channel().eventLoop().parent();            //开辟一个新线程,处理新连接任务            reconnect(group, connection);        }        ctx.fireChannelInactive();    }

在这里获取了netty本身的线程池来管理新创建的线程。

在连接过程中涉及到监听器。

public interface ReconnectListener {    void onReconnect(RedisConnection redisConnection, Promise<RedisConnection> connectionFuture) throws RedisException;}


在redission中 RedisConnection  类中提供了同步或者异步读写数据的方法。

public class RedisConnection implements RedisCommands {    private static final AttributeKey<RedisConnection> CONNECTION = AttributeKey.valueOf("connection");    //对于netty client 的封装    final RedisClient redisClient;    //表示连接的状态    private volatile boolean closed;    //实际socketchannel通道,所有输入输出数据都是通过这个通道进行的。    volatile Channel channel;    //断线重连监听器    private ReconnectListener reconnectListener;    private long lastUsageTime;    public RedisConnection(RedisClient redisClient, Channel channel) {        super();        this.redisClient = redisClient;        updateChannel(channel);        lastUsageTime = System.currentTimeMillis();    }}


其实,redission 使用netty4.x  异步io 操作,但是通过public interface Future  来使用了同步操作。





0 0