1. 程式人生 > 其它 >spring 使用easypoi進行Excel匯出的簡要操作

spring 使用easypoi進行Excel匯出的簡要操作

技術標籤:微服務java網路netty分散式

Netty

一、Netty介紹和應用場景

1.1 Netty的介紹

  • Netty是由JBOSS提供的一個Java開源框架,現為Github
    上的獨立專案。
  • Netty是一個非同步的、基於事件驅動的網路應用框架,用以快速開發高效能、高可靠性的網路IO程式。
  • Netty主要針對在TCP協議下,面向Clients端的高併發應用,或者Peer-to-Peer場景下的大量資料持續傳輸的應用。
  • Netty本質是一個NIO框架,適用於伺服器通訊相關的多種應用場景。

1.2 Netty的應用場景

1.2.1 網際網路行業
  • 網際網路行業:在分散式系統中,各個節點之間需要遠端服務呼叫,高效能的RPC框架必不可少,Netty作為非同步高效能的通訊框架,往往作為基礎通訊元件被這些RPC框架使用。
  • 典型的應用有:阿里分散式服務框架DubboRPC框架使用Dubbo
    協議進行節點間通訊,Dubbo協議預設使用Netty作為基礎通訊元件,用於實現各程序節點之間的內部通訊。
1.2.2 遊戲行業
  • 無論是手遊服務端還是大型的網路遊戲,Java語言得到了越來越廣泛的應用。
  • Netty作為高效能的基礎通訊元件,提供了TCP/UDPHTTP協議棧,方便定製和開發私有協議棧,賬號登入伺服器。
  • 地圖伺服器之間可以方便的通過Netty進行高效能的通訊。
1.2.3 大資料領域
  • 經典的Hadoop的高效能通訊和序列化元件AvroRPC框架,預設採用Netty進行跨界點通訊。
  • 它的NettyService基於Netty框架二次封裝實現。

三、JavaBIO程式設計

2.1 I/O模型

  • I/O模型簡單的理解:就是用什麼樣的通道進行資料的傳送和接收,很大程度上決定了程式通訊的效能。
  • Java共支援3種網路程式設計模型I/O模式:BIONIOAIO
  • JavaBIO:同步並阻塞(傳統阻塞型),伺服器實現模式為一個連線一個執行緒,即客戶端有連線請求時伺服器端就需要啟動一個執行緒進行處理,如果這個連線不做任何事情會造成不必要的執行緒開銷。
  • JavaNIO:同步非阻塞,伺服器實現模式為一個執行緒處理多個請求(連線),即客戶端傳送的連線請求都會註冊到多路複用器上,多路複用器輪詢到連線有I/O請求就進行處理。
  • JavaAIO(NIO.2):非同步非阻塞,AIO引入非同步通道的概念,採用了Proactor模式,簡化了程式編寫,有效的請求才啟動執行緒,它的特點是先由作業系統完成後才通知服務端程式啟動執行緒去處理,一般適用於連線數較多且連線時間較長的應用。

2.2 BIO、NIO、AIO適用場景分析

  • BIO方式適用於連線數目比較小且固定的架構,這種方式對伺服器資源要求比較高,併發侷限於應用中,JDK1.4以前的唯一選擇,但程式簡單易理解。
  • NIO方式適用於連線數目多且連線比較短(輕操作)的架構,比如聊天伺服器,彈幕系統,伺服器間通訊等。程式設計比較複雜,JDK1.4開始支援。
  • AIO方式使用於連線數目多且連線比較長(重操作)的架構,比如相簿伺服器,充分呼叫OS參與併發操作,程式設計比較複雜,JDK7開始支援。

2.3 JavaBIO基本介紹

  • JavaBIO就是傳統的javaio程式設計,其相關的類和介面在java.io
  • BIO(blockingI/O):同步阻塞,伺服器實現模式為一個連線一個執行緒,即客戶端有連線請求時伺服器端就需要啟動一個執行緒進行處理,如果這個連線不做任何事情會造成不必要的執行緒開銷,可以通過執行緒池機制改善(實現多個客戶連線伺服器)。
  • BIO方式適用於連線數目比較小且固定的架構,這種方式對伺服器資源要求比較高,併發侷限於應用中,JDK1.4以前的唯一選擇,程式簡單易理解。

2.4 JavaBIO工作機制

  • 伺服器端啟動一個ServerSocket
  • 客戶端啟動Socket對伺服器進行通訊,預設情況下伺服器端需要對每個客戶建立一個執行緒與之通訊。
  • 客戶端發出請求後,先諮詢伺服器是否有執行緒響應,如果沒有則會等待,或者被拒絕。
  • 如果有響應,客戶端執行緒會等待請求結束後,在繼續執行。

2.5 JavaBIO應用例項

  • 使用BIO模型編寫一個伺服器端,監聽6666埠,當有客戶端連線時,就啟動一個執行緒與之通訊。
  • 要求使用執行緒池機制改善,可以連線多個客戶端。
  • 伺服器端可以接收客戶端傳送的資料(telnet 方式即可)。
