將 Cloud Pub/Sub 與 Go 搭配使用

許多應用程式都需要在網路要求的內容之外執行背景處理。在這個範例中,Bookshelf 應用程式會將工作傳送至單獨的背景工作站來執行。工作站會從 Google 圖書 API 收集資訊,並更新資料庫中的書籍資訊。這個範例示範如何在 Google App Engine 中設定單獨的服務、如何在 App Engine 彈性環境中執行工作站程序,以及如何處理生命週期事件。

本頁面是多頁教學課程的一部分。如要從頭開始並閱讀設定操作說明,請前往 Go Bookshelf 應用程式頁面。

設定

  1. 前往包含程式碼範例的目錄:

    Linux/macOS

    cd $GOPATH/src/github.com/GoogleCloudPlatform/golang-samples/getting-started/bookshelf
    

    Windows

    cd %GOPATH%\src\github.com\GoogleCloudPlatform\golang-samples\getting-started\bookshelf
    

  2. 開啟 config.go 進行編輯。

  3. 將下面這一行取消註解:

    // PubsubClient, err = configurePubsub("<your-project-id>")
    
  4. "<your-project-id>" 替換為您的專案 ID。

  5. 儲存並關閉 config.go

在本機電腦執行應用程式

  1. 執行應用程式以啟動本機網路伺服器:

    cd app
    go run app.go auth.go template.go
    
  2. 在網路瀏覽器中,輸入下列網址:

    http://localhost:8080

    您會發現應用程式正在執行,但尚未新增任何書籍。

  3. 系統可以在您設定 PORT 環境變數之後,採用與前端應用程式相同的方式啟動工作站。

    Linux/macOS

    cd $GOPATH/src/github.com/GoogleCloudPlatform/golang-samples/getting-started/bookshelf
    cd pubsub_worker
    PORT=8081 go run worker.go
    

    Windows

    cd %GOPATH%\src\github.com\GoogleCloudPlatform\golang-samples\getting-started\bookshelf
    cd pubsub_worker
    set PORT=8081
    go run worker.go
    

您可以瀏覽至 http://localhost:8081,找到工作站執行個體。工作站的網頁會顯示關於工作站處理的書籍數狀態。

接著新增幾本知名的書籍到書架上。如果應用程式和工作站執行個體同時在本機上執行,您可以觀看工作站在背景更新書籍資訊。

請確保在您新增書籍之前,至少執行一次工作站。工作站會建立 Pub/Sub 訂閱項目來接聽事件。若沒有訂閱項目,發佈至主題的事件會遺失,且您無法查看對書架資料的任何變更。有訂閱項目後,即使目前沒有任何工作站在接聽事件,事件也仍會排入佇列。一旦有工作站上線,Pub/Sub 就會傳送已排入佇列的事件。

按下 Control+C 即可離開本機網路伺服器。

將應用程式部署至 App Engine 彈性環境

  1. app 目錄中,輸入下列指令以部署範例:

    Linux/macOS

    $ cd $GOPATH/src/github.com/GoogleCloudPlatform/golang-samples/getting-started/bookshelf
    
    # Deploy the worker
    cd pubsub_worker
    gcloud app deploy
    
    # Deploy the main app
    cd ../app
    gcloud app deploy
    

    Windows

    cd %GOPATH%\src\github.com\GoogleCloudPlatform\golang-samples\getting-started\bookshelf
    
    # Deploy the worker
    cd pubsub_worker
    gcloud app deploy
    
    # Deploy the main app
    cd ..\app
    gcloud app deploy
    

    這些指令會部署前端與工作站模組。這表示您不必像在電腦中執行應用程式時一樣單獨啟動工作站。如要進一步瞭解模組,請參閱 App Engine 說明文件

  2. 在網路瀏覽器中,輸入下列網址。將 [YOUR_PROJECT_ID] 替換為您的專案 ID:

    https://[YOUR_PROJECT_ID].appspot.com
    
  3. 在網路瀏覽器中,輸入下列網址,找到工作站執行個體。

    https://worker-dot-[YOUR_PROJECT_ID].appspot.com
    

若您更新了應用程式,您可以輸入第一次部署應用程式時使用的指令來部署更新版本。新部署會為您的應用程式建立新版本,並會將它升級為預設版本。 應用程式的較舊版本會保留下來,相關聯的 VM 執行個體也會保留下來。請注意,這些應用程式版本和 VM 執行個體全部都是計費資源。

您可以刪除應用程式的非預設版本來降低費用。

