BoltzEngine RPC インターフェイス

BoltzEngine が持つ RPC インターフェイスについて解説します。

インターフェイスの種類

BoltzEngine は gRPC と Go 言語の net/rpc インターフェイスを持ち、それぞれのメソッドを呼び出して動作します。RPC インターフェイスは、通常 BoltzEngine Master サービスが起動しているサーバーの 13009 ポート(gRPC) と 13010 ポート(net/rpc)で動作しています。

boltz コマンドを -json オプション付きで起動した場合、RPC インターフェイスは JSON-RPC 1.0 に切り替わり、Go 言語以外のプログラミング言語でも JSON-RPC 1.0 ライブラリもしくはソケット通信を用いて BoltzEngine を利用することができます。

net/rpc と JSON-RPC は排他利用となっており、1 つの BoltzEngine クラスタは net/rpc と JSON-RPC を同時に扱うことができません。また、JSON-RPC で動作させる場合、net/rpc で動作する場合よりも性能が低下します。JSON-RPC を使用した BoltzEngine の呼び出しは、BoltzEngine v1.1 以降でサポートされます。

gRPC

gRPC は Go, Java, Python, Ruby, PHP など複数の言語で利用可能な RPC フレームワークです。Protocol Buffers で記述された定義から各種言語のコードを生成し、それを使ってサーバとの通信を行います。

実装言語により詳細は異なりますが、基本となる処理の流れは同一です。ここでは Go を使った呼び出しを例示します。

 1// TLS 通信を行わない場合はこちら
 2//conn, err := grpc.Dial(addr, grpc.WithInsecure())
 3conn, err := grpc.Dial(addr)
 4if err != nil {
 5    log.Fatalln("Dial:", err)
 6}
 7defer conn.Close()
 8c := pb.NewBoltzGatewayClient(conn)
 9
10var key string  // APNs 秘密鍵の内容
11var cert string // APNs 証明書の内容
12var serverKey   // GCM サーバキー
13msg := pb.Message{
14    // APNsトークンが.Tokensに存在しない場合は不要
15    ApnsHeader: &apns.Header{
16        Address:      "gateway.push.apple.com:2195",
17        KeyPEMBlock:  key,
18        CertPEMBlock: cert,
19    },
20    // FCM(GCM)登録IDが.Tokensに存在しない場合は不要
21    GcmHeader: &gcm.Header{
22        RequestURL: "https://android.googleapis.com/gcm/send",
23        ServerKey:  serverKey,
24    },
25    // ADM登録IDが.Tokensに存在しない場合は不要
26    AdmHeader: &adm.Header{
27        OriginURL: "https://api.amazon.com",
28        TokenURL: "https://api.amazon.com/auth/o2/token",
29        ClientID: clientID,
30        ClientSecret: clientSecret,
31    },
32    // WebPushトークンが.Tokensに存在しない場合は不要
33    WebpushHeader: &webpush.Header{
34        Subject: "mailto:boltz@example.com",
35        PrivateKey: privateKey, // VAPID PrivateKey
36        PublicKey: publicKey, // VAPID PublicKey
37    },
38    Tokens: []string{
39        // APNs は"1"、GCM は"2"、WebPush は"4"、ADM は"5"をトークンの先頭に追加してください
40        "1(16進文字列表記の APNs トークン)",
41        "2(GCM 登録 ID)",
42        `4{"v":1,"endpoint":"(WebPush エンドポイント)","p256dh":"(ブラウザ公開鍵)","auth":"(WebPush 乱数)"}`,
43        "5(ADM 登録 ID)",
44    },
45    Priority: pb.Priority_HIGH,
46    Payload:  `{"aps":{"alert":"hello"}}`, // iOS 端末へ通知する内容
47    Parameters: &gcm.Parameters{ // Android/FireOS 端末へ通知する内容
48        Data: map[string]string{
49            "message": "hello",
50        },
51    },
52    Body: "hello", // WebPush 対応ブラウザへ通知する内容
53    BandWidth: 2000, // 1秒あたりの最大通知件数(0なら無制限)
54}
55stream, err := c.Send(context.Background(), &msg)
56if err != nil {
57    log.Fatalln("Send:", err)
58}
59for {
60    v, err := stream.Recv()
61    if err == io.EOF {
62        break
63    }
64    if err != nil {
65        log.Fatalln("Recv:", err)
66    }
67    switch e := v.Event.(type) {
68    case *pb.Event_Failed: // 通知失敗した端末
69        x := e.Failed
70        fmt.Printf("%v: %s: %v\n", v.Platform, x.Token, x.Kind)
71        // x.Kindが
72        // - INVALID_TOKEN なら無効なので次から削除した方が良い
73        // - INVALID_PAYLOAD ならメッセージがおかしいので見直しが必要(通常は発生しない)
74        // - TEMPORARY_ERROR の場合は上記以外のエラー
75    case *pb.Event_Renewed: // トークン更新の発生した端末
76        x := e.Renewed
77        fmt.Printf("%v: %s -> %s\n", v.Platform, x.RecentToken, x.LatestToken)
78        // x.RecentTokenがx.LatestTokenに切り替わったので次回からx.LatestTokenを使う
79        // x.RecentToken, x.LatestTokenのどちらも、1文字目にはサービスIDを含む
80    }
81}

