日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

java grizzly_Grizzly简介 | Java Game

發布時間:2024/3/24 java 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java grizzly_Grizzly简介 | Java Game 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

用Java技術來編寫一個擴展性能很高的服務器軟件是件很困難的事情。Java虛擬機的線程管理機制使得純Java寫的HTTP引擎很難響應成千上

萬的并發用戶。正如Tomcat一樣,在并發用戶數不是很高的情況下能夠獲得很高的吞吐量,但是在高并發的情況下性能下降很快,變得不太穩定。

在JDK

1.4推出NIO之后,有很多基于NIO的框架出現,利用NIO的新特性,來編寫高性能的HTTP引擎。其中以Jean-Francois

Arcand的Grizzly最為引人矚目。Grizzly最早被用于Sun Java System Application Server,

Platform Edition 8.1。隨后成為開源軟件GlassFish的一部分。在今后,Sun Java System

Application Server 9.x的Platform Edition和Enterprise

Edition都會使用Grizzly作為HTTP引擎。

17.2.1? Grizzly的基本架構

圖17-1描述了Grizzly的基本架構。

圖17-1? Grizzly的基本架構

Grizzly的基本架構主要包含以下幾個方

面:Pipeline、SelectorThread和Task。下面分別加以介紹。

1. Pipeline

com.sun.enterprise.web.connector.grizzly包下,有許多與Pipeline相關的類,例如Pipeline、

KeepAlivePipeline、ThreadPoolExecutorPipeline、LinkedListPipeline等。

Pipeline是個不太好理解的詞匯,其實把這些類叫做ThreadPoolWrapper可能更加合適和容易理解。只要熟悉服務器端的軟件,對

Thread

Pool(線程池)一定不會陌生。線程比起進程來說,消耗的資源要少,共享數據更加簡單。因此,現在大多數服務器軟件(特別是HTTP服務器)都會采用多

線程模式。但是線程的創建和關閉仍然是比較慢的系統服務,聰明的服務器軟件設計者會在系統啟動的時候,預先創建一些線程,并且將這些線程管理起來,在系統

正常運行的時候服務于客戶的請求。通過這樣的手段,線程不需要在使用的時候臨時創建,大大提高了軟件的運行速度和效率。對這種線程的管理方法叫做線程池。

線程池中的線程需要互相協作,有序地執行客戶的請求。一般用于同步線程的結構叫任務隊列。客戶的請求根據先后順序被放到了任務隊列中,線程池中空閑的線程

會從任務隊列中獲得任務并執行。

Grizzly中的Pipeline實際上封裝

了一個Thread

Pool(線程池)和一個任務隊列。Pipeline的主要目的是封裝了一個統一的接口,可以讓Grizzly根據配置文件任意選擇不同算法的線程池,來

獲得不同的特點和性能。在Grizzly中已經實現了好幾種線程池。其中有ThreadPoolExecutorPipeline(基于

java.util.concurrent.ThreadPoolExecutor來實現的線程池),還有LinkedListPipeline(使用簡

單的linklist數據結構管理的線程池)。在早期的Grizzly中還會看到一些其他的實現。經過測試以后,淘汰了一些性能不好的算法,目前只剩下這

兩種Pipeline了。事實上在大并發用戶的測試中,LinkedListPipeline的性能是最好的,因此被設置為默認的選擇。在以后的版本

中,ThreadPoolExecutorPipeline也可能會消失,只保留性能最好的算法是明智的選擇。但是現在還存在兩種算法,其主要原因是

java.util.concurrent.ThreadPoolExecutor的名聲太響,所有的文章和測試都曾經證明過它的高性能。就連

Grizzly的作者本身都不相信LinkedListPipeline的性能要比ThreadPoolExecutorPipeline好,只不過當前

的測試結果事實如此。因此該作者自己也說,一旦有證據證明ThreadPoolExecutorPipeline的性能又重新超過

LinkedListPipeline,他會立即將默認的設置指向ThreadPoolExecutorPipeline。

KeepAlivePipeline是一個特例,它

并不是用來執行特定任務的,而是用來維護HTTP協議中的持久連接的狀態,例如維護最大的持久連接數,持久連接的timeout時間等。另外,異步的

socketChannel中缺少一個類似socket.setSoTimeout的函數,這個函數在保證服務器軟件的可靠性和安全性(抗DOS攻擊)

上,具有重要的作用。Grizzly是用KeepAlivePipeline類來模擬socket.setSoTimeout的作用。

2. SelectorThread

這是Grizzly的主要入口類,位于

com.sun.enterprise.web.connector.grizzly的包下。在SelectorThread

中,SocketChannel和Selector被創建并被初始化。當網絡有請求進來的時候,Selector會根據不同的請求類型和NIO的不同事件

進行不同的處理。

當NIO的事件為OP_READ的時候,表明是原有

的連接中有新的請求數據傳過來了。這類請求屬于ReadTask,應該交給負責處理ReadTask的處理器來處理。ReadTask有自己的

Pipeline(也就是線程池)來處理,這樣就不會占用主線程來處理Read的請求。

當NIO的事件為OP_ACCEPT的時候,表明是

有新的請求進來了,這類請求屬于AcceptTask,應該交給負責處理AcceptTask的處理器來處理。在老版本的GlassFish

中,AcceptTask也有自己的Pipeline來處理,這樣就讓AcceptTask在主線程以外的線程中執行。但是經過多次性能測試和比較,發現

當AcceptTask在主線程(SelectorThread)中執行的時候,性能最好。因此,在讀最新的Grizzly源代碼的時候,會發現圖

17-1中的AcceptPipeline根本不存在,因為AcceptTask已經由SelectThread類中HandleAccept函數來執行

了。

當ReadTask執行完以后,表明整個請求的數據

