將 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 主控台的「App Engine Versions」(App Engine 版本) 頁面。

    前往版本頁面

  2. 找到您要刪除的非預設應用程式版本,然後點選旁邊的核取方塊。
  3. 按一下頁面頂部的 [刪除] 按鈕, 刪除應用程式版本。

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

應用程式結構

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

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 主控台的「Projects」(專案) 頁面。

    前往專案頁面

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

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

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

若要刪除應用程式版本:

  1. 前往 GCP 主控台的「App Engine Versions」(App Engine 版本) 頁面。

    前往版本頁面

  2. 找到您要刪除的非預設應用程式版本,然後點選旁邊的核取方塊。
  3. 按一下頁面頂部的 [刪除] 按鈕, 刪除應用程式版本。

刪除 Cloud SQL 執行個體

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

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

    前往 SQL 執行個體頁面

  2. 找到 您要刪除的 SQL 執行個體,然後按一下該執行個體的名稱。
  3. 按一下頁面頂端的 [刪除] 按鈕, 刪除該執行個體。

刪除 Cloud Storage 值區

如要刪除 Cloud Storage 值區:

  1. 前往 GCP 主控台的 「Cloud Storage」瀏覽器。

    前往 Cloud Storage 瀏覽器

  2. 在您要刪除的值區旁,點選對應的核取方塊。
  3. 按一下頁面頂端的 [刪除] 按鈕, 刪除該值區。

後續步驟

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

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

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

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

這個網頁