Help us understand the problem. What is going on with this article?

Pub/SubトリガーのCloud FunctionsをGoで作るときはpubsub.Messageを使おう

背景

Cloud FunctionsでGoが使えるようになってからだいぶ経ったので自分も使ってみようかと思ったのですが、Pub/Subのデータ取得がどうにも上手くいかずハマりました。

ということで備忘録を残しておきます。

ちなみにGoは初心者でCloud Functionsで触り始めたくらいのレベルです。

勢いfunctionを作成する

Cloud FunctionsでGoを入れようと思ったときはまずCloud Functionsのページから関数の作成を試すかと思います。

ランタイムに Go 1.11、トリガーに Cloud Pub/Sub を指定して作成するとデフォルトでこんなやつを書いておいてくれます。Googleさん親切!

cloud-functions
// Package p contains a Pub/Sub Cloud Function.
package p

import (
    "context"
    "log"
)

// PubSubMessage is the payload of a Pub/Sub event. Please refer to the docs for
// additional information regarding Pub/Sub events.
type PubSubMessage struct {
    Data []byte `json:"data"`
}

// HelloPubSub consumes a Pub/Sub message.
func HelloPubSub(ctx context.Context, m PubSubMessage) error {
    log.Println(string(m.Data))
    return nil
}

ちなみにトリガーをPub/Subにするとトピックも指定する必要があります。
今回は適当に go_pubsub_test としてfunctionを作成します。

Pub/Subトピックからpublishテストしてみる

最近のGCPはホント便利で、Pub/Subの画面からmessageのpublishができます。

topic_publish.png

テストメッセージということで、メッセージ本文に hello world、メッセージ属性のキーに key、 値に value を突っ込みました。

message_publish.png

functions側でこのメッセージのログを吐き出すようにしているので、下の公開ボタンを押してpublishしてみます。

logを見てみる

publishに成功するとfunctions側の呼び出し回数のグラフがはねます。ログを表示を押してloggingに遷移するとログが出てることが確認できます。

log.png

これを見ると、メッセージ本体のhello worldは出力されていますが、key/valueの情報は見当たりません。

こうなるのは当然で、今回は構造体PubSubMessageでメッセージを受けましたが、PubSubMessageはDataを持たないのでメッセージ以外の情報は出力できません。

pubsub.Messageを使う

色々調べてみると、タイトルに書いたpubsub.Messageというのが出てきました。

https://godoc.org/cloud.google.com/go/pubsub#Message

type Message struct {
    // ID identifies this message.
    // This ID is assigned by the server and is populated for Messages obtained from a subscription.
    // This field is read-only.
    ID  string

    // Data is the actual data in the message.
    Data []byte

    // Attributes represents the key-value pairs the current message
    // is labelled with.
    Attributes map[string]string

    // The time at which the message was published.
    // This is populated by the server for Messages obtained from a subscription.
    // This field is read-only.
    PublishTime time.Time
    // contains filtered or unexported fields
}

ここに色々な情報が入ってるようです。
さきほど設定したkeyvalueについては、Attributesで取れるようになります。

コードを修正する

ということでこれを利用して他のmessage情報も出力できるようにしてみます。

pubsub.Messageは cloud.google.com/go/pubsub から取得可能です。

Cloud Functions上で編集を実行し、以下のように書き換えます。

// Package p contains a Pub/Sub Cloud Function.
package p

import (
    "context"
    "log"
    "cloud.google.com/go/pubsub"
)

// HelloPubSub consumes a Pub/Sub message.
func HelloPubSub(ctx context.Context, m *pubsub.Message) error {
    log.Println(string(m.Data))
    if len(m.Attributes) > 0 {
        for k, v := range m.Attributes {
            log.Println("key", k)
            log.Println("value", v)
        }
    }
    return nil
}

デプロイして完了した後、同様にtopicを送りつけてみます。

スクリーンショット 2019-09-12 14.58.09.png

ちゃんとデータを取得することが出来ました。

公式もこれを定義しておいて欲しいんですが、きっとその辺は自分で調べられるくらいPub/Sub理解してなきゃ使うんじゃないってメッセージなんだと思ってます。きびしめ。

おまけ: メタデータ情報を取得する

Cloud Functionsではmetadataライブラリを使って他の情報を取得することができます。

試しにどんなデータが取得できるのかログを出してみます。

cloud-functions
// Package p contains a Pub/Sub Cloud Function.
package p

import (
    "context"
    "log"
    "cloud.google.com/go/pubsub"
    "cloud.google.com/go/functions/metadata"
)

// HelloPubSub consumes a Pub/Sub message.
func HelloPubSub(ctx context.Context, m *pubsub.Message) error {
    // metadata
    meta, _ := metadata.FromContext(ctx)

    log.Println("EventID", meta.EventID)
    log.Println("Timestamp", meta.Timestamp)
    log.Println("EventType", meta.EventType)

    log.Println("Resource.Service", meta.Resource.Service)
    log.Println("Resource.Name", meta.Resource.Name)
    log.Println("Resource.Type", meta.Resource.Type)

    return nil
}

出力結果は以下のような感じです。

go_log.png

pubsubの場合は Resource.Typetype.googleapis.com/google.pubsub.v1.PubsubMessage となってやってくるようですね。
また、どのtopicから来たかは Resource.Name から取れるようなので、topicに応じて処理を分岐させるような書き方もできます。

metadataに関してはpubsub以外のトリガーも扱えるため、Resource.Typeに応じて処理を変えるのが良さそうです。

とりあえず分かったことはここまで。他にも分かったことがあったら追記しようと思います。

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away