代码之家  ›  专栏  ›  技术社区  ›  BaronGrivet

如何在Silverstripe控制器中缓存JSON响应?

  •  0
  • BaronGrivet  · 技术社区  · 5 年前

    下面是一个代码示例:

    class APIController extends ContentController
    {
    
        public function index(HTTPRequest $request)
        {
            $dataArray['model1'] = AccessPointController::getModel1();
            $dataArray['model2'] = AccessPointController::getModel2();
            $dataArray['model3'] = AccessPointController::getModel3();
            $dataArray['model4'] = AccessPointController::getModel4();
            $dataArray['model5'] = AccessPointController::getModel5();
            $dataArray['model6'] = AccessPointController::getModel6();
    
            $this->response->addHeader('Content-Type', 'application/json');
            $this->response->addHeader('Access-Control-Allow-Origin', '*');
    
            return json_encode($dataArray);
        }
    

    我们面临的问题是数据模型变得如此复杂,JSON的生成时间只有几秒钟。

    JSON应该只在站点内容被更新时才改变,所以理想情况下我们希望缓存JSON,而不是为每次调用动态生成它。

    在上面的例子中,缓存JSON的最佳方式是什么?

    1 回复  |  直到 5 年前
        1
  •  4
  •   Zauberfisch    5 年前

    the silverstripe docs about caching ?
    它们确实提供了一种在缓存中存储内容的编程方法。和配置选项用于存储缓存的后端。


    我已经在这里扩展了缓存的实时时间,但是您仍然应该注意到,这个缓存并不是为了存储生成的静态内容,而是为了减少负载。应用程序仍然需要每隔86400秒(24小时)计算一次api响应。

    # app/_config/apiCache.yml
    ---
    Name: apicacheconfig
    ---
    # [... rest of your config config ...]
    SilverStripe\Core\Injector\Injector:
      Psr\SimpleCache\CacheInterface.apiResponseCache:
        factory: SilverStripe\Core\Cache\CacheFactory
        constructor:
          namespace: "apiResponseCache"
          defaultLifetime: 86400
    
    <?php // app/src/FooController.php
    
    class FooController extends \SilverStripe\Control\Controller {
        public function getCache() {
            return Injector::inst()->get('Psr\SimpleCache\CacheInterface.apiResponseCache');
            // or your own cache (see below):
            // return new MyCache();
        }
    
        protected function hasDataBeenChanged() {
            // alternative to this method, you could also simply include Page::get()->max('LastEdited') or whatever in your cache key inside index(), but if you are using your own cache system, you need to handle deleting of old unused cache files. If you are using SilverStripe's cache, it will do that for you
            $c = $this->getCache();
            $lastCacheTime = $c->has('cacheTime') ? (int)$c->get('cacheTime') : 0;
            $lastDataChangeTime = strtotime(Page::get()->max('LastEdited'));
            return $lastDataChangeTime > $lastCacheTime;
        }
    
        public function index() {
            $c = $this->getCache();
            $cacheKey = 'indexActionResponse';
            if ($c->has($cacheKey) && !$this->hasDataBeenChanged()) {
                $data = $c->get($cacheKey);
            } else {
                $dataArray['model1'] = AccessPointController::getModel1();
                $dataArray['model2'] = AccessPointController::getModel2();
                $dataArray['model3'] = AccessPointController::getModel3();
                $dataArray['model4'] = AccessPointController::getModel4();
                $dataArray['model5'] = AccessPointController::getModel5();
                $dataArray['model6'] = AccessPointController::getModel6();
                $data = json_encode($dataArray);
                $c->set($cacheKey, $data);
                $c->set('cacheTime', time());
            }
    
            $this->response->addHeader('Content-Type', 'application/json');
            $this->response->addHeader('Access-Control-Allow-Origin', '*');
    
            return json_encode($dataArray);
        }
    }
    

    如果您正在寻找一个永久/持久缓存,它只会在数据更改时更新,我建议您寻找另一个后端,或者自己实现一个简单的缓存,并使用它来代替silverstripe缓存。

    class MyCache {
        protected function fileName($key) {
            if (strpos($key, '/') !== false || strpos($key, '\\') !== false) {
                throw new \Exception("Invalid cache key '$key'");
            }
            return BASE_PATH . "/api-cache/$key.json";
        }
    
        public function get($key) {
            if ($this->has($key)) {
                return file_get_contents($this->fileName($key));
            }
            return null;
        }
    
        public function set($key, $val) {
            file_put_contents($this->fileName($key), $val);
        }
    
        public function has($key) {
            $f = $this->fileName($key);
            return @file_exists($f);
        }