背景
Cloud FunctionsでGoが使えるようになってからだいぶ経ったので自分も使ってみようかと思ったのですが、Pub/Subのデータ取得がどうにも上手くいかずハマりました。
ということで備忘録を残しておきます。
ちなみにGoは初心者でCloud Functionsで触り始めたくらいのレベルです。
勢いfunctionを作成する
Cloud FunctionsでGoを入れようと思ったときはまずCloud Functionsのページから関数の作成を試すかと思います。
ランタイムに Go 1.11
、トリガーに Cloud Pub/Sub
を指定して作成するとデフォルトでこんなやつを書いておいてくれます。Googleさん親切!
// 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ができます。
テストメッセージということで、メッセージ本文に hello world
、メッセージ属性のキーに key
、 値に value
を突っ込みました。
functions側でこのメッセージのログを吐き出すようにしているので、下の公開
ボタンを押してpublishしてみます。
logを見てみる
publishに成功するとfunctions側の呼び出し回数のグラフがはねます。ログを表示を押してloggingに遷移するとログが出てることが確認できます。
これを見ると、メッセージ本体のhello world
は出力されていますが、key/valueの情報は見当たりません。
こうなるのは当然で、今回は構造体PubSubMessageでメッセージを受けましたが、PubSubMessageはDataを持たないのでメッセージ以外の情報は出力できません。
pubsub.Messageを使う
色々調べてみると、タイトルに書いた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
}
ここに色々な情報が入ってるようです。
さきほど設定したkey
とvalue
については、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を送りつけてみます。
ちゃんとデータを取得することが出来ました。
公式もこれを定義しておいて欲しいんですが、きっとその辺は自分で調べられるくらいPub/Sub理解してなきゃ使うんじゃないってメッセージなんだと思ってます。きびしめ。
おまけ: メタデータ情報を取得する
Cloud Functionsではmetadataライブラリを使って他の情報を取得することができます。
試しにどんなデータが取得できるのかログを出してみます。
// 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
}
出力結果は以下のような感じです。
pubsubの場合は Resource.Type
が type.googleapis.com/google.pubsub.v1.PubsubMessage
となってやってくるようですね。
また、どのtopicから来たかは Resource.Name
から取れるようなので、topicに応じて処理を分岐させるような書き方もできます。
metadataに関してはpubsub以外のトリガーも扱えるため、Resource.Typeに応じて処理を変えるのが良さそうです。
とりあえず分かったことはここまで。他にも分かったことがあったら追記しようと思います。