netty是如何一步一步演化而來的,不斷進化成就netty

這是我參與更文挑戰的第14天,活動詳情查看: 更文挑戰java

簡介

  • 隨着IO類庫不斷完善,咱們基於Java的網絡編程學習成本愈來愈低。以前網絡開發都是底層語言編寫的,像C語言,C++。其實經過Java也能夠編寫。Java的io類基於tcp鏈接提供的socket就是咱們實現網絡開發的橋段。可是由於是同步阻塞式,因此效率上來講就慢了不少。在Java7以後提供了異步阻塞式io。爲咱們拉開了一個新天地。

BIO

  • 什麼是網絡編程?所謂的網絡編程其實就是C/S模型。你們都知道Java是開發B/S模型的。C/S實際是client和server端的開發。說白就是兩個進程相互通訊。client經過tcp鏈接server而後交互數據
  • BIO 即同步阻塞是編程。在JDK1.4以前咱們網絡鏈接都是採用的BIO模式,服務端經過ServerSocket(port)構建服務端。而後服務端經過accept方法阻塞式等待客戶端的鏈接。客戶端經過Socket(ip,port)構建客戶端。而後經過PrintWriter傳遞消息。
  • 默認狀況下BIO模式是一個客戶端對應一個線程。這樣對於內存的消耗是嚴重的。慢慢的這種方式也就被拋棄了。

001.jpg

server

  • 爲了緩解線程壓力。這裏構造線程池。初始大小5個
private static final ThreadLocal<ExecutorService> executorService = new ThreadLocal<ExecutorService>() {
    @Override
    protected ExecutorService initialValue() {
        return Executors.newFixedThreadPool(5);
    }
};

複製代碼
  • 而後是構建ServerSocket。而後就是一直在阻塞式等待客戶端的鏈接。什麼叫阻塞式等待。就是一直在等待客戶端鏈接。沒有新的客戶端鏈接就不繼續執行。當客戶端鏈接後。accept就會返回當前客戶端的鏈接對象。而後咱們將他進行封裝後放到線程池中。線程池中有可用資源就會將可用線程加載這個客戶端對象。
public static void start() throws IOException {
    try {
        // 經過構造函數建立ServerSocket
        server = new ServerSocket(HostConstant.PORT);
        System.out.println("服務器已啓動,端口號:" + HostConstant.PORT);
        while (true) {
            // 真正處理的仍是Socket
            Socket socket = server.accept();// 阻塞方法
            // 把客戶端請求打包成一個任務,放到線程池執行
            executorService.get().execute(new ServerHandler(socket));
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (server != null) {
            server.close();
        }
    }

}

複製代碼
public class ServerHandler implements Runnable {
    private Socket socket;

    public ServerHandler(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        try (BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()))) {
            PrintWriter pw = new PrintWriter(socket.getOutputStream(), true);
            String message;
            String result;
            // 經過輸入流讀取客戶端傳輸的數據
            while ((message = br.readLine()) != null) {
                System.out.println("server receive data:" + message);
                result = response(message);
                // 將業務結果經過輸出流返回給客戶端
                pw.println(result);
            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (socket != null) {
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                socket = null;
            }
        }
    }

    // 返回給客戶端的應答
    public static String response(String msg) {
        return "Hello," + msg + ",Now is " + new java.util.Date(System.currentTimeMillis()).toString();
    }
}

複製代碼

##client編程

