netty入门笔记

NIO简介

NIO采用了一种与传统IO完全不同的模式去读写数据,为了解决传统IO阻塞导致资源利用率和程序效率低下的问题,NIO采用了Reactor模式去处理客户端的IO请求,一个典型的多线程Reactor模型如下图所示

Reactor模式
传统的IO是一个线程对应一个连接,当连接数非常多的时候,线程就不够用了,为了解决这个问题,Reactor使用acceptor统一监听所有的连接请求,连接之后的IO操作会绑定到一个特定的NIO线程去做,因为多个客户端连接可以绑定到同一条NIO线程去处理,实际上是用单线程模拟了多线程,从而实现更大的系统吞吐量。NIO之所以可以用一条线程处理多个连接,核心在于NIO核心的三个组件

  • channel
  • buffer
  • selector

channel 是对socket的抽象,有点类似于一个stream,都是用于读写操作的,并且也分为很多类型,比如FileChannel,DatagramChannel,SocketChannel等,但却比stream更加强大,比如一个channel可以同时支持读和写,并且可以异步的读写,而stream不可以。
buffer 是我们与channel交互的一个中介,即数据从buffer读取到channel中,并且也从channel中写入到buffer里,buffer其实就是一块内存区域,我们可以在这个内存区域中进行数据的读写,它提供了对一些常见数据类型读写的封装,比如ByteBuffer,CharBuffer,DoubleBuffer等
selector 是NIO实现一对多的核心,Selector的构成如下图所示,一个selector上面注册了多个channel,selector可以通过select()方法判断当前可进行IO操作的channel数量,如果没有channel就绪,则阻塞直到至少一个chanel就绪为止,通过selectedKeys()方法获得可以进行IO的具体channel信息,再对相关的channel进行相应的操作

在这里插入图片描述

netty快速创建NIO框架

快速创建一个netty的服务端,只需要在以下模板式代码的基础上,自己实现一些handler即可

public void run() throws Exception{
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
		    .handler(new LoggingHandler(LogLevel.INFO))//对连接的预处理可以通过设置handler实现
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        public void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(65536, 1, 2, 0, 3));
                            ch.pipeline().addLast(new DataProcessHandler());//对数据流的处理可以通过增加不同的handler实现
                        }
                    })
                    .option(ChannelOption.SO_BACKLOG, 128)
                    .childOption(ChannelOption.SO_KEEPALIVE, true);

            ChannelFuture f = b.bind(port).sync();
            logger.info("start netty server...");

            f.channel().closeFuture().sync();
        } finally {
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }

NioEventLoop:NioEventLoop与一个特定线程的绑定,类似于这个线程的标记,可以直接理解为一个线程,EventLoop线程往往负责与其他进程(主要是IO操作)进行通信,也可以理解为消息线程。
NioEventLoopGroup:其实就是NioEventLoop线程的线程池
bossGroup:用来处理TCP连接请求的线程池
workerGroup:用来处理IO事件的线程池
ServerBootstrap:引导器,初始化这个server的一些配置,bossgroup,workergroup,channel类型,是否阻塞等等
handler:在服务端accept客户端请求的时候对客户端连接做一些预处理,而不是对客户端传过来的数据处理
childHandler:在客户端建立连接之后,对客户端chanel的数据做处理
pipeline:一个channel实例化必然伴随着有且仅有一个的ChannelPipeline,pipeline把所有的handler串联起来,像动脉把人体的各个部分串联起来一样,数据经由pipeline逐级流向各个handler,经过各个handler加工之后最后才会输出最终的结果。
chanelPipeline

ChannelHandler

上面介绍过netty处理数据的核心类是handler,在channelPipeline里面,handler按顺序执行,在这里我们可以定义各种事件的处理逻辑,比如连接,数据接收,异常处理,数据转换等
channelhandler有两个子类channelInbondHandler和channelOutboundHandler,这两个类对应了两个数据流向,如果数据是从外部流入我们的系统,就是inbound,从系统流入外部就是outbound
一个pipeline可以把两种handler混合在一起,当一个数据流进入channelpipeline时,pipeline头部会区分进入的数据流是inbound event还是outbound event,如果是Inbound event则会把数据传给第一个ChannelInboundHandler,而忽略掉其中的ChannelOutboundHandler,反之亦然。数据在pipeline中的流向大致如下图所示。
在这里插入图片描述
在netty中,可以按照如下的模板实现一个典型的inboundHandler

public class ReceiveHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        super.channelActive(ctx);
	//TODO 通道激活时触发,当客户端connect成功后,服务端就会接收到这个事件,从而可以把客户端的Channel记录下来,供后面复用
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws IOException {
        ByteBuf buffer = (ByteBuf) msg;
        String str = buffer.toString(CharsetUtil.UTF_8);
        //TODO 当收到对方发来的数据后,就会触发,参数msg就是发来的信息,可以是基础类型,也可以是序列化的复杂对象
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.writeAndFlush(Unpooled.EMPTY_BUFFER)//4
                .addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
	//TODO channelRead执行后触发
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
	//TODO 出错时会触发,做一些错误处理
    }
}

ChannelHandlerContext是让handler能与其他handler通信的关键,每个ChannelHandler被添加到ChannelPipeline后,都会创建一个ChannelHandlerContext并与之创建的ChannelHandler关联绑定。ChannelHandlerContext可以通知下一个ChannelHandler,并允许ChannelHandler与其他的ChannelHandler实现进行交互。还可以修改ChannelPipeline,能在运行时动态的增加、删除、替换ChannelPipeline中的ChannelHandler,我们可以保存ChannelHandlerContext供以后使用。
此外,带@Sharable注解的ChannelHandler可以被添加到多个ChannelPipeline,也就是说单个ChannelHandler可以有多个ChannelHandlerContext,反之也可以调用多个ChannelHandlerContext获取同一个ChannelHandler。如果不带@Sharable注解的ChannelHandler则不可以到多个ChannelPipeLine

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值