Home > PHP Framework > Laravel > body text

Using Observer events in Laravel causes Redis queue exception problem

藏色散人
Release: 2021-12-03 09:33:48
forward
2302 people have browsed it

The following tutorial column of Laravel will share with you the record about the Redis queue exception caused by Laravel Observer. I hope it will be helpful to everyone!

1. Business logic

After creating a new model, use the Observer model event Created to push into the asynchronous SMS sending queue

App\ Http\Controllers\UsersController

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

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

            \DB::commit();
        } catch ($e \Exception) {
            \DB::rollBack();
        }

    }
Copy after login

App\Observers\UserObserver

class UserObserver{
    public function created (User $user)
    {
        dispatch(new SmsQueue($user));
    }}
Copy after login

2. Abnormality found

Business The department reported that occasionally users could not receive SMS notifications, so I checked the logs and found that there were occasional error exceptions: No query results for model [App\Models\User]. It means that the corresponding model cannot be found

I can’t type it Yes, I made the queue call after creating the model... Then I carefully checked the business code and guessed that it should be affected by the transaction.

Verify the conjecture:

    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();
        }

    }
Copy after login

Sure enough, after waiting for three seconds, the submission queue exception is 100% triggered.

3. Cause analysis

  • $user->save() If this method successfully creates data, the scheduler will be triggered as well. Execute model events one by one.

  • Push the model to the queue in the event, and the queue process consumes data in the queue continuously.

  • In most cases, if the do something processing speed is normal, the queue process will run as usual.

  • If there is an occasional delay during the do something phase, the transaction has not yet been committed but the queue has already started consuming the new model; therefore, the above error is caused.

Then when I searched the Github Issues record, I found that this issue had been raised in an Issue in 2015, and in Laravel 8.X, support for transaction model events was finally added. Supported; learnku.com/docs/laravel/8.x/eloqu..., I don’t seem to find relevant instructions in the community documentation~

Because my version is 6.x, I can’t use this new feature[ Crying]~~

4. Solve the exception

1. Modify the MySQL transaction isolation level (not recommended)

This involves the transaction isolation level of MySQL. The default isolation level of the InnoDB engine is REPEATABLE READ. The differences between each level can be found in the official documentation.

Switching the isolation level to READ UNCOMMITTED can solve this problem, but in order to prevent bigger problems, I advise you not to use this method~

2. Add event monitoring

View the source code and learn that after the transaction is completed, the corresponding event will be called, so you only need to add event monitoring.

Using Observer events in Laravel causes Redis queue exception problem

  1. New classApp\Handlers\TransactionHandler

    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 = [];
        }}
    Copy after login
  2. Create auxiliary Function app/helpers.php

    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);
        }}
    Copy after login
  3. Create listener App\Listeners\TransactionListener

    namespace App\Listeners;use App\Handlers\TransactionHandler;class TransactionListener{
        public function handle()
        {
            app(TransactionHandler::class)->run();
        }}
    Copy after login
  4. Bind listeningApp\Providers\EventServiceProvider

    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
            ]
        ];}
    Copy after login
  5. Change the calling method App\Observers\UserObserver

class UserObserver{
    public function created (User $user)
    {
        after_transaction(function() use ($user) {
            dispatch(new SmsQueue($user));
        });
    }}
Copy after login

OK, an elegant solution is complete~~

Related recommendations: The latest five Laravel video tutorials

The above is the detailed content of Using Observer events in Laravel causes Redis queue exception problem. For more information, please follow other related articles on the PHP Chinese website!

Related labels:
source:learnku.com
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template
About us Disclaimer Sitemap
php.cn:Public welfare online PHP training,Help PHP learners grow quickly!