はじめに
Laravelで楽天APIを使ったWEBアプリを開発していたところ、APIを提供している楽天ウェブサービスのヘルプページでリクエスト制限があることを知りました。
1つのapplication_idにつき、1秒に1回以下のリクエストとしてください。
アフィリエイトサイトとして高い成果が見込まれる場合は制限緩和ができるみたいですが、そうでない場合はAPIの使用が制限されています。
もし継続的に制限を超えたリクエストを行った場合はアプリIDが利用停止になる可能性があるみたいなので対策が必要だと考えました。
楽天APIの実装方法についての記事はいくつかありましたが、リクエスト制限まで触れているものは見かけなかったので、自分なりの対策を備忘録として残しておきます。
環境
PHP 7.4.27
Laravel 8.78.1
楽天APIの実装
アプリIDを取得
楽天APIの利用にはアプリIDが必要になります。
楽天ウェブサービスでアプリを新規登録しアプリIDを発行します。
SDKをインストール
次にSDK(ソフトウェア開発キット)を導入します。
PHPで楽天APIを使うための機能がパッケージされているものがあるのでそちらを使用します。
Laravelのプロジェクトディレクトリ直下のcomposer.json
に下記を追加します。
"require": {
(略)
"rakuten-ws/rws-php-sdk": "1.*"
}
SDKをインストールするため下記のコマンドを実行します。
$ composer install
インストールできていればvendor
ディレクトリにrakuten-ws
が追加されています。
環境設定
取得したアプリIDを.env
ファイルに設定します。
RAKUTEN_APPLICATION_ID='アプリケーションID'
config
で中継しておきます。
'rakuten' => [
'application_id' => env('RAKUTEN_APPLICATION_ID'),
],
コントローラを作成
楽天検索の実行部分を実装します。
楽天ブックス書籍検索APIでの検索例です。
なお検索フォームや商品データ取得後の表示処理などについては割愛しています。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Arr;
use RakutenRws_Client;
class SearchController extends Controller
{
public function index(Request $request)
{
// RakutenRws_Clientインスタンスを作成
$client = new RakutenRws_Client();
// 定数化
define("RAKUTEN_APPLICATION_ID", config('services.rakuten.application_id'));
// アプリIDをセット
$client->setApplicationId(RAKUTEN_APPLICATION_ID);
// リクエストから検索キーワードを取得
$inputs = array_filter($request->only(['title', 'author']));
$items = [];
if (count($inputs) > 0) {
// 楽天ブックス書籍検索を実行
$response = $client->execute('BooksBookSearch', $inputs);
if ($response->isOk()) {
foreach($response as $item) {
// 取得したいパラメータでフィルタリングして保存
$items[] = Arr::only($item, [
'title',
'author',
'itemPrice',
'itemUrl',
'mediumImageUrl',
]);
}
} else {
echo 'Error:'.$response->getMessage();
}
}
// 省略
}
}
ここまでで楽天APIで商品データを取得してくることができました。
ただこのままでは冒頭で記述したようにリクエスト制限値を超えるリクエストが送られて来た場合に、アプリIDが利用停止になってしまう可能性があるため追加で対策していきます。
リクエスト制限対策
どうやって対策しようかと調べていたところ、Laravel公式ドキュメントのキャッシュのページにあるロック管理の機能がぴったりだったのでこれを使って実装しました。
リクエスト制限対策後のコードを先に記載しておきます。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Arr;
use RakutenRws_Client;
use Illuminate\Contracts\Cache\LockTimeoutException; // 追加
use Illuminate\Support\Facades\Cache; // 追加
class SearchController extends Controller
{
public function index(Request $request)
{
// 省略
if (count($inputs) > 0) {
// ロックを1秒間取得
$lock = Cache::lock('rakuten_api', 1);
try {
// ロック取得を最大5秒待つ
$lock->block(5);
$response = $client->execute('BooksBookSearch', $inputs);
if ($response->isOk()) {
foreach($response as $item) {
$items[] = Arr::only($item, [
'title',
'author',
'itemPrice',
'itemUrl',
'mediumImageUrl',
]);
}
} else {
echo 'Error:'.$response->getMessage();
}
} catch (LockTimeoutException $e) {
// ロック取得失敗時の処理
}
}
// 省略
}
}
追加した部分については以下の通りです。
// ロックを1秒間取得
$lock = Cache::lock('rakuten_api', 1);
Cache::lock
メソッドによって第一引数の名前のロックを第二引数で指定した秒数取得した状態になります。
ロックを取得している間は別のプロセスがロックを取得できなくなっています。
try {
// ロック取得を最大5秒待つ
$lock->block(5);
// ロック取得時の処理
} catch (LockTimeoutException $e) {
// ロック取得失敗時の処理
}
block
メソッドはロックを取得していない場合、処理を止めて指定の秒数ロックが解放されるのを待ちます。
ロックが解放されればロックを取得して処理を進めますが、時間内にロックが解放されなければLockTimeoutExecption
を投げます。
これで同時にリクエストがきた場合でも楽天検索の実行間隔が1秒以上離れるため、制限に引っかからなくなったのではないかと考えています。
まとめ
Qiita初投稿で至らない点もあったとは思いますが最後まで読んでいただきありがとうございます。
今回はリクエスト制限に焦点を当てましたが、さらにAPIへの理解を深めるために楽天以外のものも使っていきたいと思いました。
間違いや改善点などがあればご教示いただけますと幸いです。