首頁 > 軟體

Java筆記之從IO模型到Netty框架學習初識篇

2022-03-18 16:00:23

什麼是Netty

  • 非同步,基於事件驅動的網路應用框架,用以快速開發高效能,高可靠的網路IO程式
  • 主要針對在TCP協定下,面向Clients端的高並行應用
  • 本質是一個NIO框架,適用於伺服器通訊等場景

非同步:傳送請求無需等待響應,程式接著往下走。

事件驅動:一個連線事件或者斷開事件,或者讀事件或者寫事件,發生後的後續處理。

Netty典型應用:

  • 高效能rpc框架用來遠端服務(過程)呼叫,比如Dubbo。
  • 遊戲行業,頁面資料互動。
  • 巨量資料領域如Hadoop高效能通訊和序列化元件(AVRO)。

IO模型

簡單理解就是用什麼通道去進行資料傳送和接收。

BIO:一個連線一個執行緒,連線不做任何事會造成不必要的執行緒開銷。適用於連線數目較小且固定的架構。

NIO:伺服器端一個執行緒(也可以多個),維護一個多路複用器。由多路複用器去處理IO執行緒。適用於連線數目多且較短的架構

AIO:非同步非阻塞,還未得到廣泛應用。適用於連線數目多且連線較長的架構。

BIO

BIO程式設計簡單流程

  • 伺服器端建立啟動ServerSocket
  • 使用者端啟動Socket對伺服器進行通訊,預設伺服器會對每一個客戶建立一個執行緒。
  • 使用者端發出請求後,先諮詢執行緒是否有響應,如果沒有則等待或者拒絕。
  • 如果有響應,則等待請求結束後,再繼續執行。(阻塞)

BIO簡單範例

public class BIOserver {
    public static void main(String[] args) throws IOException {
        // 為了方便直接用了Executors建立執行緒池
        ExecutorService service = Executors.newCachedThreadPool();
        //指定伺服器端埠
        ServerSocket serverSocket = new ServerSocket(6666);
        System.out.println("伺服器啟動");
        while(true){
            //阻塞等待連線
            Socket socket = serverSocket.accept();
            System.out.println("連線到一個使用者端");
            //每個連線對應一個執行緒
            service.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        handler(socket);
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                }
            });
        }
    }
    public static void handler(Socket socket) throws IOException {
        System.out.println("Thread:"+Thread.currentThread().getId());
        byte[] bytes = new byte[1024];
        InputStream inputStream = socket.getInputStream();
        while (true){
            //阻塞等待讀取
            int n = inputStream.read(bytes);
            if(n!=-1){
                System.out.println(new String(bytes,0,n));
            }else {
                break;
            }
        }
        socket.close();
    }
}

測試:使用windows的telnet

使用 ctrl+]

 可以在伺服器端控制檯看到,已經讀取到傳送的資料

NIO

三大核心部分:Channel(可類比Socket),Buffer,Selector

大概是這個樣子。使用者端和Buffer互動,Buffer和Channel是一對一的關係。Selector選擇操作Channel(事件驅動,如果Channel有事件發生,Selector才去選擇操作。)

Buffer

Buffer基本使用

ByteBuffer使用場景較為廣泛。

buffer就是一個記憶體塊,所以說nio是面向塊/緩衝,底層是陣列。資料讀寫是通過buffer。可以使用方法flip切換讀寫。

public class BufferNio {
    public static void main(String[] args) {
        //建立buffer容量為5個int
        IntBuffer buffer = IntBuffer.allocate(5);
        //放資料
        buffer.put(1);
        buffer.put(2);
        buffer.put(3);
        buffer.put(4);
        buffer.put(5);
        //讀寫切換
        buffer.flip();
        //取資料
        //內部維護一個索引,每次get索引都會往後邊移動
        while(buffer.hasRemaining()){
            System.out.println(buffer.get());
        }
    }
}

Buffer四個主要屬性

    // Invariants: mark <= position <= limit <= capacity
    private int mark = -1;
    private int position = 0;
    private int limit;
    private int capacity;

mark:標記,很少改變

position:下一個要被讀元素的位置,為下次讀寫做準備

limit:緩衝器當前的終點,不能對緩衝區極限意外的區域讀寫,可變。

capacity:不可變,建立時指定的最大容量。

上邊出現了讀寫切換的方法flip,我們看下原始碼,可以看出來通過改變屬性實現可讀可寫的。

    public final Buffer flip() {
        limit = position;
        position = 0;
        mark = -1;
        return this;
    }

可以通過啊更改limit或者position來實現你想要的操作。引數自己決定

        buffer.limit(2);
        buffer.position(1);

Channel

可讀可寫,上接Selector,下連Buffer。

當用戶端連線ServerSocketChannel時,建立使用者端自己的SocketChannel。

本地檔案寫案例

public class ChannelNio {
    public static void main(String[] args) throws IOException {
        String str = "少壯不努力,老大徒傷悲";
        //建立輸出流
        FileOutputStream os = new FileOutputStream("D:\xxxxxxxxxxxxxxxxxxx\a.txt");
        //獲取FileChannel
        FileChannel channel = os.getChannel();
        //建立緩衝
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        //把字串放入緩衝區
        buffer.put(str.getBytes());
        //反轉ByteBuffer
        buffer.flip();
        //將ByteBuffer寫入到FileChannel
        channel.write(buffer);
        //關閉流
        os.close();
    }
}

圖示理解

本地檔案讀案例

public class ChannelNio {
    public static void main(String[] args) throws IOException {
        FileInputStream is = new FileInputStream("D:\xxxxxxxxxxxxxxxxxxx\a.txt");
        FileChannel channel = is.getChannel();
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        channel.read(buffer);
        System.out.println(new String(buffer.array()));
        is.close();
    }
}

本地檔案拷貝案例

方法一

public class ChannelNio {
    public static void main(String[] args) throws IOException {
        FileInputStream is = new FileInputStream("D:\xxxxxxxxxxxxxxxxxxx\a.txt");
        FileChannel channel = is.getChannel();
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        FileOutputStream os = new FileOutputStream("D:\xxxxxxxxxxxxxxxxxxx\b.txt");
        FileChannel osChannel = os.getChannel();
        while (true){
            buffer.clear();
            int i = channel.read(buffer);
            if(i==-1){
                break;
            }
            buffer.flip();
            osChannel.write(buffer);
        }
        is.close();
        os.close();
    }
}

方法二

public class ChannelNio {
    public static void main(String[] args) throws IOException {
        FileInputStream is = new FileInputStream("D:\xxxxxxxxxxxxxxxxxxx\HELP.md");
        FileChannel channel = is.getChannel();
        FileOutputStream os = new FileOutputStream("D:\xxxxxxxxxxxxxxxxxxx\HELP222.md");
        FileChannel osChannel = os.getChannel();
        osChannel.transferFrom(channel,0,channel.size());
        is.close();
        os.close();
    }
}

Selector

用一個執行緒處理多個使用者端連線。可以檢測多個註冊通道的事件,並作出相應處理。不用維護所有執行緒。

Selector可以獲得被註冊的SocketChannel的一個SelectionKey集合,然後監聽select,獲得有事件發生的SelectionKey,最後通過SelectionKey獲得通道進行相應操作,完成業務。

到此這篇關於Java筆記之從IO模型到Netty框架學習初識篇的文章就介紹到這了,更多相關Java Netty框架內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com