Drupal 8:自定义缓存箱

Drupal 的缓存系统健壮、功能齐全且可扩展。它可用于缓存渲染输出的小块或更复杂计算的结果。缓存可以存储在页面请求中、数据库中或不同的缓存系统中。

我最近需要存储从 API 中提取的大量数据。从 API 获取数据需要几秒钟的时间,因此将其缓存在站点中以便快速检索非常重要。即使只是将缓存存储在数据库中也比 API 快很多倍,因此在站点内缓存结果是有意义的。让用户每次做任何事情时都坐在那里等待页面加载并不是很好。

为此,我创建了一个小型 Drupal 服务来为我管理这个缓存。这是模块内服务 YAML 文件的记录。

services:

  my_module.cache:

    class: Drupal\my_module\Cache\MyCache

    arguments: ['@cache.default']

该类本身基本上是特定缓存箱的 set 和 get 方法,其中使用一些静态缓存来防止重复发出相同的数据库请求。缓存是基于时间的,因此它们会在 24 小时内到期,并且还带有标签,以便在以后明确清除它们。

<?php

 

namespace Drupal\my_module\Cache;

 

use Drupal\Core\Cache\Cache;

use Drupal\Core\Cache\CacheBackendInterface;

 

/**

 * Class MyCache.

 *

 * This class combines a static cache and an 'active' cache, loaded from a the

 * default Drupal cache location.

 *

 * @package Drupal\my_module\Cache

 */

class MyCache {

 

  /**

   * The default cache bin.

   *

   * @var \Drupal\Core\Cache\CacheBackendInterface

   */

  protected $cache;

 

  /**

   * MyCache constructor.

   *

   * @param \Drupal\Core\Cache\CacheBackendInterface $cache

   *   The default cache bin.

   */

  public function __construct(CacheBackendInterface $cache) {

    $this->cache = $cache;

  }

 

  /**

   * Sets a cache with a specific id, data and type.

   *

   * @param string $id

   *   The cache id.

   * @param mixed $data

   *   The data to be cached.

   * @param array $type

   *   The type of data being cached. This is used to set up the cache tags.

   */

  public function setCache($id, $data, $type): void {

    $cid = 'my_custom_cache:' . $type . ':' . $id;

 

    $tags = [

      'my_custom_cache:' . $type . ':' . $id,

      'my_custom_cache:' . $type,

      'my_custom_cache',

    ];

 

    $tags = Cache::mergeTags($tags, [$cid]);

 

    // 设置数据库缓存。

    $this->cache->set($cid, $data, (new \DateTime('+1 day'))->getTimestamp(), $tags);

 

    // 设置静态缓存。

    $staticCache = &drupal_static(__FUNCTION__ . $cid, NULL);

    $staticCache = $data;

  }

 

  /**

   * Get a specific cache.

   *

   * @param string $id

   *   The cache ID.

   * @param string $type

   *   The cache type.

   *

   * @return mixed

   *   The cache or false if the cache was not found.

   */

  public function getCache($id, $type) {

    $cid = 'my_custom_cache:' . $type . ':' . $id;

 

    $staticCache = &drupal_static(__FUNCTION__ . $cid, NULL);

 

    if ($staticCache) {

      // 如果静态缓存存在,则返回它。

      return $staticCache;

    }

 

    // 从数据库中取出缓存并返回数据组件。

    $result = $this->cache->get($cid);

    return $result->data ?? NULL;

  }

 

}

项目中完成的类有一些方法可以在需要时使缓存失效,这就是我添加缓存标签的原因。这些用于识别我需要清除的缓存桶。

默认情况下,这会将您缓存的任何内容添加到cache_default表中,该表包含未连接到此数据的其他缓存的集合。为了改变这一点并使用不同的表,我们需要设置一个自定义缓存箱。这是使用服务 YAML 文件中的条目完成的。然后将此自定义缓存箱传递给之前设置的缓存服务。

services:

  # Custom cache bin.

  cache.my_custom_cache:

    class: Drupal\Core\Cache\CacheBackendInterface

    tags:

      - { name:cache.bin}

    factory: cache_factory:get

    arguments: [my_custom_cache]

  # Cache service.

  my_module.cache:

    class: Drupal\my_module\Cache\MyCache

    arguments: ['@cache.my_custom_cache']

现在,由于我们已将自定义缓存 bin 注入 MyCache 类,因此我们存储在缓存中的任何数据都将存储在名为cache_my_custom_cache的表中。

我需要做的是让这个缓存与正常的 Drupal 缓存机制完全分开,这样如果缓存被清除,我就不必再次重建缓存。这将允许诸如 CSS/JavaScript 缓存之类的内容被刷新,而不会同时刷新我们的 API 缓存。为了做到这一点,我需要允许一个缓存清除事件发生,而不是从系统中清除这个自定义缓存。这是可能的,但需要覆盖一些东西才能使其正常工作。

用于加载当前缓存后端的 cache_factory 类将始终加载到默认的 Drupal 缓存系统中,因此我们需要在服务中将其覆盖为自定义服务。

