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 来使用了同步操作。
- Redis:redission 源代码剖析1 连接建立
- Redis:redission 源代码剖析3 future模式
- Redis:redission 源代码剖析2 编码解码过程
- Redis: Jedis 源代码剖析1-链接建立和收发命令
- Redis --- Redission客户端
- Redission--基于redis的分布式协调客户端
- Redis: Jedis 源代码剖析2- 发布者/订阅者模式剖析
- LibRTMP源代码分析5:建立网络连接
- redis系统命令源代码剖析笔记(3)
- RTMPdump(libRTMP) 源代码分析 6: 建立一个流媒体连接 (NetStream部分 1)
- RTMPdump(libRTMP) 源代码分析 6: 建立一个流媒体连接 (NetStream部分 1)
- RTMPdump(libRTMP) 源代码分析 6: 建立一个流媒体连接 (NetStream部分 1)
- redis 源码剖析 1 sds
- LibRTMP源代码分析5:建立网络连接(NetConnection)
- Redis 客户端使用Jedis建立连接池(一)
- 1 Java 连接redis
- BT源代码学习心得(十二):客户端源代码分析(从开始到连接建立阶段)
- BT源代码学习心得(十三):客户端源代码分析(对等客户的连接建立及其握手协议)
- ReactJS入门学习二
- 诊断Java中的内存泄露
- 第一篇Blog……
- 简单的C3P0数据库连接池配置方法
- Android 通知栏Notification的整合
- Redis:redission 源代码剖析1 连接建立
- 欢迎使用CSDN-markdown编辑器
- logback 結構
- Oracle截取字符串
- MySQL的btree索引和hash索引
- 小菜的性能日记 1
- 总结一种线性复杂度求两个数组(容器)求相同元素个数的方法
- 一款通用的欢迎页,可以有多张图片
- MPLS VPN RD RT MP-BGP谁能把这些关系给顺一下