記事検索

検索ワードを入力してください。
Sky Tech Blog
Google Drive APIの​使い方​ ②​(バッチリクエストの​パフォーマンス向上)

Google Drive APIの​使い方​ ②​(バッチリクエストの​パフォーマンス向上)

前回の記事では、Google Drive APIのバッチリクエストを使用してネットワークオーバーヘッドを抑える方法を紹介しました。今回は、バッチリクエストを小分けにして並列実行することで、全体の実行時間を短縮する方法を解説しています。

前回の記事では、Google Drive APIの使い方としてバッチリクエストを使用することでネットワーク上のオーバーヘッドを抑え、パフォーマンスを向上させる方法をご紹介しました。

前回の​記事

バッチリクエストは、複数のリクエストを1つのリクエストにまとめることによって、個別でリクエストした場合のオーバーヘッドを抑えることは可能でしたが、それぞれのリクエストに要する時間自体を短縮できるものではありませんでした。

今回の記事では、バッチリクエストを小分けにし、並列に要求を行うことによって、全体の実行時間を短縮した方法のご紹介です。

API呼び出し方​法に​よる​実行時間イメージ

1つのバッチリクエストに複数(最大100個)のGoogle Drive APIを詰め込んで実行した場合、Google Drive上では直列に処理されるため、それなりに時間がかかっていました。
一方、APIを小分けにして、複数のバッチリクエストを並列に実行してみたところ、Google Drive上も並列に処理され、処理時間を短縮することができました。

サンプルコード

以下のサンプルは、20個のファイルを4つのバッチリクエストに分けて、並列実行するためのサンプルコードです。

  • Google Drive APIを使えるようにプロジェクトは作成されているものとします。
  • JavaScriptです。

クライアントライブラリ​(googleapis)を​使う​場合

const {google} = require('googleapis');
const {OAuth2} = google.auth;

// 認証情報を取得する関数
async function getAuth() {
    const oAuth2Client = new OAuth2(
        'YOUR_CLIENT_ID',
        'YOUR_CLIENT_SECRET',
        'YOUR_REDIRECT_URL'
    );

    oAuth2Client.setCredentials({
        refresh_token: 'YOUR_REFRESH_TOKEN'
    });

    return oAuth2Client;
}

// バッチリクエストでファイルをコピーする関数(クライアントライブラリ版)
async function batchCopyFiles(auth, fileIds) {
    const driveService = google.drive({version: 'v3', auth});
    const batch = new google.BatchRequest();

    fileIds.forEach(fileId => {
        batch.add(driveService.files.copy({
            fileId: fileId,
            requestBody: {
                name: `YOUR_FILE_NAME_${fileId}`
            }
        }));
    });

    try {
        const response = await batch.execute();
        response.forEach((res, index) => {
            if (res.status === 200) {
                console.log(`API ${index + 1}が成功しました:`, res.data);
            } else {
                console.error(`API ${index + 1}が失敗しました:`, res.statusText);
            }
        });
    } catch (error) {
        console.error('バッチリクエストは失敗しました:', error);
    }
}

// メイン関数
async function main() {
    const auth = await getAuth();
    // 20個のファイルを4つのバッチリクエストに分けるため、5ファイル×4つの配列を用意
    const fileIds = [
        ['FILE_01', 'FILE_02', 'FILE_03', 'FILE_04', 'FILE_05'],
        ['FILE_06', 'FILE_07', 'FILE_08', 'FILE_09', 'FILE_10'],
        ['FILE_11', 'FILE_12', 'FILE_13', 'FILE_14', 'FILE_15'],
        ['FILE_16', 'FILE_17', 'FILE_18', 'FILE_19', 'FILE_20']
    ];
    const promises = [];

    // 合計20回のコピーを4バッチリクエストに分けて実行する
    for (let i = 0; i < fileIds.length; i++) {
        // 1バッチリクエストに5回分のコピー処理を詰める
        promises.push(batchCopyFiles(auth, fileIds[i]));
    }

    await Promise.all(promises);
    console.log('全てのバッチリクエストが完了しました');
}

main();

fetchで​呼び出す​場合