已經完全接收到,就可以進行請求處理了,請求處理屬于ProcessTask,交給負責處理ProcessTask的處理器來處理。

ProcessTask有自己的Pipeline(也就是線程池)來處理,這樣就不會占用主線程來處理請求。

3. Task

在Grizzly的框架中包含下面幾種任務。

(1)

AcceptTask:用于響應新的連接請求。前面已經說過,這個任務的類事實上已經不存在,沒有單獨抽象出來。因為處理Accept已經成為

SelectThread內部的一部分了。

(2)

ProcessTask:用于處理并且響應請求。這個任務通常是對請求的數據進行解析,解析完后再將請求傳遞給其他服務的容器(如Servlet容器)進

行真正的業務處理。

(3)

ReadTask:用于SocketChannel最初的讀取操作。由于NIO是非阻塞的操作,最初的讀取往往不能獲得全部的請求數據,這時

候,ReakTask會將任務委托給StreamAlgorithm,根據不同實現,用不同的方法將剩下的請求數據獲取。

com.sun.enterprise.web.connector.grizzly.algorithms的包下,Grizzly默認實現了4個算法:

l?? ContentLengthAlgorithm

l?? SeekHeaderAlgorithm

l?? StateMachineAlgorithm

l?? NoParsingAlgorithm

前3個算法主要是圍繞HTTP請求中的

Content-length字段來進行解析。只要能讀到這個字段的值,那么我們就可以預先判斷整個請求的長度,從而確定什么時候完成請求讀取,接著進行

請求處理了。第4個算法是對請求數據根本不進行預處理,假設所有的數據都讀進來了。如果最后發現請求數據讀得不完全,再交給請求處理任務

(ProcessTask)來負責將剩下的內容讀取過來。

17.2.2? 源碼閱讀指南

根據圖17-1的結構,結合Grizzly的源代

碼,可以看到Grizzly的大致脈絡。

SelectorThread是個入口,根據

Grizzly所在的不同環境,啟動的方法有所不同。如果Grizzly作為單獨可運行的應用(Grizzly可以從GlassFish中獨立出來),在

com.sun.enterprise.

web.connector.grizzly.standalone包下的Main類是這樣使用SelectorThread的:

【例17.4】單獨運行的Grizzly對

SelectorThread的調用:

private static void start(String args[]) throws Exception

{

SelectorThread selectorThread = null;

String selectorThreadClassname =

System.getProperty(SELECTOR_THREAD);

if (selectorThreadClassname != null){

selectorThread =

loadInstance(selectorThreadClassname);

} else {

selectorThread = new SelectorThread();

}

selectorThread.setPort(port);

StaticResourcesAdapter adapter = new

StaticResourcesAdapter();

adapter.setRootFolder(folder);

selectorThread.setAdapter(adapter);

selectorThread.setDisplayConfiguration(true);

selectorThread.initEndpoint();

selectorThread.startEndpoint();

}

如果Grizzly是在GlassFish中,它作

為服務線程,run()方法是整個線程啟動的鑰匙。從源碼中很容易看出在run()方法中調用了startEndpoint()方

法,startEndpoint()在做好一些準備工作之后,調用了startListener()。startListener()便進入了主線程的循

環之中。在循環中只有一個方法,那就是doSelect()方法。

在doSelect()中,可以很清楚地看到NIO

的框架結構。

【例17.5】SelectorThread中的

doSelect():

selectorState = selector.select(selectorTimeout);

readyKeys = selector.selectedKeys();

iterator = readyKeys.iterator();

while (iterator.hasNext()) {

key = iterator.next();

iterator.remove();

if (key.isValid()) {

handleConnection(key);

} else {

cancelKey(key);

}

}

與大多數NIO的架構一樣,先是調用

selector.select(selectorTimeout),看看當前的頻道有沒有數據準備好了。如果有的話,通過

selector.selectedKeys()將準備好的這些頻道的SelectionKey取到。對這些頻道的處理就交給

handleConnection(key)函數了。

【例17.6】SelectorThread中的

handleConnection:

protected void

handleConnection(SelectionKey key) throws

IOException,InterruptedException

{

Task task = null;

if ((key.readyOps() &

SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT){

handleAccept(key);

return;

} else

if ((key.readyOps() & SelectionKey.OP_READ) ==

SelectionKey.OP_READ){

task =

handleRead(key);

}

if (((SocketChannel)key.channel()).isOpen()) {

task.execute();

} else {

cancelKey(key);

}

}

handleConnection

函數很短,但是有一些重要的特點需要指出來。handleConnection的主要功能是區分那些已經準備好的頻道,看看它們是屬于新的連接

(OP_ACCEPT)還是有新的請求數據(OP_READ)。

如果是

OP_ACCEPT,那么就調用函數handleAccept(key)。這個函數會在當前的線程內執行,主要的功能就是根據新來的連接創建新的頻道,再

將這個頻道注冊到Selector中。如果是OP_READ,那么就調用函數handleRead(key)。這個函數返回了一個Task。通過

task.execute()將這個任務的實際運行交給Pipeline中的線程池來執行。換句話說,對新的請求數據的處理是在另外的線程中來處理的,而

不是當前的線程。

事實上,在

早期的Grizzly的版本中,對OP_ACCEPT的處理與OP_READ一樣,也是有單獨的任務(AcceptTask)和單獨的線程來執行。但是經

過性能測試,證明當對OP_ACCEPT的處理在主線程的時候性能最好。因此就取消了AcceptTask在單獨線程中的處理,演化為當前的模型。

再隨后的工

作主要就交給ReadTask和ProcessTask去做了。這里不作詳細的介紹。

總結

以上是生活随笔為你收集整理的java grizzly_Grizzly简介 | Java Game的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。