你怎么优雅的判断当前队列 worker 消费的当前消息队列中是否有重复的?

最优解肯定是在入队列之前。但是有这样几个问题,举个栗子。

try {
    DB::beginTransaction();
    // 逻辑
    Queue::connection('connection_name')->push(new Job(),null,'queue_name');
    // 逻辑
    DB::commit();
} catch (Throwable $e) {
    DB::rollback();
}

问题如下:

  1. 入队列成功 提交事务失败(mysql挂了)
  2. 入队列失败 提交事务成功 ( 这个主要看驱动 某些broker可以知道是否扔队列成功 失败抛异常)
  3. 某些数据库导致事务提交失败(如tidb的乐观锁事务只有在commit时才检测事务冲突有冲突则commit抛异常)

所以可能导致的问题是:

  1. 数据库并无更新或新数据,队列里却有消息并正在执行
    还好,消息执行时检测当前数据在数据库中的状态是否需要执行不需要执行直接delete
  2. 数据有修改 队列里却没数据
    新建队列任务对应表?
  3. 数据有修改 队列里有多个重复消息
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 4
Epona

讲道理,不要在transaction中执行 job😂

4年前 评论
TimJuly

我猜题主是想问这个问题:怎么保证数据库和消息队列强一致性呢?即数据库提交成功消息队列一定写成功,数据库提交失败消息队列一定别他妈给我写进去。

答案是:不存在的。

但是针对你这个问题是有解的。

这个问题本质上就是如何保证两个分布式事务(数据库是一个事务,消息队列是另一个事务)都能优雅的完美执行。这就需要用一些其他办法来保证了。

对于你这个问题其实也很好解决,数据库成功了才去写消息队列,失败了就不写,那么代码就变成了下面这个样子:

try {
    DB::beginTransaction();
    // 逻辑
    DB::commit();
} catch (Throwable $e) {
    DB::rollback();

    throw $e; // 都他妈别往后走了,老子挂了
}

Queue::connection('connection_name')->push(new Job(),null,'queue_name');

这样做数据库跟消息队列就解耦开来了,也防止消息队列挂了影响主流程。现在你只需要关心数据库写成功了,但是消息队列没写进去的问题了。怎么办呢?定期对数据就行,下游没有消费到的数据都跑出来重新扔到队列里就行了。

再多说一句,你自己的代码里写的那段就算能保证数据库和消息队列强一致性,也有一个问题,消息优先于数据库提交成功时进行了消费,这个时候下游是拿不到数据的,一样会出问题。

4年前 评论
cevin (楼主) 4年前
Jorry 2年前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!