ホームページ >PHPフレームワーク >Laravel >Laravel で Observer イベントを使用すると、Redis キュー例外の問題が発生する

Laravel で Observer イベントを使用すると、Redis キュー例外の問題が発生する

藏色散人
藏色散人転載
2021-12-03 09:33:482358ブラウズ

Laravel の次のチュートリアル コラムでは、Laravel Observer によって発生した Redis キュー例外に関する記録を共有します。

#1. ビジネス ロジック新しいモデルを作成した後、オブザーバー モデル イベント Created を使用して、非同期 SMS 送信キューにプッシュします

App\ Http\Controllers\UsersController

<pre class="brush:php;toolbar:false">    public function store(User $user)     {         \DB::beginTransaction();         try{             $input = request()-&gt;validated();             $user-&gt;fill($input);             $user-&gt;save();             //do something......             //其他数据表操作             \DB::commit();         } catch ($e \Exception) {             \DB::rollBack();         }     }</pre>

App\Observers\UserObserver

<pre class="brush:php;toolbar:false">class UserObserver{     public function created (User $user)     {         dispatch(new SmsQueue($user));     }}</pre>

2. 異常が見つかりましたBusiness 部門は、ユーザーが SMS 通知を受信できない場合があると報告したため、ログを確認したところ、モデル [App\Models\User] のクエリ結果がありませんという例外エラーが発生していることがわかりました。これは、対応するモデルを受信できないことを意味します。 found

入力できません はい、モデルを作成した後にキュー呼び出しを行いました...その後、ビジネス コードを注意深く確認し、トランザクションの影響を受けるはずだと推測しました。

推測を検証します:

    public function store(User $user)
    {
        \DB::beginTransaction();

        try{
            $input = request()->validated();
            $user->fill($input);
            $user->save();
            //do something......
            //其他数据表操作

            sleep(3); //三秒之后再提交事务            
            \DB::commit();
        } catch ($e \Exception) {
            \DB::rollBack();
        }

    }
案の定、3 秒待機すると、送信キュー例外が 100% トリガーされます。

3. 原因分析

    $user->save() このメソッドがデータの作成に成功すると、スケジューラーもトリガーされます。 . モデル イベントを 1 つずつ実行します。
  • イベント内のキューにモデルをプッシュすると、キュー プロセスはキュー内のデータを継続的に消費します。
  • do something の処理速度が正常であれば、ほとんどの場合、キューのプロセスは通常どおり実行されます。
  • 実行フェーズ中に時折遅延が発生する場合、トランザクションはまだコミットされていないものの、キューはすでに新しいモデルの消費を開始しているため、上記のエラーが発生します。
  • その後、Github Issues レコードを検索したところ、この問題が 2015 年の Issue で提起されていたことがわかり、Laravel 8.X ではついにトランザクション モデル イベントのサポートが追加されました。サポートされています; learnku.com/docs/laravel/8.x/eloqu...、コミュニティ ドキュメントに関連する手順が見つからないようです~

私のバージョンは 6.x なので、この新機能は使用しないでください[泣いています]~~

4. 例外を解決します

1. MySQL トランザクション分離レベルを変更します (推奨) これには、MySQL のトランザクション分離レベルが関係します。InnoDB エンジンのデフォルトの分離レベルは REPEATABLE READ です。各レベルの違いは、公式ドキュメントで見つけることができます。

分離レベルを READ UNCOMMITTED に切り替えるとこの問題は解決できますが、より大きな問題を防ぐために、この方法は使用しないことをお勧めします~

2.イベント監視ソース コードを表示すると、トランザクションの完了後に対応するイベントが呼び出されることがわかります。そのため、イベント監視を追加するだけで済みます。

Laravel で Observer イベントを使用すると、Redis キュー例外の問題が発生する

#新しいクラス
    App\Handlers\TransactionHandler
  1. class TransactionHandler{
        public array $handlers;
    
        public function __construct()
        {
            $this->handlers = [];
        }
    
        public function add(\Closure $handler)
        {
            $this->handlers[] = $handler;
        }
    
        public function run()
        {
            foreach ($this->handlers as $handler) {
                $handler();
            }
            $this->handlers = [];
        }}
    補助関数の作成
  2. app/helpers.php
  3. if (! function_exists('after_transaction')) {
        /*
         * 事务结束之后再进行操作
         * */
        function after_transaction(Closure $job)
        {
            app()->singletonIf(\App\Handlers\TransactionHandler::class, function (){
                return new \App\Handlers\TransactionHandler();
            });
            app(\App\Handlers\TransactionHandler::class)->add($job);
        }}
    リスナーの作成
  4. App\Listeners\TransactionListener
  5. namespace App\Listeners;use App\Handlers\TransactionHandler;class TransactionListener{
        public function handle()
        {
            app(TransactionHandler::class)->run();
        }}
    # #バインド リスニングApp\Providers\EventServiceProvider
  6. namespace App\Providers;use App\Listeners\TransactionListener;use Illuminate\Database\Events\TransactionCommitted;use Illuminate\Database\Events\TransactionRolledBack;use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;;class EventServiceProvider extends ServiceProvider{
        /**
         * The event listener mappings for the application.
         *
         * @var array
         */
        protected $listen = [
            TransactionCommitted::class => [
                TransactionListener::class
            ],
            TransactionRolledBack::class => [
                TransactionListener::class
            ]
        ];}

    呼び出しメソッドの変更

    App\Observers\UserObserver
  7. # #

    class UserObserver{
        public function created (User $user)
        {
            after_transaction(function() use ($user) {
                dispatch(new SmsQueue($user));
            });
        }}
    OK、洗練されたソリューションが完成しました~~関連する推奨事項:
  8. 最新の 5 つの Laravel ビデオ チュートリアル

以上がLaravel で Observer イベントを使用すると、Redis キュー例外の問題が発生するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はlearnku.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。