在Drupal 8中探索Cache API

Drupal 8 comes with many improvements over its predecessor we have grown to both love and hate. Next to prominent systems such as Views in core, configuration management or a useful translation service, there are also less known changes but that are equally important to know and use. One such improvement has been the cache API that solves many performance problems we have in Drupal 7.

Drupal 8在它的前身(我们已经成长为爱与恨)的基础上进行了许多改进。 除了诸如核心视图,配置管理或有用的翻译服务之类的著名系统之外,还鲜为人知的更改是,但了解和使用它们同样重要。 缓存API就是其中一种改进,它解决了Drupal 7中的许多性能问题。

drupal8wide

In this article, I want to shine a bit of light over the new cache API. To this end, we are going to look at how we can use it in our custom modules as we are encouraged to do so much more in Drupal 8.

在本文中,我希望对新的缓存API有所了解。 为此,我们将研究如何在自定义模块中使用它,因为我们鼓励在Drupal 8中做更多的事情。

Additionally, I have prepared a little demonstration in the shape of a module you can install for testing the impact of the cache API. It’s a simple page that in its rendering logic makes an external API call (to a dummy JSON endpoint) and caches its results. The page then displays the actual time it takes for this to happen, contrasting the external call time vs. the cached version time.

此外,我还准备了一些模块形状的演示,您可以安装该模块来测试缓存API的影响。 这是一个简单的页面,在其呈现逻辑中进行了外部API调用(到虚拟JSON端点 )并缓存了结果。 然后,页面显示发生此操作所需的实际时间,将外部呼叫时间与缓存的版本时间进行对比。

新的缓存API (The new cache API)

Cache concept.

垃圾箱 (Bins)

The new cache API (with the default DatabaseBackend storage) is stored in multiple bins which map to tables that start with the prefix cache_. When interacting with the cache, we always start by requesting a cache bin:

新的缓存API(使用默认DatabaseBackend存储)存储在多个不同映射到与前缀开头表cache_ 。 与缓存进行交互时,我们总是从请求缓存箱开始:

$cache = \Drupal::cache();

Where $cache will be an instance of the DatabaseBackend object that represents the default bin (cache_default). To request a particular bin we pass in the name in the constructor:

其中$cache将是DatabaseBackend对象的实例,该实例表示默认bin( cache_default )。 要请求特定的bin,我们在构造函数中输入名称:

$render_cache = \Drupal::cache('render');

Where $render_cache will represent the render cache bin (which is new in Drupal 8 and is supposed to improve render performance across the board).

$render_cache将代表渲染缓存容器(这是Drupal 8中的新增功能,应提高整体渲染性能)。

As you can see, we are requesting the cache service statically using the \Drupal class. If we are working inside classes, it is best practice to inject the service from the container. You can do so by specifying as an argument to your service the relevant cache bin service (such as cache.default). Here you can get a list of all core services including the ones related to cache.

如您所见,我们正在使用\Drupal类静态地请求缓存服务。 如果我们在类内部工作,则最佳实践是从container注入服务 。 您可以通过将相关的缓存bin服务(例如cache.default )指定为服务的参数来实现。 在这里,您可以获得所有核心服务的列表,包括与缓存相关的服务。

But for the sake of brevity, we will use it statically here.

但是为了简洁起见,我们将在此处静态使用它。

检索缓存的项目 (Retrieving cached items)

Once we know which bin we want to work with (for custom modules this will usually be the default bin), we can retrieve and store cache items.

一旦知道了要使用的容器(对于自定义模块,通常将是默认容器),就可以检索和存储缓存项。

$cache = \Drupal::cache()->get('my_value');

It’s that simple. $cache will be a stdClass object containing some metadata about the cache item plus the actual data available under the $cache->data property. The my_value parameter is the cache ID.

就这么简单。 $cache将是一个stdClass对象,其中包含有关缓存项的一些元数据以及$cache->data属性下可用的实际数据。 my_value参数是缓存ID。

