BoltzEngine RPC インターフェイス

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

インターフェイスの種類

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

JSON-RPC を使用した BoltzEngine の呼び出しは、BoltzEngine v1.1 - v3.0 の間でサポートされていましたが、v3.1 以降では廃止されました。

gRPC

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

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
addr := "localhost:13009" // gRPC endpoint address

// TLS 通信を行わない場合はこちら
//conn, err := grpc.Dial(addr, grpc.WithInsecure())

// v3.2.0 より Protocol Buffer 3.0 に対応してるため、keep alive を指定します
conn, err := grpc.Dial(
    addr,
    []grpc.DialOption{
        grpc.WithInsecure(),
        grpc.WithKeepaliveParams(keepalive.ClientParameters{
            Time:                time.Second,
            Timeout:             30 * time.Second,
            PermitWithoutStream: true,
        }),
        grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(math.MaxInt32)),
    }...,
)

if err != nil {
    log.Fatalln("Dial:", err)
}
defer conn.Close()
c := pb.NewBoltzGatewayClient(conn)

var key []byte            // APNs 秘密鍵の内容
var cert []byte           // APNs 証明書の内容
var topic string          // Bundle ID
var serviceAccount string // FCM サービスアカウントの中身
msg := pb.Message{
    // APNsトークンが.Tokensに存在しない場合は不要
    ApnsHeader: &apns.Header{
        Address:      "https://api.push.apple.com/",
        KeyPEMBlock:  key,
        CertPEMBlock: cert,
        Topic:        topic,
        PushType:     "alert",
    },
    // FCM登録IDが.Tokensに存在しない場合は不要
    GcmHeader: &gcm.Header{
        RequestURL:     "https://fcm.googleapis.com/v1/",
        ServiceAccount: serviceAccount,
    },
    // ADM登録IDが.Tokensに存在しない場合は不要
    AdmHeader: &adm.Header{
        OriginURL: "https://api.amazon.com",
        TokenURL: "https://api.amazon.com/auth/o2/token",
        ClientID: clientID,
        ClientSecret: clientSecret,
    },
    // WebPushトークンが.Tokensに存在しない場合は不要
    WebpushHeader: &webpush.Header{
        Subject: "mailto:boltz@example.com",
        PrivateKey: privateKey, // VAPID PrivateKey
        PublicKey: publicKey,   // VAPID PublicKey
    },
    Tokens: []string{
        // APNs は"1"、FCM は"2"、WebPush は"4"、ADM は"5"をトークンの先頭に追加してください
        "1(16進文字列表記の APNs トークン)",
        "2(FCM 登録 ID)",
        `4{"v":1,"endpoint":"(WebPush エンドポイント)","p256dh":"(ブラウザ公開鍵)","auth":"(WebPush 乱数)"}`,
        "5(ADM 登録 ID)",
    },
    Priority:   pb.Priority_HIGH,
    Payload:    `{"aps":{"alert":{"title": "test", "body": "hello world"},"sound":"default"}}`,   // iOS 端末へ通知する内容
    Parameters: &gcm.Parameters{Data: map[string]string{"title": "test", "body": "hello world"}}, // Android/FireOS 端末へ通知する内容
    Body:       `{"title": "test", "body": "hello world"}`,                                       // WebPush 対応ブラウザへ通知する内容
    BandWidth:  2000,                                                                             // 1秒あたりの最大通知件数(0なら無制限)
}
stream, err := c.Send(context.Background(), &msg)
if err != nil {
    log.Fatalln("Send:", err)
}
for {
    v, err := stream.Recv()
    if err == io.EOF {
        break
    }
    if err != nil {
        log.Fatalln("Recv:", err)
    }
    switch e := v.Event.(type) {
    case *pb.Event_Failed: // 通知失敗した端末
        x := e.Failed
        fmt.Printf("%v: %s: %v\n", v.Platform, x.Token, x.Kind)
        // x.Kindが
        // - INVALID_TOKEN なら無効なので次から削除した方が良い
        // - INVALID_PAYLOAD ならメッセージがおかしいので見直しが必要(通常は発生しない)
        // - TEMPORARY_ERROR の場合は上記以外のエラー
    case *pb.Event_Renewed: // トークン更新の発生した端末
        x := e.Renewed
        fmt.Printf("%v: %s -> %s\n", v.Platform, x.RecentToken, x.LatestToken)
        // x.RecentTokenがx.LatestTokenに切り替わったので次回からx.LatestTokenを使う
        // x.RecentToken, x.LatestTokenのどちらも、1文字目にはサービスIDを含む
    }
}

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

