一、客户端连接的最小化代码分析
今天我们同样单独把客户端代码拿出来好好分析一下,经过精简后,客户端连接服务端的最小化代码如下:
/**
* Netty实现的NIO客户端【客户端启动的最小化代码】
*
* 三要素:
* --1、线程模型
* --2、IO模型
* --3、连接读写处理逻辑
*
* @author 有梦想的肥宅
* @date 2022/5/1
*/
public class NettyNIOClient {
public static void main(String[] args) throws InterruptedException {
//1、创建客户端启动引导类,用于引导客户端的启动工作和连接服务端
Bootstrap bootstrap = new Bootstrap();
//2、创建NioEventLoopGroup对象,作用类似线程组
NioEventLoopGroup group = new NioEventLoopGroup();
//3、配置客户端启动引导类
bootstrap
.group(group)//3.1 指定线程模型【要素1:配置线程模型】
.channel(NioSocketChannel.class) //3.2 配置NIO模型【要素2:配置IO模型】
.handler(new ChannelInitializer<Channel>() {
//3.3 定义每个链接的数据读写逻辑【要素3:配置连接读写处理逻辑】
@Override
protected void initChannel(Channel ch) {
}
});
//4、与服务端建立连接
bootstrap.connect("127.0.0.1", 8000).addListener(future -> {
if (future.isSuccess()) {
System.out.println("连接成功!");
} else {
System.out.println("连接失败!");
}
});
}
}
从上面的代码我们可以看到,启动流程中最核心的部分主要就是三要素的配置【与服务端类似】,简单归纳为:
- 1、线程模型【NioEventLoopGroup】
- 2、IO模型【NioSocketChannel】
- 3、连接读写处理逻辑【重写ChannelInitializer的抽象方法】
二、客户端失败重连代码分析
相信我们都遇到过比如玩游戏或者看视频掉线重连的场景吧,那么在客户端与服务端进行交互的过程中,也可能会出现网络或系统原因需要重连的情况,我们来通过一段代码看看怎么实现这样的场景:
/**
* Netty实现的NIO客户端【失败重连机制】
*
* 三要素:
* --1、线程模型
* --2、IO模型
* --3、连接读写处理逻辑
*
* @author 有梦想的肥宅
* @date 2022/5/1
*/
public class NettyNIOClientReConnect {
private static int MAX_RETRY = 5;
public static void main(String[] args) throws InterruptedException {
//1、创建客户端启动引导类,用于引导客户端的启动工作和连接服务端
Bootstrap bootstrap = new Bootstrap();
//2、创建NioEventLoopGroup对象,作用类似线程组
NioEventLoopGroup group = new NioEventLoopGroup();
//3、配置客户端启动引导类
bootstrap
.group(group)//3.1 指定线程模型【要素1:配置线程模型】
.channel(NioSocketChannel.class) //3.2 配置NIO模型【要素2:配置IO模型】
.handler(new ChannelInitializer<Channel>() {
//3.3 定义每个链接的数据读写逻辑【要素3:配置连接读写处理逻辑】
@Override
protected void initChannel(Channel ch) {
}
});
//4、与服务端建立连接
connect(bootstrap, "127.0.0.1", 8000, 5);
}
private static void connect(Bootstrap bootstrap, String host, int port, int retry) {
bootstrap.connect(host, port).addListener(future -> {
if (future.isSuccess()) {
System.out.println("连接成功!");
} else if (0 == retry) {
System.out.println("重试次数已用完,放弃连接!");
} else {
//1、计算第几次重连
int times = MAX_RETRY - retry + 1;
//2、计算重试间隔【第1次:2秒 第2次:4秒 第3次:8秒...】
int delay = 1 << times;//位运算符操作
System.out.println(new Date() + ": 连接失败,第" + times + "次重连......");
//3、重连【通过EventLoopGroup实现定时任务逻辑】
bootstrap.config().group().schedule(new Runnable() {
@Override
public void run() {
connect(bootstrap, host, port, retry - 1);
}
}, delay, TimeUnit.SECONDS);
}
});
}
}
可以看到,当我们去尝试连接服务端时,失败了会进行重试,直到重试次数达到上限时就会断开连接,也在一定程度上保证了我们整个系统的健壮性。
三、客户端启动的主干方法之外的一些方法解析
除了上面的主干方法以外,netty的客户端同样也为我们提供了一些其余方法来丰富我们的客户端功能,具体使用方式如下,由于使用频率不高,这里我们做个简单了解即可:
/**
* Netty实现的NIO客户端【其余方法用法解析】
*
* @author 有梦想的肥宅
* @date 2022/5/1
*/
public class NettyNIOClientOtherMethod {
private static int MAX_RETRY = 5;
public static void main(String[] args) throws InterruptedException {
//1、创建客户端启动引导类,用于引导客户端的启动工作和连接服务端
Bootstrap bootstrap = new Bootstrap();
/*todo 中间代码省略,除了启动的核心代码外,一些其他方法的使用介绍如下*/
//2、attr()
//用于给客户端的IO模型设置属性,即给NioSocketChannel设置属性
bootstrap.attr(AttributeKey.newInstance("clientName"), "有梦想的肥宅的netty客户端");
//3、option()【调优用】
//用于给服务端的IO模型设置TCP属性
bootstrap
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000)//连接超时时间
.option(ChannelOption.SO_KEEPALIVE, true)//保活探测,若正常会回复ACK响应
.option(ChannelOption.TCP_NODELAY, true);//开启Nagle算法【Nagle算法通过减少需要传输的数据包,来优化网络;提高实时性:true 提高网络利用率:false】
}
}