An important thing to keep in mind is that using the get() method without a second parameter will not return the cache item if it has been invalidated (either programatically or through expiration). Passing the boolean true as a second parameter will force it to return the data.

要记住的重要一点是,如果没有使缓存项无效(以编程方式或通过到期),则不带第二个参数的get()方法将不会返回该缓存项。 将布尔值true传递为第二个参数将强制其返回数据。

存储缓存项 (Storing cache items)

Although storing new items in the cache is just as easy as retrieving them, we have more options when doing so. To store an item we use the set() method (instead of get() like before), a method that takes 2 mandatory parameters and 2 optional ones:

尽管将新项目存储在缓存中就像检索它们一样容易,但是这样做时我们有更多选择。 要存储项目,我们使用set()方法(而不是像以前的get() ),该方法需要2个必需参数和2个可选参数:

  • the cache ID (the string by which we can later reference the item)

    缓存ID(以后可用来引用该项目的字符串)
  • the data (a PHP value such as a string, array or object that gets serialised automatically and stored in the table – should not be over 1MB in size)

    数据(自动序列化并存储在表中PHP值,例如字符串,数组或对象,大小不得超过1MB)
  • the expiration time (a timestamp in the future when this cache item will automatically become invalid or -1 which basically means this item never expires. It is best practice to use the Drupal\Core\Cache\CacheBackendInterface::CACHE_PERMANENT constant to represent this value)

    过期时间(此缓存项将在未来自动失效的时间戳记,或-1 ,这基本上意味着该缓存项永不过期。最佳做法是使用Drupal\Core\Cache\CacheBackendInterface::CACHE_PERMANENT常量表示此值)

  • tags (an array of cache tags this item can be later identified by)

    标签(此项目以后可以通过其标识的一系列缓存标签)

As an example:

举个例子:

Drupal::cache()->set('my_value', $my_object, CacheBackendInterface::CACHE_PERMANENT, array('my_first_tag', 'my_second_tag'));

This will set a permanent cache item tagged with 2 tags and store a serialised version of $my_object as the data.

这将设置一个带有2个标签的永久缓存项,并将$my_object的序列化版本存储为数据。

缓存失效和删除 (Cache invalidation and removal)

Cache invalidation means that the respective items are no longer fresh and are unreliable in terms of what data they hold. They will be removed at the next garbage collection which can also be called using the garbageCollection() method on the CacheBackend object.

高速缓存失效意味着各个项目不再新鲜,并且就它们所保存的数据而言也不可靠。 它们将在下一个垃圾回收中删除,也可以使用CacheBackend对象上的trashCollection garbageCollection()方法调用它们。

As mentioned above, when storing a cache item we can specify an expiration time. When this time lapses, the cache item becomes invalid but still exists in the bin and can be retrieved. However, we can also invalidate items manually using the invalidate(), invalidateMultiple() or invalidateAll() methods on the CacheBackend object.

如上所述,在存储缓存项时,我们可以指定过期时间。 经过此时间后,缓存项将变为无效,但仍存在于bin中并可以检索。 但是,我们也可以使用CacheBackend对象上的invalidate()invalidateMultiple()invalidateAll()方法手动使项目invalidate()

Removing items altogether can be done using the delete(), deleteMultiple() or deleteAll() methods. These actions also happen only on the bin the CacheBackend is wrapping and completely remove the respective table records.

可以使用delete()deleteMultiple()deleteAll()方法完全删除项目。 这些操作也仅在CacheBackend包装的bin上发生,并完全删除相应的表记录。

缓存标签 (Cache tags)

Another cool new feature of the Cache API in Drupal 8 are the cache tags (the fourth parameter in the setter method). The role of the tags is to identify cache items across multiple bins for proper invalidation. The purpose is the ability to accurately target multiple cache items that contain data about the same object, page, etc. For example, nodes can appear both on a page and in a view (stored in different cache items in different bins but both tagged with the same node:nid formatted tag). This allows invalidating both cache items when changes happen to that node without having to know the cache ids.

