ホームページ  >  記事  >  バックエンド開発  >  ソースコード分析により、Laravel が同じキュータスクを繰り返し実行する理由が説明される

ソースコード分析により、Laravel が同じキュータスクを繰り返し実行する理由が説明される

jacklove
jackloveオリジナル
2018-07-04 17:58:401743ブラウズ

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 について複数のプロセスが同時にキューを取得する問題の詳細な説明

php-msf ソース コードの詳細な説明

thinkphp5 URL とルーティング 機能の詳しい説明と例

##

以上がソースコード分析により、Laravel が同じキュータスクを繰り返し実行する理由が説明されるの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。