public class BIOServer {
    public static void main(String[] args) throws Exception {
        //1.建立一個執行緒池
        //2.如果有客戶端連線,就建立一個執行緒,與之通訊(單獨寫一個方法)
        ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
        //建立ServerSocket
        ServerSocket serverSocket = new ServerSocket(6666);
        System.out.println("伺服器啟動了");
        while (true) {
            System.out.println("執行緒資訊id=" + Thread.currentThread().getId() + "名字=" +
                    Thread.currentThread().getName());
            //監聽,等待客戶端連線
            System.out.println("等待連線....");
            final Socket socket = serverSocket.accept();
            System.out.println("連線到一個客戶端");
            //就建立一個執行緒,與之通訊(單獨寫一個方法)
            newCachedThreadPool.execute(new Runnable() {
                public void run() {
                    //我們重寫
                    //可以和客戶端通訊
                    handler(socket);
                }
            });
        }
    }

    /**
     * 編寫一個handler方法,和客戶端通訊
     * @param socket
     */
    public static void handler(Socket socket) {
        try {
            System.out.println("執行緒資訊id=" + Thread.currentThread().getId() + "名字=" +
                    Thread.currentThread().getName());
            byte[] bytes = new byte[1024];
            //通過socket獲取輸入流
            InputStream inputStream = socket.getInputStream();
            //迴圈的讀取客戶端傳送的資料
            while (true) {
                System.out.println("執行緒資訊id=" + Thread.currentThread().getId() + "名字=" +
                        Thread.currentThread().getName());
                System.out.println("read....");
                int read = inputStream.read(bytes);
                if (read != -1) {
                    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();
            }
        }
    }
}
//啟動結果
伺服器啟動了
執行緒資訊id=1名字=main
等待連線....
//進入命令列視窗 telnet 127.0.0.1 6666
連線到一個客戶端
執行緒資訊id=1名字=main
等待連線....
執行緒資訊id=12名字=pool-1-thread-1
執行緒資訊id=12名字=pool-1-thread-1
read....
//在客戶端輸入 ctrl+]
//send client1
client1
執行緒資訊id=12名字=pool-1-thread-1
read....
//send client ok
client ok
執行緒資訊id=12名字=pool-1-thread-1
read....

//新開啟一個命令列視窗 telnet 127.0.0.1 6666
連線到一個客戶端
執行緒資訊id=1名字=main
等待連線....
執行緒資訊id=13名字=pool-1-thread-2
執行緒資訊id=13名字=pool-1-thread-2
read....
//在客戶端輸入 ctrl+]
//send client1
client2
執行緒資訊id=13名字=pool-1-thread-2
read....
client2 ok
執行緒資訊id=13名字=pool-1-thread-2
read....

2.6 JavaBIO問題分析

  • 每個請求都需要建立獨立的執行緒,與對應的客戶端進行資料Read,業務處理,資料Write
  • 當併發數較大時,需要建立大量執行緒來處理連線,系統資源佔用較大。
  • 連線建立後,如果當前執行緒暫時沒有資料可讀,則執行緒就阻塞在Read操作上,造成執行緒資源浪費。

三、JavaNIO程式設計

3.1 JavaNIO基本介紹

  • JavaNIO全稱java non-blockingIO,是指JDK提供的新API。從JDK1.4開始,Java提供了一系列改進的輸入/輸出的新特性,被統稱為NIO(即NewIO),是同步非阻塞的。

  • NIO相關類都被放在java.nio包及子包下,並且對原java.io包中的很多類進行改寫。

  • NIO有三大核心部分:Channel(通道),Buffer(緩衝區),Selector(選擇器)。

  • NIO是面向緩衝區,或者面向塊程式設計的。資料讀取到一個它稍後處理的緩衝區,需要時可在緩衝區中前後移動,這就增加了處理過程中的靈活性,使用它可以提供非阻塞式的高伸縮性網路。

  • JavaNIO的非阻塞模式,使一個執行緒從某通道傳送請求或者讀取資料,但是它僅能得到目前可用的資料,如果目前沒有資料可用時,就什麼都不會獲取,而不是保持執行緒阻塞,所以直至資料變的可以讀取之前,該執行緒可以繼續做其他的事情。非阻塞寫也是如此,一個執行緒請求寫入一些資料到某通道,但不需要等待它完全寫入,這個執行緒同時可以去做別的事情。

  • 通俗理解:NIO是可以做到用一個執行緒來處理多個操作的。假設有10000個請求過來,根據實際情況,可以分配50或者100個執行緒來處理。不像之前的阻塞IO那樣,非得分配10000個。

  • HTTP2.0使用了多路複用的技術,做到同一個連線併發處理多個請求,而且併發請求的數量比HTTP1.1大了好幾個數量級。

    public class BasicBuffer {
        public static void main(String[] args) {
            //舉例說明Buffer的使用(簡單說明)
            //建立一個Buffer,大小為5,即可以存放5個int
            IntBuffer intBuffer = IntBuffer.allocate(5);
            //向buffer存放資料
            for (int i = 0; i < intBuffer.capacity(); i++) {
                intBuffer.put(i * 2);
            }
            //如何從buffer讀取資料
            //將buffer轉換,讀寫切換(!!!)
            intBuffer.flip();
            while (intBuffer.hasRemaining()) {
                System.out.println(intBuffer.get());
            }
        }
    }
    //結果
    0
    2
    4
    6
    8