
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 // FCM サーバキー
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登録IDが.Tokensに存在しない場合は不要
21 GcmHeader: &gcm.Header{
22 RequestURL: "https://fcm.googleapis.com/fcm/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"、FCM は"2"、WebPush は"4"、ADM は"5"をトークンの先頭に追加してください
40 "1(16進文字列表記の APNs トークン)",
41 "2(FCM 登録 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"を加えてください。 また、FCM の登録 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/FCM/WebPush/ADM などのプラットフォームへメッセージを送信します。 メソッドの戻り値としてストリームを返し、ストリームからは通知の結果なんらかの処理が必要になったトークンと理由(イベント)を取得できます。 何もエラーがなければストリームはすぐに 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>> で、FCM の Registration ID が入る部分は <<REGISTRATION_ID>> 表記します。
BoltzEngine.BroadcastMessagesToAPNs
APNs へメッセージを送信します。これは Request.Addr
の値が http:// または https:// の場合は APNs HTTP/2 プロトコルを使って通知を行います。host:port 形式の場合は Binary プロトコルを利用します。
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 のフィードバックサービスへアクセスし、無効になったトークンの一覧を取得します。このメソッドは Binary プロトコルでのみ有効です。 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)
FCM へのリクエストは、HTTP と XMPP の 2 通り存在します。
gcm.Request
の URL
が http:// または https:// で開始される場合は、HTTP モードとなります。
Messages の内容は、FCM-HTTP リクエストフォーマットで定義されている項目と一致します。
制限事項:Message 各個の registration_ids には 1000 個までのレジストレーション ID を含むことができます。
- 導入バージョン:BoltzEngine v1.0
- リクエストオブジェクト:gcm.Request
- レスポンスオブジェクト:gcm.Response
net/rpc
1req := &gcm.Request{
2 URL: "https://fcm.googleapis.com/fcm/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://fcm.googleapis.com/fcm/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)
FCM へのリクエストは、HTTP と XMPP の 2 通り存在します。
gcm.Request
の URL
が http:// または https:// 以外なら、XMPP モードとなります。
Messages の内容は、FCM-XMPP リクエストフォーマットで定義されている項目と一致します。
XMPP モードの場合、プロトコルの仕様により、registration_ids
で 1000 個まとめて扱うことはできません。
message_id
と to
を使って 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://fcm.googleapis.com/fcm/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}