diff --git a/README.md b/README.md index 5c2e94c..e2448ef 100644 --- a/README.md +++ b/README.md @@ -12,61 +12,85 @@ Ctrip Apollo PHP Client 第一种基于git仓库运行 ```bash git clone git@github.com:fengzhibin/apollo-configd.git + cd apollo-configd + composer install -vvvo + php ./bin/apollo-clientd.php --help ``` 第二种基于phar包运行 ```bash wget "https://github.com/fengzhibin/apollo-sdk-clientd/releases/download/1.0.0/apollo-clientd.phar" + php apollo-clientd.phar --help ``` ## 简单例子 ```bash -基于git仓库运行和phar包运行的参数是一模一样的 +php ./bin/apollo-clientd.php --server="http://apollo-configserver.demo.com" --conf-portal="demo/test/apollo-clientd" -# 监听单个应用,并把配置通过json格式保存在/data/apollo下 -php ./bin/apollo-clientd.php --config-server-url="http://apollo-configserver.demo.com" --appid="demo" --save-config-dir="/data/apollo" +或者 -# 监听多个应用 -php ./bin/apollo-clientd.php --config-server-url="http://apollo-configserver.demo.com" --appid="demo1,demo2,demo3" --save-config-dir="/data/apollo" +php apollo-clientd.phar --server="http://apollo-configserver.demo.com" --conf-portal="demo/test/apollo-clientd" ``` ## 参数说明 -必选参数 - -| 参数 | 说明 | 默认值 | -| ---- | ---- | ---- | -| --config-server-url | Apollo配置服务的地址| 无 | -| --save-config-dir | 保存从阿波罗服务器读取到的配置文件的目录| 无 | -| --appid | 应用id | 无 | - -可选参数 - -| 参数 | 说明 |默认值| -| ---- | ---- |----| -| -h | 显示帮助信息 |无| -| --help | 同-h |无| -| --q | 开启静默模式,屏蔽运行时日志 |无| -| --quiet | 同-q |无| -| --secret | 访问密钥 |无| -| --cluster-name | 集群名 |default| -| --appid-separator | 应用id分隔符 |,| -| --app-namespace-portal | 应用namepsace列表配置入口 |application| -| --check-config-server-url | 是否自动检查config-server-url的有效性 |1| - -完整的参数说明可以通过-h或者--help查看 -```bash -php ./bin/apollo-clientd.php -h 或者 php apollo-clientd.php --help +cli启动参数 + +|参数|说明|默认值| +|----|----|----| +|--server|Apollo配置中心服务的地址| 无 | +|--conf-portal| 读取apollo-clientd运行配置的入口| 无 | +|-h | 显示帮助信息 |无| +|--help | 同-h |无| +|--q | 开启静默模式,屏蔽运行时日志 |无| +|--quiet | 同-q |无| +|--secret | 访问密钥 |无| +|--cluster-name | 集群名 |default| +|--skip-check-server | 是否跳过启动时检查Apollo配置中心是否合法的检查 |无| +|--conf-portal-separator | conf-portal参数的分隔符 |/| + +## 关于--conf-portal参数说明 +--conf-portal参数用于将一些额外的参数(例如应用id,namespace信息等)保存在阿波罗配置中心, +这样就不必在apollo-clientd启动时写死了,这也减少了apollo-clientd启动参数个数, +程序会在启动的时候到这配置入口去读取配置 + +这个参数格式为{appid}/{namespace}/{key},以下图作为例子说明 + +![Screenshot](https://raw.githubusercontent.com/fengzhibin/apollo-sdk-clientd/master/images/extra.png) + +--conf-portal=apollo-sdk-clientd/hello_world/world + +## 额外参数配置(json格式) +```json +{ + "app_namespace_list": {//应用的namespace配置 + "demo1": [//应用id + "application",//namespace + "test1",//namespace + "test2",//namespace + "test3"//namespace + ], + "demo2": [],//不配置namespace列表,程序会走指定入口读取,参考下面的说明 + "demo3": [] + }, + "save_config_dir": "/data/apollo",//从阿波罗配置中心读取的配置,缓存在这个目录下 + "app_namespace_list_portal": "application"//应用下保存namespace列表的入口 +} ``` -## 关于应用的namespace列表说明 -默认所有应用都把该应用下的namespace列表通过指定方式配置在--app-namespace-portal下(默认为application), -apollo-clientd启动时会自动去读取namespace列表,开启配置监听,配置格式参考下图 +如果不在app_namespace_list里面配置应用的namespace列表(例如应用demo2和demo3), +程序启动的时候会尝试通过以app_namespace_list_portal参数为入口读取namespace列表,格式如下: ![Screenshot](https://raw.githubusercontent.com/fengzhibin/apollo-sdk-clientd/master/images/portal.png) +程序会通过这个namespace读取当前应用下的namespace列表 + +完整示意图如下(只需要把当前应用下的namespace都配置在入口即可): + +![Screenshot](https://raw.githubusercontent.com/fengzhibin/apollo-sdk-clientd/master/images/portal_full.png) + ## 业务端读取配置 业务上需要读取阿波罗配置时,引入apollo-sdk/clientd这个composer包即可,参考以下步骤 ```bash diff --git a/bin/apollo-clientd.php b/bin/apollo-clientd.php index 72e49fc..de88701 100644 --- a/bin/apollo-clientd.php +++ b/bin/apollo-clientd.php @@ -15,38 +15,29 @@ } class App { + const VERSION = '1.0.0';//当前版本 + private $apolloSdkClient; - private $configServerUrl = ''; - private $saveConfigDir = ''; - private $clusterName = ''; - private $secret = ''; - private $appId = ''; - private $appIdSeparator = ','; - private $appNamespacePortal = 'application'; private $quiet = false; + private $saveConfigDir = ''; + private $appNamespaceList = []; + private $appNamespaceListPortal = 'application'; public function __construct() { //初始化基础参数 $this->initBaseParams(); - //初始化\ApolloSdk\Config\Client - $config = ['config_server_url' => $this->configServerUrl]; - if(!empty($this->clusterName)) {//集群名称 - $config['cluster_name'] = $this->clusterName; - } - if(!empty($this->secret)) {//密钥 - $config['secret'] = $this->secret; - } - $this->apolloSdkClient = new Client($config); + //初始化额外参数 + $this->initExtraParams(); } /** - * 检查阿波罗服务器地址是否为合法的地址 + * 检查阿波罗配置中心 * @author fengzhibin * @date 2021-03-19 */ - private function checkConfigServerUrl() { - if(Helpers\is_legal_url($this->configServerUrl) === false) { - $this->outputErrorMsg('请传入合法的config-server-url链接'); + private function checkServer($server) { + if(Helpers\is_legal_url($server) === false) { + $this->outputErrorMsg('阿波罗配置中心链接格式异常,不是合法的url'); } $errorMsg = ''; try { @@ -55,10 +46,10 @@ private function checkConfigServerUrl() { 'timeout' => 5, 'connect_timeout' => 5 ]); - $response = $guzzleHttpClient->get($this->configServerUrl); + $response = $guzzleHttpClient->get($server); $statusCode = (int)$response->getStatusCode(); if($statusCode !== 404) { - $errorMsg = "http状态码为{$statusCode},配置中心根接口的状态码应该为404,请检查config-server-url链接"; + $errorMsg = "http状态码为{$statusCode},配置中心根接口的状态码应该为404,请检查阿波罗配置中心链接"; } else { $jsonDecodeBody = []; $body = (string)$response->getBody(); @@ -73,7 +64,7 @@ private function checkConfigServerUrl() { $errorMsg = $e->getMessage(); } if(!empty($errorMsg)) { - $this->outputErrorMsg("通过curl请求{$this->configServerUrl}时产生错误,错误信息如下:".PHP_EOL.$errorMsg.PHP_EOL); + $this->outputErrorMsg("通过curl请求{$this->server}时产生错误,错误信息如下:".PHP_EOL.$errorMsg.PHP_EOL); } } @@ -85,28 +76,24 @@ private function checkConfigServerUrl() { private function outputHelpInfo() { $help = <<outputHelpInfo(); } - $checkConfigServerUrl = 1; - if(isset($opt['check-config-server-url'])) { - $checkConfigServerUrl = (int)$opt['check-config-server-url']; - } - if(empty($opt['config-server-url'])) { - $this->outputErrorMsg('必须传入--config-server-url参数'); + //输出版本号 + if(isset($opt['v']) || isset($opt['version'])) { + die('当前版本:'.self::VERSION.',获取更多版本请访问:https://github.com/fengzhibin/apollo-sdk-clientd/releases'.PHP_EOL); } - $this->configServerUrl = $opt['config-server-url']; - if($checkConfigServerUrl === 1) { - $this->checkConfigServerUrl(); + //静默模式 + if(isset($opt['q']) || isset($opt['quiet'])) { + $this->quiet = true; } - if(empty($opt['save-config-dir'])) { - $this->outputErrorMsg('必须传入--save-config-dir参数'); + //阿波罗配置中心地址 + if(empty($opt['server'])) { + $this->outputErrorMsg('必须传入--server参数'); } - $this->saveConfigDir = $opt['save-config-dir']; - if(empty($opt['appid'])) { - $this->outputErrorMsg('必须传入--appid参数'); + //检查配置中心地址 + if(!isset($opt['skip-check-server'])) { + $this->checkServer($opt['server']); } - $this->appId = $opt['appid']; + //初始化\ApolloSdk\Config\Client配置 + $config = ['config_server_url' => $opt['server']]; + //集群名称 if(!empty($opt['cluster-name'])) { - $this->clusterName = $opt['cluster-name']; + $config['cluster_name'] = $opt['cluster-name']; } + //访问密钥 if(!empty($opt['secret'])) { - $this->secret = $opt['secret']; + $config['secret'] = $opt['secret']; + } + $this->apolloSdkClient = new Client($config); + } + + /** + * 初始化额外参数 + * @author fengzhibin + * @date 2021-03-19 + */ + private function initExtraParams() { + $opt = getopt( + '', + [ + 'conf-portal:', + 'conf-portal-separator:' + ] + ); + //客户端启动时读取运行配置的入口 + if(empty($opt['conf-portal'])) { + $this->outputErrorMsg('必须传入--conf-portal参数'); } - if(!empty($opt['appid-separator'])) { - $this->appIdSeparator = $opt['appid-separator']; + $confPortalSeparator = empty($opt['conf-portal-separator'])?'/':$opt['conf-portal-separator']; + $confPortal = explode($confPortalSeparator, $opt['conf-portal']); + if(count($confPortal) !== 3) { + $this->outputErrorMsg("--conf-portal参数格式错误,正确的格式为: appid{$confPortalSeparator}namespace{$confPortalSeparator}key"); } - if(isset($opt['q']) || isset($opt['quiet'])) { - $this->quiet = true; + list($appId, $namespaceName, $key) = $confPortal; + $tmp = $this->apolloSdkClient->getConfig($appId, $namespaceName); + if(empty($tmp[$key])) { + $this->outputErrorMsg("应用:{$appId},namespace:{$namespaceName}下配置为空"); + } + if(!Helpers\is_json($tmp[$key])) { + $this->outputErrorMsg("apollo-clientd运行配置必须为json格式"); + } + $config = json_decode($tmp[$key], true); + unset($tmp); + if(empty($config['app_namespace_list'])) { + $this->outputErrorMsg("apollo-clientd运行配置没有配置app_namespace_list参数"); + } + if(empty($config['save_config_dir'])) { + $this->outputErrorMsg("apollo-clientd运行配置没有配置save_config_dir参数"); + } + $this->appNamespaceList = $config['app_namespace_list']; + $this->saveConfigDir = $config['save_config_dir']; + if(!empty($config['app_namespace_list_portal'])) { + $this->appNamespaceListPortal = $config['app_namespace_list_portal']; } } @@ -206,7 +233,7 @@ public function outputErrorMsg($msg) { * @date 2021-03-19 */ public function getAllAppIdList() { - $result = explode($this->appIdSeparator, $this->appId); + $result = array_keys($this->appNamespaceList); $this->outputDebugMsg('初始化监听的appid列表', $result); return $result; } @@ -218,26 +245,24 @@ public function getAllAppIdList() { * @date 2021-03-19 */ public function getAllAppNamespaceList() { - $result = []; - //获取所有应用id - $allAppIdList = $this->getAllAppIdList(); - if(!empty($allAppIdList)) { - //构造批量获取配置的数据结构 - $appNamespaceData = []; - foreach($allAppIdList as &$appId) { - $appNamespaceData[$appId][$this->appNamespacePortal] = ''; + //获取没有配置namespace列表的应用 + $appNamespaceData = []; + foreach($this->appNamespaceList as $appId => $namespaceList) { + if(empty($namespaceList)) { + $appNamespaceData[$appId][$this->appNamespaceListPortal] = ''; } - unset($appId); - //通过namespace入口获取所有的namespace列表 + } + //通过namespace入口获取所有的namespace列表 + if(!empty($appNamespaceData)) { $namespaceListConfigData = $this->apolloSdkClient->multiGetConfig($appNamespaceData); if(!empty($namespaceListConfigData)) { //组装成特定格式 foreach($namespaceListConfigData as $appId => &$value) { - $result[$appId] = $this->formatPortalConfigData($value[$this->appNamespacePortal]); + $this->appNamespaceList[$appId] = $this->formatPortalConfigData($value[$this->appNamespaceListPortal]); } } - unset($appNamespaceData); } + $result = $this->appNamespaceList; $this->outputDebugMsg('初始化监听的namespace列表,格式为应用id => namespace列表', $result); foreach($result as $key => &$value) { if(empty($value)) { @@ -257,15 +282,15 @@ public function getAllAppNamespaceList() { * @author fengzhibin * @date 2021-03-19 */ - private function formatPortalConfigData($configData, $appNamespacePortal = null) { - if(is_null($appNamespacePortal)) { - $appNamespacePortal = $this->appNamespacePortal; + private function formatPortalConfigData($configData, $appNamespaceListPortal = null) { + if(is_null($appNamespaceListPortal)) { + $appNamespaceListPortal = $this->appNamespaceListPortal; } if(empty($configData)) { return []; } $res = array_keys(array_filter($configData)); - array_unshift($res, $appNamespacePortal); + array_unshift($res, $appNamespaceListPortal); return array_unique($res); } @@ -296,7 +321,7 @@ function( ) { $this->outputDebugMsg("【".date('Y-m-d H:i:s')."】应用id:{$appId}的namespace:{$namespaceName}发生了配置变化"); //更新了入口namespace,需要同时更新映射数组 - if($namespaceName === $this->appNamespacePortal) { + if($namespaceName === $this->appNamespaceListPortal) { $newMapping = []; $namespaceData = $this->formatPortalConfigData($newConfig); if(!empty($namespaceData)) { diff --git a/images/extra.png b/images/extra.png new file mode 100644 index 0000000..efd6169 Binary files /dev/null and b/images/extra.png differ diff --git a/images/portal.png b/images/portal.png index a5b8397..db8ac0e 100644 Binary files a/images/portal.png and b/images/portal.png differ diff --git a/images/portal_full.png b/images/portal_full.png new file mode 100644 index 0000000..a5b8397 Binary files /dev/null and b/images/portal_full.png differ