Redis,Nginx,Netty为什么这么香?( 三 )

②I/O 多路复用:Poll

Redis,Nginx,Netty为什么这么香?

文章插图
 
简介:设计新的数据结构(链表)提供使用效率 。
Poll 和 Select 相比在本质上变化不大,只是 Poll 没有了 Select 方式的最大文件描述符数量的限制 。
缺点:逐个排查所有 FD 状态效率不高 。
③I/O 多路复用:Epoll
简介:没有 FD 个数限制,用户态拷贝到内核态只需要一次,使用事件通知机制来触发 。
通过 epoll_ctl 注册 FD,一旦 FD 就绪就会通过 Callback 回调机制来激活对应 FD,进行相关的 I/O 操作 。
缺点如下:
  • 跨平台,Linux 支持最好 。
  • 底层实现复杂 。
  • 同步 。
publicstaticvoidmain(String[]args)throwsException{finalAsynchronousServerSocketChannelserverChannel=AsynchronousServerSocketChannel.open().bind(newInetSocketAddress(Constant.HOST,Constant.PORT));serverChannel.accept(null,newCompletionHandler<AsynchronousSocketChannel,Object>(){@Overridepublicvoidcompleted(finalAsynchronousSocketChannelclient,Objectattachment){serverChannel.accept(null,this);ByteBufferbuffer=ByteBuffer.allocate(1024);client.read(buffer,buffer,newCompletionHandler<Integer,ByteBuffer>(){@Overridepublicvoidcompleted(Integerresult,ByteBufferattachment){attachment.flip();client.write(ByteBuffer.wrap("HelloClient".getBytes()));//业务逻辑}@Overridepublicvoidfailed(Throwableexc,ByteBufferattachment){System.out.println(exc.getMessage());//失败处理}});}@Overridepublicvoidfailed(Throwableexc,Objectattachment){exc.printStackTrace();//失败处理}});while(true){//不whiletruemain方法一瞬间结束}}当然上面的缺点相比较它的优点都可以忽略 。JDK 提供了异步方式实现,但在实际的 Linux 环境中底层还是 Epoll,只不过多了一层循环,不算真正的异步非阻塞 。
而且就像上图中代码调用,处理网络连接的代码和业务代码解耦得不够好 。
Netty 提供了简洁、解耦、结构清晰的 API 。
publicstaticvoidmain(String[]args){newNettyServer().serverStart();System.out.println("Nettyserverstarted!");}publicvoidserverStart(){EventLoopGroupbossGroup=newNioEventLoopGroup();EventLoopGroupworkerGroup=newNioEventLoopGroup();ServerBootstrapb=newServerBootstrap();b.group(bossGroup,workerGroup).channel(NioServerSocketChannel.class).childHandler(newChannelInitializer<SocketChannel>(){@OverrideprotectedvoidinitChannel(SocketChannelch)throwsException{ch.pipeline().addLast(newHandler());}});try{ChannelFuturef=b.localAddress(Constant.HOST,Constant.PORT).bind().sync();f.channel().closeFuture().sync();}catch(InterruptedExceptione){e.printStackTrace();}finally{workerGroup.shutdownGracefully();bossGroup.shutdownGracefully();}}}classHandlerextendsChannelInboundHandlerAdapter{@OverridepublicvoidchannelRead(ChannelHandlerContextctx,Objectmsg)throwsException{ByteBufbuf=(ByteBuf)msg;ctx.writeAndFlush(msg);ctx.close();}@OverridepublicvoidexceptionCaught(ChannelHandlerContextctx,Throwablecause)throwsException{cause.printStackTrace();ctx.close();}}bossGroup 处理网络请求的大管家(们),网络连接就绪时,交给 workGroup 干活的工人(们) 。
总结回顾上文总结如下:
  • 同步/异步,连接建立后,用户程序读写时,如果最终还是需要用户程序来调用系统 read() 来读数据,那就是同步的,反之是异步 。windows 实现了真正的异步,内核代码甚为复杂,但对用户程序来说是透明的 。
  • 阻塞/非阻塞,连接建立后,用户程序在等待可读可写时,是不是可以干别的事儿 。如果可以就是非阻塞,反之阻塞 。大多数操作系统都支持的 。
Redis,Nginx,Netty,Node.js 为什么这么香?这些技术都是伴随 Linux 内核迭代中提供了高效处理网络请求的系统调用而出现的 。
 
了解计算机底层的知识才能更深刻地理解 I/O,知其然,更要知其所以然 。与君共勉!
作者:周胜帅
简介:宜信支付结算部支付研发团队高级工程师

【Redis,Nginx,Netty为什么这么香?】


推荐阅读