Laravel中常見(jiàn)的錯(cuò)誤與解決方法小結(jié)
一、報(bào)錯(cuò): 「Can't swap PDO instance while within transaction」
通過(guò)查詢 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)此錯(cuò)誤是因?yàn)樵陂_(kāi)啟了事務(wù)的情況下,切換了數(shù)據(jù)庫(kù)連接。不過(guò)有時(shí)候,即便代碼里沒(méi)有顯式的切換數(shù)據(jù)庫(kù)連接,也有可能出現(xiàn)此錯(cuò)誤。比如說(shuō)在執(zhí)行查詢語(yǔ)句出錯(cuò)的時(shí)候,系統(tǒng)會(huì)通過(guò) tryAgainIfCausedByLostConnection
方法判斷問(wèn)題是不是因?yàn)閬G失連接導(dǎo)致的,如果是,那么系統(tǒng)會(huì)通過(guò) reconnect
方法重新連接,在重新連接的時(shí)候,系統(tǒng)會(huì)通過(guò) disconnect
方法執(zhí)行一些清理工作,其中調(diào)用了 setPdo
方法。
理清了前因后果,自然就知道如何解決問(wèn)題了:檢查網(wǎng)絡(luò)情況,確認(rèn)數(shù)據(jù)庫(kù)連接丟失的原因,這可能是某個(gè)設(shè)備有問(wèn)題,也可能是某個(gè) timeout
設(shè)置不當(dāng)所致。一個(gè)相對(duì) dirty
的處理方法是在查詢前執(zhí)行一下 DB::reconnect()
方法重新連接一下數(shù)據(jù)庫(kù)。
二、報(bào)錯(cuò):「Cannot delete job: NOT_FOUND」
此問(wèn)題實(shí)際上和 Laravel 沒(méi)太大關(guān)系,而是隊(duì)列服務(wù) Beanstalk 導(dǎo)致的。
Beanstalk
要解決這個(gè)問(wèn)題,需要先理解一個(gè)消息的生命周期:當(dāng)一個(gè)消息被放入隊(duì)列的時(shí)候,它就進(jìn)入了 READY
狀態(tài),與此同時(shí),它會(huì)關(guān)聯(lián)一個(gè) TTR(time to run)
計(jì)時(shí)器,表示此消息允許運(yùn)行的時(shí)間,當(dāng)此消息被消費(fèi)時(shí),它就進(jìn)入了 RESERVED
狀態(tài),消費(fèi)完后,此消息就會(huì)被刪除,如果消費(fèi)的時(shí)間過(guò)長(zhǎng),比 TTR
還長(zhǎng),那么系統(tǒng)會(huì)認(rèn)為認(rèn)為此消費(fèi)者已經(jīng)掛了,進(jìn)而會(huì)把消息從 RESERVED
狀態(tài)退回到 READY
狀態(tài),交給另一個(gè)消費(fèi)者重新處理。于是乎同一個(gè)消息可能會(huì)被多個(gè)消費(fèi)者處理,第一個(gè)處理完的消費(fèi)者可以正常的刪除消息,而其余的消費(fèi)者在刪除消息的時(shí)候就會(huì)報(bào)無(wú)法刪除的錯(cuò)誤。
解決方法很簡(jiǎn)單,首先,需要確保 TTR
的設(shè)置不能太??;其次,實(shí)際上 Beanstalk
提供了一個(gè)專(zhuān)門(mén)的 touch
命令來(lái)解決執(zhí)行時(shí)間過(guò)長(zhǎng)的問(wèn)題,此外,有些時(shí)候我們可能需要在應(yīng)用層面上通過(guò)加鎖來(lái)規(guī)避同一個(gè)消息被多個(gè)消費(fèi)者同時(shí)處理的情況。
三、報(bào)錯(cuò):「No query results for model」
在激活了 Laravel 讀寫(xiě)分離的前提下,當(dāng)消費(fèi)者處理消息的時(shí)候,可能會(huì)收到類(lèi)似錯(cuò)誤。一個(gè)有潛在問(wèn)題的隊(duì)列命令大概如下所示:
<?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)開(kāi)啟了 Laravel 讀寫(xiě)分離的時(shí)候,因?yàn)橹鲝难舆t的緣故,所以 find
可能查詢不到相應(yīng)的數(shù)據(jù),一旦我們分析到了這里,那么很可能會(huì)把寫(xiě)法修改成下面的樣子:
<?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 } } ?>
也就是說(shuō),通過(guò) Laravel 的 onWriteConnection
方法把查詢固定在主服務(wù)器上,不過(guò)實(shí)際上無(wú)效。問(wèn)題癥結(jié)在于反序列化的時(shí)候,系統(tǒng)會(huì)在從服務(wù)器上一次 findOrFail
調(diào)用。
<?php protected function getRestoredPropertyValue($value) { return $value instanceof ModelIdentifier ? (new $value->class)->findOrFail($value->id) : $value; } ?>
因?yàn)槲覀儫o(wú)法 HACK
到框架內(nèi)部,所以 onWriteConnection
就沒(méi)有意義了。其實(shí)換個(gè)角度看問(wèn)題,只要在系列化的時(shí)候,保證別用數(shù)據(jù)庫(kù)對(duì)象做屬性即可:
<?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遇到的幾個(gè)有代表性的報(bào)錯(cuò)以及解決方案,如果有問(wèn)題歡迎大家一起交流。希望這篇文章對(duì)大家的學(xué)習(xí)或者工作能帶來(lái)一定的幫助。
- Laravel5.1自定義500錯(cuò)誤頁(yè)面示例
- Laravel框架基于中間件實(shí)現(xiàn)禁止未登錄用戶訪問(wèn)頁(yè)面功能示例
- laravel 框架配置404等異常頁(yè)面
- Laravel 5.4重新登錄實(shí)現(xiàn)跳轉(zhuǎn)到登錄前頁(yè)面的原理和方法
- Laravel框架FormRequest中重寫(xiě)錯(cuò)誤處理的方法
- Laravel中錯(cuò)誤與異常處理的用法示例
- laravel 5異常錯(cuò)誤:FatalErrorException in Handler.php line 38的解決
- laravel migrate初學(xué)常見(jiàn)錯(cuò)誤的解決方法
- Laravel實(shí)現(xiàn)自定義錯(cuò)誤輸出內(nèi)容的方法
- Laravel 5.3 學(xué)習(xí)筆記之 錯(cuò)誤&日志
- Laravel5框架自定義錯(cuò)誤頁(yè)面配置操作示例
相關(guān)文章
微信小程序自定義可滑動(dòng)頂部TabBar選項(xiàng)卡實(shí)現(xiàn)頁(yè)面切換功能示例
這篇文章主要介紹了微信小程序自定義可滑動(dòng)頂部TabBar選項(xiàng)卡實(shí)現(xiàn)頁(yè)面切換功能,結(jié)合實(shí)例形式分析了微信小程序自定義頂部TabBar選項(xiàng)卡頁(yè)面切換功能的相關(guān)布局、樣式及功能實(shí)現(xiàn)技巧,需要的朋友可以參考下2019-05-05js拖動(dòng)滑塊和點(diǎn)擊水波紋效果實(shí)例代碼
在本文里面我們給大家分享了js實(shí)現(xiàn)拖動(dòng)滑塊和點(diǎn)擊水波紋效果的相關(guān)實(shí)例代碼,有需要的朋友們學(xué)習(xí)下。2018-10-10使用JavaScript獲取URL中的參數(shù)(兩種方法)
這篇文章主要介紹了使用JavaScript獲取URL中的參數(shù)(兩種方法)的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-11-11js實(shí)現(xiàn)iframe自動(dòng)自適應(yīng)高度的方法
這篇文章主要介紹了js實(shí)現(xiàn)iframe自動(dòng)自適應(yīng)高度的方法,涉及javascript操作iframe框架的技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2015-02-02Yii2使用Bootbox插件實(shí)現(xiàn)自定義彈窗
Bootbox.js 是一個(gè)小型的 JavaScript 庫(kù)用來(lái)創(chuàng)建簡(jiǎn)單的可編程對(duì)話框,基于 Twitter 的 Bootstrap 開(kāi)發(fā)。今天我們就來(lái)研究下,如何使用bootbox插件來(lái)實(shí)現(xiàn)自定義彈窗。2015-04-04Javascript中for循環(huán)語(yǔ)句的幾種寫(xiě)法總結(jié)對(duì)比
如果您希望一遍又一遍地運(yùn)行相同的代碼,并且每次的值都不同,那么使用循環(huán)是很方便的,javascript中for循環(huán)也是非常常用的,下面這篇文章主要介紹了Javascript中for循環(huán)的幾種寫(xiě)法,需要的朋友可以參考借鑒,一起來(lái)看看吧。2017-01-01