netty入门程序

566 阅读4分钟

1 版本说明

  1. netty的版本
<dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty-all</artifactId>
    <version>4.1.6.Final</version>
</dependency>

Netty版本在不断升级,但是4.0以上的版本使用比较广泛。Netty曾经升级到5.0,不过出现了一些问题,版本又回退了。另外,很多的大数据开源框架使用的还是3.0版本的Netty。

  1. java的版本

Netty官方建议使用JDK 1.6以上,本次使用的是JDK 1.8;

2 入门程序: 创建一个服务端类NettyDiscardServer

DiscardServer功能很简单:读取客户端的输入数据,直接丢弃,不给客户端任何回复。

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import lombok.extern.slf4j.Slf4j;

/**
 * @author wyaoyao
 * @date 2021/7/6 9:09
 * 入门程序: DiscardServer
 */
@Slf4j
public class DiscardServer {

    private final int serverPort;

    private ServerBootstrap bootstrap = new ServerBootstrap();

    public DiscardServer(int serverPort) {
        this.serverPort = serverPort;
    }

    public void start() {
        // 创建反应器轮询组
        EventLoopGroup bossLoopGroup = new NioEventLoopGroup(1);
        EventLoopGroup workLoopGroup = new NioEventLoopGroup();
        try {
            // 设置反应器轮询组
            this.bootstrap.group(bossLoopGroup, workLoopGroup);
            // 设置nio通道类型
            this.bootstrap.channel(NioServerSocketChannel.class);
            // 设置监听端口
            this.bootstrap.localAddress(this.serverPort);
            // 设置通道参数
            this.bootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);
            this.bootstrap.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
            this.bootstrap.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
            // 装备子通道流水线
            this.bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
                // 有连接到达时会创建一个通道
                @Override
                protected void initChannel(SocketChannel ch) throws Exception {
                    // 流水线的职责:负责管理通道中的处理器
                    // 向“子通道”(传输通道)流水线添加一个处理器
                    ch.pipeline().addLast(new DiscardHandler());
                }
            });
            // 开始绑定服务器
            // 通过调用sync同步方法阻塞直到绑定成功
            // 返回值是个future
            ChannelFuture channelFuture = this.bootstrap.bind().sync();
            log.info("netty discard server start success on port : [{}]", this.serverPort);
            // 等待通道关闭的异步任务结束
            // 服务监听通道会一直等待通道关闭的异步任务结束
            ChannelFuture closeFuture = channelFuture.channel().closeFuture();
            closeFuture.sync();
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            // 关闭EventLoopGroup(Gracefully: 优雅的)
            bossLoopGroup.shutdownGracefully();
            workLoopGroup.shutdownGracefully();
        }
    }

    public static void main(String[] args) {
        new DiscardServer(10010).start();
    }
}
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.ReferenceCountUtil;
import lombok.extern.slf4j.Slf4j;


/**
 * @author wyaoyao
 * @date 2021/7/6 10:31
 * Handler的作用是对应到IO事件,完成IO事件的业务处理。
 * Handler需要为业务做专门开发
 */
@Slf4j
public class DiscardHandler extends ChannelInboundHandlerAdapter {

    /**
     * 重写channelRead方法,实现Discard(丢弃)逻辑
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf byteBuf = (ByteBuf) msg;

        try {
            log.info("收到消息,丢弃如下:");
            while (byteBuf.isReadable()) {
                System.out.println((char)byteBuf.readByte());
            }
            System.out.println("----------------------------------");
        }finally {
            ReferenceCountUtil.release(msg);
        }
    }
}

反应器组件的作用是进行IO事件的查询和分发。Netty中对应的反应器组件有多种,不同应用通信场景用到的反应器组件各不相同。一般来说,对应于多线程的Java NIO通信的应用场景,Netty对应的反应器组件为NioEventLoopGroup。

在上面的例子中,使用了两个NioEventLoopGroup反应器组件实例:

  • 第一个负责服务器通道新连接的IO事件的监听;
  • 第二个主要负责传输通道的IO事件的处理和数据传输

Reactor模式中的Handler(处理器)角色组件。Handler的作用是对应到IO事件,完成IO事件的业务处理。Handler需要为业务做专门开发

上面的例子中还用到了Netty的服务引导类ServerBootstrap。服务引导类是一个组装和集成器,职责是将不同的Netty组件组装在一起。此外,ServerBootstrap能够按照应用场景的需要为组件设置好基础性的参数,最后帮助快速实现Netty服务器的监听和启动。

Netty的Handler需要处理多种IO事件(如读就绪、写就绪),对应于不同的IO事件,Netty提供了一些基础方法。这些方法都已经提前封装好,应用程序直接继承或者实现即可。比如说,对于处理入站的IO事件,其对应的接口为ChannelInboundHandler,并且Netty提供了ChannelInboundHandlerAdapter适配器作为入站处理器的默认实现。

入站和出站

简单理解,入站指的是输入,出站指的是输出。

Netty中的出/入站与Java NIO中的出/入站有些微妙的不同,Netty的出站可以理解为从Handler传递到Channel的操作,比如说write写通道、read读通道数据;

Netty的入站可以理解为从Channel传递到Handler的操作,比如说Channel数据过来之后,会触发Handler的channelRead()入站处理方法。

如果要实现自己的入站处理器,可以简单地继承ChannelInboundHandlerAdapter入站处理器适配器,再写入自己的入站处理的业务逻辑。也就是说,重写通道读取方法channelRead()即可。

在上面例子中的channelRead()方法将Netty缓冲区ByteBuf的输入数据打印到服务端控制台后,直接丢弃不管了,而且不给客户端任何回复。