愛島発電所

QT SOLAR 愛島発電所

本日の発電量
828 kWh
現在の日射量
2 Wh/㎡
現在の外気温
21.9 ℃

(2024/07/27 19:00 更新)

下増田発電所

QT SOLAR 下増田発電所

本日の発電量
142 kWh
現在の日射量
1 Wh/㎡
現在の外気温
22.9 ℃

(2024/07/27 19:11 更新)

北原東発電所

QT SOLAR 北原東発電所

本日の発電量
205 kWh
現在の日射量
4 Wh/㎡
現在の外気温
23.0 ℃

(2024/07/27 19:09 更新)

長久良辺発電所

QT SOLAR 長久良辺発電所

本日の発電量
135 kWh
現在の日射量
6 Wh/㎡
現在の外気温
22.9 ℃

(2024/07/27 18:50 更新)

白坂発電所

QT SOLAR 白坂発電所

本日の発電量
137 kWh
現在の日射量
6 Wh/㎡
現在の外気温
23.5 ℃

(2024/07/27 18:53 更新)

清水沢発電所

QT SOLAR 清水沢発電所

本日の発電量
79 kWh
現在の日射量
1 Wh/㎡
現在の外気温
21.9 ℃

(2024/07/27 18:53 更新)

Daemon[安定動作編] | Perlで作るサーバーサービス

デーモンは動き続けることが要求されます。これは、いわゆる「落ちない」こともそうですが、処理を滞らせないことも含みます。

デーモンは一般的に終了のシグナルを受け取るまでできる限り動作し続けることを要求されます。エラーが発生するたびにデーモンがダウンしていたのではダメだということです。

太陽光発電所の計測システムも、何らかのエラーが発生するたびにシステムがダウンして再起動が必要になるのでは面倒です。

そういったことから、エラー処理が必要になり、既に紹介したデーモンのサンプルでも例外処理が含まれています。

通信する相手先のサーバーがメンテナンスで止まっているような場合でも、ソケットのエラーを捕捉できているのなら、例外処理でデーモンの動作自体は続行出来ます。

これで一件落着のようですが、実際にはエラーを捕捉できない場合もあるわけです。例えば、デッドロックやブロッキングの問題です。

これらは、ある程度までは正しい実装、気の利いた実装で回避すべきでしょうが、外部サーバーとの通信であったり、得体の知れない機器なりが相手となると限界もあります。

頻発するエラーならともかく再現性の低い問題にあれこれ考えを巡らせなければならないのは面倒くさいというのもありますね。美しく言えば予見が難しいエラーにも対策を施しておきましょうということです。

例えば、ps auxで確認すると未だプロセスは生きているように見えるがログを見るとあるときを境にしばらく動いている形跡がないような場合、もしかしたら、これは、システムコールの返り(I/O待ち)を延々と待ち続けている状態なのかも知れません。

と、いうわけで、これはこれで動いていないのと一緒ですから、やはり対処が必要です。

サーバーの再起動?

まあ、異常に気づいたならそれでもいいでしょうが、これが客先で「エラーが出て止まってる? むむむ、では再起動してみて下さい」じゃダサいですよね。自作で自分しか使わないものでも、いちいち面倒ですから、デーモン自体が勝手に解決してくれるのならそのほうが良いです。

ざっと2点、追加の対策をしてみましょう。

  • 意味不明な待ち時間の経過はエラーとしてタイムアウト処理する。
  • イニシャライズする。

おっと、これらを実現するために、1つ前提条件がありました。

  • 関数や変数をクラスとして別に定義して、動的に生成する(インスタンスを作る)。

本来1回の処理が1秒以内に終了するものが1分も2分も滞っているとするならば、それは何らかの異常があるためです。待っていても仕方ありませんのでシグナルを使ってその手続きを強制的に終了させます。

一方で、途中で止めた場合、自身の変数であったり、相手との手続手順であったりは、信頼性のないものになってしまいますから、初期化(イニシャライズ)の手続きが必要になります。

スクリプトでは、タイムアウトに関するシグナル処理と、例外処理でのインスタンスを削除する処理が加わっています。インスタンスが削除されるので、次の処理時に自動的にインスタンスの再作成(ここで初期化処理をする)が行われます。

telemetry.pl

#!/usr/bin/perl
use POSIX;
$isSIGTERM = "";
$SIG{'TERM'} = sub {
$isSIGTERM="true";
};
$SIG{ALRM} = sub {
die "alarm";
};
fork and exit;
POSIX::setsid();
fork and exit;
umask 0;
chdir '/';
open STDIN , '<', '/dev/null';
open STDOUT, '>', '/dev/null';
open STDERR, '>', '/dev/null';
open LOG, '>>', '/home/your-account/telemetry.log';
print LOG "Telemetry System is started at ".localtime."\n";
close LOG;
while(!$isSIGTERM){
eval {
alarm 60;
$your_instance = YourClass->new() unless defined $your_instance;
$your_instance->update();
alarm 0;
};
alarm 0;
if($@){
open ERRLOG, '>>', '/home/your-account/telemetry.err';
print ERRLOG "An error has occurred at ".localtime." as follows:\n -> ".$@;
close ERRLOG;
undef $your_instance;
}
sleep(60);
}
open LOG, '>>', '/home/your-account/telemetry.log';
print LOG "Telemetry System is stopped at ".localtime."\n";
close LOG;
exit;

Perlには「Defined-or演算子」というのがあります。

なかなかかっこよくて、引数のデフォルト値を設定するのに使えそうです。c++を使うひとなら、Perlでも使いたいですよね。

my $arg = $_ // 0;

でも、同じくc++を使うひとには意味不明なコメントに見えるでしょうね。

今回のスクリプトで言えばこうなります。

$your_instance //= YourClass->new();

正直「なんだこれ?」と思うでしょうから、やっぱり「unless defined」かなあとも思うわけです。だって、うるう年に1回くらいしかプログラム(スクリプト)を書かないひとは、4年後に自分で書いた「//=」の意味を調べるのに検索で苦労するでしょ。