net/rpc

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

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

gRPC メソッド一覧

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

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

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

FetchStatistics

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

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

Send

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

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

  • 通知は成功したがトークンが更新された: TokenRenewal
    • FCM Legacy HTTP, FCM Legacy XMPP, ADMのみ発生
  • 通知が失敗した: DeliveryFailure

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

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

の3種類に分かれます。

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

FetchFeedback [非推奨]

この方式は現在非推奨です。
Apple より APNs の legacy I/F ( binary I/F ) が 2020 年 11 月以降に廃止されることが告知されております。
Apple Push Notification Serviceのアップデート

追記
Apple より廃止期限が 2021 年 3 月 31 日に延長される旨が告知されています。
APNs Provider APIの期限の変更

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

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

net/rpc メソッド一覧

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

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

BoltzEngine.BroadcastMessagesToAPNs

APNs へメッセージを送信します。これは Request.Addr の値が http:// または https:// の場合は APNs HTTP/2 プロトコルを使って通知を行います。 host:port 形式の場合は Binary プロトコルを利用しますが、2020-11 以降 Apple より廃止が宣言されていますのでご注意ください。 Messages の内容は、APNs Notification format(Command=2)の Frame data で定義されている項目と一致します。

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

net/rpc

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

BoltzEngine.ReceiveFeedbackFromAPNs [非推奨]

この方式は現在非推奨です。
Apple より APNs の legacy I/F ( binary I/F ) が 2020 年 11 月以降に廃止されることが告知されております。
Apple Push Notification Serviceのアップデート

追記
Apple より廃止期限が 2021 年 3 月 31 日に延長される旨が告知されています。
APNs Provider APIの期限の変更

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

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

net/rpc

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

BoltzEngine.BroadcastMessagesToGCM(HTTP)

FCM へのリクエストは、HTTP・XMPP・HTTP v1 の 3 通り存在します。 gcm.RequestURL によって、どのプロトコルを利用するかが変わります。

  • http:// または https:// で開始し /v1/含む 場合は HTTP v1 API
  • http:// または **https://*8 で開始し /v1/含まない 場合は Legacy HTTP
  • host:port 形式の場合は Legacy XMPP

このメソッドは、HTTP v1 API または Legacy HTTP の場合に利用します。

Messages の内容は、FCM-HTTP リクエストフォーマットで定義されている項目とおおよそ一致しますが、 上記3つのうちどれを利用するかによって、利用可能なフィールドは一部異なります。 例えば HTTP v1 API の場合は Message.To フィールドを参照しますが、Legacy HTTP の場合は Message.RegIDs フィールドを参照します。

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

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

net/rpc

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

BoltzEngine.BroadcastMessagesToGCM(XMPP)

FCM へのリクエストは、HTTP・XMPP・HTTP v1 の 3 通り存在します。 gcm.RequestURL によって、どのプロトコルを利用するかが変わります。

  • http:// または https:// で開始し /v1/含む 場合は HTTP v1 API
  • http:// または **https://*8 で開始し /v1/含まない 場合は Legacy HTTP
  • host:port 形式の場合は Legacy XMPP

このメソッドは、Legacy XMPP の場合に利用します。

Messages の内容は、FCM-XMPP リクエストフォーマットで定義されている項目と一致します。RegIDsTo の2つを定義していますが、Legacy XMPP プロトコルを使う場合は To を利用する必要があります。

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

net/rpc

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

BoltzEngine.BroadcastMessagesToADM

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

BoltzEngine.BroadcastMessagesToWebPush

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

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

net/rpc

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

BoltzEngine.QueryActivities

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

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

net/rpc

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

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

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

お問い合わせはこちら