PHP」タグアーカイブ

CodeigniterのDB接続不可をハンドリングして再接続する

目的

  • CodeigniterのDB接続不可時に処理をエラーにせずなんとか再接続したい。

前提・用途

  • DBに繋がらないからといってクラッシュされては困るようなクリティカルな処理が有る。
  • Amazon RDSのマルチAZ等のように、冗長化されてはいるが数秒~2分程度のダウンタイムは発生するような構成であり、それに耐えたい。
  • Web APIを提供しているなど、瞬間的に大量のアクセスが発生しDBのmax_connectionを使い切るような状況が起こり得る。が、普段のアクセスは少ない。
  • Codeigniter標準のDBフェイルオーバー機能が使えるほど冗長化された構成ではない(=フェイルオーバー用のDBが無い)。
  • この記事はMySQLにmysqliを使って接続する前提になっています。

方法

Connecting to your Database — CodeIgniter 3.0.6 documentation
http://www.codeigniter.com/user_guide/database/connecting.html

$this->db->reconnect(); を使えば行けそうに思えますが、そもそも最初からDBに接続できなかった場合、reconnect() メソッドはただただfalseを返すような実装になっています。MySQLに上手く接続できているかどうかは、$this->db 、または$this->load->database(‘hoge’, true);が返すオブジェクトのプロパティ、conn_id を見る事で確認可能です。mysqliを使って接続している場合、このプロパティにはmysqliのオブジェクトが格納されており、接続できなかった場合は false が格納されます。

function reconnectDb()
{ 
    if ($this->db->conn_id !== false) {
         return;
     }
     log_message('error', 'DB接続失敗。再接続開始');
    // スクリプト実行可能時間を延ばしておく
     ini_set('max_execution_time', '180');
     for ($retry_count = 0; $retry_count < 8; $retry_count ++) {
         sleep(15);
         $this->load->database(); 
        if ($this->db->conn_id !== false) {
             log_message('debug', 'DB再接続処理OK');
             return; 
        } 
        log_message('debug', 'DB再接続処理NG');
     } 
}  

$this->db->conn_idがfalseであった場合はスクリプトの実行可能時間を延ばしたうえで、再度databaseのロードを試みます。上手く接続できた場合はfalseではなくmysqliのオブジェクトが入っており、DBを使う処理の続行が可能になります。上記のような処理を前述のクリティカルな場所に仕込んでおきます。DBがダウンしていたり、DB側のコネクション制限を使い切っている場合でも再接続の機会を得る事が出来ます。

注意点

  • あくまでDBへの再接続チャンスを得るための処理なので万能ではありません。上記例の場合、15秒ごとの8回のリトライに失敗すればDBには接続できないままです。
  • かなりの時間を待たせる処理になるので、クリティカルな処理、バッチ的な処理、API的な処理でのみ保険的に利用するべきでしょう。
  • バッチ的な処理など、それなりに時間がかかる処理の中で何度もDBにアクセスするような場合は、最初に接続確認を行っても途中でDBがダウンして接続出来なくなるような事も考えられます。$this->db->recconect() メソッドで conn_id プロパティを更新したうえで再度、再接続処理を実行する事もできるはずですが、運悪くDB処理の真っ最中にダウンしたような場合に対しては無力です。
  • CodeigniterのDB接続設定(database.php)にて db_debug を trueにしている場合、DB周りでのError・Warningはそのまま処理の即時停止となり、この再接続処理にたどり着きません。db_debug を false にしている環境でのみ動作します。
  • 上記と同様にディスプレイにPHPのエラーを表示する設定も、DB接続失敗時にWarningが発生しているため非推奨です。

用法、用量を守ってご参考程度に。

Gist lf-uraku-yuki/ci3-db-connect-retry.php

Windows環境にちゃんと動くComposerを導入する

2016年1月現在においてPHPで何かを作るうえで、パッケージ管理ツール(兼、便利なクラスオートローダー)であるComposerを使う機会は多いと思います。そしてこのComposerはWindows環境においても簡単に導入できるのですが、実際にComposerを使ってパッケージを導入してみようと思うと内部でGitコマンドを呼び出していたりと動かないことがありますのでその辺まで含めて簡単にまとめてみたいと思います。

0.PHPをインストール済み

http://windows.php.net/download
既に導入済みかとは思いますがWindows版PHPをインストールしておきます。ファイルを展開し C:\php\バージョン\ あたりにでも展開、環境変数にパスを設定して、あとはphp.iniのタイムゾーンやら基本的な設定をしておきます。Windows 10は以前のWindowsに比べて環境変数が編集しやすくなってていいですね。

