Help us understand the problem. What is going on with this article?

GoogleAPIを高速化しよう

More than 3 years have passed since last update.

こちらの記事Google APIのBatch Requestの仕組みを理解する
でBatch Requestについて書きましたが、高速化する方法はそれだけではありません。
本記事ではそれを紹介していきます。

TL;DR

APIの高速化手法には主に以下の4つがある。

  • Batch Reuqest
  • gzip圧縮
  • fieldsパラメータを指定
  • GAE・GCE

バッチ処理等で大量データを登録するときは Batch Requestは必須。
画面などでAPIを単発で何度も実行しないといけない場合は gzipfieldsGAE・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

result.json
{
 "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
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
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
result.json
{
 "kind": "calendar#event",
 "etag": "\"2909287956280000\"",
 "id": "t5f1vqmq89iu5t3jo6dapb4boc"
}

複数指定の省略形

attendees/emailattendees/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
result.json
{
 "attendees": [
  {
   "email": "admin@howdylikes.jp",
   "responseStatus": "accepted"
  },
  {
   "email": "howdylikes.jp_37393637323732362d313230@resource.calendar.google.com",
   "responseStatus": "needsAction"
  }
 ]
}

おまけ 全てに一致

アスタリスクを孫階層以下に指定することができます。
使うとしたら予定の一覧取得時にitems/*/dateTimeitems/start/dateTimeitems/end/dateTimeが取れる感じになります。
必要性が感じないので例はだしません。

GAE,GCEを使う

Googleネットワーク内でAPI発行することになるため速度向上になります。

実際の速度比較

では実際にどれくらい変わるのか試してみます。

同じ予定取得を何度も流しても面白みがないので、検証内容はカレンダーの予定登録を200件行います。

通常リクエスト

シェルを作って順次流していきます。

requet.sh
#!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      
databinary.txt
{
 "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
databinary.txt
--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圧縮

通常のリクエスト同様シェルを作って順次流していきます。

requet.sh
#!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         
databinary.txt
{
 "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だけ取得することとします。

requet.sh
#!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         
databinary.txt
{
 "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
databinary.txt
--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を単発で何度も実行しないといけない場合は
gzipfieldsGAE・GCEを組み合わせればレスポンスの向上に大きく繋がりますね。

余談

本件検証していてGoogle APIは実行時間帯によって速度の差がかなり違いました。
負荷の少ない時間帯とかあるんでしょうかね。

howdy39
heyinc corporate engineer https://techthetoaster.booth.pm/
https://howdy39.dev/
storesjp
インターネットビジネスの企画・開発・運営、マーケティング、プロモーション、コンテンツの企画・制作
https://about.stores.jp
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした