Netty 是一个利用 Java 的高级网络的能力,隐藏其背后的复杂性而提供一个易于使用的 API 的客户端/服务器框架。
Netty 是一个普遍使用的 Java 网络编程框架(Netty 在 2011 年得到了Duke's Choice Award,见https://www.java.net/dukeschoice/2011)。它活跃和成长于用户社区,像大型公司 Facebook 和 Instagram 以及流行 开源项目如 Infinispan, HornetQ, Vert.x, Apache Cassandra 和 Elasticsearch 等,都利用其强大的对于网络抽象的核心代码。html
以上是摘自《Essential Netty In Action》这本书,本文的内容也是本人读了这本书以后的一些整理心得,若有不当之处欢迎大虾们指正java
Netty和Tomcat最大的区别就在于通讯协议,Tomcat是基于Http协议的,他的实质是一个基于http协议的web容器,可是Netty不同,他能经过编程自定义各类协议,由于netty可以经过codec本身来编码/解码字节流,完成相似redis访问的功能,这就是netty和tomcat最大的不一样。linux
有人说netty的性能就必定比tomcat性能高,其实否则,tomcat从6.x开始就支持了nio模式,而且后续还有APR模式——一种经过jni调用apache网络库的模式,相比于旧的bio模式,并发性能获得了很大提升,特别是APR模式,而netty是否比tomcat性能更高,则要取决于netty程序做者的技术实力了。git
如第一部分所述,netty是一款收到大公司青睐的框架,在我看来,netty可以受到青睐的缘由有三:web
Netty是一款基于NIO(Nonblocking I/O,非阻塞IO)开发的网络通讯框架,对比于BIO(Blocking I/O,阻塞IO),他的并发性能获得了很大提升,两张图让你了解BIO和NIO的区别:
redis
Selector
。
除了BIO和NIO以外,还有一些其余的IO模型,下面这张图就表示了五种IO模型的处理流程:数据库
以上摘自Linux IO模式及 select、poll、epoll详解apache
Netty的传输快其实也是依赖了NIO的一个特性——零拷贝。咱们知道,Java的内存有堆内存、栈内存和字符串常量池等等,其中堆内存是占用内存空间最大的一块,也是Java对象存放的地方,通常咱们的数据若是须要从IO读取到堆内存,中间须要通过Socket缓冲区,也就是说一个数据会被拷贝两次才能到达他的的终点,若是数据量大,就会形成没必要要的资源浪费。
Netty针对这种状况,使用了NIO中的另外一大特性——零拷贝,当他须要接收数据的时候,他会在堆内存以外开辟一块内存,数据就直接从IO读到了那块内存中去,在netty里面经过ByteBuf能够直接对这些数据进行直接操做,从而加快了传输速度。
下两图就介绍了两种拷贝方式的区别,摘自Linux 中的零拷贝技术,第 1 部分
编程
上文介绍的ByteBuf是Netty的一个重要概念,他是netty数据处理的容器,也是Netty封装好的一个重要体现,将在下一部分作详细介绍。segmentfault
要说Netty为何封装好,这种用文字是说不清的,直接上代码:
public class PlainOioServer { public void serve(int port) throws IOException { final ServerSocket socket = new ServerSocket(port); //1 try { for (;;) { final Socket clientSocket = socket.accept(); //2 System.out.println("Accepted connection from " + clientSocket); new Thread(new Runnable() { //3 @Override public void run() { OutputStream out; try { out = clientSocket.getOutputStream(); out.write("Hi!\r\n".getBytes(Charset.forName("UTF-8"))); //4 out.flush(); clientSocket.close(); //5 } catch (IOException e) { e.printStackTrace(); try { clientSocket.close(); } catch (IOException ex) { // ignore on close } } } }).start(); //6 } } catch (IOException e) { e.printStackTrace(); } } } 做者:追那个小女孩 连接:https://www.jianshu.com/p/b9f3f6a16911 來源:简书 简书著做权归做者全部,任何形式的转载都请联系做者得到受权并注明出处。
public class PlainNioServer { public void serve(int port) throws IOException { ServerSocketChannel serverChannel = ServerSocketChannel.open(); serverChannel.configureBlocking(false); ServerSocket ss = serverChannel.socket(); InetSocketAddress address = new InetSocketAddress(port); ss.bind(address); //1 Selector selector = Selector.open(); //2 serverChannel.register(selector, SelectionKey.OP_ACCEPT); //3 final ByteBuffer msg = ByteBuffer.wrap("Hi!\r\n".getBytes()); for (;;) { try { selector.select(); //4 } catch (IOException ex) { ex.printStackTrace(); // handle exception break; } Set<SelectionKey> readyKeys = selector.selectedKeys(); //5 Iterator<SelectionKey> iterator = readyKeys.iterator(); while (iterator.hasNext()) { SelectionKey key = iterator.next(); iterator.remove(); try { if (key.isAcceptable()) { //6 ServerSocketChannel server = (ServerSocketChannel)key.channel(); SocketChannel client = server.accept(); client.configureBlocking(false); client.register(selector, SelectionKey.OP_WRITE | SelectionKey.OP_READ, msg.duplicate()); //7 System.out.println( "Accepted connection from " + client); } if (key.isWritable()) { //8 SocketChannel client = (SocketChannel)key.channel(); ByteBuffer buffer = (ByteBuffer)key.attachment(); while (buffer.hasRemaining()) { if (client.write(buffer) == 0) { //9 break; } } client.close(); //10 } } catch (IOException ex) { key.cancel(); try { key.channel().close(); } catch (IOException cex) { // 在关闭时忽略 } } } } } } 做者:追那个小女孩 连接:https://www.jianshu.com/p/b9f3f6a16911 來源:简书 简书著做权归做者全部,任何形式的转载都请联系做者得到受权并注明出处。
public class NettyOioServer { public void server(int port) throws Exception { final ByteBuf buf = Unpooled.unreleasableBuffer( Unpooled.copiedBuffer("Hi!\r\n", Charset.forName("UTF-8"))); EventLoopGroup group = new OioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); //1 b.group(group) //2 .channel(OioServerSocketChannel.class) .localAddress(new InetSocketAddress(port)) .childHandler(new ChannelInitializer<SocketChannel>() {//3 @Override public void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new ChannelInboundHandlerAdapter() { //4 @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { ctx.writeAndFlush(buf.duplicate()).addListener(ChannelFutureListener.CLOSE);//5 } }); } }); ChannelFuture f = b.bind().sync(); //6 f.channel().closeFuture().sync(); } finally { group.shutdownGracefully().sync(); //7 } } } 做者:追那个小女孩 连接:https://www.jianshu.com/p/b9f3f6a16911 來源:简书 简书著做权归做者全部,任何形式的转载都请联系做者得到受权并注明出处。
从代码量上来看,Netty就已经秒杀传统Socket编程了,可是这一部分博大精深,仅仅贴几个代码岂能说明问题,在这里给你们介绍一下Netty的一些重要概念,让你们更理解Netty。
Channel
数据传输流,与channel相关的概念有如下四个,上一张图让你了解netty里面的Channel。
他有三种使用模式:
除此以外,他还提供一大堆api方便你使用,在这里我就不一一列出了,具体参见ByteBuf字节缓存。
Netty入门教程2——动手搭建HttpServer
Netty入门教程3——Decoder和Encoder
Netty入门教程4——如何实现长链接
以上就是我对《Netty实战》这本书的一些心得和书外的一些相关知识整理,若是有不一样的看法,欢迎讨论!