若要刪除應用程式版本:

  1. 在 GCP Console 中,前往 App Engine 的「Versions」(版本) 頁面。

    前往「Versions」(版本) 頁面

  2. 勾選您要刪除的非預設應用程式版本的核取方塊。
  3. 按一下 [Delete] (刪除) 即可刪除應用程式版本。

如需有關清除計費資源的完整資訊,請參閱本教學課程最後一個步驟的清除一節。

應用程式結構

下圖顯示應用程式元件,以及這些元件彼此之間的連線方式。

Cloud Pub/Sub 範例結構

每當資料庫中有書籍更新時,應用程式都會將事件發佈至 Cloud Pub/Sub。單獨執行的工作站會接聽這些事件。收到事件時,工作站會向 Books API 發出取得書籍資訊的要求,並會更新資料庫中的書籍記錄。更新記錄之後,您可以重新整理書籍的資訊頁面並查看新資訊。

瞭解程式碼

這部分內容會逐步引導您瞭解應用程式程式碼,並說明其運作方式。

建立主題和訂閱項目

您必須建立主題和訂閱項目,才能傳送訊息至 Cloud Pub/Sub 及接收其訊息。Cloud Pub/Sub 可讓您將一個主題中的訊息發佈給許多訂閱者。但在這個應用程式中,只有一個訂閱者。

下列程式碼會使用常數主題與訂閱項目名稱,建立主題與訂閱項目。您可以放心地重複執行這兩行程式碼。例如,Bookshelf 應用程式的 worker.go 會在 main 函式中呼叫這些程式碼:

// Create pubsub topic if it does not yet exist.
topic := bookshelf.PubsubClient.Topic(bookshelf.PubsubTopicID)
exists, err := topic.Exists(ctx)
if err != nil {
	log.Fatalf("Error checking for topic: %v", err)
}
if !exists {
	if _, err := bookshelf.PubsubClient.CreateTopic(ctx, bookshelf.PubsubTopicID); err != nil {
		log.Fatalf("Failed to create topic: %v", err)
	}
}

// Create topic subscription if it does not yet exist.
subscription = bookshelf.PubsubClient.Subscription(subName)
exists, err = subscription.Exists(ctx)
if err != nil {
	log.Fatalf("Error checking for subscription: %v", err)
}
if !exists {
	if _, err = bookshelf.PubsubClient.CreateSubscription(ctx, subName, pubsub.SubscriptionConfig{Topic: topic}); err != nil {
		log.Fatalf("Failed to create subscription: %v", err)
	}
}

發佈事件

應用程式會將含有已更新書籍 ID 的事件傳送至主題。這會讓工作站知道應該處理哪本書。

// publishUpdate notifies Pub/Sub subscribers that the book identified with
// the given ID has been added/modified.
func publishUpdate(bookID int64) {
	if bookshelf.PubsubClient == nil {
		return
	}

	ctx := context.Background()

	b, err := json.Marshal(bookID)
	if err != nil {
		return
	}
	topic := bookshelf.PubsubClient.Topic(bookshelf.PubsubTopicID)
	_, err = topic.Publish(ctx, &pubsub.Message{Data: b}).Get(ctx)
	log.Printf("Published update to Pub/Sub for Book ID %d: %v", bookID, err)
}

每當使用者建立或更新書籍時,應用程式都會呼叫 publishUpdate 函式。

工作站應用程式

工作站是單獨的應用程式,可接聽 pub/sub 事件,而非提供與使用者互動的網路應用程式。這樣會將應用程式分成兩種獨立的服務,這兩者會使用 pub/sub 通訊,而非彼此直接通訊。區分服務可以讓您分別設定及調度前端與工作站執行個體的資源數。

接聽事件

如要接收訊息,工作站必須建立或使用主題的訂閱項目。前端會將事件發佈至特定主題,而工作站會訂閱相同主題。

工作站使用 subscribe 函式接聽事件並觸發處理:

func subscribe() {
	ctx := context.Background()
	err := subscription.Receive(ctx, func(ctx context.Context, msg *pubsub.Message) {
		var id int64
		if err := json.Unmarshal(msg.Data, &id); err != nil {
			log.Printf("could not decode message data: %#v", msg)
			msg.Ack()
			return
		}

		log.Printf("[ID %d] Processing.", id)
		if err := update(id); err != nil {
			log.Printf("[ID %d] could not update: %v", id, err)
			msg.Nack()
			return
		}

		countMu.Lock()
		count++
		countMu.Unlock()

		msg.Ack()
		log.Printf("[ID %d] ACK", id)
	})
	if err != nil {
		log.Fatal(err)
	}
}

處理書籍