php環境変数設定

更に脱線すると、WebサーバはPHP内臓のビルトインサーバが使えますので、別途ApacheやXAMPP等をインストールしたりする必要は特にありません。DBを使いたい人はWindows版のMySQLなりPostgreSQLなりもどうぞ。

1.Composerをインストールする

https://getcomposer.org/
Windows版のインストーラがありますのでComposer-Setup.exeをダウンロード、実行でインストール完了です。途中、右クリックメニューにComposer関連のコマンドを追加するかのオプションが表示されますが、特に追加する必要はないかと思います。

もし、以下のような画面でインストールが失敗した場合、PHPのopensslエクステンションが有効になっていません。php.iniでopensslを有効にして再度実行してみてください。

composer_windows_openssl

php_ext_openssl

2. Git for Windowsのインストール

https://git-for-windows.github.io/
ここまででComposerの導入はできているのですが、実際に使おうと思うと内部でGitコマンドを呼んでいるなどして結局動かないことが多々あります。Gitコマンド含めLinuxコマンドも使える環境としてGit for Windowsを導入します。えっ、結局Linux互換環境入れちゃうの?と思われるかもしれませんが、基本的にComposerコマンドを使うときにコマンドプロンプトの代わりにGit Bashを開く程度で済みます。コンポーネントの選択でGit BashのチェックはデフォルトでONですので、そのままにしておきます。以下、注意が必要な選択肢。

1番目か3番目をお好みで。3番目の「Use Git and optional Unix tools from the Windows Command Prompt」を使う場合は記載の注意を要確認。

git_bash1

MinTTY(MSys2)を使います。

git_bash2

3. 使ってみる

ここまででインストール完了です。実際に使ってみます。インストール済みのGit Bashを起動します。

bit_bash_menu

おもむろに composer とタイプしてみます。動いた!

comoposer_for_gitbash

では実際にパッケージを導入してみます。今回はとりあえずCodeigniterを入れてみます。Codeigniterは公式のComposerパッケージがないのですが、@kenji_s 氏作成の使いやすい導入パッケージが利用できます。ありがたい。

CodeIgniter 3.0をコマンド一発でインストールするCodeIgniter Composer Installer

適当なインストール先ディレクトリを作成し移動、サンプルのコマンド通りに実行してみます。

composer_codeigniter

ComposerでCodeigniterが導入できました!せっかくなので動作確認まで行います。publicディレクトリまで潜り、php -S localhost:9090でビルトインサーバを起動、Webブラウザで開いてみます。

codeigniter_303_init

しっかり動いています!おしまい!

CodeIgniter 3でREST風のAPIを実装する

GET/POST以外のHTTPメソッド/JSONで入力

REST風のAPIを実装するには一般的にはGETやPOSTだけでなくPUTやDELTE等のHTTPメソッドからの入力にも対応する必要が有りますが、CodeIgniter 3.0.xを利用している環境ではどうやって拾うの?という話。

CodeIgniter 3からInputライブラリに raw_input_stream というプロパティが追加されており、そこから読み込むだけで簡単に文字列を取得できます。

Input Class
https://www.codeigniter.com/user_guide/libraries/input.html

また、このraw_input_streamを使う事でフォームのようなkey=valueではない($this->input->post()で拾えない)値が取得出来ます。あとはJSON形式で投げているならjson_decodeしたりなんなりで。

// raw_input_streamとjson_decodeはnullを返すことが有るよ!
$raw_input = $this->input->raw_input_stream;
$json = json_decode($raw_input);

Q. CodeIgniter 2.x の場合
A1. CodeIgniter 3.xにアップグレードする。1日あれば動くようになるはず。
A2. PHP 5.5以前の環境であるなら5.6以降にして php://input ストリームを開く。
A3. CI_Inputクラス魔改造。お勧め出来ない。

柔軟にURLとコントローラの対応を取りたい

コントローラクラスのフォルダ階層分けやurlヘルパーでのsegment取得、引数指定で頑張ってもいいですが、コントローラに_remapメソッドを定義すれば柔軟性が上がります。URLだけを見るルータ側での設定とは違ってHTTPメソッドによって呼び出すメソッドを変えるような事もやりやすくなるかもしれません。

Controllers — CodeIgniter 3.0.2 documentation Remapping Method Calls
https://www.codeigniter.com/user_guide/general/controllers.html#remapping-method-calls

例えば存在しない機能を叩いた場合のWeb API向け404処理などを振り分けやすくなりますね。