Laravel中常见的错误与解决方法小结

一、报错: 「Can't swap PDO instance while within transaction」

通过查询 Laravel 源代码,可以确认异常是在 setPdo 方法中抛出的: 

public function setPdo($pdo)
{
if ($this->transactions >= 1) {
throw new RuntimeException("
Can't swap PDO instance while within transaction.
");
}

$this->pdo = $pdo;

return $this;
}

?>

按字面意思理解,出现此错误是因为在开启了事务的情况下,切换了数据库连接。不过有时候,即便代码里没有显式的切换数据库连接,也有可能出现此错误。比如说在执行查询语句出错的时候,系统会通过 tryAgainIfCausedByLostConnection 方法判断问题是不是因为丢失连接导致的,如果是,那么系统会通过 reconnect 方法重新连接,在重新连接的时候,系统会通过 disconnect 方法执行一些清理工作,其中调用了 setPdo 方法。

理清了前因后果,自然就知道如何解决问题了:检查网络情况,确认数据库连接丢失的原因,这可能是某个设备有问题,也可能是某个 timeout 设置不当所致。一个相对 dirty 的处理方法是在查询前执行一下 DB::reconnect() 方法重新连接一下数据库。

二、报错:「Cannot delete job: NOT_FOUND」

此问题实际上和 Laravel 没太大关系,而是队列服务 Beanstalk 导致的。

Beanstalk

要解决这个问题,需要先理解一个消息的生命周期:当一个消息被放入队列的时候,它就进入了 READY 状态,与此同时,它会关联一个 TTR(time to run) 计时器,表示此消息允许运行的时间,当此消息被消费时,它就进入了 RESERVED 状态,消费完后,此消息就会被删除,如果消费的时间过长,比 TTR 还长,那么系统会认为认为此消费者已经挂了,进而会把消息从 RESERVED 状态退回到 READY 状态,交给另一个消费者重新处理。于是乎同一个消息可能会被多个消费者处理,第一个处理完的消费者可以正常的删除消息,而其余的消费者在删除消息的时候就会报无法删除的错误。

解决方法很简单,首先,需要确保 TTR 的设置不能太小;其次,实际上 Beanstalk 提供了一个专门的 touch 命令来解决执行时间过长的问题,此外,有些时候我们可能需要在应用层面上通过加锁来规避同一个消息被多个消费者同时处理的情况。

三、报错:「No query results for model」

在激活了 Laravel 读写分离的前提下,当消费者处理消息的时候,可能会收到类似错误。一个有潜在问题的队列命令大概如下所示: 

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
}
}

?>

很明显,当开启了 Laravel 读写分离的时候,因为主从延迟的缘故,所以 find 可能查询不到相应的数据,一旦我们分析到了这里,那么很可能会把写法修改成下面的样子:

class Foo extends Command implements SelfHandling,SerializesModels;

protected $bar;

public function __construct($id)
{
$this->bar = Bar::onWriteConnection()->find($id);
}

public function handle()
{
// $this->bar
}
}

?>

也就是说,通过 Laravel 的 onWriteConnection 方法把查询固定在主服务器上,不过实际上无效。问题症结在于反序列化的时候,系统会在从服务器上一次 findOrFail 调用。 

protected function getRestoredPropertyValue($value)
{
return $value instanceof ModelIdentifier
? (new $value->class)->findOrFail($value->id) : $value;
}

?>

因为我们无法 HACK 到框架内部,所以 onWriteConnection 就没有意义了。其实换个角度看问题,只要在系列化的时候,保证别用数据库对象做属性即可:

class Foo extends Command implements SelfHandling,SerializesModels;

protected $id;

public function __construct($id)
{
$this->id = $id;
}

public function handle()
{
$bar = Bar::onWriteConnection()->find($this->id);
}
}

?>

四、总结

以上就是我在使用Laravel遇到的几个有代表性的报错以及解决方案,如果有问题欢迎大家一起交流。希望这篇文章对大家的学习或者工作能带来一定的帮助。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


kindeditor4.x代码高亮功能默认使用的是prettify插件,prettify是Google提供的一款源代码语法高亮着色器,它提供一种简单的形式来着色HTML页面上的程序代码,实现方式如下: 首先在编辑器里面插入javascript代码: 确定后会在编辑器插入这样的代码: <pre
这一篇我将介绍如何让kindeditor4.x整合SyntaxHighlighter代码高亮,因为SyntaxHighlighter的应用非常广泛,所以将kindeditor默认的prettify替换为SyntaxHighlighter代码高亮插件 上一篇“让kindeditor显示高亮代码”中已经
js如何实现弹出form提交表单?(图文+视频)
js怎么获取复选框选中的值
js如何实现倒计时跳转页面
如何用js控制图片放大缩小
JS怎么获取当前时间戳
JS如何判断对象是否为数组
JS怎么获取图片当前宽高
JS对象如何转为json格式字符串
JS怎么获取图片原始宽高
怎么在click事件中调用多个js函数
js如何往数组中添加新元素
js如何拆分字符串
JS怎么对数组内元素进行求和
JS如何判断屏幕大小
js怎么解析json数据
js如何实时获取浏览器窗口大小
原生JS实现别踩白块小游戏(五)
原生JS实现别踩白块小游戏(一)