page contents

Laravel 的缓存源码解析

以下内容希望帮助到大家!

attachments-2020-06-EpfNw1SK5ed76402d06e2.png

使用


直接使用Laravel为我们提供的Facade

use Illuminate\Support\Facades\Cache;
$cache = Cache::get('key');


支持的大部分方法:

Cache::put('key', 'value', $minutes);
Cache::add('key', 'value', $minutes);
Cache::forever('key', 'value');
Cache::remember('key', $minutes, function(){ return 'value' });
Cache::rememberForever('key', function(){ return 'value' });
Cache::forget('key');
Cache::has('key');
Cache::get('key');
Cache::get('key', 'default');
Cache::get('key', function(){ return 'default'; });
Cache::tags('my-tag')->put('key','value', $minutes);
Cache::tags('my-tag')->has('key');
Cache::tags('my-tag')->get('key');
Cache::tags('my-tag')->forget('key');
Cache::tags('my-tag')->flush();
Cache::increment('key');
Cache::increment('key', $amount);
Cache::decrement('key');
Cache::decrement('key', $amount);
Cache::tags('group')->put('key', $value);
Cache::tags('group')->get('key');
Cache::tags('group')->flush();

其他使用方法请参照官方翻译(中文)文档: https://learnku.com/docs/lara...


源码


Laravel 中常用 Cache Facade 来操作缓存, 对应的实际类是 Illuminate\Cache\CacheManager 缓存管理类(工厂).

Cache::xxx()


我们通过 CacheManager 类获取持有不同存储驱动的 Illuminate\Cache\Repository 类

CacheManager::store($name = null)

Repository 仓库类代理了实现存储驱动接口 Illuminate\Contracts\Cache\Store 的类实例.


Cache Facade


首先从 Cache Facade 开始分析, 先看一下其源码:

<?php

namespace Illuminate\Support\Facades;

/**
 * @see \Illuminate\Cache\CacheManager
 * @see \Illuminate\Cache\Repository
 */
class Cache extends Facade
{
    /**
     * Get the registered name of the component.
     *
     * @return string
     */
    protected static function getFacadeAccessor()
    {
        return 'cache';
    }
}


在配置文件 config\app.php 中定义了 Cache 服务提供者

//...
'providers' => [
        // ......
        Illuminate\Cache\CacheServiceProvider::class,
        // ......
    ],
//...


Illuminate\Cache\CacheServiceProvider 源文件:

<?php

namespace Illuminate\Cache;

use Illuminate\Support\ServiceProvider;

class CacheServiceProvider extends ServiceProvider
{
    // ......
    
    public function register()
    {
        $this->app->singleton('cache', function ($app) {
            return new CacheManager($app);
        });

        $this->app->singleton('cache.store', function ($app) {
            return $app['cache']->driver();
        });

        $this->app->singleton('memcached.connector', function () {
            return new MemcachedConnector;
        });
    }
    
    // ......
}

通过上面源码可知, Cache Facade 关联的项是 Illuminate\Cache\CacheManager, 也就是我们通过 Cache Facade 实际调用的是 CacheManager实例的方法.


CacheManager


<?php

namespace Illuminate\Contracts\Cache;

interface Factory
{
    /**
     * Get a cache store instance by name.
     *
     * @param  string|null  $name
     * @return \Illuminate\Contracts\Cache\Repository
     */
    public function store($name = null);
}


CacheManager 实现了 Illuminate\Contracts\Cache\Factory 接口(↑), 即实现了一个简单工厂, 传入存储驱动名, 返回对应的驱动实例。

CacheManager实现的简单工厂接口方法:

<?php

namespace Illuminate\Cache;

use Closure;
use InvalidArgumentException;
use Illuminate\Contracts\Cache\Store;
use Illuminate\Contracts\Cache\Factory as FactoryContract;
use Illuminate\Contracts\Events\Dispatcher as DispatcherContract;

/**
 * @mixin \Illuminate\Contracts\Cache\Repository
 */
