|||||||||||||||||||||

なんぶ電子

- 更新: 

PHPのインメモリKeyValueストア

phpinfo(apcu)

PHPサーバーへのDOS攻撃的な繰り返しアクセスに対して503エラーを出したいと思って、Redisのようなインメモリ型のデータベースが使えないかと調べていたら、APCuという拡張ライブラリがあるということを知り設定してみました。

環境は、Windows11とDebian11のPHP8.1です。

Windowsの場合

PECLのページよりWindows用のバイナリをダウンロードします。

Windowのアイコンと「DLL」と表記のあるリンクから進むとPHPのバージョン、TSかNTS、32か64bitによりバイナリがわかれています。

PHPのTSやNTSのバージョンの違いについては過去にWindowsへPHPをインストールした際の記事を読んでいただければと思います。

ここではPHP8.1、TS、64bit版をダウンロードしました。

ダウンロードしたzipファイルを展開すると、php_apcu.dllがあるので拡張ライブラリフォルダにコピーします。これは通常PHPルートのextフォルダとなります。

php.iniに拡張ライブラリの設定を記述します。初めて拡張ライブラリを設定する際はextension_dirの設定もします。

php.ini

...
; On windows:
; コメントアウト解除
extension_dir = "ext"
...
; 新規追加
extension=apcu

Debianの場合

以前筆者はLaravelをデプロイした際にPHP8.1をビルドしたので、そこへ設定するためにビルドをします。

APTレポジトリのバージョンでインストールする場合は php-apcuをインストールするだけです。

# wget https://pecl.php.net/get/apcu-5.1.22.tgz
# tar vzxf apcu-5.1.22.tgz
...
# cd apcu-5.1.22
# phpize
...
# ./configure --enable-apcu
...
# make
...
# make test
...
# cp modules/apcu.so /usr/local/lib/php/extensions

途中、次のようなメッセージがでたらおそらくautoconfが存在しないことが原因です。

autoconfはconfigureスクリプトを自動生成するプログラムです。

Please check your autoconf installation and the $PHP_AUTOCONF environment variable. Then, rerun this script.

また、makeが実行できない場合は、built-essentialをインストールします。

# apt install autoconf build-essential

ライブラリ(apcu.so)のコピーは自身の環境に合わせて設定してください。その後php.iniに拡張ライブラリの読み込みの設定をします。

php.ini

...
; 拡張ライブラリのディレクトリを指定
extension_dir = "/usr/local/lib/php/extensions/"
...
; 新規追加
extension=apcu

APCu稼働の確認

Windowsの場合もDebianの場合もphp.iniの設定まで完了したら、Apacheを再起動します。phpinfoを読み込んで、次のような記述があれば設定は成功です。

phpinfo(apcu)

php.iniの設定とステータス確認

APCuを使用可能にすると、php.iniにおいて、いくつかそれ用の設定ができるようになります。公式ページにはAPCuは大半の環境でデフォルトの設定のまま使って問題ないとありますので、設定変更の可能性が高そうな項目だけ紹介しておきます。

  • apc.enabed

    APCuの有効化と無効化です。"1"で有効、"0"で無効です。

  • apc.shm_size

    メモリのサイズです。デフォルトでは"32M"がセットされています。

  • apc.ttl

    メモリに設定した値の有効期限です。デフォルトは"0"ですが、"0"の場合メモリ不足になった場合すべてのキャッシュが削除されます。

また、チューニングをする場合にはapc.phpファイルが役に立ちます。Linux用のtarを展開した中に含まれるPHPのコードで単体でホストしてブラウザから読みだすことでAPCuのステータスを確認できます。

apc.php

APCuの使い方

たとえば30秒間の間に同じリモートアドレスから10回以上のアクセスがあった場合に503を返すコードは次のようになります。

sample.php

<?php
define('TTL',30);
define('MAX_ACCESS',10);

$addr = $_SERVER['REMOTE_ADDR'];

// キーが存在するか確認
if(apcu_exists($addr)) {
  // 存在した場合は値を取得
  $cnt = apcu_fetch($addr);
  
  $cnt = $cnt+1;

  if (MAX_ACCESS <= $cnt) {
      // カウントアップ(TTLを更新する)
      apcu_store($addr,$cnt,TTL);
      
      // 503
      header('HTTP/1.1 503 Service Temporarily Unavailable');
      die('503 Service Temporarily Unavailable');
  } 
    
  // カウントアップ
  $result=false;
  apcu_store($addr,$cnt,TTL);

  // デバッグ情報
  echo "$addr:$cnt";
  
} else {
  // 既にキーに対する値が存在する場合はエラーになる
  apcu_add($addr,1,TTL);
  
  // デバッグ情報
  echo "$addr:access";
}

操作に関しては主に次のような関数があります。

  • apcu_add($キー,$値,$TTL)

    新規にデータを追加します、キーが既存の際はエラーになります。

  • apcu_store($キー,$値,$TTL)

    データを保存します、キーが既存の際は上書きします。

  • apcu_delete($キー)

    キャッシュから削除します。

  • apcu_clear_cache()

    キャッシュをすべてクリアします。

  • apcu_entory($キー, $コールバック関数,$TTL)

    キー値が存在した場合キー値を返します。

    キーが存在しない時コールバックを呼びます。コールバック関数ではキー値を受け取り、保存値を返すようにします。未存在時はコールバックで返した値がキーにセットされます。この時の有効期限は$TTLに設定した時間になります。

    apcu_entoryでは排他ロックがかかるためアトミック性が保たれます。

  • apcu_exists($キー)

    キーとして保存した値がある場合true、それ以外はfalseが返ります。

  • apcu_fetch($キー)

    キー値を取り出します。失敗時はfalseが返ります。

他に、メタ情報を取得する関数として、apcu_cache_info()や、apcu_key_info($キー)、などが存在します。

筆者紹介


自分の写真
がーふぁ、とか、ふぃんてっく、とか世の中すっかりハイテクになってしまいました。プログラムのコーディングに触れることもある筆者ですが、自分の作業は硯と筆で文字をかいているみたいな古臭いものだと思っています。 今やこんな風にブログを書くことすらAIにとって代わられそうなほど技術は進んでいます。 生活やビジネスでPCを活用しようとするとき、そんな第一線の技術と比べてしまうとやる気が失せてしまいがちですが、おいしいお惣菜をネットで注文できる時代でも、手作りの味はすたれていません。 提示されたもの(アプリ)に自分を合わせるのでなく、自分の活動にあったアプリを作る。それがPC活用の基本なんじゃなかと思います。 そんな意見に同調していただける方向けにLinuxのDebianOSをはじめとした基本無料のアプリの使い方を紹介できたらなと考えています。

広告