如要處理書籍,工作站會按書籍 ID 擷取書籍、尋找其他資訊,然後將更新後的資訊儲存回資料庫:

// update retrieves the book with the given ID, finds metata from the Books
// server and updates the database with the book's details.
func update(bookID int64) error {
	book, err := bookshelf.DB.GetBook(bookID)
	if err != nil {
		return err
	}

	vols, err := booksClient.Volumes.List(book.Title).Do()
	if err != nil {
		return err
	}

	if len(vols.Items) == 0 {
		return nil
	}

	info := vols.Items[0].VolumeInfo
	book.Title = info.Title
	book.Author = strings.Join(info.Authors, ", ")
	book.PublishedDate = info.PublishedDate
	if book.Description == "" {
		book.Description = info.Description
	}
	if book.ImageURL == "" && info.ImageLinks != nil {
		url := info.ImageLinks.Thumbnail
		// Replace http with https to prevent Content Security errors on the page.
		book.ImageURL = strings.Replace(url, "http://", "https://", 1)
	}

	return bookshelf.DB.UpdateBook(book)
}

在 Cloud Platform 上執行

根據預設,在 App Engine 彈性環境中執行的應用程式必須回應 HTTP 要求,健康狀態檢查工具才能指出良好的健康狀態。為了滿足這項需求,Bookshelf pub/sub 工作站會接聽 HTTP 要求,並回應處理的書籍數:

// Publish a count of processed requests to the server homepage.
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
	countMu.Lock()
	defer countMu.Unlock()
	fmt.Fprintf(w, "This worker has processed %d books.", count)
})

port := "8080"
if p := os.Getenv("PORT"); p != "" {
	port = p
}
log.Fatal(http.ListenAndServe(":"+port, nil))

工作站需要自己的模組設定。這個設定與前端使用的 app.yaml 檔案類似,但關鍵差異在於 service: worker 子句。App Engine 應用程式可以有多個獨立的服務。這表示您可以輕鬆獨立地部署、設定、調度及更新應用程式的各個部分。

# Copyright 2019 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

runtime: go
env: flex
service: worker

resources:
  cpu: .5
  memory_gb: 1.3
  disk_size_gb: 10

automatic_scaling:
  min_num_instances: 1
  max_num_instances: 2
  cool_down_period_sec: 60
  cpu_utilization:
    target_utilization: 0.75

清除所用資源

如要避免系統向您的 Google Cloud Platform 帳戶,收取您在本教學課程中所用資源的相關費用:

刪除專案

如要避免系統向您收費,最簡單的方法就是刪除您在教學課程中建立的專案。

刪除專案:

  1. 前往 GCP Console 中的「Manage resources」(管理資源) 頁面。

    前往「Manage resources」(管理資源) 頁面

  2. 在專案清單中選取您要刪除的專案,然後按一下「Delete」(刪除) 圖示
  3. 在對話方塊中輸入專案 ID,然後按一下 [Shut down] (關閉) 以刪除專案。

刪除應用程式的非預設版本

如果您不想刪除專案,可以透過刪除應用程式的非預設版本來降低費用。

若要刪除應用程式版本:

  1. 在 GCP Console 中,前往 App Engine 的「Versions」(版本) 頁面。

    前往「Versions」(版本) 頁面

  2. 勾選您要刪除的非預設應用程式版本的核取方塊。
  3. 按一下 [Delete] (刪除) 即可刪除應用程式版本。

刪除 Cloud SQL 執行個體

如要刪除 Cloud SQL 執行個體,請執行以下操作:

  1. 前往 GCP Console 的「SQL Instances」(SQL 執行個體) 頁面。

    前往「SQL Instances」(SQL 執行個體) 頁面

  2. 按一下您要刪除的 SQL 執行個體名稱。
  3. 按一下 [Delete] (刪除) ,藉此刪除執行個體。

刪除 Cloud Storage 值區

如要刪除 Cloud Storage 值區:

  1. 前往 GCP Console 的「Cloud Storage Browser」(Cloud Storage 瀏覽器) 頁面。

    前往「Cloud Storage Browser」(Cloud Storage 瀏覽器) 頁面

  2. 按一下您要刪除的值區的核取方塊。
  3. 按一下「Delete」(刪除) 圖示 以刪除值區。

後續步驟

瞭解如何在 Compute Engine 中執行 Go Bookshelf 範例

歡迎親自試用其他 Google Cloud Platform 功能。請參考我們的教學課程

本頁內容對您是否有任何幫助?請提供意見:

傳送您對下列選項的寶貴意見...

這個網頁