cache.my_custom_cache:

  class: Drupal\Core\Cache\CacheBackendInterface

  tags:

    - { name:cache.bin}

  factory: my_custom_cache.cache_factory:get

  arguments: [my_custom_cache]

有了这个,我们需要建立我们刚刚定义的工厂。这首先通过设置另一个名为my_module.cache_factory 的服务来完成

# Custom cache factory, used to stipulate the loading of cache.backend.database.my_module.

my_module.cache_factory:

  class: Drupal\my_module\Cache\CustomCacheFactory

  arguments: ['@settings', '%cache_default_bin_backends%']

  calls:

    - [setContainer, ['@service_container']]

随着服务的建立,我们需要创建一个具体的缓存工厂类。这个类非常简单,我们所做的就是返回一个名为cache.backend.database.my_module的服务,该服务(尚不存在)

<?php

 

namespace Drupal\my_module\Cache;

 

use Drupal\Core\Cache\CacheFactory;

 

class CustomCacheFactory extends CacheFactory {

 

  public function get($bin) {

    $service_name = 'cache.backend.database.my_module';

    return $this->container->get($service_name)->get($bin);

  }

}

要创建此服务,我们首先需要在模块服务 YAML 文件中定义该服务。

# Custom cache backend database factory, used to load in the CacheDatabaseBackend object.

cache.backend.database.my_module:

  class: Drupal\my_module\Cache\DatabaseBackendFactory

  arguments: ['@database', '@cache_tags.invalidator.checksum', '@settings']

接下来,我们定义刚刚在上面的服务文件中定义的 DatabaseBackendFactory 类。该工厂用于返回一个名为 CacheDatabaseBackend 的对象,该对象是自定义定义的类。核心 Drupal 数据库缓存还保留一个名为 DEFAULT_MAX_ROWS 的设置,用于防止缓存系统变得太大。我打算在自定义类中增加这个设置,所以我还包含了 getMaxRowsForBin 方法的覆盖,允许 Drupal 的上游垃圾收集使用这个自定义值。

<?php

 

namespace Drupal\my_module\Cache;

 

use Drupal\Core\Cache\DatabaseBackendFactory;

use Drupal\my_module\Cache\CacheDatabaseBackend;

 

class DatabaseBackendFactory extends DatabaseBackendFactory {

 

  /**

   * {@inheritDoc}

   */

  public function get($bin) {

    $max_rows = $this->getMaxRowsForBin($bin);

    return new CacheDatabaseBackend($this->connection, $this->checksumProvider, $bin, $max_rows);

  }

 

  /**

   * {@inheritDoc}

   */

  protected function getMaxRowsForBin($bin) {

    return CacheDatabaseBackend::DEFAULT_MAX_ROWS;

  }

}

现在我们终于可以创建自定义缓存后端并覆盖基本缓存清除机制。这个类扩展了 DatabaseBackend 类,但允许我们专门覆盖缓存清除机制。本质上,我们将deleteAll()方法设置deleteCustomCaches()为什么都不做,同时还添加了我们自己的方法,该方法将使用deleteAll()父类中相同的覆盖方法删除缓存中的任何内容。

<?php

 

namespace Drupal\my_module\Cache;

 

use Drupal\Core\Cache\DatabaseBackend;

 

class CacheDatabaseBackend extends DatabaseBackend {

 

  /**

   * {@inheritdoc}

   *

   * We deliberately set this higher as we have a lot of data to store.

   */

  const DEFAULT_MAX_ROWS = 50000;

 

  /**

   * {@inheritdoc}

   */

  public function deleteAll() {

    //在正常的 Drupal 缓存清除期间调用此方法。我们绝对

    // 不想刷新缓存所以我们什么都不做。

  }

 

  /**

   * This method will call the original deleteAll() method.

   *

   * Calling this method will permanently delete the caches for this cache bin.

   *

   * @throws \Exception

   */

  public function deleteCustomCaches() {

    parent::deleteAll();

  }

}

这里有很多不同的部分在起作用,但我们现在有一个自定义的缓存箱,它已与主要的缓存清除机制隔离开来。这确实意味着,如果我们确实需要在需要实现该功能时清除自定义缓存。对于我正在处理的项目,我创建了一个表单和一个自定义 Drush 命令,以便可以轻松清除和重建缓存。这超出了本文的范围,但确实没有太多代码可以做到这一点。您只需要创建一个表单,其中提交处理程序运行deleteCustomCaches()CacheDatabaseBackend 服务中的方法。

还有一个问题是我在构建所有这些时都考虑了数据库缓存系统。通过向 CustomCacheFactory get 方法添加一些检测并返回不同的缓存箱,完全可以创建缓存箱的不同实现(例如 memcache)。然而,这也意味着编写大量代码来覆盖不同的缓存机制。通过上面的实现,我确信这个缓存被存储在数据库中,这仍然是获取我需要的数据的一种更快的方式,并最终降低了问题的复杂性。覆盖这些类来创建自定义接口很好,但是当我在 6 个月后再次访问相同的代码时,我需要记住它是如何工作的。文档只能到此为止。

以上是 Drupal 8:自定义缓存箱 的全部内容, 来源链接: utcz.com/z/338736.html

回到顶部