• [原创]Laravel 的缓存源码解析


    前言

    Laravel 支持多种缓存系统, 并提供了统一的api接口.

    (Laravel 5.5)默认支持的存储驱动包括如下:

    • file (默认使用)
    • apc
    • array (数组, 测试用)
    • database (关系型数据库)
    • memcached
    • redis

    默认的缓存配置文件在 config/cache.php

    参考链接:

    使用

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

    use IlluminateSupportFacadesCache;
    $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/laravel/5.5/cache/1316

    源码

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

    Cache::xxx()

    我们通过 CacheManager 类获取持有不同存储驱动的 IlluminateCacheRepository

    CacheManager::store($name = null)

    Repository 仓库类代理了实现存储驱动接口 IlluminateContractsCacheStore 的类实例.

    Cache Facade

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

    <?php
    
    namespace IlluminateSupportFacades;
    
    /**
     * @see IlluminateCacheCacheManager
     * @see IlluminateCacheRepository
     */
    class Cache extends Facade
    {
        /**
         * Get the registered name of the component.
         *
         * @return string
         */
        protected static function getFacadeAccessor()
        {
            return 'cache';
        }
    }
    
    

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

    //...
    'providers' => [
            // ......
            IlluminateCacheCacheServiceProvider::class,
            // ......
        ],
    //...
    

    IlluminateCacheCacheServiceProvider 源文件:

    <?php
    
    namespace IlluminateCache;
    
    use IlluminateSupportServiceProvider;
    
    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 关联的项是 IlluminateCacheCacheManager, 也就是我们通过 Cache Facade 实际调用的是 CacheManager实例的方法.

    CacheManager

    <?php
    
    namespace IlluminateContractsCache;
    
    interface Factory
    {
        /**
         * Get a cache store instance by name.
         *
         * @param  string|null  $name
         * @return IlluminateContractsCacheRepository
         */
        public function store($name = null);
    }
    

    CacheManager 实现了 IlluminateContractsCacheFactory 接口(↑), 即实现了一个简单工厂, 传入存储驱动名, 返回对应的驱动实例.

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

    <?php
    
    namespace IlluminateCache;
    
    use Closure;
    use InvalidArgumentException;
    use IlluminateContractsCacheStore;
    use IlluminateContractsCacheFactory as FactoryContract;
    use IlluminateContractsEventsDispatcher as DispatcherContract;
    
    /**
     * @mixin IlluminateContractsCacheRepository
     */
    class CacheManager implements FactoryContract
    {
    	/**
         * Get a cache store instance by name.
         *
         * @param  string|null  $name
         * @return IlluminateContractsCacheRepository
         */
        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 IlluminateContractsCacheRepository
         */
        protected function get($name)
        {
            return $this->stores[$name] ?? $this->resolve($name);
        }
    
        /**
         * Resolve the given store.
         *
         * @param  string  $name
         * @return IlluminateContractsCacheRepository
         *
         * @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(...)方法

    这些方法返回的实例必须是实现了 IlluminateContractsCacheRepository 接口

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

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

    Repository

    IlluminateContractsCacheRepository 接口

    <?php
    
    namespace IlluminateContractsCache;
    
    use Closure;
    use PsrSimpleCacheCacheInterface;
    
    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 是一个符合 PSR-16: Common Interface for Caching Libraries 规范的缓存仓库类, 其在Laravel相应的实现类: IlluminateCacheRepository

    IlluminateCacheRepository 部分代码如下:

    <?php
    
    namespace IlluminateCache;
    
    use Closure;
    use ArrayAccess;
    use DateTimeInterface;
    use BadMethodCallException;
    use IlluminateSupportCarbon;
    use IlluminateCacheEventsCacheHit;
    use IlluminateContractsCacheStore;
    use IlluminateCacheEventsKeyWritten;
    use IlluminateCacheEventsCacheMissed;
    use IlluminateSupportTraitsMacroable;
    use IlluminateCacheEventsKeyForgotten;
    use IlluminateSupportInteractsWithTime;
    use IlluminateContractsEventsDispatcher;
    use IlluminateContractsCacheRepository as CacheContract;
    
    /**
     * @mixin IlluminateContractsCacheStore
     */
    class Repository implements CacheContract, ArrayAccess
    {
        use InteractsWithTime;
        use Macroable {
            __call as macroCall;
        }
    
        /**
         * The cache store implementation.
         *
         * @var IlluminateContractsCacheStore
         */
        protected $store;
    
        /**
         * The event dispatcher implementation.
         *
         * @var IlluminateContractsEventsDispatcher
         */
        protected $events;
    
        protected $default = 60;
    
        /**
         * Create a new cache repository instance.
         *
         * @param  IlluminateContractsCacheStore  $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  IlluminateContractsEventsDispatcher  $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;
        }
    }
    
    

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

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

    Store

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

    <?php
    
    namespace IlluminateContractsCache;
    
    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
  • 相关阅读:
    Python IDLE快捷键一览
    面试题——中
    面试题——君
    面试题——太(下)
    面试题——2面常见问题
    MYSQL数据库设计规范与原则
    字符串操作
    集合
    java-数组
    java异常处理机制
  • 原文地址:https://www.cnblogs.com/youjiaxing/p/10595354.html
Copyright © 2020-2023  润新知