Eloquent 模型观察者表现

Eloquent model observer performance

如果我有多个模型并且我大量处理事件,那么在我的 AppServiceProvider 中我为我的模型创建一个观察者,例如:

class AppServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        User::observe(UserObserver::class);
        User2::observe(UserObserver2::class);
        User3::observe(UserObserver3::class);
        User4::observe(UserObserver4::class);
        User5::observe(UserObserver5::class);
        User6::observe(UserObserver6::class);
        User7::observe(UserObserver7::class);
        User8::observe(UserObserver8::class);
        User9::observe(UserObserver9::class);
    }

    /**
     * Register the service provider.
     *
     * @return void
     */
    public function register()
    {
        //
    }
}

它会造成一些性能损失吗?如果我对每个应用程序访问都正确理解 observe method,它将遍历我所有的模型观察者方法并将它们与 eloquent 事件匹配,所以如果我有 30 个模型观察者,它会导致性能不佳吗?

是否有一些聪明的方法可以仅在使用模型时声明观察 class?因此,即使不需要,每个模型也不会在每个应用程序访问中声明观察者,只有在使用时才会知道它的观察者?

如果您将事件与 Laravel 一起使用,将在每次请求时注册(但不会触发)事件。

更准确地说,事件将被注册意味着 User::observe(UserObserver::class);UserObserverloop through all methods 并将它们保存在 [=12= 的调度程序数组中] class。仅当通过 User 模型执行 update / create 方法时,才会触发已注册的 UserObserver 方法,即从调度程序中调用。

因此,如果您有 30 个模型,每个模型在 Observer class 中有 5 个方法,那么对调度程序的每个请求都会有 150 个数组绑定。但是,不会在每次请求期间调用观察者的方法,仅在事件触发时调用。因此,与加载 120kb 徽标或执行查询调用所需的时间相比,我认为 150 个数组分配不会对请求时间产生影响。

最好的办法当然是自己测量。在绑定观察者和不绑定观察者的情况下测量请求的时间。您可以使用 debugbar 作为它。


旁注:使用 deferred providers 不会有任何区别,因为您必须将观察者调用置于提供者的 boot 方法中。来自文档:

If your provider is only registering bindings in the service container, you may choose to defer its registration until one of the registered bindings is actually needed.

特别是模型观察者,其性能影响大于典型的事件注册。额外的开销来自 Laravel 如何处理观察者事件映射。由于正在注册的事件基于现有方法,因此 Laravel 对每个观察者进行 最少 13 次 method_exists 调用。

对于几个观察者来说,这没什么大不了的。但是,如果您开始将观察者用于审计跟踪流程并涉及多个模型,则此成本可能会开始增加。

举个例子,我曾经开发过一个有四个广义观察者的应用程序,他们观察了大约 300 个模型(其中一些模型由观察者共享)。这变成了 600 多次 Model::observe 调用,因此超过 7,800 次调用 method_exists.

我实际上已经创建了一个包 (reedware/laravel-events) 来解决这个问题。您改为通过配置文件注册观察者(和其他事件),并且可以缓存事件。缓存机制实际上也适用于观察者(它不适用于开箱即用的 Laravel),因此观察者的成本变得非常小。

或者,您可以放弃观察者设计模式,并在可启动特征中使用 Model::creating(...) 挂钩,或者只是抛出自定义事件并为它们注册侦听器。