当前位置 : 主页 > 网络编程 > PHP >

DHTCache.php

来源:互联网 收集:自由互联 发布时间:2021-06-28
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();}/** * 初始化节
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();
	}
	
	/**
	 * 初始化节点配置
	 * @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);
上一篇:RedisLock.php
下一篇:download.php
网友评论