昔の掲示板を設置するMojolicousからCGIを呼ぶ他者のPerlを書き替える他者のPerlを書き替える~2~全ページに告知アクセスカウンターを設置2019-08-13背景ホームページの基本、アクセスカウンターがないことに気付いたので設置しました。ホントはこちらのツイートから。昔のアクセスカウンターはトップページに設置して、クライアントからのリクエストを受けてカウントします。他のページへいきなりジャンプするとアクセスカウンターがリクエストを把握できずカウントできません。いう背景があるのか直リン(他のページに直接リンクする行為)が禁止になっているサイトをよく見かけました。Twitter」みたいだ(老眼)。当サイトはトップページ用のコントローラーを書いてあります。クライアントがトップページを表示した際に呼ばれるので、そこにカウント処理を入れます。リロードでのカウントアップ抑止として1時間有効のクッキーがあればカウントアップしません。それとカウント結果をスタッシュに入れてトップページのテンプレートへ渡します。昔のApacheとかのWebサーバーですと、クライアントからのリクエストに合わせてCGIが呼ばれてしまいます。それは非同期ですから競合するもの(カウント値)は調整しないといけませんでした。当サイトのMojoliciousは単一プロセスで完結しているので、複数クライアントからのリクエストであっても競合しません。$count);表示処理トップページのテンプレートでカウンタの文言とスタッシュを参照します。一見すると昔なつかしSSI(サーバーサイドインクルード)のように見えますけれど、そこは我らがMojoliciousです。テンプレートからページを生成する際にスタッシュを参照してカウント結果に置き換わります。%>人目のお客様です。現在で455ページあります。時は流れて2019年、トップページでクライアントからのリクエストのみでカウントする時代は終わりました。WebサーバーがMojoliciousならルーター(応答ページの処理順の設定モジュール)の設定を調整すれば、どのページにアクセスしても必ずカウントできます。プラグインアクセスカウンターのプラグインを作ります。プラグインが登録される際にヘルパー関数を追加します。2つのヘルパー関数(PpAccessCounter_get、PpAccessCounter_count)はコントローラーからいつでも呼び出せます。3600);}});}1;カウントアップのタイミングサイトのどこにアクセス(クライアントからのリクエスト)があってもカウントするよう工夫します。アプリケーションのstartupでルーターを設定する冒頭に次のコードを入れます。リクエストのURLでドメイン名の次が「/」で始まる場合に(つまり全部)コールバックを登録してヘルパー関数のカウント処理を呼び、1を返してルーター処理を継続させます。サブルーチンの中はコントローラーなので$selfで良いのですが、startupの$selfと混同しそうなので$cにしています。このコードに続いてルーターの設定があるので$rを書き替えています。1;});表示処理テンプレートでの表示は第一段階とほぼ同じですがトップページのリクエスト時に行われたスタッシュの追加がありません。代わりにヘルパー関数を呼び出します。%>人目のお客様です。現在で455ページあります。複数プロセスで並行処理できるアクセスカウンターにしたい。第二段階までは単一プロセスだから競合の心配なく簡単な処理でした。並行処理が前提になると、1個のリソースを奪い合うので調停が面倒くさいです。Mojoliciousのログ出力は1個のリソースだから、この処理が参考になりそうと見てみると普通にflock掛けてた。確かにflockで済むけれど、なんかこう、つまらない。perldoc.jpredis何かないかなーと探してredisというのを見つけました。redis」してから一般ユーザーで「redis-server」(redisのサーバーを常駐)させておき、Mojoliciousのコントローラーからredisのクライアントを実行してredisへ連絡できます。都合の良いことに単一の値を数値とみなしてインクリメント(+1)する機能があり、そのままアクセスカウンターに使えます。redisサーバーの常駐コマンドラインでredis-serverを起動すれば常駐しますけれど、sshで普通に起動するとログアウトで終了してしまいます。そこでバックグラウンドで実行、ログアウトでも常駐できるようなスクリプト「redis_start.sh」を作りました。redis-serverプラグインの修正プラグインの中でredisのクライアントを実行します。カウントアップと表示の処理は変更ありません。});}1;カウント値の調整カウント値の引き継ぎや修正は「redis-cli」コマンドでクライアントを実行してできます。第四段階を追加しました。redisはサーバー(redis-server)を起動しておくのが前提です。Mojoliciousと同様に一般ユーザーで稼働させている都合があり、Mojoliciousが稼働していてもredisが停止しているケースがあります。試しにredisを止めるとMojoliciousからMojo::redisを使った途端にdieしてメッセージが出ます。ウチのMojoliciousはシングルプロセスで稼働させていますから、dieするのは以ての外です。redisサーバーの生死判定を加えてdieを回避しましょう。死んでるサーバーにpingはdieMojo::redisを追いかけるとpingがありサーバーにpingするとpongが返るというので、死んだサーバーにpingすると途端にdieします。metacpan.orggetやincrのpromise版promise版は完了コールバックを指定し、呼び出し直後に戻る形式でエラー時もdieしないでコールバックされます。エラー時にdieしないのは良いのですけれど、呼び出し直後に戻ってきますから、普通に書くと応答を得る前に制御が戻ってしまいます。リクエストと独立させてタイマー駆動にします。プラグインが登録された際に読み出しイベントとインクリメントイベントの2つのタイマーを起動、3秒毎に繰り返し実行されます。リクエストに応じてカウント値の参照やインクリメント用の変数を操作しますが、このスクリプト自体は単一プロセスなので競合しません。hypnotoad稼働でもプロセス毎に独立、redisを呼ぶ所までは競合せず、redisのサーバーで調停されます。mojolicious。