プロダクション
BEAR.Sunday既定のprod
束縛に対して、アプリケーションがそれぞれのディプロイ環境に応じたモジュールをカスタマイズして束縛を行います。
既定のProdModule
既定のprod
束縛では以下のインターフェイスの束縛がされています。
- エラーページ生成ファクトリー
- PSRロガーインターフェイス
- ローカルキャッシュ
- 分散キャッシュ
詳細はBEAR.PackageのProdModule.php参照。
アプリケーションのProdModule
既定のProdModuleに対してアプリケーションのProdModule
をsrc/Module/ProdModule.php
に設置してカスタマイズします。特にエラーページと分散キャッシュは重要です。
<?php
namespace MyVendor\Todo\Module;
use BEAR\Package\Context\ProdModule as PackageProdModule;
use BEAR\QueryRepository\CacheVersionModule;
use BEAR\Resource\Module\OptionsMethodModule;
use BEAR\Package\AbstractAppModule;
class ProdModule extends AbstractModule
{
/**
* {@inheritdoc}
*/
protected function configure()
{
$this->install(new PackageProdModule); // デフォルトのprod設定
$this->override(new OptionsMethodModule); // OPTIONSメソッドをプロダクションでも有効に
$this->install(new CacheVersionModule('1')); // リソースキャッシュのバージョン指定
// 独自のエラーページ
$this->bind(ErrorPageFactoryInterface::class)->to(MyErrorPageFactory::class);
}
}
キャッシュ
キャッシュはローカルキャッシュと、複数のWebサーバー間でシェアをする分散キャッシュの2種類があります。どちらのキャッシュもデフォルトはPhpFileCacheです。
ローカルキャッシュ
ローカルキャッシュはdeploy後に変更のないアノテーション等のキャッシュ例に使われ、分散キャッシュはリソース状態の保存に使われます。
分散キャッシュ
2つ以上のWebサーバーでサービスを行うためには分散キャッシュの構成が必要です。代表的なmemcached、Redisのキャッシュエンジンのそれぞれのモジュールが用意されています。
Memcached
<?php
namespace BEAR\HelloWorld\Module;
use BEAR\QueryRepository\StorageMemcachedModule;
use BEAR\Resource\Module\ProdLoggerModule;
use BEAR\Package\Context\ProdModule as PackageProdModule;
use BEAR\Package\AbstractAppModule;
use Ray\Di\Scope;
class ProdModule extends AbstractModule
{
protected function configure()
{
// memcache
// {host}:{port}:{weight},...
$memcachedServers = 'mem1.domain.com:11211:33,mem2.domain.com:11211:67';
$this->install(new StorageMemcachedModule(memcachedServers));
// Prodロガーのインストール
$this->install(new ProdLoggerModule);
// デフォルトのProdModuleのインストール
$this->install(new PackageProdModule);
}
}
Redis
// redis
$redisServer = 'localhost:6379'; // {host}:{port}
$this->install(new StorageRedisModule($redisServer));
リソースの状態保存は単にTTLによる時間更新のキャッシュとの他に、TTL時間では消えない永続的なストレージとして(CQRS)の運用も可能です。その場合にはRedis
で永続処理を行うか、Cassandraなどの他KVSのストレージアダプターを独自で用意する必要があります。
キャッシュ時間の指定
デフォルトのTTLを変更する場合StorageExpiryModule
をインストールします。
// Cache time
$short = 60;
$medium = 3600;
$long = 24 * 3600;
$this->install(new StorageExpiryModule($short, $medium, $long));
キャッシュバージョンの指定
リソースのスキーマが変わり、互換性が失われる時にはキャッシュバージョンを変更します。特にTTL時間で消えないCQRS運用の場合に重要です。
$this->install(new CacheVersionModule($cacheVersion));
ディプロイの度にリソースキャッシュを破棄するためには$cacheVersion
に時刻や乱数の値を割り当てると変更が不要で便利です。
ログ
ProdLoggerModule
はプロダクション用のリソース実行ログモジュールです。インストールするとGET以外のリクエストをPsr\Log\LoggerInterface
にバインドされているロガーでログします。
特定のリソースや特定の状態でログしたい場合は、カスタムのログをBEAR\Resource\LoggerInterfaceにバインドします。
use BEAR\Resource\LoggerInterface;
use Ray\Di\AbstractModule;
final class MyProdLoggerModule extends AbstractModule
{
protected function configure(): void
{
$this->bind(LoggerInterface::class)->to(MyProdLogger::class);
}
}
LoggerInterfaceの__invoke
メソッドでリソースのURIとリソース状態がResourceObject
オブジェクトとして渡されるのでその内容で必要な部分をログします。作成には既存の実装 ProdLoggerを参考にしてください。
デプロイ
⚠️ 上書き更新を避ける
サーバーにディプロイする場合
- 駆動中のプロジェクトフォルダを
rsync
などで上書きするのはキャッシュやオンデマンドで生成されるファイルの不一致や、高負荷のサイトではキャパシティを超えるリスクがあります。安全のために別のディレクトリでセットアップを行い、そのセットアップが成功すれば切り替えるようにします。 - DeployerのBEAR.Sundayレシピを利用することができます。
クラウドにディプロイする時には
- コンパイルが成功すると0、依存関係の問題を見つけるとコンパイラはexitコード1を出力します。それを利用してCIにコンパイルを組み込むことを推奨します。
コンパイル
推奨セットアップを行う際にvendor/bin/bear.compile
スクリプトを使ってプロジェクトをウォームアップすることができます。コンパイルスクリプトはDI/AOP用の動的に作成されるファイルやアノテーションなどの静的なキャッシュファイルを全て事前に作成し、最適化されたautoload.phpファイルとpreload.phpを出力します。
- コンパイルをすれば全てのクラスでインジェクションを行うのでランタイムでDIのエラーが出る可能性が極めて低くなります。
.env
には含まれた内容はPHPファイルに取り込まれるのでコンパイル後に.env
を消去可能です。コンテントネゴシエーションを行う場合など(例:api-app, html-app)1つのアプリケーションで複数コンテキストのコンパイルを行うときにはファイルの退避が必要です。
mv autoload.php api.autoload.php
composer.json
を編集してcomposer compile
の内容を変更します。
autoload.php
{project_path}/autoload.php
に最適化されたautoload.phpファイルが出力されます。composer dumpa-autoload --optimize
で出力されるvendor/autoload.php
よりずっと高速です。
注意:preload.php
を利用する場合、ほとんどの利用クラスが読み込まれた状態で起動するのでコンパイルされたautoload.php
は不要です。composerが生成するvendor/autload.php
をご利用ください。
preload.php
{project_path}/preload.php
に最適化されたpreload.phpファイルが出力されます。preloadを有効にするためにはphp.iniでopcache.preload、opcache.preload_userを指定する必要があります。
PHP 7.4でサポートされた機能ですが、7.4
初期のバージョンでは不安定です。7.4.4
以上の最新版を使いましょう。
例)
opcache.preload=/path/to/project/preload.php
opcache.preload_user=www-data
Note: パフォーマンスベンチマークはbenchmarkを参考にしてください。
.compile.php
実環境ではないと生成ができないクラス(例えば認証が成功しないとインジェクトが完了しないResourceObject)がある場合には、コンパイル時にのみ読み込まれるダミークラス読み込みをルートの.compile.php
に記述することによってコンパイルをすることができます。
.compile.php
<?php
require __DIR__ . '/tests/Null/AuthProvider.php'; // 常に生成可能なNullオブジェクト
$_SERVER[__REQUIRED_KEY__] = 'fake';
module.dot
コンパイルをすると”dotファイル”が出力されるのでgraphvizで画像ファイルに変換するか、GraphvizOnlineを利用すればオブジェクトグラフを表示することができます。スケルトンのオブジェクトグラフもご覧ください。
dot -T svg module.dot > module.svg
ブートストラップのパフォーマンスチューニング
immutable_cacheは、不変の値を共有メモリにキャッシュするためのPECLパッケージです。APCuをベースにしていますが、PHPのオブジェクトや配列などの不変の値を共有メモリに保存するため、APCuよりも高速です。また、APCuでもimmutable_cacheでも、PECLのIgbinaryをインストールすることでメモリ使用量が減り、さらなる高速化が期待できます。
現在、専用のキャッシュアダプターなどは用意されていません。ImmutableBootstrapを参考に、専用のBootstrapを作成し呼び出してください。初期化コストを最小限に抑え、最大のパフォーマンスを得ることができます。
php.ini
// エクステンション
extension="apcu.so"
extension="immutable_cache.so"
extension="igbinary.so"
// シリアライザーの指定
apc.serializer=igbinary
immutable_cache.serializer=igbinary