経緯
レンタルサーバー(エックスサーバー)での稼働でスクリプトを設置したのですが、予算やらの都合で他のサーバースペースを借りれない状態になってしまいました。ついては、1台のサーバーでの負荷分散を余儀なくされる状態となりました。正確に言えば負荷分散というより、スクリプトの効率的書き方です。応用すれば複数サーバーを使用した負荷分散に応用できると思います。現状、スマホアプリ(WebView)及び、スマホブラウザ用ページ、PCページ(IE,FF,Webkit)のページを同一サーバー上に展開。
レスポンス的に負荷が少ないまとめ系サイトだったので、3媒体を追加。
現状余裕で稼働しています。想定ではあと7媒体ぐらいは載せても稼働できる帯域はある(エックスサーバーの場合)と思います。
単純な負荷分散ですが、実装すると理論上はSQLインジェクションを完全に無効化、また、DoS攻撃もある程度軽減できると考えられます。
実装
PHPでの実装になります(他言語でも可)。設置は単純でメインのMySQLをフロント側PHPから完全に隔離し、バック側のCRONで1時間に1回ぐらいのペースで必要最低限のデータをMySQLを実行生成し、JSONコードに変換、特定のディレクトリに吐き出す仕様としました。フロント側のPHPはMySQLにアクセスすることはできず、定期的に吐き出されたJSONコードをデータベースの代用として稼働させます。これによりいちいちSQLを実行する負荷を省き、バック側で整形、生成されたデータ(JSON)を元に出力処理をする形になります。
メリット&デメリット
メリット
・実装が簡単、単純なデータ構成で、それほどデータベースの更新がかからない場合には有効
・JSON形式なので多言語でのデータストアとしての接着剤的な扱いが可能で多言語を使う場合、非常に無駄がない
・rsyncなどを活用すれば別ドメインサーバーにJSONファイルごと転送することで負荷分散が可能
デメリット
・JSONPではないため、クライアント側のブラウザからJavaScriptのソースとして直接読みに行くことができない。
(今回の施策ではJSONPを使う機会がなかったのでやってません。手が空いてる人、誰か試してください...おねがいします...)
・即時反映されるようなシステムでは稼働しずらい。CGMやらは不向き。ニュースサイトやまとめサイトなどあまり更新がかからないサイトが有用
コード
$sql = sprintf("SELECT * FROM table WHERE start_time <= '%s' AND fin_time >= '%s'",
mysql_real_escape_string($date),
mysql_real_escape_string($date));
$result = mysql_query($sql);
$newest_result = array();
while($result_row = mysql_fetch_object($result)) {
$newest_result[] = array(
'title'=> $result_row->title
,'link'=> $result_row->link
,'dc'=> $result_row->dc
,'site'=> $result_row->site
);
}
for($cnt=0;$cnt<30;$cnt++){
$newest_output_30[$cnt] = $newest_result[$cnt];
}
// 中略
$file = fopen('../RSSReader/json/newest_output_30.json', 'wb') or die ('$newest_output_30 書き込みエラー');
flock($file, LOCK_EX);
fwrite($file, json_encode($newest_output_30));
fclose($file);
// 中略
試しに上記のようにします。ミソはJSONファイルをできれば小サイズになるよう(できれば1ページ分だけの情報量)などに分割することでディスクI/Oを効率化し処理のレスポンスを早くします。
json_encode がかかっているのでマルチバイトだろうと関係なく文字化けも心配ないです(自分のとこでは)。
JSONを吐き出すディレクトリのパーミッションとバッチのスクリプトディレクトリのパーミッションをあれこれしてディレクトリトラバーサルに気をつければそれほど問題はないかと思います。JSONに出力したらPHP以外の言語でも利用できるので便利(ためしたことないが)。
$file = './json/newest_output_30.json';
$json = file_get_contents($file);
$obj = json_decode($json, true);
foreach($obj as $key => $value){
$smarty->append( 'title', $value[title]);
$smarty->append( 'link', $value[link]);
$smarty->append( 'dc', $value[dc]);
$smarty->append( 'site', $value[site]);
}
あとはこれをフロント側のPHPなどの言語で利用すれば終わりです。上記ではSmartyにデータ渡しています。
JSON読み込む前は当然ですが json_decode してください。
まとめ
JSONは多数の言語で使われているデータフォーマットのためそれを核としたらいろいろできるんじゃねという安直な考えのもとやってみました。JSONPは後からしり、悶絶させられてしまいましたがサーバー間の処理で使う分には問題ないと思います。SQLiteよりも単純で読み取りだけならJSONのほうが扱いやすいためいいと思います。