001-核心技術-IO模型簡介、BIO簡介及示例

1、概述

  基礎:java編程、OOP編程、多線程、IO編程、網絡編程、經常使用設計模式(觀察者、命令、職責鏈等)、經常使用數據結構(鏈表等)java

1.一、簡介

  netty是一個java開源框架,是一個異步的,基於事件驅動的網絡應用框架,用於快速開發高性能、高可靠性的網絡IO程序。編程

  主要是針對在TCP協議下,面向Clients端的高併發應用,或者Peer-to-Peer場景下大量數據持續傳輸的應用。設計模式

  本質是一個NIO框架,適用於服務器通信相關的多種應用場景。 服務器

Netty
NIO【IO、網絡】
JDK 原生IO[網絡]
TCP/IP

1.二、應用場景

  互聯網:在分佈式系統中,各個節點之間須要遠程服務調用,高性能的RPC框架不可缺乏,Netty做爲異步高性能的通信框架,通常被做爲基礎通信組件被RPC框架使用。網絡

  典型應用:dubbo、網遊、大數據[AVRO數據文件共享]、Akka、Flink、Spark數據結構

  書籍:Netty in action、netty 權威指南多線程

2、IO模型

  IO模型簡單理解:就是用什麼樣的通道進行數據的發送和接收,很大程度上決定了程序通訊的性能併發

  java供支持3中網絡編程模型IO模式:BIO、NIO、AIO框架

  BIO:同步並阻塞(傳統阻塞型):服務器實現模式爲一個鏈接一個線程,即客戶端有鏈接請求時服務器端就須要啓動一個線程進行處理,若是這個鏈接不作任何事情會形成沒必要要的線程開銷。異步

  NIO:同步非阻塞,服務器實現模式爲一個線程處理多個請求(鏈接),即客戶端發送的鏈接請求都會註冊到多路複用器上,多路複用器輪詢到鏈接有IO請求就進行處理

  AIO【NIO2.0】:異步非阻塞,AIO引入異步通道的概念,採用了Proactor模式,簡化了程序編寫,有效的請求才啓動線程,他的特色是先有操做系統完成後成才通知服務端程序啓動線程去處理,通常適用於鏈接數多且鏈接時間較長的應用。

        

3、BIO簡介

   同步並阻塞(傳統阻塞型):服務器實現模式爲一個鏈接一個線程,即客戶端有鏈接請求時服務器端就須要啓動一個線程進行處理,若是這個鏈接不作任何事情會形成沒必要要的線程開銷。能夠經過線程池機制改善。(實現多個客戶鏈接服務器)

3.一、編程流程

  一、服務器端啓動一個ServerSocket

  二、客戶端自動啓動Socket對服務器進行通訊,默認狀況下服務器端須要對每一個客戶創建一個線程與之通訊

  三、客戶端發出請求後,先諮詢服務器是否有線程響應,若是沒有則會等待或者被拒絕

  四、若是有響應,客戶端線程會等待請求結束後,在繼續執行

3.二、 應用實例

  一、監聽6666端口,有鏈接啓動線程

  二、使用線程池機制改善,能夠鏈接多個客戶端

  三、客戶端使用telnet 測試

代碼:

public class BIOServer {
    public static void main(String[] args) throws IOException {
        //一、建立一個線程池
        //二、客戶端鏈接,啓動一個線程

        ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
        ServerSocket serverSocket = new ServerSocket(6666);

        System.out.println("服務啓動了:6666端口,telnet 127.0.0.1 6666");
        while (true) {
            System.out.println("等待鏈接……");
            final Socket socket = serverSocket.accept();

            System.out.println("一個客戶端被鏈接");
            newCachedThreadPool.execute(new Runnable() {
                @Override
                public void run() {
                    handler(socket);
                }
            });
        }
    }

    public static void handler(Socket socket) {
        try {
            System.out.println("Thread id:" + Thread.currentThread().getId() +";name:"+ Thread.currentThread().getName());
            byte[] bytes = new byte[1024];
            //經過socket獲取輸入流
            InputStream inputStream = socket.getInputStream();
            //循環讀取客戶端數據
            while (true) {

                System.out.println("等待讀取……");
                int read = inputStream.read(bytes);//將inputStream 讀取到 bytes
                if (read != -1) {
                    System.out.println("Thread id:" + Thread.currentThread().getId() +";name:"+ Thread.currentThread().getName());

                    System.out.println("輸出客戶端發送的數據");
                    System.out.println(new String(bytes, 0, read));
                } else {
                    break;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            System.out.println("關閉和client的鏈接");
            try {
                socket.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

}

測試 使用:

telnet 127.0.0.1 6666

3.三、BIO問題分析

  一、每一個請求都須要建立獨立的線程,與對應的客戶端進行數據Read,業務處理,數據Write

  二、當併發數較大時,須要建立大量線程來處理鏈接,系統資源佔用較大

  三、當鏈接創建後,若是當前線程暫時沒有數據可讀,則線程就阻塞在Read上,形成線程資源浪費 

相關文章
相關標籤/搜索