라라벨 옵저버로 인해 발생하는 Redis 큐 예외에 대한 기록을 Laravel의 튜토리얼 칼럼에서 공유해드리도록 하겠습니다.
새 모델을 생성한 후 Created Observer 모델 이벤트를 사용하여 비동기 SMS 전송 대기열로 푸시합니다
AppHttpControllersUsersController
AppHttpControllersUsersController
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(); } }
AppObserversUserObserver
class UserObserver{ public function created (User $user) { dispatch(new SmsQueue($user)); }}
业务部门反馈偶尔有用户收取不到短信通知,我便查看日志发现偶尔有错误异常:No query results for model [AppModelsUser]. 表示找不到对应的模型
我敲不应该啊,我是在创建模型之后再进行队列调用……,遂对业务代码再进行仔细核查猜测应该是受事务影响。
验证猜想:
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(); } }
果然在等待三秒之后提交队列异常已是 100% 触发。
$user->save() 这个方法创建数据成功,便会一并触发调度器,将模型事件一一执行。
在事件中推送模型至队列中,而队列进程在不间断消费队列中数据。
在大部分情况下 do something 处理速度正常的话,队列进程将会照常运行。
如果在 do something 阶段偶尔出现延迟,造成事务还未 commit 而队列已经开始消费新模型;故引发上述错误。
然后我在搜索 Github Issues 记录时,发现此问题在 2015 年的一个 Issue 已经有人提出,而在 Laravel 8.X 中终于新增了对事务模型事件的支持;learnku.com/docs/laravel/8.x/eloqu... ,在社区文档似乎并没有找到相关说明~
由于我的版本是 6.x 所以用不了这个新特性[哭唧唧]~~
这里涉及到 MySQL 的事务隔离级别,InnoDB 引擎的默认隔离级别是 REPEATABLE READ,关于各个级别的区别可以在 官方文档 找到。
将隔离级别切换到 READ UNCOMMITTED 即可解决此问题,但是为了防止出现更大的问题我劝你别用这种方式~
查看源码得知在事务完成之后,会调用对应的事件,所以只需增加对事件的监听即可。
新增类 AppHandlersTransactionHandler
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 = []; }}
创建辅助函数 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); }}
创建监听 AppListenersTransactionListener
namespace App\Listeners;use App\Handlers\TransactionHandler;class TransactionListener{ public function handle() { app(TransactionHandler::class)->run(); }}
绑定监听 AppProvidersEventServiceProvider
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 ] ];}
更改调用方式 AppObserversUserObserver
class UserObserver{ public function created (User $user) { after_transaction(function() use ($user) { dispatch(new SmsQueue($user)); }); }}
AppObserversUserObserver
사업부에서 가끔 사용자가 SMS 알림을 받을 수 없다고 보고하여 로그를 확인해보니 가끔 [AppModelsUser] 모델에 대한 쿼리 결과가 없습니다. 해당 모델을 찾을 수 없다는 뜻입니다Me 맞지 않아야 합니다. 모델을 생성한 후 대기열 호출을 했습니다... 그리고 비즈니스 코드를 주의 깊게 확인하여 거래에 영향을 받은 것으로 추측했습니다. 🎜🎜추측 검증:🎜rrreee🎜 물론 3초를 기다린 후 제출 대기열 예외가 100% 발생합니다. 🎜🎜🎜🎜3. 원인 분석🎜
AppHandlersTransactionHandler
🎜rrreee🎜app/helpers.php
🎜rrreee🎜 AppListenersTransactionListener
🎜rrreee🎜AppProvidersEventServiceProvider
🎜rrreee🎜AppObserversUserObserver
🎜🎜🎜rrreee🎜OK, 우아한 The 솔루션이 완성되었습니다~~🎜🎜관련 권장 사항: 🎜최신 5개의 Laravel 비디오 튜토리얼🎜🎜위 내용은 Laravel에서 Observer 이벤트를 사용하면 Redis 대기열 예외 문제가 발생합니다.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!