Send メソッドから返却された stream から、通知時に発生したエラーをトークン毎に取得できます。 すべての通知を送り、発生したエラーをすべて取得し終わると、stream は EOF に到達します。

Go 言語 net/rpc

Go 言語で net/rpc パッケージ使用する場合、rpc.Dial で BoltzEngine と接続します。 その後、Call または Go メソッドの第1引数でサービスメソッドを指定して呼び出します。

 1req := &apns.Request{
 2    Addr:       "gateway.push.apple.com:2195",
 3    Credential: config.Credential(),
 4    Messages:   []*Message{},
 5}
 6for i, token := range tokens {
 7    msg := &apns.Message{
 8        ID:      uint32(i),
 9        Token:   token,
10        Payload: []byte(`{"aps":{"alert":"hello"}}`),
11    }
12    req.Messages = append(req.Messages, msg)
13}
14c, err := rpc.Dial("tcp", "boltz-server:13010")
15if err != nil {
16    log.Fatal("Dial:", err)
17}
18defer c.Close()
19 
20var resp apns.Response
21err = c.Call("BoltzEngine.BroadcastMessagesToAPNs", req, &resp)
22if err != nil {
23    log.Fatal("Call:", err)
24}

JSON-RPC

JSON-RPC を使用する場合、JSON-RPC v1.0 の仕様に対応したクライアントライブラリを使用するか、ソケット通信にてJSONを送受信して処理を行います。

不正な JSON を送信した場合、戻り値の error 要素にエラーメッセージがセットされます。

1{
2  "id": 0,
3  "result": null,
4  "error": "json: cannot unmarshal number into Go value of type string"
5}

gRPC メソッド一覧

BoltzEngine の gRPC インターフェイスが提供するメソッドについて、概要と使用方法について解説します。 メッセージオブジェクトの詳細は Protocol Buffers の定義または生成されたコードを参照ください。

以下の説明の中で、トークンとある表現は、BoltzEngine gRPC サービスで扱う形のトークンを意味します。 APNs のトークンは16進エンコーディングで文字列にして、その先頭に"1"を加えてください。 また、GCM の登録 ID (Registration ID)はそのまま、先頭に"2"を加えてください。

同じように、gRPC サービスから返却されるトークンも、同様のルールにより符号化されています。

FetchStatistics

BoltzEngine クラスタの状況を取得します。

  • 導入バージョン:BoltzEngine v2.0
  • リクエストオブジェクト:StatisticsQuery
  • レスポンスオブジェクト:MasterStatistics
1c := pb.NewBoltzGatewayClient(conn)
2var query pb.StatisticsQuery
3v, err := c.FetchStatistics(context.Background(), &q)

Send

APNs/GCM 両プラットフォームへメッセージを送信します。 メソッドの戻り値としてストリームを返し、ストリームからは通知の結果なんらかの処理が必要になったトークンと理由(イベント)を取得できます。 何もエラーがなければストリームはすぐに EOF を返します。

