Laravel中常見的錯誤與解決方法小結(jié)
一、報錯: 「Can't swap PDO instance while within transaction」
通過查詢 Laravel 源代碼,可以確認(rèn)異常是在 setPdo 方法中拋出的:
<?php
public function setPdo($pdo)
{
if ($this->transactions >= 1) {
throw new RuntimeException("
Can't swap PDO instance while within transaction.
");
}
$this->pdo = $pdo;
return $this;
}
?>
按字面意思理解,出現(xiàn)此錯誤是因為在開啟了事務(wù)的情況下,切換了數(shù)據(jù)庫連接。不過有時候,即便代碼里沒有顯式的切換數(shù)據(jù)庫連接,也有可能出現(xiàn)此錯誤。比如說在執(zhí)行查詢語句出錯的時候,系統(tǒng)會通過 tryAgainIfCausedByLostConnection 方法判斷問題是不是因為丟失連接導(dǎo)致的,如果是,那么系統(tǒng)會通過 reconnect 方法重新連接,在重新連接的時候,系統(tǒng)會通過 disconnect 方法執(zhí)行一些清理工作,其中調(diào)用了 setPdo 方法。
理清了前因后果,自然就知道如何解決問題了:檢查網(wǎng)絡(luò)情況,確認(rèn)數(shù)據(jù)庫連接丟失的原因,這可能是某個設(shè)備有問題,也可能是某個 timeout 設(shè)置不當(dāng)所致。一個相對 dirty 的處理方法是在查詢前執(zhí)行一下 DB::reconnect() 方法重新連接一下數(shù)據(jù)庫。
二、報錯:「Cannot delete job: NOT_FOUND」
此問題實際上和 Laravel 沒太大關(guān)系,而是隊列服務(wù) Beanstalk 導(dǎo)致的。

Beanstalk
要解決這個問題,需要先理解一個消息的生命周期:當(dāng)一個消息被放入隊列的時候,它就進(jìn)入了 READY 狀態(tài),與此同時,它會關(guān)聯(lián)一個 TTR(time to run) 計時器,表示此消息允許運行的時間,當(dāng)此消息被消費時,它就進(jìn)入了 RESERVED 狀態(tài),消費完后,此消息就會被刪除,如果消費的時間過長,比 TTR 還長,那么系統(tǒng)會認(rèn)為認(rèn)為此消費者已經(jīng)掛了,進(jìn)而會把消息從 RESERVED 狀態(tài)退回到 READY 狀態(tài),交給另一個消費者重新處理。于是乎同一個消息可能會被多個消費者處理,第一個處理完的消費者可以正常的刪除消息,而其余的消費者在刪除消息的時候就會報無法刪除的錯誤。
解決方法很簡單,首先,需要確保 TTR 的設(shè)置不能太??;其次,實際上 Beanstalk 提供了一個專門的 touch 命令來解決執(zhí)行時間過長的問題,此外,有些時候我們可能需要在應(yīng)用層面上通過加鎖來規(guī)避同一個消息被多個消費者同時處理的情況。
三、報錯:「No query results for model」
在激活了 Laravel 讀寫分離的前提下,當(dāng)消費者處理消息的時候,可能會收到類似錯誤。一個有潛在問題的隊列命令大概如下所示:
<?php
class Foo extends Command implements SelfHandling, ShouldBeQueued
{
use InteractsWithQueue, SerializesModels;
protected $bar;
public function __construct($id)
{
$this->bar = Bar::find($id);
}
public function handle()
{
// $this->bar
}
}
?>
很明顯,當(dāng)開啟了 Laravel 讀寫分離的時候,因為主從延遲的緣故,所以 find 可能查詢不到相應(yīng)的數(shù)據(jù),一旦我們分析到了這里,那么很可能會把寫法修改成下面的樣子:
<?php
class Foo extends Command implements SelfHandling, ShouldBeQueued
{
use InteractsWithQueue, SerializesModels;
protected $bar;
public function __construct($id)
{
$this->bar = Bar::onWriteConnection()->find($id);
}
public function handle()
{
// $this->bar
}
}
?>
也就是說,通過 Laravel 的 onWriteConnection 方法把查詢固定在主服務(wù)器上,不過實際上無效。問題癥結(jié)在于反序列化的時候,系統(tǒng)會在從服務(wù)器上一次 findOrFail 調(diào)用?!?br />
<?php
protected function getRestoredPropertyValue($value)
{
return $value instanceof ModelIdentifier
? (new $value->class)->findOrFail($value->id) : $value;
}
?>
因為我們無法 HACK 到框架內(nèi)部,所以 onWriteConnection 就沒有意義了。其實換個角度看問題,只要在系列化的時候,保證別用數(shù)據(jù)庫對象做屬性即可:
<?php
class Foo extends Command implements SelfHandling, ShouldBeQueued
{
use InteractsWithQueue, SerializesModels;
protected $id;
public function __construct($id)
{
$this->id = $id;
}
public function handle()
{
$bar = Bar::onWriteConnection()->find($this->id);
}
}
?>
四、總結(jié)
以上就是我在使用Laravel遇到的幾個有代表性的報錯以及解決方案,如果有問題歡迎大家一起交流。希望這篇文章對大家的學(xué)習(xí)或者工作能帶來一定的幫助。
- Laravel5.1自定義500錯誤頁面示例
- Laravel框架基于中間件實現(xiàn)禁止未登錄用戶訪問頁面功能示例
- laravel 框架配置404等異常頁面
- Laravel 5.4重新登錄實現(xiàn)跳轉(zhuǎn)到登錄前頁面的原理和方法
- Laravel框架FormRequest中重寫錯誤處理的方法
- Laravel中錯誤與異常處理的用法示例
- laravel 5異常錯誤:FatalErrorException in Handler.php line 38的解決
- laravel migrate初學(xué)常見錯誤的解決方法
- Laravel實現(xiàn)自定義錯誤輸出內(nèi)容的方法
- Laravel 5.3 學(xué)習(xí)筆記之 錯誤&日志
- Laravel5框架自定義錯誤頁面配置操作示例
相關(guān)文章
JavaScript判斷數(shù)組重復(fù)內(nèi)容的兩種方法(推薦)
本文給大家介紹兩種JavaScript判斷數(shù)組重復(fù)內(nèi)容的方法(推薦)非常不錯具有參考借鑒價值,感興趣的朋友一起看看吧2016-06-06
javascript 獲取所有id中包含某關(guān)鍵字的控件的實現(xiàn)代碼
獲取某容器控件中id包含某字符串的控件id列表2010-11-11
微信小程序中上傳圖片并進(jìn)行壓縮的實現(xiàn)代碼
這篇文章主要介紹了微信小程序中上傳圖片并進(jìn)行壓縮的實現(xiàn)代碼,文中提到了上傳圖片并進(jìn)行壓縮的屬性值,需要的朋友可以參考下2018-08-08
webpack打包后index.html引用文件地址問題小結(jié)
在前端開發(fā)中,src 屬性指定的相對路徑是相對于當(dāng)前 HTML 文件的路徑,而不是相對于網(wǎng)站的根目錄,這篇文章主要介紹了webpack打包后index.html引用文件地址問題,需要的朋友可以參考下2024-05-05
JS實現(xiàn)鍵值對遍歷json數(shù)組功能示例
這篇文章主要介紹了JS實現(xiàn)鍵值對遍歷json數(shù)組功能,結(jié)合實例形式分析了javascript遍歷json數(shù)組相關(guān)操作技巧,需要的朋友可以參考下2018-05-05
JS中的==運算: [''''] == false —>true
這篇文章主要介紹了JS中的==運算: [''] == false —>true的相關(guān)資料,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2016-07-07

