こちらの記事Google APIのBatch Requestの仕組みを理解する
でBatch Requestについて書きましたが、高速化する方法はそれだけではありません。
本記事ではそれを紹介していきます。
TL;DR
APIの高速化手法には主に以下の4つがある。
- Batch Reuqest
- gzip圧縮
- fieldsパラメータを指定
- GAE・GCE
バッチ処理等で大量データを登録するときは Batch Requestは必須。
画面などでAPIを単発で何度も実行しないといけない場合は gzip、 fields、 GAE・GCEを組み合わせることでレスポンスの向上に繋がる。
検証用データ
カレンダーの予定で解説します。
こんな感じの普通の予定を用意しときます。
Request
curl -H "Authorization: Bearer $ACCESS_TOKEN" https://www.googleapis.com/calendar/v3/calendars/admin@howdylikes.jp/events/t5f1vqmq89iu5t3jo6dapb4boc > result.json
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1373 100 1373 0 0 542 0 0:00:02 0:00:02 --:--:-- 543
Reponse
{
"kind": "calendar#event",
"etag": "\"2909287956280000\"",
"id": "t5f1vqmq89iu5t3jo6dapb4boc",
"status": "confirmed",
"htmlLink": "https://www.google.com/calendar/event?eid=dDVmMXZxbXE4OWl1NXQzam82ZGFwYjRib2MgYWRtaW5AaG93ZHlsaWtlcy5qcA",
"created": "2016-02-05T03:39:43.000Z",
"updated": "2016-02-05T03:46:18.140Z",
"summary": "テスト予定",
"description": "これはテスト用の予定です",
"location": "リソース名AAA",
"creator": {
"email": "admin@howdylikes.jp",
"displayName": "Tatsuya Nakano",
"self": true
},
"organizer": {
"email": "admin@howdylikes.jp",
"displayName": "Tatsuya Nakano",
"self": true
},
"start": {
"dateTime": "2016-02-05T09:00:00+09:00"
},
"end": {
"dateTime": "2016-02-05T11:00:00+09:00"
},
"iCalUID": "t5f1vqmq89iu5t3jo6dapb4boc@google.com",
"sequence": 0,
"attendees": [
{
"email": "admin@howdylikes.jp",
"displayName": "Tatsuya Nakano",
"organizer": true,
"self": true,
"responseStatus": "accepted"
},
{
"email": "howdylikes.jp_37393637323732362d313230@resource.calendar.google.com",
"displayName": "テスト用会議室A",
"resource": true,
"responseStatus": "needsAction"
}
],
"hangoutLink": "https://plus.google.com/hangouts/_/howdylikes.jp/admin?hceid=YWRtaW5AaG93ZHlsaWtlcy5qcA.t5f1vqmq89iu5t3jo6dapb4boc",
"reminders": {
"useDefault": true
}
}
gzip圧縮
HTTPには Accept-Encodingヘッダをつけてリクエストを送ることで圧縮して受け取ることが可能な機能があります。
似たようなことがGoogle APIでも可能です。
※この機能は最近のGoogle提供のライブラリならデフォルトONになっている可能性が高いです。(PHPはなっています、他は未確認)
使い方
Accept-Encoding Header
gzipを指定します
User-Agent Header
適当な文字列 (gzip)
を指定します
ここが通常のHTTPと違います。
必ず括弧で囲ってgzipと記載する必要があります。
@リファレンスには書いていないのですが、deflateも指定可能みたいです。
使用例
curl -H "Authorization: Bearer $ACCESS_TOKEN" -H "Accept-Encoding: gzip" -H "User-Agent: my program (gzip)" https://www.googleapis.com/calendar/v3/calendars/admin@howdylikes.jp/events/t5f1vqmq89iu5t3jo6dapb4boc > result.gzip
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 677 0 677 0 0 644 0 0:00:01 0:00:01 --:--:-- 644
Receivedのところをみてください。
1373 -> 677 と減っているのがわかるかと思います。
fieldsパラメータを指定
検証用データのレスポンスを見てもらえるとわかると思うのですが、とにかく項目がおおいです。
Google APIは原則全ての値を返すため肥大化してしまうのです。
SQLでいうとSELECT *
を指定しているようなものですね。
※Drive API v3のようにで標準で全てを返さない特殊なものもあります。
fieldsパラメータを指定し、必要な項目に絞ることでサイズを小さくすることが可能です。
またこのfieldsパラメータは取得時だけではなく登録時や更新時のレスポンスにも有効です。
使い方&使用例
getパラメータにfields=XXX
の形で指定します。
通常の指定
fields=creator
普通に項目名を指定するだけです
指定した項目の子階層も含めて全て取得されます
curl -H "Authorization: Bearer $ACCESS_TOKEN" https://www.googleapis.com/calendar/v3/calendars/admin@howdylikes.jp/events/t5f1vqmq89iu5t3jo6dapb4boc?fields=creator > result.json
{
"creator": {
"email": "admin@howdylikes.jp",
"displayName": "Tatsuya Nakano",
"self": true
}
}
###深い階層を指定
スラッシュで繋ぎます
fields=creator/email
curl -H "Authorization: Bearer $ACCESS_TOKEN" https://www.googleapis.com/calendar/v3/calendars/admin@howdylikes.jp/events/t5f1vqmq89iu5t3jo6dapb4boc?fields=creator/email > result.json
{
"creator": {
"email": "admin@howdylikes.jp"
}
}
###複数指定
カンマで区切ります
fields=id,etag,kind
curl -H "Authorization: Bearer $ACCESS_TOKEN" https://www.googleapis.com/calendar/v3/calendars/admin@howdylikes.jp/events/t5f1vqmq89iu5t3jo6dapb4boc?fields=id,etag,kind > result.json
{
"kind": "calendar#event",
"etag": "\"2909287956280000\"",
"id": "t5f1vqmq89iu5t3jo6dapb4boc"
}
###複数指定の省略形
attendees/email
とattendees/responseStatus
を指定したい場合
fields=attendees/email,attendees/responseStatus
となりますが
このままだと冗長なので括弧で囲むことで以下のように書くことが可能です。
fields=attendees(email,responseStatus)
curl -H "Authorization: Bearer $ACCESS_TOKEN" https://www.googleapis.com/calendar/v3/calendars/admin@howdylikes.jp/events/t5f1vqmq89iu5t3jo6dapb4boc?fields="attendees(email,responseStatus)" > result.json
{
"attendees": [
{
"email": "admin@howdylikes.jp",
"responseStatus": "accepted"
},
{
"email": "howdylikes.jp_37393637323732362d313230@resource.calendar.google.com",
"responseStatus": "needsAction"
}
]
}
###おまけ 全てに一致
アスタリスクを孫階層以下に指定することができます。
使うとしたら予定の一覧取得時にitems/*/dateTime
でitems/start/dateTime
とitems/end/dateTime
が取れる感じになります。
必要性が感じないので例はだしません。
GAE,GCEを使う
Googleネットワーク内でAPI発行することになるため速度向上になります。
実際の速度比較
では実際にどれくらい変わるのか試してみます。
同じ予定取得を何度も流しても面白みがないので、検証内容はカレンダーの予定登録を200件行います。
通常リクエスト
シェルを作って順次流していきます。
#!bin/sh
date
for i in `seq 1 200`
do
curl -H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-type: application/json" https://www.googleapis.com/calendar/v3/calendars/admin@howdylikes.jp/events --data-binary @databinary.txt >> result.json
done
date
{
"end": {
"dateTime": "2016-02-02T13:00:00+09:00"
},
"start": {
"dateTime": "2016-02-02T12:00:00+09:00"
},
"summary": "通常のリクエストで登録"
}
sh ./request.sh
Tue Feb 9 06:09:03 JST 2016
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1104 0 938 100 166 1272 225 --:--:-- --:--:-- --:--:-- 1271
...
...
...
Tue Feb 9 06:11:31 JST 2016
148秒かかりました。
1件あたり1秒強ですね。大量データがあると辛い数字です。
Batch Request
curl https://www.googleapis.com/batch -H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-Type: multipart/mixed; boundary=BOUNDARY" --data-binary @databinary.txt > result.json
--BOUNDARY
Content-Type: application/http
Content-ID: 1
POST https://www.googleapis.com/calendar/v3/calendars/admin@howdylikes.jp/events
Content-type: application/json
{
"end": {
"dateTime": "2016-02-01T13:00:00+09:00"
},
"start": {
"dateTime": "2016-02-01T12:00:00+09:00"
},
"summary": "BatchRequestで登録"
}
--BOUNDARY
Content-Type: application/http
Content-ID: 2
POST https://www.googleapis.com/calendar/v3/calendars/admin@howdylikes.jp/events
Content-type: application/json
...
...
...
--BOUNDARY
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 307k 0 243k 100 65547 15439 4058 0:00:16 0:00:16 --:--:-- 46163
16秒です。
さすがにBatch Requestは速いですね。
Google内部の処理時間はほとんどかかっておらずHTTP通信を何度も行うのが圧倒的に大きいのがわかりますね。
gzip圧縮
通常のリクエスト同様シェルを作って順次流していきます。
#!bin/sh
date
for i in `seq 1 200`
do
curl -H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-type: application/json" -H "Accept-Encoding: gzip" -H "User-Agent: my program (gzip)" https://www.googleapis.com/calendar/v3/calendars/admin@howdylikes.jp/events --data-binary @databinary.txt >> result.json
done
date
{
"end": {
"dateTime": "2016-02-03T13:00:00+09:00"
},
"start": {
"dateTime": "2016-02-03T12:00:00+09:00"
},
"summary": "gzipリクエストで登録"
}
sh ./request.sh
Tue Feb 9 06:05:04 JST 2016
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 667 0 506 100 161 1023 325 --:--:-- --:--:-- --:--:-- 1024
...
...
...
Tue Feb 9 06:07:15 JST 2016
131秒かかりました。
若干早くなっているようです。
fields使用
idだけ取得することとします。
#!bin/sh
date
for i in `seq 1 200`
do
curl -H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-type: application/json" https://www.googleapis.com/calendar/v3/calendars/admin@howdylikes.jp/events?fields=id --data-binary @databinary.txt >> result.json
done
date
{
"end": {
"dateTime": "2016-02-04T13:00:00+09:00"
},
"start": {
"dateTime": "2016-02-04T12:00:00+09:00"
},
"summary": "fieldsのリクエストで登録"
}
Tue Feb 9 06:21:50 JST 2016
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 206 0 40 100 166 64 268 --:--:-- --:--:-- --:--:-- 268
...
...
...
Tue Feb 9 06:23:56 JST 2016
126秒かかりました。
Receivedが40とgzipを使った時よりはるかに小さくなっていますがそこまで差はないですね。
GCE使用
f1-micro、asia-east1-bのインスタンスで実行しました。
リクエスト内容は通常リクエストと同じため省略。
Mon Feb 8 21:29:27 UTC 2016
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1098 0 935 100 163 2250 392 --:--:-- --:--:-- --:--:-- 2253
...
...
...
Mon Feb 8 21:31:13 UTC 2016
106秒かかりました
なにも考ずに早くなるのはうれしいですね。
全部入り
今までやった高速化を全て入れてやってみます。
curl https://www.googleapis.com/batch -H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-Type: multipart/mixed; boundary=BOUNDARY" -H "Accept-Encoding: gzip" -H "User-Agent: my program (gzip)" --data-binary @databinary.txt > result.json
--BOUNDARY
Content-Type: application/http
Content-ID: 1
POST https://www.googleapis.com/calendar/v3/calendars/admin@howdylikes.jp/events?fields=id
Content-type: application/json
{
"end": {
"dateTime": "2016-02-06T13:00:00+09:00"
},
"start": {
"dateTime": "2016-02-06T12:00:00+09:00"
},
"summary": "全部入りで登録"
}
--BOUNDARY
Content-Type: application/http
Content-ID: 2
POST https://www.googleapis.com/calendar/v3/calendars/admin@howdylikes.jp/events?fields=id
Content-type: application/json
...
...
...
--BOUNDARY
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 72657 0 5107 100 67550 339 4487 0:00:15 0:00:15 --:--:-- 0
15秒かかりました。
#まとめ
予定を200件登録する時間
種別 | 時間 |
---|---|
通常 | 148秒 |
Batch Request | 16秒 |
gzip | 131秒 |
fields(idのみ) | 126秒 |
GCE | 106秒 |
全部入り | 15秒 |
バッチ処理等で大量データを登録するときは Batch Requestは必須ですね。天と地の差があります。
画面などでAPIを単発で何度も実行しないといけない場合は
gzip、 fields、 GAE・GCEを組み合わせればレスポンスの向上に大きく繋がりますね。
余談
本件検証していてGoogle APIは実行時間帯によって速度の差がかなり違いました。
負荷の少ない時間帯とかあるんでしょうかね。