PHP Laravel 队列技巧:Fail、Retry 或者 Delay

这篇博客介绍了在 Laravel 中处理队列作业的技巧,包括检查尝试次数、删除队列作业、处理失败的作业以及延迟作业。通过`InteractsWithQueue`trait,开发者可以更好地控制队列作业的行为,例如在转码任务中根据需要进行重试、删除或延迟处理。此外,还讨论了如何在特定条件下避免处理作业并利用`failed()`方法执行清理操作。

 

当创建队列jobs、监听器或订阅服务器以推送到队列中时,您可能会开始认为,一旦分派,队列工作器决定如何处理您的逻辑就完全由您自己决定了。

嗯……并不是说你不能从作业内部与队列工作器交互,但是通常情况下,哪怕你做了,也是没必要的。

这个神奇的骚操作的出现是因为“InteractsWithQueue”这个trait。.当排队作业正在从队列中拉出, 这个 [CallQueuedListener](https://github.com/laravel/framework/blob/5.8/src/Illuminate/Events/CallQueuedListener.php#L90-L104) 会检查它是否在使用 InteractsWithQueue trait, 如果是的话,框架会将底层的“队列jobs”实例注入到内部。

这个 “任务” 实例类似于一个包装了真正的 Job 类的驱动,其中包含队列连接和尝试等信息。


背景

我将以一个转码 Job 为例。 这是一个将广播音频文件转换成192kbps MP3格式的任务。因为这是在自由转码队列中设置的,所以它的作用有限。大型项目实战直播加入(点击→)我的直播群677079770


检查尝试次数

attempts()是被调用的第一个方法, 顾名思义,它返回尝试次数,一个队列 job总是伴随着一个attempt启动。

此方法旨在与其他方法一起使用 ..., 类似 fail() 或者 release() (delay). 为了便于说明,我们将通知用户第几次重试: 每次我们尝试在空闲队列中转换(转换代码)时,我们都会通知用户我们正在第几次重试,让他可以选择取消将来的转换(转换代码)。

<?php
namespace App\Jobs;
use App\Podcast;
use Transcoder\Transcoder;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use App\Notifications\PodcastTranscoded;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use App\Notifications\RetyingPodcastTranscode;
class TranscodePodcast
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
    /**
     * Transcoder Instance
     *
     * @var \App\Podcast
     */
    protected $podcast;
    /**
     * 创建一个新的转码podcast实例。
     *
     * @param \App\Podcast $podcast
     * @return void
     */
    public function __construct(Podcast $podcast)
    {
        $this->podcast = $podcast;
    }
    /**
     * 执行队列job.
     *
     * @param \Transcoder\Transcoder $podcast
     * @return void
     */
    public function handle(Transcoder $transcoder)
    {
        // 告诉用户我们第几次重试
        if ($this->attempts() > 1) {
            $this->podcast->publisher->notify(new Re
<think>好的,我现在需要帮助用户完善他们在UniApp中使用蓝牙分包发送数据时遇到的两个问题:数据顺序错乱和写入失败重试。用户之前已经得到了一些解决方案,现在希望更详细地了解如何实现同步发送队列和自动重试机制。 首先,我得回顾一下之前的对话历史。用户之前询问了关于UniApp蓝牙分包发送的问题,我给出了核心步骤,其中在常见问题处理部分提到数据顺序错乱需要使用同步发送队列,写入失败需要重试最多3次。现在用户希望更详细地了解这两个问题的解决方法。 接下来,我需要考虑用户可能的实际使用场景。他们可能在开发一个需要通过蓝牙传输较大数据的应用,比如固件更新、文件传输或者实时数据传输。在这种情况下,数据包的顺序和可靠性至关重要。如果数据包顺序错乱,接收方可能无法正确重组数据,导致数据损坏。而写入失败可能由于信号干扰、设备忙或其他临时问题引起,需要重试机制来增强鲁棒性。 用户可能已经尝试过简单的发送循环,但遇到了数据顺序问题或者写入失败的情况。他们现在希望有更健壮的解决方案,确保数据按顺序发送,并且在失败时能够自动重试,同时不超过最大重试次数。 首先,处理数据顺序错乱的问题。用户需要确保每个数据包按顺序发送,前一个发送完成后再发送下一个。这可以通过同步队列来实现,即使用Promise和async/await来确保异步操作按顺序执行。可能需要创建一个队列,将发送任务按顺序加入队列,并依次处理。 然后是写入失败的重试机制。当一次写入失败时,需要捕获错误,并尝试重新发送,最多三次。这可以通过在发送函数中加入重试计数器,使用循环或递归来实现。需要注意的是,每次重试之间可能需要一定的延迟,以避免频繁请求导致的问题。 接下来,我需要考虑如何将这些概念转化为具体的代码示例,并解释每一步的作用。同时,需要指出可能遇到的陷阱,比如重试次数过多导致性能问题,或者队列管理不当导致内存泄漏。 对于同步发送队列,可能需要维护一个任务队列,每个任务是一个发送操作。使用async函数和队列处理,确保即使有多个发送请求,也会按顺序处理。例如,使用数组作为队列,每次执行队列头部的任务,完成后移除并执行下一个。 对于自动重试,可以在发送函数中包裹一个循环,尝试发送,如果失败则重试,直到成功或达到最大次数。同时,需要处理可能的错误类型,比如超时、设备未连接等,并根据不同错误决定是否重试。 还需要考虑用户体验,比如在发送过程中显示进度,或者在重试时给出提示。此外,跨平台差异也需要考虑,比如iOS和Android在蓝牙处理上的不同,可能需要不同的重试策略或延迟时间。 最后,要确保代码示例符合UniApp的API规范,使用正确的BLE方法和事件处理。可能需要使用uni.writeBLECharacteristicValue,并在Promise中封装以便于异步处理。 总结下来,我需要分步骤解释同步队列和重试机制的实现,提供可运行的代码示例,并说明注意事项和最佳实践,帮助用户理解和应用这些解决方案。</think>以下是针对蓝牙通信中数据顺序错乱和写入失败问题的完善解决方案: --- ### 一、数据顺序错乱解决方案:同步发送队列 #### 实现原理 通过队列机制强制所有发送请求 **按序执行** ,确保前一个数据包发送完成后再处理下一个请求,避免并发写入导致的顺序错乱。 #### 代码实现 ```javascript class BluetoothQueue { constructor() { this.queue = [] this.isProcessing = false } // 添加发送任务到队列 enqueue(task) { this.queue.push(task) if (!this.isProcessing) { this.processQueue() } } // 处理队列 async processQueue() { this.isProcessing = true while (this.queue.length > 0) { const task = this.queue[0] try { await task.execute() this.queue.shift() // 成功则移除任务 } catch (error) { console.error('任务执行失败:', error) break // 保留失败任务在队列中 } } this.isProcessing = false } } // 创建全局队列实例 const bleQueue = new BluetoothQueue() // 封裝发送任务 function createWriteTask(deviceId, serviceId, characteristicId, data) { return { execute: () => new Promise((resolve, reject) => { uni.writeBLECharacteristicValue({ deviceId, serviceId, characteristicId, value: data, success: resolve, fail: reject }) }) } } // 使用示例 bleQueue.enqueue( createWriteTask(deviceId, serviceId, charId, chunk1) ) bleQueue.enqueue( createWriteTask(deviceId, serviceId, charId, chunk2) ) ``` #### 关键特性 - **先进先出(FIFO)** 保证发送顺序 - **自动续传** :前一个任务失败时暂停队列 - **线程安全** :通过 `isProcessing` 标志防止并发处理 --- ### 二、写入失败重试机制 #### 智能重试策略 ```javascript async function reliableWrite(deviceId, serviceId, charId, data) { const MAX_RETRY = 3 let retryCount = 0 while (retryCount <= MAX_RETRY) { try { await writeChunk(deviceId, serviceId, charId, data) return // 成功则退出 } catch (error) { if (retryCount === MAX_RETRY) { throw new Error(`写入失败,已达最大重试次数: ${error}`) } console.warn(`第 ${retryCount+1} 次重试...`) await delay(100 * (retryCount + 1)) // 指数退避 retryCount++ } } } // 改进后的发送函数 async function sendData(...args) { // ...原有逻辑... await reliableWrite(deviceId, serviceId, charId, chunk) // ... } ``` #### 重试优化策略 1. **指数退避** :重试间隔按 100ms → 200ms → 300ms 递增 2. **错误过滤** :仅重试可恢复错误(通过错误码判断) ```javascript if (error.errCode === 10004) { // 假设10004是可恢复错误码 // 执行重试 } ``` 3. **重试日志** :记录每次重试的详细信息 --- ### 三、组合应用方案 ```javascript // 完整发送流程 async function safeSendLargeData() { try { const data = prepareData() // 准备待发送数据 const chunks = splitData(data, MTU) // 数据分片 for (const chunk of chunks) { // 将每个分片包装成队列任务 const task = createWriteTask(deviceId, serviceId, charId, chunk) // 执行带重试机制的入队操作 await new Promise((resolve) => { bleQueue.enqueue({ execute: async () => { await reliableWrite(task) resolve() } }) }) } } catch (error) { handleError(error) // 统一错误处理 } } ``` --- ### 四、调试建议 1. **顺序验证** :在数据包头部添加序号标记(如:`[1/20]...`) 2. **重试监控** :通过事件总线记录重试事件 ```javascript uni.$on('ble_retry', (count) => { console.log(`正在进行第 ${count} 次重试`) }) ``` 3. **性能分析** :使用 `console.time()` 统计单包传输耗时 --- ### 五、跨平台差异处理 | 特性 | Android | iOS | |--------------|--------------------------|--------------------------| | 默认MTU | 23字节 | 185字节 | | 重试敏感度 | 可较快速重试 | 建议增加重试间隔 | | 错误码 | 系统蓝牙相关错误 | 需要处理`OSStatus`错误码 | 通过这种组合方案,可以有效解决蓝牙通信中的顺序错乱和可靠性问题,同时保持代码的可维护性和跨平台兼容性。实际开发中建议配合心跳机制和传输校验(如CRC校验)使用,进一步提升通信稳定性。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值