const {OAuth2} = require('google-auth-library');
const fetch = require('node-fetch');

// 認証情報を取得する関数
async function getAuthToken() {
    const oAuth2Client = new OAuth2(
        'YOUR_CLIENT_ID',
        'YOUR_CLIENT_SECRET',
        'YOUR_REDIRECT_URL'
    );

    oAuth2Client.setCredentials({
        refresh_token: 'YOUR_REFRESH_TOKEN'
    });

    const token = await oAuth2Client.getAccessToken();
    return token.token;
}

// バッチリクエストでファイルをコピーする関数(fetch版)
async function batchCopyFilesWithFetch(authToken, fileIds) {
    const url = 'https://www.googleapis.com/batch/drive/v3';
    const boundary = 'batch_boundary';
    let body = '';

    fileIds.forEach(fileId => {
        body += `--${boundary}\n`;
        body += 'Content-Type: application/http\n\n';
        body += `POST /drive/v3/files/${fileId}/copy\n`;
        body += 'Content-Type: application/json\n\n';
        body += JSON.stringify({ name: `YOUR_FILE_NAME_${fileId}` }) + '\n\n';
    });
    body += `--${boundary}--`;

    try {
        const response = await fetch(url, {
            method: 'POST',
            headers: {
                'Authorization': `Bearer ${authToken}`,
                'Content-Type': `multipart/mixed; boundary=${boundary}`
            },
            body: body
        });

        if (!response.ok) {
            throw new Error('バッチリクエストの実行に失敗しました');
        } else {
            console.log('バッチリクエストの実行に成功しました');
        }

        const data = await response.text();
        const parts = data.split(`--${boundary}`);
        parts.forEach((part, index) => {
            if (part.includes('HTTP/1.1 200 OK')) {
                console.log(`API ${index + 1}が成功しました:`, part);
            } else if (part.includes('HTTP/1.1')) {
                console.error(`API ${index + 1}が失敗しました:`, part);
            }
        });
    } catch (error) {
        console.error('バッチリクエストの実行に失敗しました:', error);
    }
}

// メイン関数
async function main() {
    const authToken = await getAuthToken();
    // 20個のファイルを4つのバッチリクエストに分けるため、5ファイル×4つの配列を用意
    const fileIds = [
        ['FILE_01', 'FILE_02', 'FILE_03', 'FILE_04', 'FILE_05'],
        ['FILE_06', 'FILE_07', 'FILE_08', 'FILE_09', 'FILE_10'],
        ['FILE_11', 'FILE_12', 'FILE_13', 'FILE_14', 'FILE_15'],
        ['FILE_16', 'FILE_17', 'FILE_18', 'FILE_19', 'FILE_20']
    ];
    const promises = [];

    // 合計20回のコピーを4バッチリクエストに分けて実行する
    for (let i = 0; i < fileIds.length; i++) {
        // 1バッチリクエストに5回分のコピー処理を詰める
        promises.push(batchCopyFilesWithFetch(authToken, fileIds[i]));
    }

    await Promise.all(promises);
    console.log('全てのバッチリクエストが完了しました');
}

main();

まとめ

上記サンプルをベースに、複数のバッチリクエストに小分けにして並列実行した場合、バッチリクエストに全て詰め込んで実行した場合に比べて、実行時間は 約34% 速くなりました。

バッチ処理を並列化することで、APIのパフォーマンスが改善するかどうかは、それぞれのアプリケーションでの検証が必要ですが、同じリクエストでも、呼び出し方を工夫することにより、実行時間を短縮できることが分かりました。

注意事項

並列実行のバッチリクエストに格納するAPIが多くなると、単位時間の間に実行するAPI数が増えるため、実行上限に到達し、処理が失敗する可能性があります。


XFacebookLINE
キャリア採用募集中!

入社後にスキルアップを目指す若手の方も、ご自身の経験を幅広いフィールドで生かしたいベテランの方も、お一人おひとりの経験に応じたキャリア採用を行っています。

Sky株式会社のソフトウェア開発や製品、採用に関するお問い合わせについては、下記のリンクをご確認ください。
お問い合わせ
ホーム