netty权威指南学习笔记一——NIO入门(1)BIO
公司的一些项目采用了netty框架,为了加速适应公司开发,本博主认真学习netty框架,前一段时间主要看了看书,发现编程这东西,不上手还是觉得差点什么,于是为了加深理解,深入学习,本博主还是决定多动手,一方面记录一些总结性的思考性东西,另一方面也为日后复习查看留下一些东西。
那么废话少说,现在开始,首先在进入netty学习之前,我们是要认识一下IO并引出NIO的概念。
与IO相关的几种编程及特点分为:
编程类别 | 特点 |
传统BIO编程 |
典型的一请求一应答模型,一个请求新建一个线程,应答完成线程销毁,线程个数和客户端并发访问呈现1:1的正比关系,当线程数膨胀之后,虚拟机性能下降明显,随着并发量继续增大,线程耗尽就会出现线程堆栈溢出,创建新线程失败,并最终宕机。该模型无法满足高性能高并发的场景。 |
伪异步IO编程 |
伪异步IO是对BIO的优化,引入线程池和消息队列,改善了传统BIO编程,但是其底层仍然是BIO模型,该模型引入线程池后界定了线程的数量,N个线程处理M个客户端请求,M可以远远大于N,线程池灵活调配线程资源,有最大线程数限制,避免了一个客户端请求创建一个线程的弊端,不会发生高并发访问下的线程资源耗尽而造成堆栈溢出或宕机的问题。 其不利因素是当一个客户端请求没有处理完成时,如IO读写或网络慢时,会造成任务队列中的任务一直处于等待状态,被阻塞了。当线程池中所有线程都满程处于阻塞状态时,就无法接收客户端新的请求,请求总是超时,此时可以认为是系统崩溃。 |
NIO编程 |
NIO模型引入了缓冲区Buffer,所有数据的读写都是用缓冲区处理的。引入channel通道,和selector多路复用器。 客户端发起的连接操作是异步的,可以通过在多路复用器注册OP_CONNECT等待后续结果,不需要像之前的客户端那样被同步阻塞, socketchannel的读写都是一步的,如果没有可以读写的数据他不会同步等待,直接返回,这样I/O通信线程就可以处理其他的链路,不需要同步等待这个链路可用; Selector线程可以同时处理成千上万个客户端连接,而且性能不会随客户端的增加而线性下降,非常适合高性能、高负载的网络服务器。 |
AIO编程 |
AIO是NIO2.0,它引入了异步通道概念,提供了异步文件通道和异步套接字通道的实现。异步通道提供了java.util.concurrent.Future类来表示异步操作的结果。 在执行异步操作时候传入一个java.io.channels. CompletionHandler接口的实现类作为操作完成的回调。 NIO2.0的异步套接字通道是真正的异步非阻塞I/O,它不需要通过多路复用器Selector对注册的通道进行轮询操作即可实现异步读写,从而简化了NIO的编程模型。 |
现在看看逐个看看相关模型和代码:
BIO模型
传统的BIO代码 时间服务端
package com.example.biodemo; import java.io.*; import java.net.ServerSocket; import java.net.Socket; public class TimeServer { public static void main(String[] args) throws IOException { int port = 8090; if (args != null && args.length > 0) { try { port = Integer.valueOf(args[0]); } catch (NumberFormatException e) { port = 8090; } } ServerSocket server = null; try { server = new ServerSocket(port); System.out.println("the timeServer is start in port :" + port); Socket socket = null; while (true) { socket = server.accept(); new Thread(new TimeServerHandler(socket)).start(); } } finally { if (server != null) { System.out.println("the time server close"); server.close(); server = null; } } } }
传统BIO 时间处理
package com.example.biodemo; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.Socket; public class TimeServerHandler implements Runnable { private Socket socket; public TimeServerHandler(Socket socket) { this.socket = socket; } @Override public void run() { BufferedReader in = null; PrintWriter out = null; try { in = new BufferedReader(new InputStreamReader(socket.getInputStream())); out = new PrintWriter(this.socket.getOutputStream(), true); String currentTime = null; String body = null; while (true) { body = in.readLine(); if (body == null) { break; } System.out.println("the time server receive order:" + body); currentTime = "QUERY TIME ORDER".equalsIgnoreCase(body) ? new java.util.Date(System.currentTimeMillis()).toString() : "bad order"; out.println(currentTime); } } catch (Exception e) { if (in != null) { try { in.close(); } catch (IOException el) { el.printStackTrace(); } } if (out != null) { out.close(); out = null; } if (this.socket != null) { try { this.socket.close(); } catch (IOException e1) { e1.printStackTrace(); } this.socket = null; } } } }
客户端代码
1 package com.example.biodemo; 2 3 import java.io.*; 4 import java.net.Socket; 5 6 public class TimeClient { 7 public static void main(String[] args) { 8 int port = 8090; 9 if (args != null && args.length > 0) { 10 try { 11 port = Integer.valueOf(args[0]); 12 } catch (NumberFormatException ne) { 13 port = 8090; 14 } 15 } 16 Socket socket = null; 17 BufferedReader in = null; 18 PrintWriter out = null; 19 try { 20 socket = new Socket("127.0.0.1", port); 21 System.out.println(socket.getInputStream()); 22 in = new BufferedReader(new InputStreamReader(socket.getInputStream())); 23 out = new PrintWriter(socket.getOutputStream(), true); 24 out.println("QUERY TIME ORDER"); 25 System.out.println("send order 2 server succeed."); 26 String resp = in.readLine(); 27 System.out.println("now is :" + resp); 28 } catch (IOException e1) { 29 30 } finally { 31 if (out != null) { 32 out.close(); 33 out = null; 34 } 35 36 if (in != null) { 37 try { 38 in.close(); 39 } catch (IOException e2) { 40 e2.printStackTrace(); 41 } 42 in = null; 43 if (socket != null) { 44 try { 45 socket.close(); 46 } catch (IOException e3) { 47 e3.printStackTrace(); 48 } 49 50 } 51 socket = null; 52 } 53 54 55 } 56 } 57 }
启动服务端
启动客户端
再看看服务端
整个过程是:服务端先在某一端口启动服务,本博主是在8090上启动,启动后,客户端启动,发送请求,请求被服务端接收后,进行逻辑处理,并将逻辑处理结果返回,这里的逻辑处理结果是当前时间,返回的结果客户端接收后显示出来。这就是一个典型的BIO模型。