【问题标题】:How can plugin systems be designed so they don't waste so many resources?插件系统如何设计才能不浪费这么多资源?
【发布时间】:2012-02-15 19:22:30
【问题描述】:

我正在尝试构建一个基本的plugin system,就像您在 WordPress 等 CMS 中经常找到的那种。您有一个插件文件夹,这些插件使用 Observer 或 Event 设计模式通过事件通知与主系统的操作相关联。

问题是系统不可能知道插件想要处理的事件 - 所以系统必须为每个页面请求加载每个插件找出在某个时候是否确实需要该插件。不用说,这会浪费很多资源——在 WordPress 的情况下,每个请求会增加几 MB 的内存!

是否有其他方法可以做到这一点?

例如,有没有办法一次性加载所有这些,然后缓存结果,以便您的系统知道如何延迟加载插件?换句话说,系统会加载一个配置文件,该文件指定插件希望绑定的所有事件,然后将其保存在 APC 或其他东西中以供将来请求?

如果这也表现不佳,那么也许有一个特殊的文件结构可用于对某些插件何时不需要满足请求进行有根据的猜测。

【问题讨论】:

    标签: php performance design-patterns plugins content-management-system


    【解决方案1】:

    我确实有一个插件管理工具,但我只将它与主要的程序插件一起使用,并且通常一次加载所有包含。但是对于基于事件和延迟加载的 API,我可以想象使用浅层包装器进行插件管理,并使用自动加载来进行实际扩展。

    <?php
      /**
       * api: whatever
       * version: 0.1
       * title: plugin example
       * description: ...
       * config: <var name="cfg[pretty]" type="boolean" ...>
       * depends: otherplugin
       */
    
     $plugins["title_event"] = "TitleEventClass";
     $plugins["secondary"] = array("Class2", "callback");
    ?>
    

    在这个例子中,我假设插件 API 是一个简单的列表。这个示例feature-plugin-123.php 脚本只会在加载时添加到数组中。因此,即使您有十几个功能插件,也只会产生一个额外的include_once。

    但是主应用程序/或插件 API 可以只实例化上述类(new $eventcb; 用于原始类名或 call_user_func_array 用于回调)。反过来,它将实际任务卸载到自动加载器。因此,您有一个双重系统,其中一部分管理列表,另一部分定位实际代码。

    因此,我仍在构思一个简单的config.php,它只列出插件和设置,如下所示:

    <?php
    include_once("user/feature-plugin-123.php");
    include_once("user/otherplugin2.php");
    include_once("user/wrapper-for-htmlpurifier.php");
    $cfg["pretty"] = 1;
    

    再次记住,这些只是包装器/数据脚本,具有可管理性的插件描述。也可以使用实际的register_even() API 并在每个 API 中定义一个额外的包装函数。但是列出类名似乎是最简单的选择。

    上述管理工具有点生锈和丑陋:http://milki.include-once.org/genericplugins/
    但是,如果您只需要一个列表(sql 表)而没有设置管理,则不需要它。该开销仅用于漂亮打印插件元数据并保持人类可读的config.php。

    总结:

    spl_autoload() 在 include_path 上,以及一个简单的 event->classname 注册表,每个都有一个包装脚本,一次包含所有内容。

    【讨论】:

      【解决方案2】:

      Wordpress 和其他 CMS 系统就是非常糟糕的例子。

      我们必须了解的是,模块化几乎总是意味着更重。

      我用来解决这种情况的最佳方案是基于类的插件,使用自动加载程序具有严格的命名约定。

      因此,在使用插件之前,您需要创建一个实例,或使用静态函数。

      你甚至可以像这样调用插件:

      <?php $thePlugin::method(); ?>
      

      例如:

      <?php
      
          spl_autoload_register('systemAutoload');
      
          function systemAutoload($class)
          {
              $parts = explode('_',$class);
      
      
              switch($parts[1])
              {
                  case "Plugin":
                      include("/plugins/{$parts[2]}/{$parts[2]}.php");
                  break;
              }
      
              // ...    
      
          }
      
      ?>
      

      关于事件:

      您必须静态注册此事件以避免以动态方式引入它。

      数据库将是执行此操作的正确位置。您可以有一个事件表,以及插件类上的 install() 和 uninstall() 方法来添加特定事件或将方法绑定到其他事件。这是一个数据库查询,如果您想从中获得更多信息,请将其添加到 memcached 或平面 ini 文件中。

      对我来说效果很好。通过这种方式,我能够得到一个繁重的系统,每个请求消耗 8mb 到 1mb,具有完全相同的功能列表,没有高级缓存。现在我们可以添加更多功能并保持系统“干净”

      希望有帮助

      【讨论】:

      • 是的,wordpress 和其他软件如此臃肿的原因是它们不支持自动加载,所以它们只是在每次请求时加载所有内容。无论如何,您是对的,保持严格的基于类的命名方案是将回调映射到文件系统上匹配的插件类的方法,以便仅在需要时加载类。但是,您应该使用符合 PSR-0 的自动加载器,以便在需要时仍然可以使用其他系统的类(如 Zend)。
      【解决方案3】:

      例如,我会将插件类名称及其订阅事件保存在配置文件中,然后将解析的配置文件保存在 APC 中。然后当触发事件时,系统可以根据需要延迟加载适当的插件类。

      【讨论】:

      • 正如我所说 - 但是,关于实际实现这样的系统还有什么想法吗?
      • 是的,我非常仔细地解释了您的答案!我将有一个总线类,您可以在其中向其发布事件,并根据需要处理加载插件。您可以通过 cron 作业或长时间运行的进程处理插件注册。
      猜你喜欢
      • 1970-01-01
      • 2016-06-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-02-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多