Drupal 8中的Cache API的另一个很酷的新功能是缓存标记(setter方法中的第四个参数)。 标签的作用是识别多个容器中的缓存项,以使其正确无效。 目的是能够准确地针对包含有关同一对象,页面等数据的多个缓存项进行定位。例如,节点可以同时出现在页面和视图中(存储在不同bin中的不同缓存项中,但都标记有)相同的node:nid格式标签)。 这样,当该节点发生更改时,两个缓存项都将无效,而不必知道缓存ID。

To manually invalidate caches using the tags, we can use the invalidateTags() method statically on the \Drupal\Core\Cache\Cache class:

要使用标签手动使缓存无效,我们可以在\Drupal\Core\Cache\Cache类上静态使用invalidateTags()方法:

\Drupal\Core\Cache\Cache::invalidateTags(array('node:5', 'my_tag'));

This will call the cache invalidator service and invalidate all the cache items tagged with node:5 and my_tag.

这将调用缓存失效器服务,并使所有标记有node:5my_tag的缓存项无效。

Additionally, for Drupal entities we don’t have to create our own tags but can retrieve them from the entity system:

此外,对于Drupal实体,我们不必创建自己的标签,但可以从实体系统中检索它们:

  • \Drupal\Core\Entity\EntityInterface::getCacheTags()

    \Drupal\Core\Entity\EntityInterface::getCacheTags()

  • \Drupal\Core\Entity\EntityTypeInterface::getListCacheTags()

    \Drupal\Core\Entity\EntityTypeInterface::getListCacheTags()

This keeps the tags for Drupal entities consistent across the board.

这样可以使Drupal实体的标签在所有方面保持一致。

演示缓存API (Demonstrating the cache API)

As I mentioned before, I created a small module that allows us to see the benefits of caching data. You can find the module in this git repository but here is the crux of it:

如前所述,我创建了一个小模块,使我们可以看到缓存数据的好处。 您可以在此git存储库中找到该模块,但这是关键所在:

Please note that in this example I access the cache backend service statically to save some space. For a dependency injection approach (the correct approach), take a look at the repository code.

请注意,在此示例中,我静态访问缓存后端服务以节省一些空间。 对于依赖项注入方法(正确的方法),请查看存储库代码。

A route file that adds a new route to the /cache-demo path:

一个路由文件,它将新的路由添加到/cache-demo路径:

cache_demo_page:
  path: 'cache-demo'
  defaults:
    _controller: '\Drupal\cache_demo\Controller\CacheDemoController::index'
    _title: 'Cache demo'
  requirements:
    _permission: 'access content'

And the controller class that returns the page inside src/Controller/CacheDemoController.php:

以及返回src/Controller/CacheDemoController.php内部页面的控制器类:

<?php

/**
 * @file
 * Contains \Drupal\cache_demo\Controller\CacheDemoController.
 */

namespace Drupal\cache_demo\Controller;

use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Url;
use \GuzzleHttp\Client;

/**
 * Cache demo main page.
 */
class CacheDemoController extends ControllerBase {

  public function index(Request $request) {
    $output = array();

    $clear = $request->query->get('clear');
    if ($clear) {
      $this->clearPosts();
    }

    if (!$clear) {
      $start_time = microtime(TRUE);
      $data = $this->loadPosts();
      $end_time = microtime(TRUE);

      $duration = $end_time - $start_time;
      $reload = $data['means'] == 'API' ? 'Reload the page to retrieve the posts from cache and see the difference.' : '';
      $output['duration'] = array(
        '#type' => 'markup',
        '#prefix' => '<div>',
        '#suffix' => '</div>',
        '#markup' => t('The duration for loading the posts has been @duration ms using the @means. @reload',
          array(
            '@duration' => number_format($duration * 1000, 2),
            '@means' => $data['means'],
            '@reload' => $reload
          )),
      );
    }

    if ($cache = \Drupal::cache()->get('cache_demo_posts') && $data['means'] == 'cache') {
      $url = new Url('cache_demo_page', array(), array('query' => array('clear' => true)));
      $output['clear'] = array(
        '#type' => 'markup',
        '#markup' => $this->l('Clear the cache and try again', $url),
      );
    }

    if (!$cache = \Drupal::cache()->get('cache_demo_posts')) {
      $url = new Url('cache_demo_page');
      $output['populate'] = array(
        '#type' => 'markup',
        '#markup' => $this->l('Try loading again to query the API and re-populate the cache', $url),
      );
    }

    return $output;
  }

