前言
最近有个需求,需要客户端分别连接多个服务端来检测状态,于是就大概研究了一下,其实还是挺简单的,在这里记录一下先。
客户端启动类
/**
* @author: zhouwenjie
* @description: 客户端
* @create: 2022-07-20 17:14
**/
@Slf4j
@Component
public class NettyClient {
@Value("${monitor.server.host_roadside}")
private String hostRoadside;
@Value("${monitor.server.port_roadside}")
private int portRoadside;
@Value("${monitor.server.host_ld}")
private String hostLd;
@Value("${monitor.server.port_ld}")
private int portLd;
@Value("${monitor.delimiter}")
private String delimiter;
@Autowired
private NettyClientHandler nettyClientHandler;
private NioEventLoopGroup eventLoopGroup = new NioEventLoopGroup();
private Bootstrap bootstrap;
@PostConstruct
public void run() throws UnknownHostException {
bootstrap = new Bootstrap();
bootstrap.group(eventLoopGroup)
.channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
.option(ChannelOption.SO_KEEPALIVE, true)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) {
//客户端初始化
socketChannel.pipeline().addLast(new DelimiterBasedFrameDecoder(1024 * 8, Unpooled.wrappedBuffer(delimiter.getBytes())));
socketChannel.pipeline().addLast(new ProtostuffDecoder(PlaneMessageData.class));
socketChannel.pipeline().addLast(new ProtostuffEncoder(delimiter));
socketChannel.pipeline().addLast(nettyClientHandler);
}
});
String hostAddress = InetAddress.getLocalHost().getHostAddress();
// 指定本机ip端口,用来给服务端区分,指定端口,重启客户端会等两分钟才能连接上服务端
bootstrap.localAddress(hostAddress, 0);
//连接netty服务器
reconnect(hostRoadside,portRoadside);
reconnect(hostLd,portLd);
}
/**
* 功能描述: 断线重连,客户端有断线重连机制,就更不能使用异步阻塞了
*
* @param
* @return void
* @author zhouwenjie
* @date 2021/3/19 14:53
*/
public void reconnect(String host, Integer port) {
bootstrap.remoteAddress(host, port);
ChannelFuture channelFuture = bootstrap.connect();
//使用最新的ChannelFuture -> 开启最新的监听器
channelFuture.addListener((ChannelFutureListener) future -> {
if (future.cause() != null) {
log.error(host + ":" + port + "连接失败。。。");
future.channel().eventLoop().schedule(() -> reconnect(host,port), 3, TimeUnit.SECONDS);
} else {
log.info(host + ":" + port + "客户端连接成功。。。");
}
});
}
/**
* 关闭 client
*/
@PreDestroy
public void shutdown() {
// 优雅关闭 EventLoopGroup 对象
eventLoopGroup.shutdownGracefully();
log.info("[*Netty客户端关闭]");
}
}
关键代码:
这里实现多连接
这里处理每个连接,并且监听是否在线
客户端handler
主要实现断线重连
@Component
@Slf4j
@ChannelHandler.Sharable
public class NettyClientHandler extends SimpleChannelInboundHandler<BerthGuide> {
/**
* 注入NettyClient
*/
@Autowired
private NettyClient nettyClient;
/**
* 连接成功
*/
@Override
public void channelActive(ChannelHandlerContext ctx) {
//连接成功,发送上线信息
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
InetSocketAddress ipSocket = (InetSocketAddress) ctx.channel().remoteAddress();
int port = ipSocket.getPort();
String host = ipSocket.getHostString();
log.error("与设备" + host + ":" + port + "连接断开!");
ctx.close();
ctx.deregister();
ctx.pipeline().remove(this);
super.channelInactive(ctx);
nettyClient.reconnect(host,port);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
super.exceptionCaught(ctx, cause);
log.error("[* Netty connection exception]:{}", cause.toString());
cause.printStackTrace();
}
@Override
public void channelRead0(ChannelHandlerContext ctx, BerthGuide msg) {
try {
} catch (Exception e) {
e.printStackTrace();
log.error("接收错误信息:" + msg);
}
}
}
关键代码:
关闭通道并重试连接
消息处理
如果服务端有消息发送过来,那么可以通过ChannelHandlerContext获取地址或者自定义的消息体里唯一标识来区分是哪个服务端发送来的地址,同时也可以编写多个handler分别处理不同类型格式的消息,具体可参考本专栏下其他netty文章