Laravel のキュー サービスは、さまざまなバックグラウンド キュー サービスに統合された API を提供します。次の記事では、Laravel が同じキュー タスクを繰り返し実行する理由をソース コード分析を通じて紹介します。記事では、サンプル コードを通じて紹介します。詳細については、友人以下を見てみましょう。
まえがき
Laravel のキュー サービスは、さまざまなバックグラウンド キュー サービスに統合された API を提供します。キューを使用すると、電子メールの送信など、時間のかかるタスクの実行を遅らせることができます。これにより、リクエストの応答時間を効果的に短縮できます。
問題が見つかりました
Laravel で Redis を使用してキュー タスクを処理するフレームワークが提供する機能は非常に強力ですが、最近問題が発生しました。問題は、タスクが複数回実行されていることがわかったのですが、これはなぜでしょうか?
まず理由について話しましょう:
Laravel では、キュー (タスク) の場合実行時間が 60 秒を超えると、実行失敗とみなされ、キューに再度追加され、同じタスクが繰り返し実行されます。
このタスクのロジックは、コンテンツをユーザーにプッシュすることです。ユーザーは、キューのコンテンツに基づいて取得および走査され、リクエスト バックエンド HTTP インターフェイスを通じて送信される必要があります。たとえば、ユーザーが 10,000 人いる場合、ユーザー数が多かったり、インターフェースの処理速度がそれほど速くない場合、実行時間は確実に 60 秒を超えるため、タスクは再びキューに追加されます。状況はさらに悪く、前のタスクが 60 秒以内に実行されなかった場合、それらのタスクは再びキューに追加され、同じタスクが 1 回だけでなく複数回実行されることになります。
Laravel のソースコードから犯人を見つけてみましょう。
ソースコードファイル:vendor/laravel/framework/src/Illuminate/Queue/RedisQueue.php
/** * The expiration time of a job. * * @var int|null */ protected $expire = 60;
$expire メンバー変数は固定値であり、Laravel は 60 秒かかってもキューは実行されるべきであると考えています。キューを取得する方法:
public function pop($queue = null) { $original = $queue ?: $this->default; $queue = $this->getQueue($queue); $this->migrateExpiredJobs($queue.':delayed', $queue); if (! is_null($this->expire)) { $this->migrateExpiredJobs($queue.':reserved', $queue); } list($job, $reserved) = $this->getConnection()->eval( LuaScripts::pop(), 2, $queue, $queue.':reserved', $this->getTime() + $this->expire ); if ($reserved) { return new RedisJob($this->container, $this, $job, $reserved, $original); } }
キューを取得するにはいくつかの手順があります。これは、キューが実行に失敗したり、実行がタイムアウトした場合に発生するためです。別のコレクションに入れられて保存されます。再試行するためのプロセスは次のとおりです:
1. 実行が原因で失敗したキューを、遅延コレクションから現在のコレクションに再プッシュします。実行されたキュー。
2. 実行タイムアウトのため、予約されたコレクションから現在実行されているキューにキューを再プッシュします。
3. 次に、キューからタスクを取得して実行を開始し、予約された順序付きコレクションにキューを入れます。
ここでは eval コマンドを使用してこの処理を実行し、いくつかの lua スクリプトを使用します。
実行するキューからタスクを取得します:
local job = redis.call('lpop', KEYS[1]) local reserved = false if(job ~= false) then reserved = cjson.decode(job) reserved['attempts'] = reserved['attempts'] + 1 reserved = cjson.encode(reserved) redis.call('zadd', KEYS[2], ARGV[1], reserved) end return {job, reserved}
Laravel がタスクを取得していることがわかります。 Redis によって実行されます。キューに入れると、コピーが同時に順序付けられたコレクションに入れられ、有効期限のタイムスタンプがスコアとして使用されます。
タスクが完了した場合にのみ、タスクは順序付けセットから削除されます。この順序付けされたコレクションからキューを削除するコードは省略されていますが、実行時間が 60 秒を超えるキューを Laravel がどのように処理するかを見てみましょう。
これは、この lua スクリプトによって実行される操作です:
local val = redis.call('zrangebyscore', KEYS[1], '-inf', ARGV[1]) if(next(val) ~= nil) then redis.call('zremrangebyrank', KEYS[1], 0, #val - 1) for i = 1, #val, 100 do redis.call('rpush', KEYS[2], unpack(val, i, math.min(i+99, #val))) end end return true
ここで、zrangebyscore は無限小からスコアを検索します。現在のタイムスタンプの要素、つまり 60 秒前にコレクションに追加されたタスクの場合、これらの要素は、zremrangebyrank および rpush を介してコレクションから削除され、キューに追加されます。
これを見たらハッと気づくはずです。
キューが 60 秒以内に実行されなかった場合、プロセスはキューをフェッチするときに予約セットからタスクを再度キューにプッシュします。
概要
Laravel Redis について複数のプロセスが同時にキューを取得する問題の詳細な説明
thinkphp5 URL とルーティング 機能の詳しい説明と例
##
以上がソースコード分析により、Laravel が同じキュータスクを繰り返し実行する理由が説明されるの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。