netty重连
注意点
netty连接时,如果用eventLoop线程,那么会有资源耗尽问题,就是重连操作几次后,就不会再进行重连操作。如果想一直进行重连操作,可以用独立的ScheduledExecutorService
非eventloop线程连接
错误提示:关闭了这个注册的channel,因为需要eventloop线程来做注册功能,但是这次的绑定并不是。
你也可以从图中的堆栈错误信息,点进去看源码,有个验证线程是否是eventloop,如果不是,就报这个错误:Zforce-closing a channel ....
重连代码
@Slf4j
public class NettyClient {
private NioEventLoopGroup group;
private Bootstrap bootstrap;
private SocketAddress address;
// 定义一个独立的scheduled线程来处理重连,这样可以一直进行重连操作。如果eventLoop,会有资源耗尽问题,资源耗尽就不会再进行重连操作
private final ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
private NettyClient(){}
public void start(String host, int port) {
address = new InetSocketAddress(host, port);
group = new NioEventLoopGroup();
// 编解码器
MessageCodec codec = new MessageCodec();
bootstrap = new Bootstrap();
bootstrap.channel(NioSocketChannel.class);
bootstrap.group(group);
bootstrap.handler(new ChannelInitializer<SocketChannel>(){
@Override
protected void initChannel(SocketChannel ch) throws Exception {
// ProtocolFrameDecoder是粘包处理器
ch.pipeline().addLast(new ProtocolFrameDecoder());
ch.pipeline().addLast(codec);
ch.pipeline().addLast("client handler", new ClientHandler());
}
});
connection();
}
public void connection() {
InetSocketAddress socketAddress = (InetSocketAddress) address;
ChannelFuture channelFuture = bootstrap.connect(address);
// 添加一个监听,失败的话,继续connect
channelFuture.addListener(future->{
if(!future.isSUccess()){
scheduledExecutorService.schedule(()->{
log.info("与服务器失去连接,正在重连 host:{},port:{}", socketAddress.getHostName(), socketAddress.getPort());
connect();
}, 30L, TimeUnit.SECONDS);
}else{
log.info("连接成功 host:{},port:{}", socketAddress.getHostName(), socketAddress.getPort());
Channel channel = ((DefaultChannelPromise) future).channel();
NettySession.bind(channel);
}
});
try{
channelFuture.channel().closeFuture().sync();
}catch(InterruptedException e){
throw new RuntimeException(e);
}
}
public static NettyClient getInstance(){ return LazyHolder.NETTY_CLIENT;}
public static class LayzHolder{
private static final NettyClient NETTY_CLIENT = new NettyClient();
}
}
@Slf4j
public class Nettysession{
private static volatile Channel serverChannel;
public static Channel getChannel(){
for(;;){
if(serverChannel != null && serverChannel.isActive()){
return serverChannel;
}
}
}
public static void disposeChannel(ChannelHandlerContext ctx, ChannelHandler handler){
ctx.pipeline().remove(handler);
ctx.channel().close();
NettySession.serverChannel = null;
}
public static void bind(Channel serverChannel){
NettySession.serverChannel = serverChannel;
}
}
自定义心跳代码
@Slf4j
public class NettySession{
public static void ping(Long heartbeat){
ScheduledFuture<?> schedule = getChannel().eventLoop().schedule(()->{
// 组装心跳数据
HeartbeatMessage message = new HeartbeatMessage();
getChannel().writeAndFlush(message)
}, heartbeat, TimeUnit.SECONDS);
schedule.addListener(future->{
if(future.isSuccess()){
ping(heartbeat)
}
});
}
}