Berdasarkan prinsip-prinsip jawaban dari user1599237 , di mana Anda membiarkan cron job berjalan di semua contoh tetapi kemudian di awal pekerjaan menentukan apakah mereka harus diizinkan untuk berjalan, saya telah membuat solusi lain.
Alih-alih melihat instans yang berjalan (dan harus menyimpan kunci dan rahasia AWS Anda), saya menggunakan database MySQL yang sudah saya sambungkan dari semua instans.
Tidak ada kerugiannya, hanya positifnya:
- tidak ada contoh atau biaya tambahan
- solusi yang kokoh - tidak ada kemungkinan eksekusi ganda
- scalable - secara otomatis bekerja saat instance Anda ditingkatkan dan diturunkan
- failover - secara otomatis bekerja jika sebuah instance mengalami kegagalan
Alternatifnya, Anda juga dapat menggunakan sistem file yang umum dibagikan (seperti AWS EFS melalui protokol NFS) daripada database.
Solusi berikut dibuat dalam kerangka PHP Yii tetapi Anda dapat dengan mudah menyesuaikannya untuk kerangka kerja dan bahasa lain. Juga penangan pengecualian Yii::$app->system
adalah modul saya sendiri. Gantilah dengan apa pun yang Anda gunakan.
public function actionLock() {
$argsAll = $args = func_get_args();
if (!is_numeric($args[0])) {
\Yii::$app->system->error('Duration for obtaining process lock is not numeric.', ['Args' => $argsAll]);
}
if (!$args[1]) {
\Yii::$app->system->error('Job name for obtaining process lock is missing.', ['Args' => $argsAll]);
}
$durationMins = $args[0];
$jobName = $args[1];
$instanceID = null;
unset($args[0], $args[1]);
$command = trim(implode(' ', $args));
if (!$command) {
\Yii::$app->system->error('Command to execute after obtaining process lock is missing.', ['Args' => $argsAll]);
}
if (file_exists('/etc/elasticbeanstalk/.aws-eb-system-initialized')) {
if ($awsEb = file_get_contents('/etc/elasticbeanstalk/.aws-eb-system-initialized')) {
$awsEb = json_decode($awsEb);
if (is_object($awsEb) && $awsEb->instance_id) {
$instanceID = $awsEb->instance_id;
}
}
}
$updateColumns = false;
$affectedRows = \Yii::$app->db->createCommand()->upsert('system_job_locks', [
'job_name' => $jobName,
'locked' => gmdate('Y-m-d H:i:s'),
'duration' => $durationMins,
'source' => $instanceID,
], $updateColumns)->execute();
if ($affectedRows == 0) {
$affectedRows = \Yii::$app->db->createCommand()->update('system_job_locks', [
'locked' => gmdate('Y-m-d H:i:s'),
'duration' => $durationMins,
'source' => $instanceID,
],
'job_name = :jobName AND DATE_ADD(locked, INTERVAL duration MINUTE) < NOW()', ['jobName' => $jobName]
)->execute();
if ($affectedRows == 0) {
exit;
}
}
$command = str_replace('StdOUT', '>', $command);
$command = str_replace('StdERR.ditto', '2>&1', $command);
$command = str_replace('StdERR', '2>', $command);
$command .= ' &';
$output = []; $exitcode = null;
exec($command, $output, $exitcode);
exit($exitcode);
}
Ini adalah skema database yang saya gunakan:
CREATE TABLE `system_job_locks` (
`job_name` VARCHAR(50) NOT NULL,
`locked` DATETIME NOT NULL COMMENT 'UTC',
`duration` SMALLINT(5) UNSIGNED NOT NULL COMMENT 'Minutes',
`source` VARCHAR(255) NULL DEFAULT NULL,
PRIMARY KEY (`job_name`)
)