IronPython for ASP.NET CTP WhitePaper 摘要翻譯

來源:互聯網
上載者:User

翻譯,摘要:木野狐
2006-11-3
原文連結:http://www.asp.net/ironpython/WhitePaper.doc

全新的 ASP.NET 動態語言可擴充模型

介紹:為什麼需要新的編譯模型?

傳統 ASP.NET 頁面編譯的步驟

1. Parsing.
  對 .aspx 檔案進行解析。
2. 構建 CodeDOM 樹
  根據解析結果構建出 CodeDOM 樹(不依賴於具體語言的)
3. 代碼產生
  根據 @Page 指令中定義的語言,選擇合適的 CodeDOM provider,
  讓這個特定的 provider 根據 CodeDOM 樹產生衍生類別的原始碼。
4. 編譯
  CodeDOM provider 編譯產生的程式碼為 dll.
5. 執行
  ASP.NET 載入產生的程式集,執行個體化其中的代碼類,並用它來執行 HTTP 要求。

在使用 Codebehind 的情況下,會有一個 partial class 參與以上過程的運作。
.ascx 和 .master 的運作機制跟這個情形類似。

由以上的運作過程可以發現,如果需要其他語言支援 asp.net,則該語言需要
提供相應的 CodeDOM Provider,並且要能建立 System.Web.UI.Page 的子類。
由於 IronPython 是非強型別的,這樣的工作顯得意義不大。
因此需要通過新的途徑來實現 IronPython 對 asp.net 的支援:

“不需編譯”的頁面

asp.net 2.0 已經支援了不需編譯頁面模型。聲明方法如下:

<%@ Page CompilationMode="Never" %>

如果不指定該屬性值,則預設情況下是 "Auto".
不需編譯的頁面跟普通頁面的區別是,他們是資料驅動的,因此,通常會具有
更好的效能。
因為不編譯,這種頁面有個限制:
即不能包含使用者代碼。只可以包含靜態 HTML 和伺服器端控制項。
而對動態語言的支援也正是從這裡入手,從中解除這些限制,並提供對動態
語言的支援。

不需編譯的頁面如何工作

1. Parsing
2. 控制項產生器(Control Builder)樹的構建
  拿 <asp:textbox> 來說,這時解析的結果並不是產生一小段定義該 TextBox
  控制項的代碼,而是建立一個相應的控制項產生器節點。
3. 執行
  ASP.NET 請求控制項產生器樹進行初始化,包括其中的控制項。

在這種情況下,並不會產生 Page 類的子類。響應請求的是 Page 類本身(也可能
是一個自訂的基類,如果指定了 inherits 屬性)。

支援動態語言的全新 ASP.NET 模型

該模型是從不需編譯的 asp.net 模型修改而來。
首先,因為不需編譯的模型在發現使用者代碼時就會出錯,因此要對這個行為進行
修改,而這是在 System.Web.dll 中實現的。當你安裝了該動態語言的支援時,就
使用了一個新版本的 System.Web.dll

對 ASP.NET 的改變是 PageParserFilter 這個API. 在這裡我們給了外部代碼得以執行
的一個鉤子。
在新的模型中,我們在 web.config 中註冊了 PageParserFilter 類,以便自訂 parsing
的行為,並在不需編譯的頁面中開啟對使用者代碼的支援:
<pages
  pageParserFilterType="Microsoft.Web.IronPython.UI.NoCompileCodePageParserFilter"
  ... />

PageParserFilter 的功能如下:

1. 如果頁面使用了動態語言(當前只支援 IronPython),它需要確認頁面是否繼承
  自某個基類。