class CacheManager implements FactoryContract
{
    /**
     * Get a cache store instance by name.
     *
     * @param  string|null  $name
     * @return \Illuminate\Contracts\Cache\Repository
     */
    public function store($name = null)
    {
        $name = $name ?: $this->getDefaultDriver();

        return $this->stores[$name] = $this->get($name);
    }
    
    /**
     * Get the default cache driver name.
     *
     * @return string
     */
    public function getDefaultDriver()
    {
        return $this->app['config']['cache.default'];
    }

    /**
     * Attempt to get the store from the local cache.
     *
     * @param  string  $name
     * @return \Illuminate\Contracts\Cache\Repository
     */
    protected function get($name)
    {
        return $this->stores[$name] ?? $this->resolve($name);
    }

    /**
     * Resolve the given store.
     *
     * @param  string  $name
     * @return \Illuminate\Contracts\Cache\Repository
     *
     * @throws \InvalidArgumentException
     */
    protected function resolve($name)
    {
        $config = $this->getConfig($name);

        if (is_null($config)) {
            throw new InvalidArgumentException("Cache store [{$name}] is not defined.");
        }

        if (isset($this->customCreators[$config['driver']])) {
            return $this->callCustomCreator($config);
        } else {
            $driverMethod = 'create'.ucfirst($config['driver']).'Driver';

            if (method_exists($this, $driverMethod)) {
                return $this->{$driverMethod}($config);
            } else {
                throw new InvalidArgumentException("Driver [{$config['driver']}] is not supported.");
            }
        }
    }
    
    /**
     * Dynamically call the default driver instance.
     *
     * @param  string  $method
     * @param  array   $parameters
     * @return mixed
     */
    public function __call($method, $parameters)
    {
        return $this->store()->$method(...$parameters);
    }
}

可以看到 CacheManager 提供了会话级别的实例缓存, 当解析驱动名时, 它会按如下顺序解析:

  1. 自定义驱动: 查看是否有通过 CacheManager::extend(...)自定义的驱动
  2. Laravel提供的驱动: 查看是否存在 CacheManager::createXxxDriver(...)方法


这些方法返回的实例必须是实现了 Illuminate\Contracts\Cache\Repository 接口

本质上, CacheManager 就是一个提供了会话级别缓存的 Repository 实例工厂, 同时它提供了一个 __call 魔术方法, 以便快速调用默认缓存驱动。

$value = Cache::store('file')->get('foo');

// 通过 _call, 调用默认缓存驱动的 get 方法
$value = Cache::get('key');


Repository


Illuminate\Contracts\Cache\Repository 接口

<?php

namespace Illuminate\Contracts\Cache;

use Closure;
use Psr\SimpleCache\CacheInterface;

interface Repository extends CacheInterface
{
    public function has($key);
    public function get($key, $default = null);
    public function pull($key, $default = null);
    public function put($key, $value, $minutes);
    public function add($key, $value, $minutes);
    public function increment($key, $value = 1);
    public function decrement($key, $value = 1);
    public function forever($key, $value);
    public function remember($key, $minutes, Closure $callback);
    public function sear($key, Closure $callback);
    public function rememberForever($key, Closure $callback);
    public function forget($key);
    public function getStore();
}


Repository 是一个符合 href="http://www.baidu.com/link?

url=9LkUzilH74QMr6SuXLs5wqzeJAXlxKxOoz7qJLQSMmy8JHcYUiJj_wtVBslGK2dB">PSR-16: Common Interface for Caching Libraries 规范的缓存仓库类, 其在Laravel相应的实现类: Illuminate\Cache\Repository


Illuminate\Cache\Repository 部分代码如下:

<?php

namespace Illuminate\Cache;

