1.Netty是什么

Netty is an asynchronous event-driven network application framework
for rapid development of maintainable high performance protocol servers & clients.

Netty是一个异步、事件驱动的网络应用框架
用于快速开发可维护的高性能协议服务器和客户端。


注意:Netty的异步并非是用AIO实现的。

Netty的异步机制是通过Java NIO 来实现的。NIO是一种基于缓冲区、非阻塞、事件驱动的I/O模型,它提供了一套新的I/O API,包括了Channel、Selector、Buffer等核心组件,可以实现高效、可靠的异步I/O操作。Netty在其基础上进行了封装和优化,提供了更加易用、高效的异步I/O框架。在Netty中,当进行I/O操作时,可以通过Channel和Selector来实现异步执行,而回调机制则是实现异步执行的重要手段。


2.快速开始

所需依赖

<dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.86.Final</version>
        </dependency>


服务端

HelloServerHelloServerimport io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;

public class HelloServer {
    public static void main(String[] args) {
        // 1. ServerBootstrap:启动器,负责组装netty组件,启动服务器
        new ServerBootstrap()
                // 2. BossEventLoop(负责连接) , WorkerEventLoop(负责读写) (selector, thread)
                .group(new NioEventLoopGroup())
                // 3.选择服务器的ServerSocketChannel实现
                .channel(NioServerSocketChannel.class)
                // 4. worker(child) , 决定了worker能执行什么操作(handler)
                .childHandler(
                        // 5. channel 代表和客户端进行读写的通道 Initializer:初始化器,负责添加别的handler
                        new ChannelInitializer<NioSocketChannel>() {
                    @Override
                    protected void initChannel(NioSocketChannel ch) throws Exception {
                        // 6. 添加具体handler
                        ch.pipeline().addLast(new StringDecoder()); //将 ByteBuf 转为字符串
                        ch.pipeline().addLast(new ChannelInboundHandlerAdapter(){   //自定义handler
                            @Override   //读事件
                            public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                                System.out.println(msg);
                            }
                        });
                    }
                })
                .bind(8080);
    }
}

客户端

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringEncoder;

import java.net.InetSocketAddress;

public class HelloClient {
    public static void main(String[] args) throws InterruptedException {
        // 1.启动类
        new Bootstrap()
                // 2.添加EventLoop
                .group(new NioEventLoopGroup())
                .channel(NioSocketChannel.class)
                .handler(new ChannelInitializer<NioSocketChannel>() {
                    @Override
                    protected void initChannel(NioSocketChannel ch) throws Exception {
                        ch.pipeline().addLast(new StringEncoder());
                    }
                })
                .connect(new InetSocketAddress("localhost",8080))
                .sync()
                .channel()
                //发送数据
                .writeAndFlush("hello world");
    }
}


3.对HelloServer中组件的解释

ServerBootstrap()

ServerBootstrap是Netty中用于创建服务器端的启动类,它提供了一组用于配置、启动和关闭服务器的API,是Netty服务器端应用程序的入口点。

在使用ServerBootstrap时,开发者需要配置一些参数,例如服务器监听的端口号、通道类型、事件处理器等。在启动服务器后,ServerBootstrap会自动创建一个I/O线程池和一个事件循环组,用于处理客户端连接、请求和响应等I/O操作。

ServerBootstrap的主要方法包括:

  • group(EventLoopGroup bossGroup, EventLoopGroup workerGroup):用于设置服务端的主从事件循环组。
  • channel(Class<? extends ServerChannel> channelClass):用于设置服务端的通道类型。
  • childHandler(ChannelInitializer<Channel> childHandler):用于设置连接到服务器的客户端的处理器,用于处理客户端的请求和响应。
  • bind(int port):用于绑定服务器端口并启动服务器。

通过ServerBootstrap的API,开发者可以轻松创建一个高性能、可扩展的服务器端应用程序,并且可以方便地配置、启动和关闭服务器。

NioEventLoopGroup()

NioEventLoopGroup是Netty中用于处理I/O事件的线程组,它是基于Java NIO实现的多线程事件循环器。

在Netty中,所有的I/O操作都是在NioEventLoopGroup中的事件循环器中进行的,NioEventLoopGroup会自动创建多个NioEventLoop线程,每个NioEventLoop线程负责处理一组Channel的I/O事件。

NioEventLoopGroup的主要作用是管理NioEventLoop线程的创建、启动和关闭,并且将I/O事件分发到各个NioEventLoop线程中进行处理。在Netty中,通常会创建两个NioEventLoopGroup,一个用于处理连接请求的bossGroup,一个用于处理客户端的I/O请求的workerGroup。

当有新的连接请求时,bossGroup会将连接请求分配给workerGroup中的某个NioEventLoop线程进行处理;当有I/O事件发生时,NioEventLoop线程会将事件分发给对应的ChannelHandler进行处理,并且可以将处理结果返回给客户端。