ストリームから取得できる Event は、

  • 通知は成功したがトークンが更新された: TokenRenewal
  • 通知が失敗した: DeliveryFailure

の2通り存在します。TokenRenewal の場合は、次回からは更新後のトークンを使わなければなりません。 また、DeliveryFailure の場合は

  • 通信状況等の一時的なエラー: FailureKind_TEMPORARY_ERROR
  • 無効なトークン: FailureKind_INVALID_TOKEN
  • メッセージに不備がある: FailureKind_INVALID_PAYLOAD

の3種類に分かれます。

  • 導入バージョン:BoltzEngine v2.0
  • リクエストオブジェクト:Message
  • レスポンスオブジェクト:Event ストリーム
1c := pb.NewBoltzGatewayClient(conn)
2msg := pb.Message{ ... }
3stream, err := c.Send(context.Background(), &msg)

FetchFeedback

APNs のフィードバックサービスから無効になったトークンを取得します。

  • 導入バージョン:BoltzEngine v2.0
  • リクエストオブジェクト:apns.Header
  • レスポンスオブジェクト:UnavailableTokenEvent ストリーム
1c := pb.NewBoltzGatewayClient(conn)
2h := apns.Header{ ... }
3stream, err := c.FetchFeedback(context.Background(), &h)

net/rpc メソッド一覧

BoltzEngine の RPC インターフェイスが提供するメソッドについて、概要と使用方法について解説します。

net/rpc で呼び出す際は、Call または Go メソッドの第1引数に文字列でメソッド名を指定、第2引数にリクエストオブジェクト、第3引数にレスポンスオブジェクトの戻り先の変数の参照を渡します。

JSON-RPC で送信する場合、params の第1要素にリクエストオブジェクトを渡し、レスポンスの result 要素にレスポンスオブジェクトが渡されます。

以下の説明の中で、JSON-RPC インターフェイスのサンプルで —> はサーバーへのリクエスト、 <— はサーバーからのレスポンスを表します。また、長い BASE64 エンコードの文字列が入る部分は <<BASE64>> で、GCM の Registration ID が入る部分は <<REGISTRATION_ID>> 表記します。

BoltzEngine.BroadcastMessagesToAPNs

APNs へメッセージを送信します。 Messages の内容は、APNs Notification format(Command=2)の Frame data で定義されている項目と一致します。

  • 導入バージョン:BoltzEngine v1.0
  • リクエストオブジェクト:apns.Request
  • レスポンスオブジェクト:apns.Response

net/rpc

1req := &apns.Request{ ... }
2var resp apns.Response
3err = client.Call("BoltzEngine.BroadcastMessagesToAPNs", req, &resp)

JSON-RPC

 1---> {
 2  "method": "BoltzEngine.BroadcastMessagesToAPNs",
 3  "params": [
 4    {
 5      "Addr": "gateway.sandbox.push.apple.com:2195",
 6      "Credential": {
 7        "KeyPEMBlock": "<<BASE64>>",
 8        "CertPEMBlock": "<<BASE64>>",
 9        "InsecureSkipVerify": false
10      },
11      "Messages": [
12        {
13          "ID": 0,
14          "Expir": 1432798652,
15          "Token": "<<BASE64>>",
16          "Payload": "<<BASE64>>",
17          "Priority": 10
18        },
19        {
20          "ID": 0,
21          "Expir": 1432798652,
22          "Token": "<<BASE64>>",
23          "Payload": "<<BASE64>>",
24          "Priority": 10
25        }
26      ]
27    }
28  ],
29  "id": 0
30}
31
32<--- {
33  "id": 0,
34  "result": {
35    "FailedMessages": null
36  },
37  "error": null
38}

BoltzEngine.ReceiveFeedbackFromAPNs

APNs のフィードバックサービスへアクセスし、無効になったトークンの一覧を取得します。 Body の内容は、APNs Feedback Service で定義されている項目と一致します。

  • 導入バージョン:BoltzEngine v1.0
  • リクエストオブジェクト:apns.FBRequest
  • レスポンスオブジェクト:apns.FBResponse