2. 如果 parser 遇到了一個程式碼片段(<% ... %>, <%= ... %>, 或 <%# ... %>),
  它將這些程式碼片段替換為特定的控制項,而這些控制項包含其中的代碼。這樣,從
  asp.net 的觀點來看,的確是沒有代碼了,只是程式碼片段變成了控制項。這個做法
  使得我們突破了不需編譯的頁面不能包含代碼這個限制。
3. 如果 parser 發現一個事件處理函數的定義,(如 onClick="MethodName"),同樣,
  也將他替換為一個特定的控制項。
4. 如果 parser 發 <script runat="server">,則將此元素當作頁面層級的屬性處理。
  在接下來的步驟中,將會把它用字串的方式傳遞給自訂基類。

自訂的 HTTP Module

該模型中實現了一個自訂的 HTTP Module, 需要在 web.config 中註冊如下:
<httpModules>
  <add name="PythonModule"
    type="Microsoft.Web.IronPython.DynamicLanguageHttpModule" />
</httpModules>

註冊這個 HTTP module 的作用在於,能夠及早的掛鈎到 application domain cycle 中,
並向動態語言環境中註冊各種組件。(application domain 是當前進程中的一塊地區,
當前 Web 程式中的所有代碼都在其中執行).
這個 HTTP module 還提供 Global.asax 檔案對動態語言的類似支援,這個會在稍後討論。

自訂的頁面基類

新的模型中,所有頁面繼承自一個叫做 ScriptPage 的基類。而該類繼承自
System.Web.UI.Page. 類似的,我們有使用者控制項的基類 ScriptUserControl 和主版頁面的基類
ScriptMaster

新的模型支援哪些頁面特性

原有的大部分特性基本都支援,只是其工作原理不同。

Application File

新的模型支援類似 Global.asax 的檔案,但其名稱是 Global.ext( ext 是語言特定的,
比如 IronPython 就是 Global.py)

另一個不同點是,該檔案只能包含代碼,不需要包含 <%@ %> 或者 runat="server"
這樣的聲明。
比如 Global.py 可以這樣寫:

def Application_BeginRequest(app):
 app.Response.Write("Hello application!");

App_Script 目錄

該目錄類似於 asp.net 2.0 的 App_Code 目錄。其中可以包含動態語言的檔案。這裡
的檔案可以被整個應用程式所調用。

Generic HTTP Handlers

動態語言的 asp.net 程式中可以包含一個和傳統 asp.net 中 .ashx 檔案類似的 HTTP
handler, 但和 Global.asax 一樣,它也稍有不同。
具體來說,handler 只能包含代碼,而沒有聲明性的語句。
其命名方式是:
Web_name.ext
比如 IronPython 的一個 handler 可以這樣命名:
Web_MyHelloHandler.py

注意 "Web_"  這個首碼是規定的,用以識別一個 HTTP handler.

動態語言的 handler 必須包含一個 ProcessRequest 方法,這類似於 .ashx 檔案的
IHttpHandler.ProcessRequest 方法。樣本如下:

def ProcessRequest(context):
 context.Response.Write("Hello web handler!")

不支援:Web Services

主要原因是 Web Service 的架構只能用基礎的 .net framework 類型,而動態語言來
建立他們是不容易的。並且 Web Services 的方法需要加上類似 [WebMethod()] 的標籤,
在動態語言裡也沒有類似的文法。

在新的模型中,代碼是如何被處理的?

每一個使用者代碼的片段會被當作一個獨立的實體來處理。下面具體分析不同類型的
使用者代碼是怎麼被處理的:

<script runat="server"> 元素中的代碼:

在傳統模型中,這裡面的代碼會被產生為頁面類的一部分。而新的模型不會產生新類,
asp.net 直接執行個體化在 inherits 屬性裡指定的類(ScriptPage)。其實這裡 "inherits" 的含義
已經不準確了。

<script> 元素中的代碼會變為 ScriptPage 類的一些附加代碼,也可以大致理解為類似於
partial class 的機制。

下面示範一個具體的例子:
<script runat="server">
def Page_Load():
 Response.Write("<p>Page_Load!</p>")
</script>

這段代碼並不會成為任何類的一部分。實際的情況是,頁面類的成員會被 asp.net 注入
(injected) 到環境中,以便這裡能調用他們。(比如使用 Respnose 對象)
從實踐的角度講,你可以當作這個方法是頁面類的一部分,雖然實際上不是。

用 IronPython 的術語來說,代碼塊中的代碼存在於模組(module)中。通常一個頁面類,
或使用者控制項,或主版頁面都會對應於一個 module.(注意是每個頁面一個對應的 module,
而不是每次 HTTP 要求產生一個)。

再看另一個例子:
<script runat="server">
someVar = 5
</script>

注意這個變數是模組層級的,而上面提到模組是每個頁面對應一個,所以這個變數也是。
這跟 asp.net 的靜態變數語義類似。

後端代碼檔案(Code-Behind Files)

新的模型也支援後端代碼的方式,但和傳統模型的原理不同。
在新的模型中,後端代碼中沒有類定義,方法直接定義在檔案中。這和剛才提到的
<script runat="server"> 塊中的代碼沒有區別。
後端代碼的檔案名稱可以命名為類似 MyPage.aspx.py 這樣的。

程式碼片段(Code Snippets)

程式碼片段運算式 (<%= ... %>), 和語句(<% ... %>) 同樣在 page 伴隨的 module 中執行。
因為這個原理,程式碼片段中就可以自由訪問 module 中定義的任何方法。
例如:<%= Multiply(6,7) %>, 其中 Multiply 在後端代碼中定義。程式碼片段中甚至可以
訪問 Page 類的成員。如:
<%= Title %> 可以顯示當前頁面類的標題。

資料繫結運算式

資料繫結運算式雖然也是程式碼片段的一種,但有必要單獨說明一下。因為在 IronPython
中,資料繫結特別有意思,他比普通的 asp.net 綁定更自然。
比如:在 GridView 的模版列中,我們可以用 <%# Eval("City") %> 的文法來綁定 City 欄位
的內容。這很平常,但需要通過 Eval 文法來調用顯得挺彆扭的~
新的模型中,我們只需要這樣:<%# City %>,是不是很興奮?
這裡的 City 其實是動態語言的一個真實程式碼片段,而不是原先 Eval 方法的一個字串
類型的參數。所以,這裡就有很大的發揮空間。比如,你可以這樣:
<%# City.lower() %> 來顯示為小寫字母。
這種文法的支援是由動態語言的後期綁定估算(late-bound evaluation)特性所實現的。
雖然 City 的含義在 parse 階段尚不可知,但在運行時動態語言引擎卻能將他綁定到正確
的對象。

動態注入器機制(Dynamic Injector Mechanism)

動態語言相比於靜態編譯語言的另一個優勢是注入器的機制。用例子來說明:
假設你需要從下列頁面的查詢字串中擷取一個值:
http://someserver/somepage.aspx?MyValue=17

在 C# 中,你需要這樣:
string myValue = Request.QueryString["MyValue"];

而動態語言中這樣就可以了:
myVar = Request.MyValue;

為什麼能這樣寫呢?在新的模型中,我們註冊了一個特殊的對象,叫做注入器(injector).
這個注入器的作用是,就好像它對動態語言引擎發出如下指令:"如果發現一個運算式 SomeObj.SomeName,
而 SomeObj 是一個 HttpRequest 對象,並且 SomeName 不是 HttpRequest 的一個屬性,
則讓我來處理他,而不是拋出失敗"。

而這個注入器處理運算式的方式就是通過調用 SomeObj.QueryString["SomeName"].

同樣的注入器機制還實現在其他場合。比如,
C#: SomeControl.FindControl("SomeChildControl")
IronPython: SomeControl.SomeChildControl.

注入器的機制是可擴充的。所以,如果你有個自訂的集合通過字串做索引的話,
你可以實現自訂的注入器來簡化文法。

雖然不是革命性的變化,但像上面提到的注入器的特性,和簡化的綁定運算式會使得
開發 Web 程式變得更輕鬆。

動態代碼的編譯

以上提到,新的模型採用了不編譯的頁面模型,這也許會帶來誤解,讓你覺得頁面是解釋
執行的,其實不是這樣。
具體的解釋是:術語 "不編譯(no-compile)" 其實指的是 CodeDOM 方式的靜態編譯。
而新的模型中,動態語言的代碼是由動態語言引擎就地編譯的(being compiled on-the-fly)。

新模型的好處

更快的頁面初始化處理
  傳統的頁面在第一次訪問某頁面時,會有一個複雜的 CodeDOM 方式的編譯過程,並且會
  載入一個獨立的進程(比如 csc.exe) 來進行編譯,顯得非常慢。
  而對於不編譯的頁面而言,最昂貴的步驟是解析(parsing). 所以頁面會執行的更快。

更好的效能
  傳統的模型會將頁面編譯為程式集,並載入到 application domain 中執行。但程式集一旦
  被載入,則不能被卸載掉。所以,如果你有一個非常大的網站,有很多的頁面,則越來越多
  的程式集會被載入,甚至會導致記憶體不足。(out of memory)

  asp.net 採用了一系列手段來減輕這個負擔。首先,它可以將多個頁面批量編譯到一個
  程式集中,以減少被載入的程式集數量。這的確有些協助,但帶來了非常高的複雜性。
  這隻是延遲了問題的解決。在某種情況下,asp.net 會卸載整個 application domain,並
  重新啟動一個來運行應用程式。但這是一個非常重量級操作,因為很多程式集要求在短
  時間內被重新載入。

  與之相反的是,新的模型完全不存在這個問題,因為它根本不產生程式集。當然,這樣
  處理頁面會有一些額外的代價,但是記憶體回收行程會回收這些佔用的記憶體。

  儘管動態代碼通常會被編譯為 IL, 這種編譯和靜態編譯卻是很不一樣的。動態語言編譯到
  IL 採用的是通用語言執行平台(common language runtime) 的一個特性,叫做輕量級代碼產生
  (lightweight code generation, LCG). 這個特性可以使得編譯代碼時佔用的記憶體,會在不再
  使用時被回收-- 不需要重啟整個 application domain.

看看數字
  新的模型究竟帶來多大的效能收益?新模型和傳統模型的差別是非常明顯的。
  在靜態編譯模型下,一個有 10,000 個 asp.net 頁面的網站會對伺服器造成非常大的負擔。
  (具體還取決於硬體設定)。相比而言,在新的模型上運行一個超過一百萬個動態網頁面
  的網站,伺服器並未感受到很大的壓力!
 
  需要說明的是,asp.net 靜態編譯模型對大多數項目都工作的很好。只有對特別大的項目
  才會感覺到這種效能的限制。

降低了的複雜度(Reduced Complexity)
  前面提到,為了提高靜態編譯的效能,asp.net 對頁面進行批量編譯。但要達到這一點卻
  非常有技巧性,而且這種最佳化已經大幅度的提高了整個編譯系統的複雜度。

  簡單的描述一下這種複雜度。在很常見的情形下,你有很多個頁面和很多個使用者控制項。
  為了能批量編譯他們,我們首先要弄清楚他們的依賴樹。(比如:頁面A引用了使用者控制項B),
  因為被依賴的檔案必須在當前編譯的頁面之前被編譯。並且我們需要處理使用不同語言
  書寫的頁面。考慮一下,如果一個老的程式集已經無效了,但卻無法卸載時怎麼辦呢?
  如果兩個頁面使用了同一個類名怎麼辦?批量編譯他們會導致編譯錯誤。類似的問題還有
  很多,但你大概能理解這種複雜度了吧。

  上面說這麼多並不是抱怨靜態編譯模型是多麼難實現,而是為了和新的模型做對比。
  因為總之只處理頁面自身,並且不需要考慮老的程式集是否已經在當前 application domain
  中載入,我麼可以顯著的降低複雜度。我們希望這能帶來更加穩定的產品,和更少的
  bugs. 總之,簡單的總是更好!

運行時效能
  總體來說,靜態編譯的語言,比如 C#,會比動態語言如 IronPython 更快。因為動態語言
  通過晚綁定的方式執行。就是說在動態語言中 Object.Perperty 這個運算式,Property 的
  含義在編譯時間是不會解析的,而是在運行時。解析運算式需要某種形式的尋找,這本質上
  就會比靜態編譯的方式要慢。靜態編譯是在編譯期間就知道了這個值。

  然而,我們的問題不在於動態語言是否能和靜態語言跑得一樣快,而是 asp.net 頁面用
  動態語言編寫是否可以得到比較好的效能。經過測試,IronPython 書寫的 asp.net 頁面
  啟動並執行效能相當好。原因是,執行使用者的代碼(不管是靜態還是動態)只是 HTTP
  請求處理工作中很小的一個部分。即使更多的時間花費在使用者代碼上,總體的請求
  時間的差別也是小到可以忽略的。

  當然你可以輕易的書寫一個頁面來執行複雜的運算,說明這個觀點是錯的。但其實
  大部分網站頁面所執行的代碼都不是 CPU 要求很高的。實際的網頁邏輯,大部分都
  花費在處理輸入,設定數值到控制項的屬性,查詢資料庫等。

  總之,對於大部分你需要建立的 Web 應用程式而言,使用動態語言並不會帶來多少
  負面影響。

結論
  在這篇文檔中,我們比較了已有的靜態編譯模型和新的模型的區別,目的並不是要說服
  已有的 C# Web 開發人員改用 IronPython。實際上,該文檔的目的只是為瞭解釋兩種
  模型的區別,讓你能對我們所從事的工作保持關注。並且,仍然有很多工作在繼續進行,
  希望在將來能提供該文檔的一個修訂版本。

 

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.