とあるIT企業のインフラエンジニア。プライベートでは開発もちょっとやります。
※本ブログの内容はすべて個人の見解であり、所属する企業とは関連ありません。
2023/09/30 暫く更新停止中m
生活・子育て(10)
FaaS(1)
働き方(2)
SaaS(2)
自作PC(6)
IT入門(1)
IaaS(13)
IDaaS(2)
ITIL(1)
PHP(2)
OS(6)
システム監視(1)
コミュニティ(1)
PCアプリ(10)
ストレージ(4)
ブログ(9)
ActiveDirectory(2)
デバイス(7)
旅行(10)
デザイン(3)
カンファレンス(5)
セキュリティ(9)
インフラ(19)
コーディング(11)
ゲーム(28)
インターネット(18)
未分類(8)
547 [今日]
437 [昨日]
【備忘】phpでアクセスカウンターを作った時に詰まった話
2021/06/30
コーディング
お疲れ様です。
しらせです。
今更ですが、2019年の2月にphpでブログのアクセスカウンターを実装した時のつまりポイントをまとめておきます。
なぜかある日突然カウントがゼロに戻るという事象になります。
割とビギナーが陥りやすいメジャーな事象なんですかね?
自分で言うのも難ですが昔から車輪の再発明ばっかりやってます。
もくじ
当初の実装
まずは何も知らないで実装した問題のあるパターンです。
一見するとカウンター用ファイルを読み込んで表示した後で、インクリメントしてファイルに書き戻すという単純な処理ですが、
これを各ページに埋め込んでしまうと、悲しいことにある日突然カウントがゼロに戻るんです。
echo $blog_access;
$blog_access += 1;
file_put_contents('/data/access.txt',$blog_access);
原因も分からずにとりあえずデイリーでバックアップするcronを書いて、
事象が起きたら手動で書き戻すという辛い作業をしていました。
原因
phpに詳しい方ならすぐに分かるんだと思いますが、
私は詳しくないので数か月悩んで調べて解決に至りました。
問題なのは1行目のfile_get_contentsになります。
これは、カウンターを設置しているページに同時アクセスがあった場合に発生します。
公式マニュアルにも丁寧に記載があります。
i.e. in concurrent scenarios file_get_contents may return empty if you don't wrap it like this:
<?php
$myfile=fopen('test.txt','rt');
flock($myfile,LOCK_SH);
$read=file_get_contents('test.txt');
fclose($myfile);
?>
If you have code that does a file_get_contents on a file, changes the string, then re-saves using file_put_contents, you better be sure to do this correctly or your file will randomly wipe itself out.
(参考)https://www.php.net/manual/ja/function.file-put-contents.php
ちゃんとflock使わないとランダムにファイルがリセットされると。。。
修正版
そして修正版のコードがこちらです。
これでもなかなか無理やりに見えますよね。
文字量も多いのであまりスマートな書き方ではないと思いますが。。
if($fp && flock($fp, LOCK_EX)){
$counter = (int)trim(fgets($fp))+1; #インクリメント
rewind($fp); #ファイルポインタを先頭に
ftruncate($fp, 0); #ファイルを初期化
fputs($fp, $counter); #出力
fclose($fp); #ファイルポインタクローズ
}
重要なのが2行目でファイルポインタを開いた後にロックをします。
こうすることで、同時アクセスの際にも排他ロックを掛けて後発のアクセスを待ち状態にさせます。
その後、ファイルを数値で取得してインクリメントした後でまたファイルポインターの先頭から書き出します。
ちなみに公式ではロックにLOCK_SHを使っているみたいなんですが共有ロックだと別の問題があります。
処理によっては同じタイミングで参照した場合に後発のアクセスがカウントされないケースがあるんです。
「排他ロックで他のセッションを待たせる方が問題では?」
まぁそうなんですが、
カウントの処理なんてほんの数msですし、
アクセスの少ないウェブサイトにはちょうど良いのですw
待ち時間が長くなるくらいアクセス増えたら考えます。
以上、
おつかれさまでした。