net/rpc

1req := &apns.FBRequest{ ... }
2var resp apns.FBResponse
3err = client.Call("BoltzEngine.ReceiveFeedbackFromAPNs", req, &resp)

JSON-RPC

 1---> {
 2  "method": "BoltzEngine.ReceiveFeedbackFromAPNs",
 3  "params": [
 4    {
 5      "Addr": "feedback.push.apple.com:2196",
 6      "Credential": {
 7        "KeyPEMBlock": "<<BASE64>>",
 8        "CertPEMBlock": "<<BASE64>>",
 9        "InsecureSkipVerify": false
10      },
11    }
12  ],
13  "id": 0
14}
15
16<--- {
17  "id": 0,
18  "result": {
19    "Body":[
20      {
21        "Timestamp": 1432798652,
22        "Token": "<<BASE64>>"
23      },
24      {
25        "Timestamp": 1432798652,
26        "Token": "<<BASE64>>"
27      },
28    ]
29  },
30  "error": null
31}

BoltzEngine.BroadcastMessagesToGCM(HTTP)

GCM へのリクエストは、HTTP と XMPP の 2 通り存在します。 gcm.RequestURLhttp:// または https:// で開始される場合は、HTTP モードとなります。 Messages の内容は、GCM-HTTP リクエストフォーマットで定義されている項目と一致します。

制限事項:Message 各個の registration_ids には 1000 個までのレジストレーション ID を含むことができます。

  • 導入バージョン:BoltzEngine v1.0
  • リクエストオブジェクト:gcm.Request
  • レスポンスオブジェクト:gcm.Response

net/rpc

1req := &gcm.Request{
2    URL: "https://android.googleapis.com/gcm/send",
3    ...
4}
5var resp gcm.Response
6err = client.Call("BoltzEngine.BroadcastMessagesToGCM", req, &resp)

JSON-RPC

 1---> {
 2  "method": "BoltzEngine.BroadcastMessagesToGCM",
 3  "params": [
 4    {
 5      "URL": "https://android.googleapis.com/gcm/send",
 6      "Credential": {
 7        "ServerKey": "o4sQfx2xD_ktryafbibakigczq7Hfekve9zmFdm",
 8        "InsecureSkipVerify": false
 9      },
10      "Messages": [
11        {
12          "registration_ids": [
13            "<<REGISTRATION_ID>>",
14            "<<REGISTRATION_ID>>"
15          ],
16          "data": {
17            "alert": "........................",
18            "mid": "55",
19            "sound": "default"
20          },
21          "collapse_key": "37",
22          "time_to_live": 300
23        }
24      ]
25    }
26  ],
27  "id": 0
28}
29
30<--- {
31  "id": 0,
32  "result": {
33    "FailedMessages": null
34  },
35  "error": null
36}

BoltzEngine.BroadcastMessagesToGCM(XMPP)

GCM へのリクエストは、HTTP と XMPP の 2 通り存在します。 gcm.RequestURLhttp:// または https:// 以外なら、XMPP モードとなります。 Messages の内容は、GCM-XMPP リクエストフォーマットで定義されている項目と一致します。

XMPP モードの場合、プロトコルの仕様により、registration_ids で 1000 個まとめて扱うことはできません。 message_idto を使って 1 つずつメッセージを作成する必要があります。

  • 導入バージョン:BoltzEngine v2.1
  • リクエストオブジェクト:gcm.Request
  • レスポンスオブジェクト:gcm.Response

net/rpc

1req := &gcm.Request{
2    URL: "fcm-xmpp.googleapis.com:5235",
3    ...
4}
5var resp gcm.Response
6err = client.Call("BoltzEngine.BroadcastMessagesToGCM", req, &resp)

