Google Code 提供下列語言介面: English - Español - 日本語 - 한국어 - Português - Pусский - 中文(简体) - 中文(繁體)
「應用服務引擎」會在安全的「沙箱」環境中,使用預先載入的 Python 直譯器來執行您的 Python 應用程式碼。您的應用程式會與這個環境互動,藉此接收網路要求、執行工作,以及傳送回應。
當您搭配使用 app.yaml 設定檔以及 Python SDK 中的 appcfg.py 工具時,「應用服務引擎」會為您的應用程式提供 Python 執行階段環境。您可以透過下列設定元素選取 Python 執行階段環境:
runtime: python api_version: 1
第一個元素 (runtime) 會選取 Python 執行階段環境。截至筆者撰稿時,「應用服務引擎」僅支援一種執行階段環境,即 python。
第二個元素 (api_version) 會選取要使用的 Python 執行階段環境版本。截至筆者撰稿時,「應用服務引擎」僅支援一個版本的 Python 環境,即 1。如果「應用服務引擎」團隊需要發佈環境變更,且這些變更可能無法相容於現有程式碼時,他們會使用新的版本識別碼。除非您變更 api_version 設定並上傳您的應用程式,否則您的應用程式將繼續使用選取的版本。
如需 app.yaml 和 appcfg.py 的詳細資訊,請參閱「Python 應用程式設定」以及「上傳應用程式」。
「應用服務引擎」接收到應用程式的網頁請求時,會呼叫與 URL 相應的處理常式指令碼,如同應用程式的 app.yaml 設定檔所述。「應用服務引擎」使用 CGI 標準來溝通要求資料與處理常式,並接收回應。
「應用服務引擎」使用多個網頁伺服器來執行您的應用程式,並可靠地自動調整用來處理要求的伺服器數量。指定的要求可能會透過傳送到任一部伺服器,而且處理它的伺服器,與來自相同使用者之前送出要求的處理伺服器,可能不是同一部。
伺服器會比較要求的 URL 和應用程式設定檔中的 URL 模式,以決定要執行的 Python 處理常式指令碼。然後,它會在填入要求資料的 CGI 環境中,執行處理常式。如同 CGI 標準所描述,伺服器會將要求放入環境變數以及標準輸入串流。指令碼針對要求執行適當的動作,然後準備回應,並將它放入標準輸出串流。
大部分應用程式會使用程式庫來剖析 CGI 要求與傳回 CGI 回應 (例如 Python 標準程式庫的 cgi 模組),或使用熟悉 CGI 通訊協定的網頁架構 (例如 webapp)。您可以參考 CGI 文件,進一步瞭解環境變數和輸入串流資料的格式。
下列範例處理常式指令碼會在使用者的瀏覽器顯示訊息。它會將 HTTP 標頭 (識別為訊息類型和訊息內容者) 列印至標準輸出串流。
print "Content-Type: text/plain" print "" print "Hello, world!"
「應用服務引擎」會收集要求處理常式指令碼寫入標準輸出串流的所有資料,然後等待指令碼結束。指令碼結束時,會將所有輸出的資料傳送給使用者。
「應用服務引擎」不支援在結束處理常式之前,將資料傳送給使用者的瀏覽器。部分網頁伺服器在回應單一要求時會透過此技術,在一段時間內將資料「串流」至使用者的瀏覽器。「應用服務引擎」不支援這種串流技術。
如果用戶端傳送 HTTP 標頭的要求指出用戶端可接受壓縮的 (gzip'd) 內容,則「應用服務引擎」會自動壓縮回應資料,並附加適當的回應標頭。它會使用 Accept-Encoding 和 User-Agent 的要求標頭來判斷用戶端是否能夠可靠地接收壓縮的回應。自訂用戶端可以強制壓縮內容,只要將 Accept-Encoding 和 User-Agent 標頭指定為「gzip」值即可。
要求處理常式必須在限制時間內 (一般是在 30 秒左右),根據要求產生並傳回回應。一旦超過限制時間,要求處理常式將遭到中斷。
Python 執行階段會從 google.appengine.runtime 套件引發 DeadlineExceededError,藉此中斷要求處理常式。如果要求處理常式未能捕捉此例外狀況,則與所有未捕捉到的例外狀況一樣,執行階段環境將傳回 HTTP 500 伺服器錯誤給用戶端。
要求處理常式可以捕捉此錯誤,並自訂回應。執行階段環境會在引發例外狀況後,給予要求處理常式較多時間 (一秒以內) 準備自訂回應。
from google.appengine.runtime import DeadlineExceededError
class MainPage(webapp.RequestHandler):
def get(self):
try:
# Do stuff...
except DeadlineExceededError:
self.response.clear()
self.response.set_status(500)
self.response.out.write("This operation could not be completed in time...")
如果處理常式無法在第二個期限內傳回回應或引發例外狀況,處理常式將終止並傳回預設的錯誤回應。
回應一個要求的時間可能長達 30 秒,不過如果應用程式的要求都是短期要求,尤其是只需花費幾百毫秒的要求,則「應用服務引擎」將能展現最佳效能。有效率的應用程式可以快速回應絕大多數的要求。無法快速回應的應用程式,將無法順利地透過「應用服務引擎」的基礎結構擴充。
為了讓「應用服務引擎」將應用程式的要求分散到多個網頁伺服器,並防止應用程式彼此干擾,應用程式會在限制的「沙箱」環境中執行。在此環境中,應用程式可以執行程式碼、儲存和查詢「應用服務引擎」資料存放區的資料、使用「應用服務引擎」郵件、URL 擷取和使用者服務以及檢查使用者的網路要求和準備回應。
「應用服務引擎」無法執行下列動作:
Python 執行階段環境使用 Python 2.5.2。
Python 執行階段環境的所有程式碼必須都是 Python,且不包括任何 C 延伸或其他必須編譯的程式碼。
環境包括 Python 標準程式庫。部分模組因為「應用服務引擎」不支援,而被停用,例如網路功能或寫入檔案系統功能。此外,可以使用 os 模組,但是會停用不支援的功能。如果嘗試匯入不支援的模組或使用不支援的功能,便會引發例外狀況。
標準程式庫的幾個模組已被取代或自訂,以便與「應用服務引擎」搭配。例如:
TemporaryFile 的別名為 StringIO。除了 Python 標準程式庫和應用程式庫之外,執行階段環境包括下列第三方程式庫:
您可以在應用程式目錄中放置相關的程式碼,為應用程式加入其他純 Python 程式庫。若在應用程式目錄中製作模組目錄的符號連結,appcfg.py 會根據該連結,將模組包括在您的應用程式中。
Python 模組包括路徑包含應用程式的根目錄 (該目錄包含 app.yaml 檔案)。使用根路徑,則可以使用您在應用程式根目錄中建立的模組。請不要忘記在子目錄中建立 __init__.py 檔案,這樣 Python 會將子目錄視為套件。
在單一網頁伺服器上,Python 執行階段環境會快取要求之間的匯入模組;就像是獨立的 Python 應用程式僅載入模組一次 (即使該模組被多個檔案匯入)。若處理常式指令碼提供 main() 常式,則執行階段也會快取指令碼。否則,每次要求都會載入該處理常式指令碼一次。
應用程式快取可大幅縮短回應時間。我們建議您針對所有應用程式使用 main() 常式,如下所描述。
為了提升效能,網頁伺服器會將匯入的模組保持在記憶體,在相同伺服器之相同應用程式的後續要求中,都不用重新載入或重新評估它們。大部分模組在匯入時,都不會初始化任何全域資料或有任何副作用,因此快取它們不會改變應用程式的行為。
若您的應用程式匯入的模組,在每次要求時都要進行評估,則應用程式必須使用此快取行為。
下列範例示範快取匯入模組的方式。因為單一網頁伺服器只會匯入一次 mymodule,全域 mymodule.counter 只會在伺服器第一次服務要求時初始化為 0。後續的要求會使用之前要求的值。
### mymodule.py counter = 0 def increment(): global counter counter += 1 return counter ### myhandler.py import mymodule print "Content-Type: text/plain" print "" print "My number: " + str(mymodule.increment())
此輸出 My number: #:其中的 # 是此常式被處理要求的網頁伺服器呼叫的次數。
您除了可以讓「應用服務引擎」快取匯入的模組,也可以快取常式指令碼本身。若常式指令碼定義名稱為 main() 的函式,然後指令碼及其全域環境都會被快取,就像匯入的模組一樣。指定網頁伺服器第一次要求該指令碼時,會正常地評估指令碼。針對後續的要求,「應用服務引擎」會壓快取的環境中呼叫 main() 函式。
要快取常式指令碼,「應用服務引擎」必須能夠呼叫沒有引數的 main()。若處理常式指令碼沒有定義 main() 函式,或者 main() 函式需要引數 (預設不需要),則「應用服務引擎」會在每次要求都載入和評估整個指令碼。
將剖析的 Python 程式碼保留在記憶體中,可以節省時間並獲得更快速的回應。快取全域環境還有其他潛在的用途:
處理常式指令碼在匯入時應呼叫 main()。「應用服務引擎」認為該指令碼的匯入會呼叫 main(),因此「應用服務引擎」在伺服器上首次載入要求處理常式時,不會呼叫它。
下列範例與之前的範例的目的都相同,使用常式指令碼的全域環境:
### myhandler.py # A global variable, cached between requests on this web server. counter = 0 def main(): global counter counter += 1 print "Content-Type: text/plain" print "" print "My number: " + str(counter) if __name__ == "__main__": main()
注意:小心不要在要求之間「洩露」使用者的資訊。使用全域變數時,請使用快取功能,並於 main() 常式中初始化要求特定的資料。
快取 main() 的應用程式可大幅縮短應用程式的回應時間。我們建議所有應用程式都使用此方式。
「應用服務引擎」網頁伺服器會擷取處理常式指令碼寫入標準輸出串流的所有資料,以便回應給網頁要求。它也會擷取處理常式指令碼寫入標準錯誤串流的所有資料,並將它儲存為記錄資料。應用程式的記錄資料可以透過「管理控制台」檢視與分析,也可以透過 appcfg.py request_logs 下載。
「應用服務引擎」Python 執行階段環境,對 Python 標準程式庫的 logging 模組有特殊的支援,以瞭解記錄概念例如記錄等級 (debug - 偵錯、info - 資訊、warning - 警告、error - 錯誤、critical - 重大)。
import logging
from google.appengine.api import users
from google.appengine.ext import db
user = users.get_current_user()
if user:
q = db.GqlQuery("SELECT * FROM UserPrefs WHERE user = :1", user)
results = q.fetch(2)
if len(results) > 1:
logging.error("more than one UserPrefs object for user %s", str(user))
if len(results) == 0:
logging.debug("creating UserPrefs object for user %s", str(user))
userprefs = UserPrefs(user=user)
userprefs.put()
else:
userprefs = results[0]
else:
logging.debug("creating dummy UserPrefs for anonymous user")
執行環境包括幾個對應用程式有用的環境變數。部分環境變數是「應用服務引擎」特有的,而其他則為 CGI 標準的一部分。Python 程式碼可以使用 os.environ 字典來存取這些變數。
下列環境變數是「應用服務引擎」特有的:
APPLICATION_ID:目前正在執行之應用程式的 ID。CURRENT_VERSION_ID:目前正在執行之應用程式的主要和次要版本,顯示為「X.Y」。應用程式的 app.yaml 檔案會指定主要版本號碼 (「X」)。而應用程式的各個版本上傳到「應用服務引擎」時,會自動設定次要版本號碼 (「Y」)。在開發網頁伺服器上,次要版本是「1」。 AUTH_DOMAIN:使用此網域來驗證有 Users API (使用者 API) 的使用者。於 appspot.com 主控的應用服務具有 gmail.com 的 AUTH_DOMAIN,並接受任何 Google 帳戶。使用「Google 應用服務」、於自訂網域主控的應用服務,具有等於自訂網域的 AUTH_DOMAIN。下列環境變數是 CGI 標準的一部分,在「應用服務引擎」中有特殊行為:
SERVER_SOFTWARE:在開發網頁伺服器中,此值是 「Development/X.Y」,其中的 「X.Y」是執行階段的版本。 其他環境變數會根據 CGI 標準來設定。如需關於這些變數的詳細資訊,請參閱 CGI 標準。
提示:下列 webapp 要求處理常式會在瀏覽器中顯示應用程式可見的所有環境變數:
from google.appengine.ext import webapp
import os
class PrintEnvironmentHandler(webapp.RequestHandler):
def get(self):
for name in os.environ.keys():
self.response.out.write("%s = %s<br />\n" % (name, os.environ[name]))
應用程式的每個連入要求會算入「要求」的配額中。
接收要求而收到的資料會算入「連入頻寬 (可計費)」的配額中。回應要求而傳送的資料會算入「連出頻寬 (可計費)」的配額中。
HTTP 和 HTTPS (安全) 要求會算入「要求」、「連入頻寬 (可計費)」以及「連出頻寬 (可計費)」的配額中。「管理控制台」的 [Quota Details] (配額詳細資訊) 頁面也會另外提供「安全要求」、「安全連入頻寬」以及「安全連出頻寬」的數據,以供參考。只有 HTTPS 要求才會算入這些數據。
執行要求處理常式所花費的 CPU 處理時間會算入「CPU 時間 (可計費)」的配額中。
如需瞭解配額的詳細資訊,請參閱「配額」,或參閱「管理控制台」的「配額詳細資訊」一節。
除了配額之外,使用要求處理常式時也需遵循下列限制:
| 限制 | 大小 |
|---|---|
| 要求大小 | 10 MB |
| 回應大小 | 10 MB |
| 要求持續時間 | 30 秒 |
| 同時動態要求 | 30 |
| 應用程式檔案數量上限 | 1,000 |
| 靜態檔案數量上限 | 1,000 |
| 應用程式檔案大小上限 | 10 MB |
| 靜態檔案大小上限 | 10 MB |
| 所有應用程式和靜態檔案的總計大小上限 | 150 MB |
* 應用程式可以同時處理大約 30 個作用中的動態要求。這表示如果一個應用程式處理伺服器端要求的平均時間為 75 毫秒,則在沒有發生任何額外延遲的狀況下,每一秒最多可以處理 400 個要求 (1000 毫秒/秒 / 75 毫秒/要求 * 30 = 400)。為挪出空間給其他共用相同伺服器的應用程式,受到 CPU 嚴重限制的應用程式在處理長期執行的要求時,可能會發生額外的延遲。靜態檔案的要求則不會受到此限制的影響。