指数退避重连: 每隔1秒,2秒,4秒, 8秒....以2的幂次来建立连接,到达一定次数之后放弃连接...
下面代码默认重试10次
直接上Netty客户端代码:
package org.jy.sso.websocket.stomp.push.netty.chat.system.client;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringEncoder;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.time.DateFormatUtils;
import java.util.Date;
import java.util.concurrent.TimeUnit;
/**
* Netty基于异步事件驱动的高性能通信框架:
* 客户端重连处理逻辑
*/
@Slf4j
public class NettyClient {
// 最大重试次数
private static final int MAX_RETRY_NUM = 10;
// 重试初始值
private static final int INI_RETRY_NUM = 0;
// 步长
private static final int STEP_RETRY_NUM_LENGTH = 1;
// 连接的端口
private static final int NETTY_SERVER_PORT = 8000;
// 主机IP
private static final String NETTY_SERVER_HOST_IP = "127.0.0.1";
public static void main(String[] args) {
Bootstrap bootstrap = new Bootstrap();
NioEventLoopGroup group = new NioEventLoopGroup();
bootstrap.group(group)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel channel) {
// 注册字符串编码器
channel.pipeline().addLast(new StringEncoder());
}
});
connect(bootstrap, NETTY_SERVER_HOST_IP, NETTY_SERVER_PORT, MAX_RETRY_NUM);
}
/**
*
*
* @param bootstrap 客户端启动器
* @param host 主机
* @param port 端口
* @param retryNum 指数退避重新连接的次数
* @author yh19166
* @deprecated 连接和重连机制,实现了指数退避重连
*/
private static void connect(Bootstrap bootstrap, String host, int port, int retryNum) {
bootstrap.connect(host, port).addListener(future -> {
// 通过future.isSuccess() 判断是否连接成功
if (future.isSuccess()) {
System.out.println(DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss") + " -->> 连接成功....");
} else if (retryNum == INI_RETRY_NUM) {
System.out.println(DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss") + " -->> 重连次数已用完,放弃连接....");
} else {
// 第几次重连
int order = (MAX_RETRY_NUM - retryNum) + STEP_RETRY_NUM_LENGTH;
// 客户考虑重新连接的逻辑,分梯度连接......
System.out.println("第 " + order + " 连接失败......");
// 本次重连的间隔: 通过左移操作快速计算出重连时间间隔
int delay = STEP_RETRY_NUM_LENGTH << order;
System.out.println(DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss") + " -->> 连接失败,第" + order + "次重新连接....");
// 实现定时任务逻辑
bootstrap.config().group().schedule(() -> connect(bootstrap, host, port, retryNum - STEP_RETRY_NUM_LENGTH), delay, TimeUnit.SECONDS);
}
});
}
/**
* @param bootstrap 客户端启动类
* @param host 主机
* @param port 端口
* @param retryNum 指数退避重新连接的次数
* @author yh19166
* @deprecated 连接和重连机制,实现了指数退避重连
*/
private static void retryConnect(Bootstrap bootstrap, String host, int port, int retryNum) {
bootstrap.connect(host, port).addListener(future -> {
if (future.isSuccess()) {
log.info("连接服务器成功!");
} else if (retryNum == INI_RETRY_NUM) {
log.error("重连次数已用完,放弃连接!");
} else {
// 第几次重连
int order = (MAX_RETRY_NUM - retryNum) + STEP_RETRY_NUM_LENGTH;
// 本次重连的间隔
int delay = STEP_RETRY_NUM_LENGTH << order;
log.error(DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss") + ": 连接失败,第" + order + "次重新连接....");
bootstrap.config().group().schedule(() -> connect(bootstrap, host, port, retryNum - STEP_RETRY_NUM_LENGTH), delay, TimeUnit.SECONDS);
}
});
}
}
基于Netty服务器端测试代码:
package org.jy.sso.websocket.stomp.push.netty.chat.system.server;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
/**
* 居于异步事件驱动高性能网络通信框架
*/
public class NettyServer {
public static void main(String[] args) {
// 服务器端启动类
ServerBootstrap serverBootstrap = new ServerBootstrap();
// boss主线程(父线程)
NioEventLoopGroup bossLoopGroup = new NioEventLoopGroup();
// 工作线程(子线程)
NioEventLoopGroup workerLoopGroup = new NioEventLoopGroup();
serverBootstrap // 父子线程建立组连接
.group(bossLoopGroup, workerLoopGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<NioSocketChannel>() {
@Override
protected void initChannel(NioSocketChannel ch) throws Exception {
// 初始化一个Channel(通道),pipeline(管道线)
// 字符串解码器
ch.pipeline().addLast(new StringDecoder());
ch.pipeline().addLast(new SimpleChannelInboundHandler<String>() {
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, String msg) throws Exception {
System.out.println("这是服务器端Netty接收到的消息: " + msg);
}
});
}
}).bind(8000);
}
}
测试结果:
11:17:38.131 [main] DEBUG io.netty.util.internal.logging.InternalLoggerFactory - Using SLF4J as the default logging framework
11:17:38.151 [main] DEBUG io.netty.channel.MultithreadEventLoopGroup - -Dio.netty.eventLoopThreads: 40
11:17:38.192 [main] DEBUG io.netty.util.internal.PlatformDependent0 - java.nio.Buffer.address: available
11:17:38.193 [main] DEBUG io.netty.util.internal.PlatformDependent0 - sun.misc.Unsafe.theUnsafe: available
11:17:38.194 [main] DEBUG io.netty.util.internal.PlatformDependent0 - sun.misc.Unsafe.copyMemory: available
11:17:38.195 [main] DEBUG io.netty.util.internal.PlatformDependent0 - direct buffer constructor: available
11:17:38.196 [main] DEBUG io.netty.util.internal.PlatformDependent0 - java.nio.Bits.unaligned: available, true
11:17:38.196 [main] DEBUG io.netty.util.internal.PlatformDependent0 - java.nio.DirectByteBuffer.<init>(long, int): available
11:17:38.197 [main] DEBUG io.netty.util.internal.Cleaner0 - java.nio.ByteBuffer.cleaner(): available
11:17:38.198 [main] DEBUG io.netty.util.internal.PlatformDependent - Platform: Windows
11:17:38.211 [main] DEBUG io.netty.util.internal.PlatformDependent - Java version: 8
11:17:38.211 [main] DEBUG io.netty.util.internal.PlatformDependent - -Dio.netty.noUnsafe: false
11:17:38.211 [main] DEBUG io.netty.util.internal.PlatformDependent - sun.misc.Unsafe: available
11:17:38.212 [main] DEBUG io.netty.util.internal.PlatformDependent - -Dio.netty.noJavassist: false
11:17:38.213 [main] DEBUG io.netty.util.internal.PlatformDependent - Javassist: unavailable
11:17:38.213 [main] DEBUG io.netty.util.internal.PlatformDependent - You don't have Javassist in your class path or you don't have enough permission to load dynamically generated classes. Please check the configuration for better performance.
11:17:38.214 [main] DEBUG io.netty.util.internal.PlatformDependent - -Dio.netty.tmpdir: C:\Users\ADMINI~1\AppData\Local\Temp (java.io.tmpdir)
11:17:38.214 [main] DEBUG io.netty.util.internal.PlatformDependent - -Dio.netty.bitMode: 64 (sun.arch.data.model)
11:17:38.215 [main] DEBUG io.netty.util.internal.PlatformDependent - -Dio.netty.noPreferDirect: false
11:17:38.215 [main] DEBUG io.netty.util.internal.PlatformDependent - io.netty.maxDirectMemory: 15252586496 bytes
11:17:38.244 [main] DEBUG io.netty.channel.nio.NioEventLoop - -Dio.netty.noKeySetOptimization: false
11:17:38.244 [main] DEBUG io.netty.channel.nio.NioEventLoop - -Dio.netty.selectorAutoRebuildThreshold: 512
11:17:38.248 [main] DEBUG io.netty.util.internal.PlatformDependent - org.jctools-core.MpscChunkedArrayQueue: available
11:17:38.483 [main] DEBUG io.netty.channel.DefaultChannelId - -Dio.netty.processId: 16148 (auto-detected)
11:17:38.485 [main] DEBUG io.netty.util.NetUtil - -Djava.net.preferIPv4Stack: false
11:17:38.486 [main] DEBUG io.netty.util.NetUtil - -Djava.net.preferIPv6Addresses: false
11:17:39.809 [main] DEBUG io.netty.util.NetUtil - Loopback interface: lo (Software Loopback Interface 1, 127.0.0.1)
11:17:39.810 [main] DEBUG io.netty.util.NetUtil - \proc\sys\net\core\somaxconn: 200 (non-existent)
11:17:41.147 [main] DEBUG io.netty.channel.DefaultChannelId - -Dio.netty.machineId: 00:50:56:ff:fe:c0:00:01 (auto-detected)
11:17:41.149 [main] DEBUG io.netty.util.internal.ThreadLocalRandom - -Dio.netty.initialSeedUniquifier: 0xca7695bdb7e785ff
11:17:41.169 [main] DEBUG io.netty.util.ResourceLeakDetector - -Dio.netty.leakDetection.level: simple
11:17:41.169 [main] DEBUG io.netty.util.ResourceLeakDetector - -Dio.netty.leakDetection.maxRecords: 4
11:17:41.206 [main] DEBUG io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.numHeapArenas: 40
11:17:41.206 [main] DEBUG io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.numDirectArenas: 40
11:17:41.206 [main] DEBUG io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.pageSize: 8192
11:17:41.206 [main] DEBUG io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.maxOrder: 11
11:17:41.206 [main] DEBUG io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.chunkSize: 16777216
11:17:41.206 [main] DEBUG io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.tinyCacheSize: 512
11:17:41.206 [main] DEBUG io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.smallCacheSize: 256
11:17:41.206 [main] DEBUG io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.normalCacheSize: 64
11:17:41.206 [main] DEBUG io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.maxCachedBufferCapacity: 32768
11:17:41.206 [main] DEBUG io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.cacheTrimInterval: 8192
11:17:41.221 [main] DEBUG io.netty.buffer.ByteBufUtil - -Dio.netty.allocator.type: pooled
11:17:41.221 [main] DEBUG io.netty.buffer.ByteBufUtil - -Dio.netty.threadLocalDirectBufferSize: 65536
11:17:41.221 [main] DEBUG io.netty.buffer.ByteBufUtil - -Dio.netty.maxThreadLocalCharBufferSize: 16384
第 1 连接失败......
2023-05-21 11:17:42 -->> 连接失败,第1次重新连接....
第 2 连接失败......
2023-05-21 11:17:45 -->> 连接失败,第2次重新连接....
第 3 连接失败......
2023-05-21 11:17:50 -->> 连接失败,第3次重新连接....
第 4 连接失败......
2023-05-21 11:17:59 -->> 连接失败,第4次重新连接....
第 5 连接失败......
2023-05-21 11:18:16 -->> 连接失败,第5次重新连接....
第 6 连接失败......
2023-05-21 11:18:49 -->> 连接失败,第6次重新连接....
第 7 连接失败......
2023-05-21 11:19:54 -->> 连接失败,第7次重新连接....
2023-05-21 11:22:02 -->> 连接成功....