JSON-RPC

 1---> {
 2  "method": "BoltzEngine.BroadcastMessagesToGCM",
 3  "params": [
 4    {
 5      "URL": "https://android.googleapis.com/gcm/send",
 6      "Credential": {
 7        "ServerKey": "o4sQfx2xD_ktryafbibakigczq7Hfekve9zmFdm",
 8        "SenderID": "123456789",
 9        "InsecureSkipVerify": false
10      },
11      "Messages": [
12        {
13          "message_id": "<<MESSAGE ID>>",
14          "to": "<<REGISTRATION_ID>>",
15          "data": {
16            "alert": "........................",
17            "mid": "55",
18            "sound": "default"
19          },
20          "collapse_key": "37",
21          "time_to_live": 300
22        },
23        {
24          "message_id": "<<MESSAGE ID>>",
25          "to": "<<REGISTRATION_ID>>",
26          "data": {
27            "alert": "........................",
28            "mid": "55",
29            "sound": "default"
30          },
31          "collapse_key": "37",
32          "time_to_live": 300
33        },
34      ]
35    }
36  ],
37  "id": 0
38}
39
40<--- {
41  "id": 0,
42  "result": {
43    "FailedMessages": null
44  },
45  "error": null
46}

BoltzEngine.BroadcastMessagesToADM

net/rpc と JSON-RPC では、ADM への通知はサポートしていません。

BoltzEngine.BroadcastMessagesToWebPush

WebPush へメッセージを送信します。 Messages の内容は、WebPush 仕様 (RFC 8030) の Requesting Push Message Delivery で定義されている項目と一致します。

  • 導入バージョン:BoltzEngine v2.2
  • リクエストオブジェクト:webpush.Request
  • レスポンスオブジェクト:webpush.Response

net/rpc

1req := &webpush.Request{ ... }
2var resp webpush.Response
3err = client.Call("BoltzEngine.BroadcastMessagesToWebPush", req, &resp)

JSON-RPC

 1---> {
 2  "method": "BoltzEngine.BroadcastMessagesToWebPush",
 3  "params": [
 4    {
 5      "Credential": {
 6        "VAPID": {
 7          "Subject": "mailto:info@example.com",
 8          "PublicKey": "<<BASE64>>",
 9          "PrivateKey": "<<BASE64>>"
10        },
11        "InsecureSkipVerify": false
12      },
13      "Messages": [
14        {
15          "Token": "{\"v\":1,\"endpoint\":\"https://example.com/token1\",\"p256dh\":\"xxxx\",\"auth\":\"xxxx\"}",
16          "Payload": "{\"alert\":\"hello\"}",
17          "TimeToLive": 60,
18          "Urgency": "high"
19        },
20        {
21          "Token": "{\"v\":1,\"endpoint\":\"https://example.com/token2\",\"p256dh\":\"xxxx\",\"auth\":\"xxxx\"}",
22          "Payload": "{\"alert\":\"hello\"}",
23          "TimeToLive": 60,
24          "Urgency": "high"
25        }
26      ]
27    }
28  ],
29  "id": 0
30}
31
32<--- {
33  "id": 0,
34  "result": {
35    "FailedMessages": null
36  },
37  "error": null
38}

BoltzEngine.QueryActivities

BoltzEngine の性能情報を取得します。引数には空文字列を指定します。

  • 導入バージョン:BoltzEngine v1.0
  • リクエストオブジェクト:string
  • レスポンスオブジェクト:boltz.MasterActivity

net/rpc

1req := ""
2var resp boltz.MasterActivity
3err = client.Call("BoltzEngine.QueryActivities", req, &resp)

JSON-RPC

 1---> {
 2  "method": "BoltzEngine.QueryActivities",
 3  "params": [
 4    ""
 5  ],
 6  "id": 0
 7}
 8
 9<--- {
10  "id": 0,
11  "result": {
12    "PendingCount": 0,
13    "SlaveActivities": {
14      ":13007": {
15        "MaxAgents": 30,
16        "RequestCount": 0,
17        "DeliveringCount": 0,
18        "DeliveredCount": 0,
19        "TotalExecutionTime": 0,
20        "LatestExecutionTime": 0,
21        "LastUpdate": "2015-05-29T16:27:46.538940751+09:00"
22      }
23    }
24  },
25  "error": null
26}

ご不明な点はありませんか?

機能の詳細、導入のご検討、お見積もり依頼などは、お気軽にお問い合わせください。
担当者から追ってご連絡いたします。

お問い合わせはこちら