<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    gembin

    OSGi, Eclipse Equinox, ECF, Virgo, Gemini, Apache Felix, Karaf, Aires, Camel, Eclipse RCP

    HBase, Hadoop, ZooKeeper, Cassandra

    Flex4, AS3, Swiz framework, GraniteDS, BlazeDS etc.

    There is nothing that software can't fix. Unfortunately, there is also nothing that software can't completely fuck up. That gap is called talent.

    About Me

     

    Java I/O API之性能分析

    IO API的可伸縮性對(duì)Web應(yīng)用有著極其重要的意義。Java 1.4版以前的API中,阻塞I/O令許多人失望。從J2SE 1.4版本開始,Java終于有了可伸縮的I/O API。本文分析并計(jì)算了新舊I/O API在可伸縮性方面的差異。

      一、概述

    IO API的可伸縮性對(duì)Web應(yīng)用有著極其重要的意義。Java 1.4版以前的API中,阻塞I/O令許多人失望。從J2SE 1.4版本開始,Java終于有了可伸縮的I/O API。本文分析并計(jì)算了新舊IO API在可伸縮性方面的差異。Java向Socket寫入數(shù)據(jù)時(shí)必須調(diào)用關(guān)聯(lián)的OutputStream的write()方法。只有當(dāng)所有的數(shù)據(jù)全部寫入 時(shí),write()方法調(diào)用才會(huì)返回。倘若發(fā)送緩沖區(qū)已滿且連接速度很低,這個(gè)調(diào)用可能需要一段時(shí)間才能完成。如果程序只使用單一的線程,其他連接就必須 等待,即使那些連接已經(jīng)做好了調(diào)用write()的準(zhǔn)備也一樣。為了解決這個(gè)問題,你必須把每一個(gè)Socket和一個(gè)線程關(guān)聯(lián)起來;采用這種方法之后,當(dāng) 一個(gè)線程由于I/O相關(guān)的任務(wù)被阻塞時(shí),另一個(gè)線程仍舊能夠運(yùn)行。

      盡管線程的開銷不如進(jìn)程那么大,但是,考慮到底層的操作平臺(tái),線 程和進(jìn)程都屬于消耗大量資源的程序結(jié)構(gòu)。每一個(gè)線程都要占用一定數(shù)量的內(nèi)存,而且除此之外,多個(gè)線程還意味著線程上下文的切換,而這種切換也需要昂貴的資 源開銷。因此,Java需要一個(gè)新的API來分離Socket與線程之間過于緊密的聯(lián)系。在新的Java I/O API(java.nio.*)中,這個(gè)目標(biāo)終于實(shí)現(xiàn)了。

      本文分析和比較了用新、舊兩種I/O API編寫的簡(jiǎn)單Web服務(wù)器。由于作為Web協(xié)議的HTTP不再象原來那樣只用于一些簡(jiǎn)單的目的,因此這里介紹的例子只包含關(guān)鍵的功能,或者說,它們既不考慮安全因素,也不嚴(yán)格遵從協(xié)議規(guī)范。

      二、用舊API編寫的HTTP服務(wù)器

      首先我們來看看用舊式API編寫的HTTP服務(wù)器。這個(gè)實(shí)現(xiàn)只使用了一個(gè)類。main()方法首先創(chuàng)建了一個(gè)綁定到8080端口的ServerSocket:


      public static void main() throws IOException {
    ServerSocket serverSocket = new ServerSocket(8080);
    for (int i=0; i < Integer.parseInt(args[0]); i++) {
    new Httpd(serverSocket);
    }
    }

    接下來,main()方法創(chuàng)建了一系列的Httpd對(duì)象,并用共享的ServerSocket初始化它們。在Httpd的構(gòu)造函數(shù)中,我們保證每一個(gè)實(shí) 例都有一個(gè)有意義的名字,設(shè)置默認(rèn)協(xié)議,然后通過調(diào)用其超類Thread的start()方法啟動(dòng)服務(wù)器。此舉導(dǎo)致對(duì)run()方法的一次異步調(diào)用,而 run()方法包含一個(gè)無限循環(huán)。

      在run()方法的無限循環(huán)中,ServerSocket的阻塞性accpet()方法被調(diào)用。 當(dāng)客戶程序連接服務(wù)器的8080端口,accept()方法將返回一個(gè)Socket對(duì)象。每一個(gè)Socket關(guān)聯(lián)著一個(gè)InputStream和一個(gè) OutputStream,兩者都要在后繼的handleRequest()方法調(diào)用中用到。這個(gè)方法將讀取客戶程序的請(qǐng)求,經(jīng)過檢查和處理,然后把合適 的應(yīng)答發(fā)送給客戶程序。如果客戶程序的請(qǐng)求合法,通過sendFile()方法返回客戶程序請(qǐng)求的文件;否則,客戶程序?qū)⑹盏较鄳?yīng)的錯(cuò)誤信息(調(diào)用 sendError())方法。


      while (true) {
    ...
    socket = serverSocket.accept();
    ...
    handleRequest();
    ...
    socket.close();
    }

    現(xiàn)在我們來分析一下這個(gè)實(shí)現(xiàn)。它能夠出色地完成任務(wù)嗎?答案基本上是肯定的。當(dāng)然,請(qǐng)求分析過程還可以進(jìn)一步優(yōu)化,因?yàn)樵谛阅芊矫? StringTokenizer的聲譽(yù)一直不佳。但這個(gè)程序至少已經(jīng)關(guān)閉了TCP延遲(對(duì)于短暫的連接來說它很不合適),同時(shí)為外發(fā)的文件設(shè)置了緩沖。而 且更重要的是,所有的線程操作都相互獨(dú)立。新的連接請(qǐng)求由哪一個(gè)線程處理由本機(jī)的(因而也是速度較快的)accept()方法決定。除了 ServerSocket對(duì)象之外,各個(gè)線程之間不共享可能需要同步的任何其他資源。這個(gè)方案速度較快,但令人遺憾的是,它不具有很好的可伸縮性,其原因 就在于,很顯然地,線程是一種有限的資源。

      三、非阻塞的HTTP服務(wù)器

      下面我們來看看另一個(gè)使用非阻塞的新I/O API的方案。新的方案要比原來的方案稍微復(fù)雜一點(diǎn),而且它需要各個(gè)線程的協(xié)作。它包含下面四個(gè)類:

      ·NIOHttpd
    ·Acceptor
    ·Connection
    ·ConnectionSelector


    NIOHttpd的主要任務(wù)是啟動(dòng)服務(wù)器。就象前面的Httpd一樣,一個(gè)服務(wù)器Socket被綁定到8080端口。兩者主要的區(qū)別在于,新版本的 服務(wù)器使用java.nio.channels.ServerSocketChannel而不是ServerSocket。在利用bind()方法顯式地 把Socket綁定到端口之前,必須先打開一個(gè)管道(Channel)。然后,main()方法實(shí)例化了一個(gè)ConnectionSelector和一個(gè) Acceptor。這樣,每一個(gè)ConnectionSelector都可以用一個(gè)Acceptor注冊(cè);另外,實(shí)例化Acceptor時(shí)還提供了 ServerSocketChannel。


      public static void main() throws IOException {
    ServerSocketChannel ssc = ServerSocketChannel.open();
    ssc.socket().bind(new InetSocketAddress(8080));
    ConnectionSelector cs = new ConnectionSelector();
    new Acceptor(ssc, cs);
    }

    為了理解這兩個(gè)線程之間的交互過程,首先我們來仔細(xì)地分析一下Acceptor。Acceptor的主要任務(wù)是接受傳入的連接請(qǐng)求,并通過 ConnectionSelector注冊(cè)它們。Acceptor的構(gòu)造函數(shù)調(diào)用了超類的start()方法;run()方法包含了必需的無限循環(huán)。在這 個(gè)循環(huán)中,一個(gè)阻塞性的accept()方法被調(diào)用,它最終將返回一個(gè)Socket對(duì)象——這個(gè)過程幾乎與Httpd的處理過程一樣,但這里使用的是 ServerSocketChannel的accept()方法,而不是ServerSocket的accept()方法。最后,以調(diào)用accept() 方法獲得的socketChannel對(duì)象為參數(shù)創(chuàng)建一個(gè)Connection對(duì)象,并通過ConnectionSelector的queue()方法注 冊(cè)它。


      while (true) {
    ...
    socketChannel = serverSocketChannel.accept();
    connectionSelector.queue(new Connection(socketChannel));
    ...
    }

    總而言之:Acceptor只能在一個(gè)無限循環(huán)中接受連接請(qǐng)求和通過ConnectionSelector注冊(cè)連接。與Acceptor一 樣,ConnectionSelector也是一個(gè)線程。在構(gòu)造函數(shù)中,它構(gòu)造了一個(gè)隊(duì)列,并用Selector.open()方法打開了一個(gè) java.nio.channels.Selector。Selector是整個(gè)服務(wù)器中最重要的部分之一,它使得程序能夠注冊(cè)連接,能夠獲取已經(jīng)允許讀 取和寫入操作的連接的清單。

      構(gòu)造函數(shù)調(diào)用start()方法之后,run()方法里面的無限循環(huán)開始執(zhí)行。在這個(gè)循環(huán)中,程序調(diào)用了Selector的select()方法。這個(gè)方法一直阻塞,直到已經(jīng)注冊(cè)的連接之一做好了I/O操作的準(zhǔn)備,或Selector的wakeup()方法被調(diào)用。


      while (true) {
    ...
    int i = selector.select();
    registerQueuedConnections();
    ...
    // 處理連接...
    }

    當(dāng)ConnectionSelector線程執(zhí)行select()時(shí),沒有一個(gè)Acceptor線程能夠用該Selector注冊(cè)連接,因?yàn)閷?duì)應(yīng)的方法是同步方法,理解這一點(diǎn)是很重要的。因此這里使用了隊(duì)列,必要時(shí)Acceptor線程向隊(duì)列加入連接。


      public void queue(Connection connection) {
    synchronized (queue) {
    queue.add(connection);
    }
    selector.wakeup();
    }

    緊接著把連接放入隊(duì)列的操作,Acceptor調(diào)用Selector的wakeup()方法。這個(gè)調(diào)用導(dǎo)致ConnectionSelector線程繼 續(xù)執(zhí)行,從正在被阻塞的select()調(diào)用返回。由于Selector不再被阻塞,ConnectionSelector現(xiàn)在能夠從隊(duì)列注冊(cè)連接。在 registerQueuedConnections()方法中,其實(shí)施過程如下:


      if (!queue.isEmpty()) {
    synchronized (queue) {
    while (!queue.isEmpty()) {
    Connection connection =
    (Connection)queue.remove(queue.size()-1);
    connection.register(selector);
    }
    }
    }

    posted on 2008-04-11 10:28 gembin 閱讀(480) 評(píng)論(0)  編輯  收藏 所屬分類: JavaSE

    導(dǎo)航

    統(tǒng)計(jì)

    常用鏈接

    留言簿(6)

    隨筆分類(440)

    隨筆檔案(378)

    文章檔案(6)

    新聞檔案(1)

    相冊(cè)

    收藏夾(9)

    Adobe

    Android

    AS3

    Blog-Links

    Build

    Design Pattern

    Eclipse

    Favorite Links

    Flickr

    Game Dev

    HBase

    Identity Management

    IT resources

    JEE

    Language

    OpenID

    OSGi

    SOA

    Version Control

    最新隨筆

    搜索

    積分與排名

    最新評(píng)論

    閱讀排行榜

    評(píng)論排行榜

    free counters
    主站蜘蛛池模板: 亚洲毛片一级带毛片基地| 99久久亚洲综合精品成人网| 亚洲精品无码久久久影院相关影片| 亚洲午夜国产精品无码| 色婷婷六月亚洲婷婷丁香| 中文无码亚洲精品字幕| 日韩在线观看免费| 一级毛片免费毛片一级毛片免费 | 人人玩人人添人人澡免费| 久久久久av无码免费网| 国产又大又长又粗又硬的免费视频| 亚洲男人天堂2020| 久久亚洲精品成人无码网站| 亚洲精品中文字幕| 国产一级a毛一级a看免费视频 | 久久国产精品免费视频| 国产精品美女午夜爽爽爽免费| 亚洲XX00视频| 亚洲系列中文字幕| 麻豆一区二区三区蜜桃免费| 嫩草在线视频www免费观看 | 在线观看亚洲免费| 久久精品国产精品亚洲艾| 亚洲成年网站在线观看| 久久久久久噜噜精品免费直播| 91九色老熟女免费资源站| 又粗又大又长又爽免费视频| 亚洲国产一区二区三区青草影视| 亚洲国产精品自在自线观看| 国产猛男猛女超爽免费视频| 免费无码成人AV片在线在线播放| 亚洲av无码一区二区三区乱子伦| 亚洲人成色在线观看| 无码AV片在线观看免费| 免费看一级做a爰片久久| 亚洲黄色中文字幕| 一级一级毛片免费播放| 成人性生免费视频| 亚洲人成网www| 新最免费影视大全在线播放| 永久免费毛片在线播放|