Netty 基础

Netty 是一个异步事件驱动的网络应用程序框架。Netty 的主要目的是构建基于 NIO 的高性能协议服务器,具有网络和业务逻辑组件的分离和松耦合。用来实现各种网络传输协议,例如 HTTP,RPC 等等。

为什么选择 Netty

JDK 原生的 NIO API,不仅类库和 API 繁杂,需要对多线程和网路编程非常熟悉,才能编写出高质量的 NIO 程序,开发工作量和难度都非常大。而 Netty 对 原生的 NIO 进行封装,并提供了一套优雅、高定制、使用方便、高性能、吞吐量更高,而且更安全的 API。大多数服务,只要有网络需求,基本上都会选择使用 Netty 作为首选,而大厂也都在用,例如:

  • Hadoop
  • RocketMQ
  • ElasticSearc
  • gRPC
  • Dubbo

使用 Netty 的代码不仅更加的简洁,而且想要将 NIO 转变为 OIO,原生 API 的话就需要重写一个程序,而 Netty 只需要改变两行代码。

Netty 基础

Netty 是一个非阻塞框架。与阻塞 IO 相比,这会导致高吞吐量。所以了解非阻塞 IO 对于了解 Netty 的核心组件及其关系至关重要。

基本组件

BootStrap
Netty 通过配置启动器 bootstrap 类(提供了一个用于应用程序网络层配置的容器)来进行 Socket 连接,而 Bootstrap 分为客户端(Bootstrap)和服务端(ServerBootstrap)。

后台开发基本都为 ServerBootstrap,Bootstrap 类似

@Slf4j
@AutoConfiguration
@EnableConfigurationProperties(NettyProperties.class)
public class NettySocketAutoConfiguration {

    private final NettyProperties nettyProperties;
	/**
     * boss线程池-进行客户端连接
     *
     * @return NioEventLoopGroup parent group
     */
    @Bean
    public NioEventLoopGroup boosGroup() {
        return new NioEventLoopGroup(nettyProperties.getBoss());
    }
    
    /**
     * worker线程池-进行业务处理
     *
     * @return NioEventLoopGroup child group
     */
    @Bean
    public NioEventLoopGroup workerGroup() {
        return new NioEventLoopGroup(nettyProperties.getWorker());
    }
    
	/**
     * 服务端启动器
     * 
     * @return serverBootstrap
     */
    @Bean
    public ServerBootstrap serverBootstrap(ChannelInitializer channelInitializer) {
        ServerBootstrap bootstrap = new ServerBootstrap()
        .group(boosGroup(), workerGroup()) // 指定使用的线程组bootstrap.group(boss, worker);
        .channel(NioServerSocketChannel.class) // 指定使用的通道
        .option(ChannelOption.SO_BACKLOG, 1024)// 默认128 Socket 参数,服务端接受连接的队列长度,如果队列已满,客户端连接将被拒绝。默认值,Windows为200,其他为128。
        .option(ChannelOption.SO_RCVBUF, 1024 * 1024)
        .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, nettyProperties.getTimeout()) // 指定连接超时时间
        .childOption(ChannelOption.SO_KEEPALIVE, true)//Socket参数,连接保活,默认值为False。启用该功能时,TCP会主动探测空闲连接的有效性。
        .childOption(ChannelOption.TCP_NODELAY, true)//TCP参数,立即发送数据,默认值为Ture。
        .handler(new LoggingHandler(LogLevel.DEBUG))
        .childHandler(channelInitializer);
        try {
            ChannelFuture cf = bootstrap.bind(nettyProperties.getPort()).sync();
            log.info("Netty Server 监听中,端口:{} ......", nettyProperties.getPort());
            if (cf.isSuccess()) {
                log.info("NettyServer启动成功");
            } else {
                log.error("NettyServer启动失败", cf.cause());
            }
            cf.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            bootstrap.config().group().shutdownGracefully();
            bootstrap.config().childGroup().shutdownGracefully();
        }
        return bootstrap;
    }

一个 ServerBootstrap 需要 2个 EventLoopGroup(可以简单理解为 线程池 + Selector),服务器需要两组不同的 Channel。Boss 包含一个 ServerChannel,用来处理连接到服务端的连接。而 Worker 将用来处理读取数据和写出数据。

EventLoop
一个 EventLoopGroup 包含一个或者多个 EventLoop。EventLoop 本质是一个单线程执行器(同时维护了一个 Selector),run 方法处理 Channel 上源源不断的 I/O 事件。而在它的生命周期内只和一个 Thread 绑定,所有由 EventLoop 处理的 I/O 事件都将在它专有的 Thread 上被处理,而一个 Channel 在它的生命周期内只注册于一个 EventLoop,一个 EventLoop 可能会被分配给一个或多个 Channel。

Channel
最基本的客户端与服务端连接通道,包含了 I/O 的基本操作(bindconnectreadwriteclose),依赖于底层网络传输。在基于 Java 的网络编程中,其基本的构造是 Class Socket。Netty 的 Channel 接 口所提供的 API,大大地降低了直接使用 Socket 类的复杂性。

ChannelFuture
Netty 中所有的 I/O 操作都是异步的。一个操作可能不会立即返回,所以要一种用于在之后的某个时间点确定其结果的方法。为此,Netty 提供了 ChannelFuture 接口,其 addListener 方法注册了一个 ChannelFutureListener,以便在某个操作完成时(无论是否成功)得到通知

ChannelInitializer
顾名思义就是初始化 Channel 的处理器,可以通过继承 ChannelInitializer<SocketChannel> 类,重写 initChannel 来对数据进行自定义解码器、ChannelHandler 等。

ChannelHandler
Netty 本身提供了许多协议支持的 ChannelHandler,例如 HTTP 等,并且提供用于数据处理的容器。 ChannelHandler 由特定事件触发。 ChannelHandler 可用于几乎所有的动作,包括将一个对象转为字节(或相反),执行过程中抛出的异常处理。我们可也可以通过自定义 ChannelInboundHandlerAdapter 来实现自定义协议的数据处理。

ChannelPipeline
ChannelPipeline 提供了一个容器给 ChannelHandler 链并提供 API 用于管理沿着链入站和出站事件的流动。每个 Channel 都有自己的 ChannelPipeline,当 Channel 创建时自动创建的,通过 ServerBootstrap 进行注册。当它的方法 initChannel() 被调用时,这个对象将安装自定义的 ChannelHandler 集到 pipeline。当这个操作完成时,ChannelInitializer 则从 ChannelPipeline 自动删除自身。

/**
 * 通道初始化
 * @author tszfung
 */
@Slf4j
@Component
public class SocketInitializer extends ChannelInitializer<SocketChannel> {
    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        pipeline.addLast("idleStateHandler", new IdleStateHandler(15, 0, 0, TimeUnit.MINUTES)); // 超时
        pipeline.addLast(new YKCMessageDecoder()); // 解码
        pipeline.addLast(new YKCMessageEncoder()); // 编码
        pipeline.addLast(new YKCDataHandler()); // 编码
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值