configServer = $configServer; $this->appId = $appId; $this->cluster=$cluster; $configFiles=array(); foreach ($namespaces as $namespace) { $this->notifications[$namespace] = array('namespaceName' => $namespace, 'notificationId' => -1); $configFiles[$namespace]=$this->getConfigFileNameSpace($cluster,$namespace); } $this->save_dir = $configFile; $this->publicConfigDir=$config; //读取配置的版本号 if($configFiles){ foreach ($configFiles as $namespace=>$file){ $filePath= $this->publicConfigDir.$this->getConfigFileNameSpace($cluster,$namespace,1); if(file_exists($filePath)){ $configValues=require $filePath; if(isset($configValues['params']['notification'][$namespace])){ $this->checkNotificationsId[$namespace] = array('namespaceName' => $namespace, 'notificationId' => $configValues['params']['notification'][$namespace]); } unset($configValues); } } } } public function setCluster($cluster) { $this->cluster = $cluster; } public function setClientIp($ip) { $this->clientIp = $ip; } public function setPullTimeout($pullTimeout) { $pullTimeout = intval($pullTimeout); if ($pullTimeout < 1 || $pullTimeout > 300) { return; } $this->pullTimeout = $pullTimeout; } public function setIntervalTimeout($intervalTimeout) { $intervalTimeout = intval($intervalTimeout); if ($intervalTimeout < 1 || $intervalTimeout > 300) { return; } $this->intervalTimeout = $intervalTimeout; } private function _getReleaseKey($config_file) { $releaseKey = ''; if (file_exists($config_file)) { $last_config = require $config_file; is_array($last_config) && isset($last_config['releaseKey']) && $releaseKey = $last_config['releaseKey']; } return $releaseKey; } //获取单个namespace的配置文件路径 public function getConfigFile($cluster) { return $this->save_dir.DIRECTORY_SEPARATOR.'main.'.$cluster.'.php'; } //获取单个namespace/集群文件名 public function getConfigFileNameSpace($cluster,$nameSpace,$public=0){ $fileFirstName=''; $nameSpace=substr($nameSpace, 0,strrpos($nameSpace, '.')); if($nameSpace=='application'){ $fileFirstName='main'; }else{ $fileFirstName=$nameSpace; } if($cluster=='default'){ $cluster='production'; } if(!$public){ return $this->save_dir.DIRECTORY_SEPARATOR.$fileFirstName.'.'.$cluster.'.php'; }else{ return $fileFirstName.'.'.$cluster.'.php'; } } //获取单个namespace的配置-无缓存的方式 public function pullConfig($namespaceName) { $base_api = rtrim($this->configServer, '/').'/configs/'.$this->appId.'/'.$this->cluster.'/'; $api = $base_api.$namespaceName; $args = array(); $args['ip'] = $this->clientIp; $config_file = $this->getConfigFile($this->cluster); $args['releaseKey'] = $this->_getReleaseKey($config_file); $api .= '?' . http_build_query($args); $ch = curl_init($api); curl_setopt($ch, CURLOPT_TIMEOUT, $this->pullTimeout); curl_setopt($ch, CURLOPT_HEADER, false); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); $body = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); $error = curl_error($ch); curl_close($ch); if ($httpCode == 200) { $result = json_decode($body, true); $content = 'configServer, '/').'/configs/'.$this->appId.'/'.$this->cluster.'/'; $query_args = array(); $query_args['ip'] = $this->clientIp; foreach ($namespaceNames as $namespaceName=>$notificationId) { $request = array(); $request_url = $base_url.$namespaceName; // $query_args['releaseKey'] = $this->_getReleaseKey($config_file); // print_r($query_args['releaseKey']);exit; $query_string = '?'.http_build_query($query_args); $request_url .= $query_string; $ch = curl_init($request_url); curl_setopt($ch, CURLOPT_TIMEOUT, $this->pullTimeout); curl_setopt($ch, CURLOPT_HEADER, false); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); $request['ch'] = $ch; $request['notificationId'] = $notificationId; $request_list[$namespaceName] = $request; curl_multi_add_handle($multi_ch, $ch); } $active = null; // 执行批处理句柄 do { $mrc = curl_multi_exec($multi_ch, $active); } while ($mrc == CURLM_CALL_MULTI_PERFORM); while ($active && $mrc == CURLM_OK) { if (curl_multi_select($multi_ch) == -1) { usleep(100); } do { $mrc = curl_multi_exec($multi_ch, $active); } while ($mrc == CURLM_CALL_MULTI_PERFORM); } // 获取结果 $response_list = array(); foreach ($request_list as $namespaceName => $req) { $response_list[$namespaceName] = true; $result = curl_multi_getcontent($req['ch']); $code = curl_getinfo($req['ch'], CURLINFO_HTTP_CODE); $error = curl_error($req['ch']); curl_multi_remove_handle($multi_ch,$req['ch']); curl_close($req['ch']); if ($code == 200) { $result = json_decode($result,true); $result=$this->YamalParseConfigData($result,$namespaceName,$req); $configFile=$this->getConfigFileNameSpace($this->cluster,$namespaceName); file_put_contents($configFile, $result); }elseif ($code != 304) { echo 'pull config of namespace['.$namespaceName.'] error:'.($result ?: $error)."\n"; $response_list[$namespaceName] = false; } } curl_multi_close($multi_ch); return $response_list; } //格式化处理配置参数,合并公用配置 protected function formatConfigData($result,$namespaceName){ if(!$result || !is_array($result)) return null; $config=array(); $publicConfig= require $this->publicConfigDir.$this->getConfigFileNameSpace($this->cluster,$namespaceName,1); foreach ($result['configurations'] as $key => $value){ if(strpos($key,'.')!==false){ $keyArr=explode('.',$key); if($keyArr){ $realVal=json_decode($value,true)?json_decode($value,true):$value; $keyStr='$config'; for($i=0;$irecur($config); if(strpos($namespaceName,'qcloud')!==false && isset($config['string'])){ $content=" configServer, '/').'/notifications/v2?'; $params = array(); $params['appId'] = $this->appId; $params['cluster'] = $this->cluster; $params['notifications'] = json_encode(array_values($this->notifications)); $query = http_build_query($params); curl_setopt($ch, CURLOPT_URL, $base_url.$query); $response = curl_exec($ch); $httpCode = curl_getinfo($ch,CURLINFO_HTTP_CODE); $error = curl_error($ch); if ($httpCode == 200) { $res = json_decode($response, true); $change_list=array(); foreach ($res as $r) { if (!isset($this->checkNotificationsId[$r['namespaceName']]) || $r['notificationId'] != $this->checkNotificationsId[$r['namespaceName']]['notificationId']) { $change_list[$r['namespaceName']] = $r['notificationId']; } } if($change_list){ $response_list= $this->pullConfigBatch($change_list); foreach ($response_list as $name => $value){ if(!$value){ echo $name.':同步配置失败'.PHP_EOL; }else{ echo $name.':同步配置成功'.PHP_EOL; } } } }elseif ($httpCode != 304) { throw new \Exception($response ?: $error); } } /** * @param $callback 监听到配置变更时的回调处理 * @return mixed */ public function start() { $ch = curl_init(); curl_setopt($ch, CURLOPT_TIMEOUT, $this->intervalTimeout); curl_setopt($ch, CURLOPT_HEADER, false); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); try { $this->_listenChange($ch); }catch (\Exception $e) { curl_close($ch); return $e->getMessage(); } } public function setReplaceArr($arr){ $this->replaceValueArr=$arr; } //查找需要替换的变量 function recur($array){ $data = array(); array_walk_recursive($array, function ($v, $k) use (&$data) { if(strpos($v,'////')!==false){ $data["'".$v."'"]=str_replace('////','',$v); } if(strpos($k,'////')!==false){ $data["'".$k."'"]=str_replace('////','',$k); } }); return $data; } }