  /**
   * Loads a bunch of dummy posts from cache or API
   * @return array
   */
  private function loadPosts() {
    if ($cache = \Drupal::cache()->get('cache_demo_posts')) {
      return array(
        'data' => $cache->data,
        'means' => 'cache',
      );
    }
    else {
      $guzzle = new Client();
      $response = $guzzle->get('http://jsonplaceholder.typicode.com/posts');
      $posts = $response->json();
      \Drupal::cache()->set('cache_demo_posts', $posts, CacheBackendInterface::CACHE_PERMANENT);
      return array(
        'data' => $posts,
        'means' => 'API',
      );
    }
  }

  /**
   * Clears the posts from the cache.
   */
  function clearPosts() {
    if ($cache = \Drupal::cache()->get('cache_demo_posts')) {
      \Drupal::cache()->delete('cache_demo_posts');
      drupal_set_message('Posts have been removed from cache.', 'status');
    }
    else {
      drupal_set_message('No posts in cache.', 'error');
    }
  }

}

Inside the index() method we do a quick check to see whether the clear query parameter is present in the url and call the clearPosts() method responsible for deleting the cache item. If there isn’t one, we calculate how long it takes for the loadPosts() method to return its value (which can be either the posts from the cache or from the API). We use Guzzle to make the API call and when we do, we also store the results directly. Then we just output the duration of the call in milliseconds and print 2 different links depending on whether there is cache stored or not (to allow us to clear the cache item and run the API call again).

index()方法内部,我们进行了快速检查以查看url中是否存在clear查询参数,并调用负责删除缓存项的clearPosts()方法。 如果没有,我们将计算loadPosts()方法返回其值(可以是来自缓存或来自API的帖子loadPosts()花费的时间。 我们使用Guzzle进行API调用,当我们这样做时,我们也直接存储结果。 然后,我们仅以毫秒为单位输出调用的持续时间,并根据是否存储了缓存来打印2个不同的链接(以允许我们清除缓存项并再次运行API调用)。

When you navigate to cache-demo for the first time, the API call gets made and the 100 posts get stored in the cache. You can then reload the page to see how long it takes for those posts to be retrieved from the cache. Upon doing that, you’ll have a link to clear the cache (by a page refresh with the clear query string) followed by another link which refreshes the page without the clear query string and that in turn makes the API call again. And on like that to test the contrast in duration.

首次导航到cache-demo时,将进行API调用,并将100个帖子存储在缓存中。 然后,您可以重新加载页面,以查看从缓存中检索这些帖子需要花费多长时间。 完成此操作后,您将具有一个链接以清除缓存(通过使用clear查询字符串刷新页面),然后是另一个链接,该链接将在不使用clear查询字符串的情况下刷新页面,并依次进行API调用。 然后测试持续时间的对比。

结论 (Conclusion)

In this article we’ve looked at how easy it is to use the Cache API in Drupal 8. There are some very simple class methods that we can use to manage cache items and it has become too straightforward for us not to start using it in our custom modules. I encourage you to check it out, play around with the API and see for yourself how easy it is to use.

在本文中,我们研究了在Drupal 8中使用Cache API有多么容易。我们可以使用一些非常简单的类方法来管理缓存项,对于我们来说,直接使用它变得太简单了,以至于无法开始使用它。我们的自定义模块。 我鼓励您检查一下,试用一下API,亲自了解它的易用性。

翻译自: https://www.sitepoint.com/exploring-cache-api-drupal-8/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值