DHTCache.php params['dht_cache_conf'];if( empty($conf) ) {throw new \Exception('Error cache config!');}self::$_obj = new self($conf);}return self::$_obj;}private function __construct($conf) {$this-conf = $conf;$this-init();}/** * 初始化节
params['dht_cache_conf']; if( empty($conf) ) { throw new \Exception('Error cache config!'); } self::$_obj = new self($conf); } return self::$_obj; } private function __construct($conf) { $this->conf = $conf; $this->init(); } /** * 初始化节点配置 * @return number */ protected function init() { $incr = floor( $this->conf['hash_range'] / ( $this->conf['virtual_number']+1 ) ); foreach($this->conf['nodes'] as $key => $node) { $this->conf['nodes'][$key]['hash'] = $this->getHash($node['host'].':'.$node['port']); } // 生成虚拟节点 $newNodes = $this->conf['nodes']; for($i = 0; $i < $this->conf['virtual_number']; $i++) { foreach($this->conf['nodes'] as $node) { $hash = $incr * ( $i + 1 ) + $node['hash']; $node['hash'] = $hash > $this->conf['hash_range'] ? $hash - $this->conf['hash_range'] : $hash; $newNodes[] = $node; } } usort($newNodes, function($a, $b) { return $a['hash'] > $b['hash'] ? 1 : -1; }); $this->conf['nodes'] = $newNodes; } /** * 缓存 * @param Array|String $key 缓存键 * 若为数组,会自动使用 下划线(_) 连接各元素,将至转换为字符串 * * @param Array $callback 获取数据回调函数配置 * 可以为: * 1. 数组, [{Class},{Method}] * 2. 匿名函数, function(){...} * 3. 函数名, 'functionName' * * @param Array $params $callback所需要的参数 * @param number $expire 过期时间,单位秒(s) * @return number */ public function run($key, $callback, $params = [], $expire = 0) { is_array($key) && $key = implode('_', $key); !$expire && $expire = $this->conf['default_expires']; $cacheKey = $this->conf['key_prefix'] . $key; $redis = $this->getNode($cacheKey); $result = $redis->get($cacheKey); // 是否需要更新缓存 $update = true; $nowTime = time(); !empty($result) && $update = $result['expires'] < $nowTime; if($update) { $lock = new RedisLock($key, $redis); if( $lock->begin() ) { // 获取数据 try{ $data = call_user_func_array($callback, $params); } catch(\Exception $e) { $data = ['code'=>-500,'msg'=>$e->getMessage()]; } // 数据过期时间, 需要根据这个时间, 判断是否更新缓存 $result = [ 'expires'=>$nowTime + max($this->conf['min_expires'], (int)$expire), 'data'=> $data ]; // 长时间缓存数据, 在这个期间遇到争锁的情况, 也能正常返回数据 $res = $redis->setex($cacheKey, $this->conf['keep_time'], $result); $lock->release(); } } return $result['data']; } /** * 获取缓存节点 * @param String $cacheKey */ public function getNode($cacheKey) { $hash = $this->getHash($cacheKey); $conf = []; foreach($this->conf['nodes'] as $node) { if($hash <= $node['hash']) { $conf = $node; break; } } empty($conf) && $conf = $this->conf['nodes'][0]; $conf = array_merge($this->conf['redis_conf'], $conf); unset($conf['hash']); return Yii::createObject($conf); } /** * 计算字符串hash, 0 到 2^31 - 1之间的数 * @param String $string * @return number */ protected function getHash($string) { $val = base_convert( hash('fnv1a32', $string), 16, 10 ) / 2; return (int)$val; } } /**配置**/ return [ // redis 节点 'nodes'=>[ [ 'host' => '10.71.182.79', 'port' => 6379, 'slaves'=>[ ['host' => '10.71.182.79','port' => 6379] ], ] ], 'redis_conf'=>[ 'class'=>'yii\common\components\Redis', 'serializer'=>true, 'timeout'=>2, 'database'=>0 ], // 统一缓存键前缀 'key_prefix'=>'cache_', // 默认过期时间 秒(s) 'default_expires'=>30, // 过期时间最小值 秒(s) 'min_expires'=> 1, // 锁过期时间 秒(s) 'lock_expires'=>10, // 缓存保留时间 秒(s) // 缓存过期后,并不会直接删除 'keep_time'=>86400, // 每个真实节点对应的虚拟节点数 'virtual_number'=>2, // hash范围2^31 - 1 'hash_range'=> 2147483647, ]; /**使用**/ $data = DHTCache::instance($conf)->run('some_key', function($a) { return [...];}, [1], 60);