星期日熊资源缓存

这篇文章是BEAR.Sunday Advent Calendar 2017 – Qiita第20天的文章,作者是@kalibora,继续尝试使用BEAR.Sunday的资源缓存。

默认的缓存引擎

在那之前,我卡住的地方是内部使用的缓存机制的默认值是Doctrine\Common\Cache\ArrayCache。

缓存存储的默认选项确实是PHP的ArrayCache,但这是因为它在开发环境而非prod或stage环境中运行。

在ProdModule中,默認綁定是ProdCacheProvider,而在ProdCacheProvider中,則綁定了APC+文件緩存的鏈式緩存。

在开发期间,由于频繁更改,我们会禁用缓存。在使用多个Web服务器时,资源缓存需要使用Redis或memcache等存储设备,但在单个情况下,APC+文件缓存也可以工作。

现金策略

在计算机科学中,有两个困难的问题,分别是缓存无效化和命名。

— 菲尔·卡尔顿

在缓存失效控制方面最常用的是根据时间来进行控制(TTL)。

访问量大的内容将受益于时间缓存的有效功能。如果将1秒钟的缓存设置在每秒有100次访问的网站上,则命中率为99%,内容延迟也最多仅需1秒。但是如果将时间延长到10倍的10秒,则延迟时间将增加10倍,但是命中率仅提高了0.9%,达到了99.9%。

尽管长尾内容的访问量不多,但缓存却无法很好地应用。即使内容没有更改,如果没有访问,内容也会被重新生成。结果有时候缺乏缓存反而能提高性能。

使用REST方法进行缓存更新可以实现“在调用更新方法时自动清除缓存,这是一种非常合理的行为”。

onGet()方法在GET请求中不执行是理想的。

如果在更新方法中生成缓存,那么在GET请求中,onGet()方法的内容将不会被执行。因为缓存会被返回,所以onGet()内部的方法不会被执行。为了创建用于读取的内容,在POST和PUT请求中,将执行onGet()方法的内容。

现金的理想

让我们来构想一个理想的资源缓存。

    1. 只有在发生变更时才会检测并重新生成

 

    1. 具有低挥发性,在数据结构不变的情况下(即使进行部署等操作),不会被破坏

 

    1. 可以获取缓存生成的日期(Last-Modified头部)

 

    1. 当HTTP客户端已经拥有该内容时,可以通知其无需返回缓存,因为内容没有变化(304 Not Modified)

 

    缓存不是核心关注点,因此不会出现在应用逻辑代码中(AOP)

在以下情况下,您可以通过指定资源缓存版本来指定缓存失效。有关详细信息,请查看手册中的生产部分。

$this->install(new CacheVersionModule('1')); 

根据HTTP/1.1 RFC 2616的规定,HTTP/1.1服务器应尽可能地添加Last-Modified头(就像文件有时间戳一样),但许多动态页面无法实现这一点。我们将解决这个问题。

一个源服务器应该尽可能接近生成其响应的Date值的时间获取实体的Last-Modified值。这样可以使接收方准确评估实体的修改时间,特别是如果实体在生成响应的时刻附近发生了变化。
HTTP/1.1服务器应该在可行的情况下发送Last-Modified。

不仅仅是缓存,更像是永久的只读内容数据,即CQRS的查询存储库。
1-5目前在BEAR.Sunday中得到支持。

现金的理想 (2)

这个功能还没有实现。它只是一个创意阶段的想法。

当内容没有改变时,可以在框架级别返回304 Not Modified,在GET请求时仅返回无期限缓存,这是一个强大的功能,但最理想的情况是当内容没有改变时,PHP不会执行。

为了实现这一目的,在PHP前端部署了具有缓存功能的反向代理服务器,如Varnish。与通过拦截器生成资源缓存相反(或同时),我们将生成内容交给Varnish处理。由于内容可能包含动态配置(例如用户登录信息),因此在这种情况下,我们将使用ESI(边缘侧包含)来合成资源。

这个例子中的更新频率非常高,就像Twitter的时间线一样,是不可能的。但是如果在博客或电商网站等单独的页面中,即使访问量不高(但整体上很多),长尾内容也可以充分利用,结合传统的简单TTL缓存控制,可以成为性能最佳的系统。

随时清除缓存.

假设有一个名为优惠券的资源,并且它有一个有效期限。如果这个资源中有一个名为isExpired的只读布尔字段,那么它的值只有在过了有效期限时才会发生变化。因此,希望在这个时候清除缓存。

如果存在一个设置名为” isExpired “的布尔字段的资源,那么在方法内部,当 isExpired=true 时,可以使用注入的 QueryReposity 进行清除。

class Foo
{
    private $queryRepository;

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

    /**
     * isExpiredの変更をするリソースのメソッド
     */
    public function onPut()
    {
        if ($isExpired) {
            $this->queryRepository->purge(new Uri('app://self/coupon/?id={id}', ['id' => 1]));
            $this->queryRepository->purge(new Uri('page://self/coupon/?id={id}', ['id' => 1]));
        }
    }
}
广告
将在 10 秒后关闭
bannerAds