use Closure;
use ArrayAccess;
use DateTimeInterface;
use BadMethodCallException;
use Illuminate\Support\Carbon;
use Illuminate\Cache\Events\CacheHit;
use Illuminate\Contracts\Cache\Store;
use Illuminate\Cache\Events\KeyWritten;
use Illuminate\Cache\Events\CacheMissed;
use Illuminate\Support\Traits\Macroable;
use Illuminate\Cache\Events\KeyForgotten;
use Illuminate\Support\InteractsWithTime;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Contracts\Cache\Repository as CacheContract;

/**
 * @mixin \Illuminate\Contracts\Cache\Store
 */
class Repository implements CacheContract, ArrayAccess
{
    use InteractsWithTime;
    use Macroable {
        __call as macroCall;
    }

    /**
     * The cache store implementation.
     *
     * @var \Illuminate\Contracts\Cache\Store
     */
    protected $store;

    /**
     * The event dispatcher implementation.
     *
     * @var \Illuminate\Contracts\Events\Dispatcher
     */
    protected $events;

    protected $default = 60;

    /**
     * Create a new cache repository instance.
     *
     * @param  \Illuminate\Contracts\Cache\Store  $store
     * @return void
     */
    public function __construct(Store $store)
    {
        $this->store = $store;
    }

    public function has($key)
    {
        return ! is_null($this->get($key));
    }

    public function get($key, $default = null)
    {
        if (is_array($key)) {
            return $this->many($key);
        }

        $value = $this->store->get($this->itemKey($key));

        // If we could not find the cache value, we will fire the missed event and get
        // the default value for this cache value. This default could be a callback
        // so we will execute the value function which will resolve it if needed.
        if (is_null($value)) {
            $this->event(new CacheMissed($key));

            $value = value($default);
        } else {
            $this->event(new CacheHit($key, $value));
        }

        return $value;
    }

    public function pull($key, $default = null)
    {
        return tap($this->get($key, $default), function ($value) use ($key) {
            $this->forget($key);
        });
    }
    
    protected function event($event)
    {
        if (isset($this->events)) {
            $this->events->dispatch($event);
        }
    }

    /**
     * Set the event dispatcher instance.
     *
     * @param  \Illuminate\Contracts\Events\Dispatcher  $events
     * @return void
     */
    public function setEventDispatcher(Dispatcher $events)
    {
        $this->events = $events;
    }

    public function __call($method, $parameters)
    {
        if (static::hasMacro($method)) {
            return $this->macroCall($method, $parameters);
        }

        return $this->store->$method(...$parameters);
    }
    
    public function __clone()
    {
        $this->store = clone $this->store;
    }
}

从源码可以看出, Illuminate\Cache\Repository 实现了代理模式, 具体的实现是交由 Illuminate\Contracts\Cache\Store 来处理, Repository 主要作用是

  1. 提供一些便捷操作(可以理解为语法糖)
  2. Event 事件触发, 包括缓存命中/未命中、写入/删除键值


Store


Illuminate\Contracts\Cache 缓存驱动是实际处理缓存如何写入/读取/删除的类, 接口内容如下:

<?php

namespace Illuminate\Contracts\Cache;

interface Store
{
    public function get($key);
    public function many(array $keys);
    public function put($key, $value, $minutes);
    public function putMany(array $values, $minutes);
    public function increment($key, $value = 1);
    public function decrement($key, $value = 1);
    public function forever($key, $value);
    public function forget($key);
    public function flush();
    public function getPrefix();
}


具体的实现类有:

  • ApcStore
  • ArrayStore
  • NullStore
  • DatabaseStore
  • FileStore
  • MemcachedStore
  • RedisStore

attachments-2020-06-Yse2yjQt5ed763d7d86ca.jpg

  • 发表于 2020-06-03 16:49
  • 阅读 ( 703 )
  • 分类:PHP开发

0 条评论

请先 登录 后评论
Pack
Pack

1135 篇文章

作家榜 »

  1. 轩辕小不懂 2403 文章
  2. 小柒 1482 文章
  3. Pack 1135 文章
  4. Nen 576 文章
  5. 王昭君 209 文章
  6. 文双 71 文章
  7. 小威 64 文章
  8. Cara 36 文章