public class BIOClient {
    public void startConnect() {
        try {
            Socket socket = new Socket(HostConstant.IP, HostConstant.PORT);
            new ReadMsg(socket).start();
            PrintWriter pw = null;
            // 寫數據到服務端
            pw = new PrintWriter(socket.getOutputStream());
            pw.println(UUID.randomUUID());
            pw.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static class ReadMsg extends Thread {
        Socket socket;

        public ReadMsg(Socket socket) {
            this.socket = socket;
        }

        @Override
        public void run() {
            try (BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()))) {
                String line = null;
                // 經過輸入流讀取服務端傳輸的數據
                while ((line = br.readLine()) != null) {
                    System.out.printf("%s\n", line);
                }

            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    public static void main(String[] args) {
        Integer time = 6;
        for (Integer i = 0; i < time; i++) {
            new BIOClient().startConnect();
        }
    }
}

複製代碼

缺點

  • 上面服務端和客戶端其實是僞異步。表面上看起來不會由於客戶端的增長致使內存溢出。可是由於實際上仍是accpet同步阻塞等待。因此在鏈接性能上仍是很差。服務器

  • 咱們在ServerHanndler中是讀取客戶端傳輸的數據經過BufferedReader.readLine這個方法。咱們跟蹤下去發現實際調用的是InputStream.read這個方法。markdown

002.jpg

/** * Reads the next byte of data from the input stream. The value byte is * returned as an <code>int</code> in the range <code>0</code> to * <code>255</code>. If no byte is available because the end of the stream * has been reached, the value <code>-1</code> is returned. This method * blocks until input data is available, the end of the stream is detected, * or an exception is thrown. * * <p> A subclass must provide an implementation of this method. * * @return the next byte of data, or <code>-1</code> if the end of the * stream is reached. * @exception IOException if an I/O error occurs. */
public abstract int read() throws IOException;

複製代碼
  • 咱們在翻閱他們的源碼說明,會發現上面註釋大意爲:當沒有字節的時候意味着已經結束了咱們會返回-1。這個方法一直阻塞知道獲取到字節或者是結束或者是拋出異常。咱們以前也說了服務端等待鏈接的時候是阻塞式等待。這會形成客戶端鏈接的一些問題。可是客戶端鏈接上之後開始通訊了。服務端獲取客戶端的消息也是採用阻塞式等待的。這會直接形成交互等待從而形成交互擁堵。換句話說客戶端A發送了100條消息。服務端會一條一條處理。像食堂打菜排隊同樣。
  • 一樣的道理OutputStream也是阻塞式的。這裏有興趣的讀者能夠自行翻閱源碼查看。這也說明咱們鏈接是阻塞的。通訊也是阻塞的。這就是BIO暴露的缺點。
  • 由於阻塞,會形成後期客戶端的接入沒法成功,會一直等待。形成鏈接超時。

NIO

NIO=Non Block IO .即非阻塞式編程。網絡

  • 在BIO中咱們經過ServerSocket、Socket實現服務端和客戶端的開發。在NIO中對應提供了ServerSocketChannel、SocketChannel兩個類實現通訊。這兩個類支持阻塞式和非阻塞式模式。阻塞模式這裏不作介紹形成的後果由和BIO同樣。下面咱們來看看如何實現NIO

##ByteBuffer框架

  • ByteBuffer使咱們NIO通訊的一個緩衝區,咱們的讀寫都是藉助與它傳遞的。由於他提供了相似指針指向,咱們操做指向就能夠獲取到字節。

##Channeldom

  • 在NIO中他被看作是一個通道。經過Channel控制讀和寫。BIO中是經過Stream方式傳遞的。Channel和Stream相比最大的特色Channel是雙向的。簡單的理解就是讀用Channel,寫也用Channel。在BIO中InputStream和OutputStream分別負責讀和寫。上面提到的ServerSocketChannelSocketChannel都是Channel的子類。

##Selector異步

-多路複用器。這裏咱們能夠理解爲註冊中心。全部的handler再向selector註冊的時候會帶上標籤(SelectorKey)。在某個Channel發生讀或寫的事件時這個Channel會處於就緒狀態。在Selector輪詢的時候會篩選出來。而後咱們在根據SelectorKey判斷監聽的是什麼事件。從而作出處理。查閱資料得知selector沒有鏈接限制。理論上一個selector能夠管理N個Channel。socket

003.jpg

  • 上面是服務器的開發流程。其實就是用一個線程來管理selector。而後不停遍歷selector而後捕獲事件。服務端和客戶端捕獲不一樣的事件。下面大概列舉了下事件

004.jpg

  • 框架搭建就是流程上的,剩下的就是咱們在selectorKey這個事件上進行業務的io處理。在讀寫期間就是ByteBuffer來實現。NIO的實現和BIO相對比較複雜。

進化

關於NIO2.0 和nettry 他們在次基礎上進行提高!不得不說如今基本上是netty的天下了。下章節咱們針對netty來展開討論!今天端午節不說了我吃糉子去了tcp

記得吃完糉子,回來點個贊哦!!!

相關文章
相關標籤/搜索