早くエンジニアになりたい

masatany's memorandum

Laravel のセッションストレージにDynamoDBを利用する

yoshitake_1201 がスルーしているので代わりに書いちゃいます。

この記事は、 Fusic Advent Calendar 2018 5日目の記事です。

はじめに

インフラのメイン担当として関わっている案件で、 ECS を使った構成で Laravel を動かすことになりました。

認証を含むWEBアプリケーションが複数のコンテナで動作するので、セッション管理が問題になってきます。
アプリケーション担当者と議論して、結果としてDynamoDBを使う運びになりました。

しかし、Laravel にはデフォルトで DynamoDB をセッションストレージとして使う機構は用意されていないので、今回はチーム内で実装することにしました。

やってみる

Laravel の公式ドキュメントを確認しながら進めます。
HTTPセッション 5.7 Laravel

ドライバの実装

カスタムセッションドライバでは、SessionHandlerInterfaceを実装してください。
このインターフェイスには実装する必要のある、シンプルなメソッドが数個含まれています。
MongoDBの実装をスタブしてみると、次のようになります。

<?php

namespace App\Extensions;

class MongoSessionHandler implements \SessionHandlerInterface
{
    public function open($savePath, $sessionName) {}
    public function close() {}
    public function read($sessionId) {}
    public function write($sessionId, $data) {}
    public function destroy($sessionId) {}
    public function gc($lifetime) {}
}

独自のセッションドライバを利用するためには、SessionHandlerInterface を実装する必要があるようです。
と言うことで、DynamoSessionHandler.php を作成します。

<?php
namespace App\Extensions;

class DynamoSessionHandler implements  implements \SessionHandlerInterface
{
    public function open($savePath, $sessionName) {}
    public function close() {}
    public function read($sessionId) {}
    public function write($sessionId, $data) {}
    public function destroy($sessionId) {}
    public function gc($lifetime) {}
}

DynamoDB SessionHandler のドキュメントを見ながら、実装を追加

Class Aws\DynamoDb\SessionHandler | AWS SDK for PHP 3.x

DynamoDbClient と SessionHandler が必要そうです。

<?php

use Aws\DynamoDb\DynamoDbClient;
use Aws\DynamoDb\SessionHandler;

SessionHandler の初期化時にDynamoDbSessionHandler も初期化します。

<?php

class DynamoSessionHandler implements  implements \SessionHandlerInterface
{
    protected $client;
    protected $handler;

    public function __construct(DynamoDbClient $client, array $config)
    {
        $this->client = $client;
        $this->handler = \Aws\DynamoDb\SessionHandler::fromClient($client, $config);
    }

    ...

}

open(), read()など、実際に DynamoDB をセッションハンドラーとして利用するための実装は AWS SDK の SessionHandler が実装しているので、ドライバからSDKにそのまま横流しすればOKです。

<?php

class DynamoSessionHandler implements  implements \SessionHandlerInterface
{

    ...

    public function open($save_path, $session_id)
    {
        return $this->handler->open($save_path, $session_id);
    }

   ...
}

ドライバの登録

再度 Laravel の公式ドキュメントを確認します。

ドライバを実装したら、フレームワークへ登録する準備が整いました。
Laravelのセッションバックエンドへドライバを追加するには、Sessionファサードのextendメソッドを呼び出します。
サービスプロバイダのbootメソッドから、extendメソッドを呼び出してください。
既存のAppServiceProviderか真新しく作成し、呼び出してください。

<?php

namespace App\Providers;

use App\Extensions\MongoSessionHandler;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\ServiceProvider;

class SessionServiceProvider extends ServiceProvider
{
    /**
     * サービス起動処理の事前登録
     *
     * @return void
     */
    public function boot()
    {
        Session::extend('mongo', function ($app) {
            // SessionHandlerInterfaceの実装を返す…
            return new MongoSessionHandler;
        });
    }

    /**
     * コンテナへ結合を登録する
     *
     * @return void
     */
    public function register()
    {
        //
    }
}

セッションドライバを登録したら、config/session.php設定ファイルでmongoドライバが使用できます。

ドライバを Laravel に認識させる必要があるようですので、公式ドキュメントにしたがって登録します。

まずは、 DynamoSessionServiceProvider.php を作成します。

<?php

namespace App\Providers;

use App\Extensions\DynamoSessionHandler;
use Aws\DynamoDb\DynamoDbClient;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\ServiceProvider;

class DynamoSessionServiceProvider extends ServiceProvider
{
    const DEFAULT_REGION = 'ap-northeast-1';

    public function boot()
    {
        Session::extend('dynamo', function ($app) {
            $cfg = $app['config']->get('session');

            $dynamoDbClient = DynamoDbClient::factory([
                'region' => (isset($cfg['region']) ? $cfg['region'] : self::DEFAULT_REGION),
                "endpoint" => (isset($cfg['endpoint']) ? $cfg['endpoint'] : 'https://dynamodb.'.self::DEFAULT_REGION.'.amazonaws.com'),
                'version'  => 'latest',
            ]);

            $config = [
                'table_name'               => $cfg['table'],
                'hash_key'                 => 'id',
                'session_lifetime'         => 60 * $cfg['lifetime'],
                'consistent_read'          => true,
                'locking_strategy'         => null,
                'automatic_gc'             => true,
                'gc_batch_size'            => 25,
                'max_lock_wait_time'       => 10,
                'min_lock_retry_microtime' => 10000,
                'max_lock_retry_microtime' => 50000
            ];
            // SessionHandlerInterfaceの実装を返す…
            return new DynamoSessionHandler($dynamoDbClient, $config);
        });
    }

    // 今回は直接実装しているので register() は利用しない
    public function register() {}
}

DynamoDbClient を作成する時に、リージョンやエンドポイントを環境変数から指定できるようにしました。

これで、 Laravel アプリケーションで DynamoSessionHandler が使用できるようになりました。

セッションストレージとして使う

いよいよ、DynamoDB をセッションストレージとして使用する準備が整いました。

セッション管理に使用する DynamoDB は session-table としましょう。 ハッシュキーは、コード上で id としているので間違えないように作成します。

$ aws dynamodb create-table \
    --table-name session-table \
    --attribute-definitions '[{"AttributeName":"id","AttributeType": "S"}]' \
    --key-schema '[{"AttributeName":"id","KeyType": "HASH"}]' \
    --provisioned-throughput '{"ReadCapacityUnits": 5,"WriteCapacityUnits": 5}'

あとは、config/session.php に必要な情報を記載するだけです。

<?php
return [

    /*
    |--------------------------------------------------------------------------
    | Default Session Driver
    |--------------------------------------------------------------------------
    |
    | This option controls the default session "driver" that will be used on
    | requests. By default, we will use the lightweight native driver but
    | you may specify any of the other wonderful drivers provided here.
    |
    | Supported: "file", "cookie", "database", "apc",
    |            "memcached", "redis", "array"
    |
    */

    'driver' => 'dynamo',
    'region' => 'ap-northeast-1',
    'table' => 'session-table',

    ...

無事に Laravel で DynamoDB をセッションストレージとして使用することができました!! (画面上は何の変化もない)

最後に

github.com

(^_^;)

でも、エンドポイントの指定ができるように作ったから、こっちは dynamodb-local が使えるもんね(強がり