【3. Netty Bootstrap客户端启动类】

1.前言

上一节,我们主要讲解了一个Netty入门案例,其中无论是客户端还是服务端的代码编写,都是分为 2 个核心步骤,分别是:启动类模板代码 + 自定义业务 Handler,其中 Handler 可以根据不同的业务定义多个。

本节主要介绍 Bootstrap 客户端启动类的代码含义。

2. Bootstrap流程

客户端启动类的写法都是固定模板的写法,需要掌握几个核心的流程,有助于理解模板代码,具体如下:

  1. 指定线程模型: 通过.group(group) 给引导类(Bootstrap)配置线程组,这个引导类的线程模型也就定型了。每个通道,也就是 Channel 都需要绑定一个线程,该线程是线程池分配的线程,专门负责处理相关 Handler;
  2. 指定 IO 模型: 我们通过.channel(NioServerSocketChannel.class) 来指定 NIO 模型。如果指定 IO 模型为 BIO,那么这里配置上 OioServerSocketChannel.class 类型即可,通常都是使用 NIO,因为 Netty 的优势就在于 NIO;
  3. 指定处理逻辑器: 通过 childHandler () 方法,给这个引导类创建一个 ChannelInitializer,这里主要就是管理自定义 Handler,最终把这些 Handler 组装成一条双向链表,Channel 有事件时则触发链表进行业务处理逻辑;
  4. 连接服务端:调用 connect () 连接服务端,需要传递两个参数,分别是服务端的 IP 地址和端口号。

3. 核心方法

方法说明
group()指定线程模型
channel()指定 IO 模型
attr()给客户端 Channel 设置自定义属性
handler()给客户端 Channel 指定处理逻辑 Handler
option()给客户端 Channel 设置底层 TCP 的属性
connect()连接服务端,返回 ChannelFuture

需要指定每个方法的功能是什么,下面将讲解其具体使用。

4. 核心方法详解 

4.1 connect()

connect () 用来连接服务端。

常见的运用场景主要有三点,分别是

①监听连接结果;

②失败重连;

③断开重连。

4.1.1 连接监听

connect () 方法返回的是 ChannelFuture,也就是说不需要等待连接成功或失败才往下执行代码,后期可以监听连接结果。

//1.连接Netty服务端
ChannelFuture future=bootstrap.connect("127.0.0.1",80);

//2.监听连接结果
future.addListener(future -> {
    if (future.isSuccess()) {
        System.out.println("连接成功!");
    } else {
        System.err.println("连接失败!");
    }
});

总结,这种模式的好处是,连接是异步的,无需等待连接响应代码才会往下执行。

4.1.2 失败重连

在网络情况差的情况下,客户端第一次连接可能会连接失败,这个时候我们可能会尝试重新连接,具体实现如下:

方案一: 通过 ChannelFuture 的返回状态来监听连接是否成功。

private static void connect(Bootstrap bootstrap, String host, int port) {
    bootstrap.connect(host, port).addListener(future -> {
        if (future.isSuccess()) {
            System.out.println("连接成功!");
        } else {
            System.err.println("连接失败,开始重连");
            //递归调用连接方法
            connect(bootstrap, host, port);
        }
    });
}

方案二: 避免短时间内频繁的请求连接,可以使用定时线程池来每隔 n 秒重连一次。

private static void connect(Bootstrap bootstrap, String host, int port) {
    bootstrap.connect(host, port).addListener(future -> {
        if (future.isSuccess()) {
            System.out.println("连接成功!");
        } else {
            //获取EventLoopGroup
            EventLoopGroup thread=bootstrap.config().group();
            //每隔5秒钟重连一次
            thread.schedule(new Runnable() {
                public void run() {
                    connect(bootstrap, host, port)
                }
            }, 5, TimeUnit.SECONDS);
        }
    });
}

代码说明:

bootstrap.config().group() 获取的 EventLoopGroup,它是一个线程池,线程池里面有一个叫定时线程池。

4.2 attr()

attr () 方法可以给客户端 Channel 初始属性,也就是 NioSocketChannel 绑定自定义属性,然后我们可以通过 channel.attr() 取出这个属性。其实就是给 NioSocketChannel 维护一个 map。

给 Channel 绑定属性。

//省略其它代码,只保留核心部分
bootstrap.attr(AttributeKey.newInstance("token"), "123");

取出 Channel 所绑定的属性。

public class NettyClientHandler extends ChannelInboundHandlerAdapter {
    //客户端启动的时候,触发事件
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        //获取Channel的绑定的属性值
        AttributeKey<String> key=AttributeKey.valueOf("token");
        String value=ctx.channel().attr(key).get();
    }
}

通过以上代码,我们介绍了 attr () 如何绑定属性和取出属性。

4.3 option()

可以通过 option () 方法可以给连接设置一些 TCP 底层相关的属性,以下是常见的三种 TCP 属性设置。

//省略其它代码,只保留核心部分
bootstrap
        .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000)
        .option(ChannelOption.SO_KEEPALIVE, true)
        .option(ChannelOption.TCP_NODELAY, true)

代码说明:

  1. ChannelOption.CONNECT_TIMEOUT_MILLIS 表示连接的超时时间,超过这个时间还是建立不上的话则代表连接失败;
  2. ChannelOption.SO_KEEPALIVE 表示是否开启 TCP 底层心跳机制,true 为开启;
  3. ChannelOption.TCP_NODELAY 表示是否开始 Nagle 算法,true 表示关闭,false 表示开启,通俗地说,如果要求高实时性,有数据发送时就马上发送,就设置为 true 关闭,如果需要减少发送次数减少网络交互,就设置为 false 开启

5. 问题?

什么是线程模型?

什么是IO模型?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值