從 Go 1.11 遷移至最新的 Go 執行階段

本頁說明如何從第一代遷移至第二代 Go 執行階段。如要升級第二代應用程式,改用最新支援的 Go 版本,請參閱「升級現有應用程式」。

Go 1.11 已於 2024 年 1 月 30 日終止支援。現有的 Go 1.11 應用程式會繼續執行並接收流量。不過,如果應用程式使用的執行階段已過支援期限,App Engine 可能會禁止重新部署。建議您按照本頁面的指南,遷移至 Go 的最新支援執行階段

遷移至支援的第二代 Go 執行階段,即可使用最新語言功能,並以慣用程式碼建構更具可攜性的應用程式。

第二代執行階段的變更

升級至支援的第二代 Go 執行階段時,請注意下列差異:

  • 為減少執行階段遷移作業的負擔和複雜度,App Engine 標準環境可讓您在第二代執行階段中存取許多舊版套裝服務和 API,例如 Memcache。第二代 Go 應用程式可透過 Go 適用的 App Engine SDK 呼叫套裝組合服務 API,並存取與 Go 1.11 執行階段相同的大部分功能。

    您也可以選擇使用 Google Cloud 產品,這些產品提供的功能與舊版服務套裝組合類似。這些產品提供慣用的 Go 適用的 Cloud 用戶端程式庫。對於Google Cloud中未以獨立產品形式提供的套裝服務 (例如圖片處理、搜尋和訊息),您可以改用第三方供應商或其他解決方法。 Google Cloud

    如要進一步瞭解如何遷移至未綁定的服務,請參閱「從套裝組合服務遷移」。

  • app.yaml 設定檔中部分元素的行為已經過修改。詳情請參閱「app.yaml 檔案異動」。

  • 第二代執行階段的記錄功能遵循 Cloud Logging 的記錄標準。在第二代執行階段中,應用程式記錄不再與要求記錄捆綁在一起,而是分開記錄。如要進一步瞭解如何在第二代執行階段讀取及寫入記錄,請參閱記錄指南

記憶體用量差異

與第一代執行階段相比,第二代執行階段的記憶體用量基準較高。這是因為多項因素,例如基本圖片版本不同,以及兩代產品計算記憶體用量的方式不同。

第二代執行階段會計算執行個體記憶體用量,方法是將應用程式程序使用的記憶體量,以及動態快取在記憶體中的應用程式檔案數量加總。為避免記憶體用量過高的應用程式因超出記憶體限制而導致執行個體關閉,請升級至記憶體較大的執行個體類別

CPU 用量差異

第二代執行階段在執行個體冷啟動時,CPU 使用率的基準可能會較高。視應用程式的縮放設定而定,這可能會產生非預期的副作用,例如:如果應用程式設定為根據 CPU 使用率進行縮放,執行個體數量可能會超出預期。為避免發生這個問題,請檢查並測試應用程式的擴充設定,確保執行個體數量可接受。

要求標頭差異

第一代執行階段允許將含有底線 (例如 X-Test-Foo_bar) 的要求標頭轉送至應用程式。第二代執行階段會在主機架構中導入 Nginx。這項變更會導致第二代執行階段自動移除含有底線 (_) 的標頭。為避免應用程式發生問題,請勿在應用程式要求標頭中使用底線。

app.yaml 檔案的變更

app.yaml 設定檔中部分元素的行為已經過修改:

元素 變更類型 說明
app_engine_apis 使用舊版套裝組合服務的應用程式必須提供 如要存取 第二代執行階段的舊版服務套裝組合,必須設為 true
login 如果 app_engine_apistrue,則支援此功能 如果您未使用第二代執行階段的舊版套裝服務,請改用 替代方法來驗證使用者
runtime 已修改 runtime 元素變更為指定第二代執行階段

詳情請參閱 app.yaml 參考資料

建立 main 套件

您的服務至少要在一個來源檔案內包含 package main 陳述式。

App Engine 舊版套裝組合服務

如果您的服務使用第二代執行階段的舊版套裝服務

  • 您的服務只能使用 v2 (google.golang.org/appengine/v2) 套件。 使用舊版 v1 (google.golang.org/appengine) 套件會導致錯誤。

  • main() 函式中,請呼叫 appengine.Main(),而非 http.ListenAndServe()。確保 userappengine API 可以存取目前的要求情境。

編寫 main 套件

如果您的服務尚未包含 main 套件,請新增 package main 陳述式並編寫 main() 函式。main() 函式「至少」應:

  • 讀取 PORT 環境變數並呼叫 http.ListenAndServe() 函式:

    port := os.Getenv("PORT")
    if port == "" {
    	port = "8080"
    	log.Printf("Defaulting to port %s", port)
    }
    
    log.Printf("Listening on port %s", port)
    if err := http.ListenAndServe(":"+port, nil); err != nil {
    	log.Fatal(err)
    }

註冊 HTTP 處理常式

您可以透過下列其中一個選項註冊 HTTP 處理常式:

  • 建議使用的方法為手動將所有 http.HandleFunc() 呼叫從您的套件移至 main 套件的 main() 函式。
  • 或者,將應用程式的套件匯入您的 main 套件,確保每個 init() 函式 (當中包含 http.HandleFunc()) 都能在啟動時執行。

    您可以透過下列的 bash 指令碼找到使用 http.HandleFunc() 呼叫的所有套件,並將輸出內容複製到 main 套件的 import 區塊:

    gp=$(go env GOPATH) && p=$(pwd) && pkg=${p#"$gp/src/"} && find . -name "*.go" | xargs grep "http.HandleFunc" --files-with-matches | grep -v vendor/ | grep -v '/main.go' | sed "s#\./\(.*\)/[^/]\+\.go#\t_ \"$pkg/\1\"#" | sort | uniq
    

建立檔案結構

Go 要求每個套件都必須要有自己的目錄。您可以在專案的 app.yaml 檔案中使用 main:,來通知 App Engine 您 main 套件的位置。舉例來說,如果您應用程式的檔案結構如下:

myapp/
├── app.yaml
├── foo.go
├── bar.go
└── web/
    └── main.go

您的 app.yaml 檔案就會包含:

main: ./web # Relative filepath to the directory containing your main package.

如要進一步瞭解 main 標記,請參閱app.yaml參考資料

將檔案移至您的 GOPATH

使用下列指令找出您的 GOPATH

go env GOPATH

請將所有相關檔案及匯入內容移至您的 GOPATH。如果使用相對路徑來匯入 (例如 import ./guestbook),請將匯入路徑更新為完整路徑:import github.com/example/myapp/guestbook