总之,NioEventLoopGroup是Netty中用于处理I/O事件的线程组,它可以提高程序的并发性能和响应速度。

NioServerSocketChannel

NioServerSocketChannel是Netty中用于表示服务器端监听的通道类型,它继承自AbstractNioChannel类,是基于Java NIO实现的ServerSocketChannel的封装。

在Netty中,NioServerSocketChannel用于监听客户端的连接请求,并将连接请求分配给对应的NioSocketChannel进行处理。当有新的连接请求到达时,NioServerSocketChannel会将请求封装成NioSocketChannel对象,并将其注册到EventLoop中,等待I/O事件的发生。

NioServerSocketChannel的主要属性和方法包括:

  • pipeline():用于获取当前通道的ChannelPipeline对象,可以向其中添加或删除ChannelHandler。
  • bind(InetSocketAddress localAddress):用于将当前通道绑定到指定的本地地址。
  • eventLoop():用于获取当前通道所在的EventLoop对象。
  • channelRead(Object msg):用于处理客户端连接请求,将连接请求封装成NioSocketChannel对象,并将其注册到EventLoop中。

总之,NioServerSocketChannel是Netty中用于表示服务器端监听的通道类型,通过它可以高效地监听客户端的连接请求,并将请求分配给对应的NioSocketChannel进行处理。

ChannelInitializer

ChannelInitializer是Netty中用于初始化Channel的抽象类,它提供了一些方法用于初始化新创建的Channel的ChannelPipeline。

在Netty中,每个Channel都有一个ChannelPipeline,用于处理该Channel的所有事件。ChannelInitializer是一个特殊的ChannelHandler,它可以向ChannelPipeline中添加其他的ChannelHandler,并完成一些初始化工作。

ChannelInitializer的主要方法是initChannel(),它会在Channel被创建时被调用,并且会将新创建的Channel作为参数传递进来。开发者可以在该方法中向ChannelPipeline中添加一些自定义的ChannelHandler,例如解码器、编码器、业务逻辑处理器等。

public class MyChannelInitializer extends ChannelInitializer<NioSocketChannel> {
    @Override
    protected void initChannel(NioSocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        pipeline.addLast("decoder", new MyDecoder());
        pipeline.addLast("encoder", new MyEncoder());
        pipeline.addLast("handler", new MyHandler());
    }
}

在上述代码中,MyChannelInitializer继承自ChannelInitializer<NioSocketChannel>,并且实现了initChannel()方法,该方法将自定义的MyDecoder、MyEncoder和MyHandler添加到了NioSocketChannel的ChannelPipeline中,完成了NioSocketChannel的初始化工作。


为什么服务器端是new ChannelInitializer<NioSocketChannel>,而不是NioServerSocketChannel ?

在服务器端,使用的是NioServerSocketChannel来表示服务器端监听的通道类型,而不是在ChannelInitializer中指定NioServerSocketChannel类型。

NioServerSocketChannel是用于监听客户端连接请求的通道类型,当有新的连接请求到达时,NioServerSocketChannel会将请求封装成NioSocketChannel对象,并将其注册到EventLoop中进行处理。因此,在服务器端,需要使用NioServerSocketChannel来监听客户端连接请求。

而在ChannelInitializer中,需要指定要对哪种类型的Channel进行初始化。在服务器端,需要对客户端连接请求分配的NioSocketChannel进行初始化,因此需要将ChannelInitializer的泛型类型设置为NioSocketChannel。


ChannelInboundHandlerAdapter

ChannelInboundHandlerAdapter是Netty中实现了ChannelInboundHandler接口的适配器类,它提供了一些默认的实现,使得开发者可以只实现需要的方法,而不必实现接口中的所有方法。

在Netty中,所有的ChannelHandler都必须实现ChannelHandler接口,其中包括ChannelInboundHandler和ChannelOutboundHandler接口。ChannelInboundHandler用于处理入站数据,而ChannelOutboundHandler用于处理出站数据。

ChannelInboundHandlerAdapter是ChannelInboundHandler接口的适配器类,它提供了一些默认的实现,例如channelRead()、channelActive()、channelInactive()等方法。开发者可以继承ChannelInboundHandlerAdapter类,并重写需要的方法,从而实现自己的业务逻辑。

public class MyHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        // 处理接收到的数据
        String data = (String) msg;
        // 打印接收到的数据
        System.out.println("Receive data from client: " + data);
        // 将处理结果返回给客户端
        String response = "Received your message: " + data;
        ctx.writeAndFlush(response);
    }
}

在上述代码中,MyHandler继承自ChannelInboundHandlerAdapter,重写了channelRead()方法,该方法用于处理接收到的消息。在方法中,将接收到的消息转换成String类型,并且打印出来,然后将处理结果返回给客户端。