Netty学习

简介

高性能,事件驱动的异步非阻塞的IO框架,用于建立TCP等底层连接

基于netty可以构建高性能的http服务器,已经被很多项目作为socket底层基础

  • 传统IO的读取

在打开一个I/O通道后,read()将一直等待在端口一边读取字节内容,如果没有内容进来,read()也是傻傻的等,这会影响我们程序继续做其他事情

标准的IO基于字节流和字符流进行操作

InputStream is = new FileInputStream("input.bin");
int byte = is.read(); // 当前线程等待结果到达直至错误
  • NIO的读取

Java NIO非堵塞技术实际是采取Reactor模式,或者说是Observer模式为我们监察I/O端口,如果有内容进来,会自动通知我们,这样,我们就不必开启多个线程死等,从外界看,实现了流畅的I/O读写,不堵塞了。

NIO是基于通道(Channel)和缓冲区(Buffer)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中

while (true) {
 selector.select(); // 从多个通道请求事件
 Iterator it = selector.selectedKeys().iterator();
 while (it.hasNext()) {
  SelectorKey key = (SelectionKey) it.next();
  handleKey(key);
  it.remove();
 }

阻塞与非阻塞原理

  • 传统硬件的堵塞,从内存中取数据,写到磁盘,而CPU一直等到磁盘写完成,磁盘写操作是很慢的,这段时间CPU被堵塞不能发挥效率

image

  • CPU只发出写操作这样的指令,做一些初始化工作,DMA具体执行,从内存中读数据,然后写到磁盘,当完成写后发出一个中断事件给CPU,这段时间CPU是空闲的,可以做别的事情,这个原理称为Zero.copy零拷贝

image

netty主要原理和适用

nio有一个主要的类selector,这个类似一个观察者,只要我们把需要探知的socketChannel告诉selector,我们继续做别的事情,当有事件发生时,他会通知我们,传回一组SelectionKey,我们读取这些key,就会获得我们刚刚注册的socketChannel,然后我们从Channel中读取数据

netty服务端示例

ServerBootstrap是socket服务端的入口

// 初始化用于Acceptor的主"线程池"以及用于I/O工作的从"线程池"
EventLoopGroup bossGroup = new NioEventLoopGroup(); 
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
    //初始化ServerBootstrap实例, 此实例是netty服务端应用开发的入口
    ServerBootstrap b = new ServerBootstrap(); 
    b.group(bossGroup, workerGroup)  
     //指定通道channel的类型,由于是服务端,故而是NioServerSocketChannel
     .channel(NioServerSocketChannel.class) 
      //设置ServerSocketChannel的处理器
     .handler(new LoggingHandler())   
     .childHandler(new ChannelInitializer<SocketChannel>() { 
       //设置子通道也就是SocketChannel的处理器, 其内部是实际业务开发的"主战场"
         @Override
         public void initChannel(SocketChannel ch) throws Exception {
             ch.pipeline().addLast(new DiscardServerHandler());
         }
     })
      //配置ServerSocketChannel的选项
     .option(ChannelOption.SO_BACKLOG, 128)   
      //配置子通道也就是SocketChannel的选项
     .childOption(ChannelOption.SO_KEEPALIVE, true); 
    
     // 绑定并侦听某个端口
     ChannelFuture f = b.bind(port).sync(); 
    
     // Wait until the server socket is closed.
     // In this example, this does not happen, but you can do that to gracefully
     // shut down your server.
     f.channel().closeFuture().sync();
} finally {
    workerGroup.shutdownGracefully();
    bossGroup.shutdownGracefully();
}

原理

image

channelPipeline是关键,类似Unix的管道

  • 为每个channel保留channelHandlers,如EchoServerHandler
  • 所有事件都要通过它
  • 一个channel对应一个channelPipeline
  • 包含协议编码解码,安全验证SSL和应用逻辑

image



blog comments powered by Disqus