公司的一些項目采用了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模型。