mirror of
https://gitee.com/hyperf/hyperf.git
synced 2024-11-29 18:27:44 +08:00
Added foundation of apollo config-center
This commit is contained in:
parent
608a18ef46
commit
3b47191a62
@ -46,7 +46,9 @@
|
||||
"hyperf/amqp": "self.version",
|
||||
"hyperf/cache": "self.version",
|
||||
"hyperf/config": "self.version",
|
||||
"hyperf/config-apollo": "self.version",
|
||||
"hyperf/constants": "self.version",
|
||||
"hyperf/consul": "self.version",
|
||||
"hyperf/contract": "self.version",
|
||||
"hyperf/database": "self.version",
|
||||
"hyperf/db-connection": "self.version",
|
||||
@ -80,7 +82,9 @@
|
||||
"Hyperf\\Amqp\\": "src/amqp/src/",
|
||||
"Hyperf\\Cache\\": "src/cache/src/",
|
||||
"Hyperf\\Config\\": "src/config/src/",
|
||||
"Hyperf\\ConfigApollo\\": "src/config-apollo/src/",
|
||||
"Hyperf\\Constants\\": "src/constants/src/",
|
||||
"Hyperf\\Consul\\": "src/consul/src/",
|
||||
"Hyperf\\Contract\\": "src/contract/src/",
|
||||
"Hyperf\\Database\\": "src/database/src/",
|
||||
"Hyperf\\DbConnection\\": "src/db-connection/src/",
|
||||
@ -106,9 +110,12 @@
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"HyperfTest\\ConfigApollo\\": "./src/config-apollo/tests",
|
||||
"HyperfTest\\Consul\\": "./src/consul/tests",
|
||||
"HyperfTest\\Dispatcher\\": "./src/dispatcher/test",
|
||||
"HyperfTest\\Database\\": "./src/database/tests",
|
||||
"HyperfTest\\Event\\": "./src/event/tests"
|
||||
"HyperfTest\\Event\\": "./src/event/tests",
|
||||
"HyperfTest\\Utils\\": "./src/utils/tests"
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
@ -122,6 +129,7 @@
|
||||
"Hyperf\\Amqp\\ConfigProvider",
|
||||
"Hyperf\\Cache\\ConfigProvider",
|
||||
"Hyperf\\Config\\ConfigProvider",
|
||||
"Hyperf\\ConfigApollo\\ConfigProvider",
|
||||
"Hyperf\\Devtool\\ConfigProvider",
|
||||
"Hyperf\\DbConnection\\ConfigProvider",
|
||||
"Hyperf\\Di\\ConfigProvider",
|
||||
|
55
src/config-apollo/composer.json
Normal file
55
src/config-apollo/composer.json
Normal file
@ -0,0 +1,55 @@
|
||||
{
|
||||
"name": "hyperf/config-apollo",
|
||||
"description": "An apollo adapter for Hyperf config component.",
|
||||
"license": "Apache-2.0",
|
||||
"keywords": [
|
||||
"php",
|
||||
"swoole",
|
||||
"hyperf",
|
||||
"config",
|
||||
"configuration",
|
||||
"apollo"
|
||||
],
|
||||
"support": {
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.2",
|
||||
"hyperf/config": "dev-master"
|
||||
},
|
||||
"require-dev": {
|
||||
"malukenho/docheader": "^0.1.6",
|
||||
"mockery/mockery": "^1.0",
|
||||
"phpunit/phpunit": "^7.0.0",
|
||||
"friendsofphp/php-cs-fixer": "^2.9"
|
||||
},
|
||||
"suggest": {
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
],
|
||||
"psr-4": {
|
||||
"Hyperf\\ConfigApollo\\": "src"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"HyperfTest\\ConfigApollo\\": "tests/"
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"sort-packages": true
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
},
|
||||
"hyperf": {
|
||||
"config": "Hyperf\\ConfigApollo\\ConfigProvider"
|
||||
}
|
||||
},
|
||||
"bin": [
|
||||
],
|
||||
"scripts": {
|
||||
"cs-fix": "php-cs-fixer fix $1",
|
||||
"test": "phpunit --colors=always"
|
||||
}
|
||||
}
|
134
src/config-apollo/src/Client.php
Normal file
134
src/config-apollo/src/Client.php
Normal file
@ -0,0 +1,134 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://hyperf.org
|
||||
* @document https://wiki.hyperf.org
|
||||
* @contact group@hyperf.org
|
||||
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace Hyperf\ConfigApollo;
|
||||
|
||||
use Closure;
|
||||
use Hyperf\Utils\Parallel;
|
||||
use Hyperf\Utils\Coroutine;
|
||||
|
||||
class Client
|
||||
{
|
||||
/**
|
||||
* @var Option
|
||||
*/
|
||||
private $option;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $callbacks;
|
||||
|
||||
/**
|
||||
* @var Closure
|
||||
*/
|
||||
private $httpClientFactory;
|
||||
|
||||
public function __construct(
|
||||
Option $option,
|
||||
array $callbacks = [],
|
||||
Closure $httpClientFactory
|
||||
) {
|
||||
$this->option = $option;
|
||||
$this->callbacks = $callbacks;
|
||||
$this->httpClientFactory = $httpClientFactory;
|
||||
}
|
||||
|
||||
public function pull(array $namespaces)
|
||||
{
|
||||
if (! $namespaces) {
|
||||
return [];
|
||||
}
|
||||
if (Coroutine::inCoroutine()) {
|
||||
// @todo needs test.
|
||||
$result = $this->coroutinePull($namespaces);
|
||||
} else {
|
||||
$result = $this->blockingPull($namespaces);
|
||||
}
|
||||
foreach ($result as $namespace => $value) {
|
||||
if (isset($this->callbacks[$namespace]) && is_callable($this->callbacks[$namespace])) {
|
||||
call($this->callbacks[$namespace], [$value]);
|
||||
if (isset($value['releaseKey']) && $value['releaseKey']) {
|
||||
ReleaseKey::set($namespace, $value['releaseKey']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function coroutinePull(array $namespaces)
|
||||
{
|
||||
$option = $this->option;
|
||||
$parallel = new Parallel();
|
||||
$httpClientFactory = $this->httpClientFactory;
|
||||
foreach ($namespaces as $namespace) {
|
||||
$parallel->add(function () use ($httpClientFactory, $option, $namespace) {
|
||||
$client = $httpClientFactory();
|
||||
if (! $client instanceof \GuzzleHttp\Client) {
|
||||
throw new \RuntimeException('Invalid http client.');
|
||||
}
|
||||
$releaseKey = ReleaseKey::get($namespace, null);
|
||||
$response = $client->get($option->buildBaseUrl() . $namespace, [
|
||||
'query' => [
|
||||
'ip' => $option->getClientIp(),
|
||||
'releaseKey' => $releaseKey,
|
||||
],
|
||||
]);
|
||||
if ($response->getStatusCode() === 200 && strpos($response->getHeaderLine('Content-Type'), 'application/json') !== false) {
|
||||
$body = json_decode((string)$response->getBody(), true);
|
||||
$result[$namespace] = [
|
||||
'configurations' => $body['configurations'] ?? [],
|
||||
'releaseKey' => $body['releaseKey'] ?? '',
|
||||
];
|
||||
} else {
|
||||
$result[$namespace] = [
|
||||
'configurations' => [],
|
||||
'releaseKey' => '',
|
||||
];
|
||||
}
|
||||
});
|
||||
}
|
||||
return $parallel->wait();
|
||||
}
|
||||
|
||||
protected function blockingPull(array $namespaces)
|
||||
{
|
||||
$result = [];
|
||||
$url = $this->option->buildBaseUrl();
|
||||
$httpClientFactory = $this->httpClientFactory;
|
||||
foreach ($namespaces as $namespace) {
|
||||
$client = $httpClientFactory();
|
||||
if (! $client instanceof \GuzzleHttp\Client) {
|
||||
throw new \RuntimeException('Invalid http client.');
|
||||
}
|
||||
$releaseKey = ReleaseKey::get($namespace, null);
|
||||
$response = $client->get($url . $namespace, [
|
||||
'query' => [
|
||||
'ip' => $this->option->getClientIp(),
|
||||
'releaseKey' => $releaseKey,
|
||||
],
|
||||
]);
|
||||
if ($response->getStatusCode() === 200 && strpos($response->getHeaderLine('Content-Type'), 'application/json') !== false) {
|
||||
$body = json_decode((string)$response->getBody(), true);
|
||||
$result[$namespace] = [
|
||||
'configurations' => $body['configurations'] ?? [],
|
||||
'releaseKey' => $body['releaseKey'] ?? '',
|
||||
];
|
||||
} else {
|
||||
$result[$namespace] = [
|
||||
'configurations' => [],
|
||||
'releaseKey' => '',
|
||||
];
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
29
src/config-apollo/src/ConfigProvider.php
Normal file
29
src/config-apollo/src/ConfigProvider.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://hyperf.org
|
||||
* @document https://wiki.hyperf.org
|
||||
* @contact group@hyperf.org
|
||||
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace Hyperf\ConfigApollo;
|
||||
|
||||
class ConfigProvider
|
||||
{
|
||||
public function __invoke(): array
|
||||
{
|
||||
return [
|
||||
'dependencies' => [
|
||||
],
|
||||
'scan' => [
|
||||
'paths' => [
|
||||
__DIR__,
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
143
src/config-apollo/src/Option.php
Normal file
143
src/config-apollo/src/Option.php
Normal file
@ -0,0 +1,143 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://hyperf.org
|
||||
* @document https://wiki.hyperf.org
|
||||
* @contact group@hyperf.org
|
||||
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace Hyperf\ConfigApollo;
|
||||
|
||||
use Hyperf\Utils\Str;
|
||||
|
||||
class Option
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $server = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $appid = '';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $namespaces = [];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $cluster = 'default';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $clientIp = '127.0.0.1';
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $pullTimeout = 10;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $intervalTimeout = 60;
|
||||
|
||||
public function buildBaseUrl(): string
|
||||
{
|
||||
return implode('/', [
|
||||
$this->getServer(),
|
||||
'configs',
|
||||
$this->getAppid(),
|
||||
$this->getCluster()
|
||||
]) . '/';
|
||||
}
|
||||
|
||||
public function getServer(): string
|
||||
{
|
||||
return $this->server;
|
||||
}
|
||||
|
||||
public function setServer(string $server): self
|
||||
{
|
||||
if (! Str::startsWith($server, 'http://')) {
|
||||
$server = 'http://' . $server;
|
||||
}
|
||||
$this->server = $server;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAppid(): string
|
||||
{
|
||||
return $this->appid;
|
||||
}
|
||||
|
||||
public function setAppid(string $appid): self
|
||||
{
|
||||
$this->appid = $appid;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getNamespaces(): array
|
||||
{
|
||||
return $this->namespaces;
|
||||
}
|
||||
|
||||
public function setNamespaces(string $namespaces): self
|
||||
{
|
||||
$this->namespaces = $namespaces;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCluster(): string
|
||||
{
|
||||
return $this->cluster;
|
||||
}
|
||||
|
||||
public function setCluster(string $cluster): self
|
||||
{
|
||||
$this->cluster = $cluster;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getClientIp(): string
|
||||
{
|
||||
return $this->clientIp;
|
||||
}
|
||||
|
||||
public function setClientIp(string $clientIp): self
|
||||
{
|
||||
$this->clientIp = $clientIp;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPullTimeout(): int
|
||||
{
|
||||
return $this->pullTimeout;
|
||||
}
|
||||
|
||||
public function setPullTimeout(int $pullTimeout): self
|
||||
{
|
||||
$this->pullTimeout = $pullTimeout;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getIntervalTimeout(): int
|
||||
{
|
||||
return $this->intervalTimeout;
|
||||
}
|
||||
|
||||
public function setIntervalTimeout(int $intervalTimeout): self
|
||||
{
|
||||
$this->intervalTimeout = $intervalTimeout;
|
||||
return $this;
|
||||
}
|
||||
}
|
18
src/config-apollo/src/ReleaseKey.php
Normal file
18
src/config-apollo/src/ReleaseKey.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace Hyperf\ConfigApollo;
|
||||
|
||||
|
||||
use Hyperf\Utils\Traits\Container;
|
||||
|
||||
class ReleaseKey
|
||||
{
|
||||
|
||||
use Container;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected static $container = [];
|
||||
|
||||
}
|
46
src/config-apollo/tests/ClientTest.php
Normal file
46
src/config-apollo/tests/ClientTest.php
Normal file
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace HyperfTest\ConfigApollo;
|
||||
|
||||
|
||||
use Hyperf\Config\Config;
|
||||
use Hyperf\ConfigApollo\Client;
|
||||
use Hyperf\ConfigApollo\Option;
|
||||
use Hyperf\Contract\ConfigInterface;
|
||||
use Hyperf\Guzzle\ClientFactory;
|
||||
use Hyperf\Utils\ApplicationContext;
|
||||
use Hyperf\Utils\Coroutine;
|
||||
use Mockery;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Psr\Container\ContainerInterface;
|
||||
|
||||
class ClientTest extends TestCase
|
||||
{
|
||||
public function testPull()
|
||||
{
|
||||
$option = new Option();
|
||||
$option->setServer('http://127.0.0.1:8080')->setAppid('test')->setCluster('default')->setClientIp('127.0.0.1');
|
||||
$container = Mockery::mock(ContainerInterface::class);
|
||||
$container->shouldReceive('get')->with(ConfigInterface::class)->andReturn(new Config([]));
|
||||
ApplicationContext::setContainer($container);
|
||||
$callbacks = [
|
||||
'application' => function ($configs) {
|
||||
$container = ApplicationContext::getContainer();
|
||||
$config = $container->get(ConfigInterface::class);
|
||||
foreach ($configs['configurations'] ?? [] as $key => $value) {
|
||||
$config->set($key, $value);
|
||||
}
|
||||
},
|
||||
];
|
||||
$client = new Client($option, $callbacks, function (array $options = []) use ($container) {
|
||||
return (new ClientFactory($container))->create($options);
|
||||
});
|
||||
$client->pull([
|
||||
'application',
|
||||
]);
|
||||
$config = $container->get(ConfigInterface::class);
|
||||
$this->assertSame('test-value', $config->get('test-key'));
|
||||
}
|
||||
|
||||
|
||||
}
|
34
src/config-apollo/tests/OptionTest.php
Normal file
34
src/config-apollo/tests/OptionTest.php
Normal file
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://hyperf.org
|
||||
* @document https://wiki.hyperf.org
|
||||
* @contact group@hyperf.org
|
||||
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace HyperfTest\ConfigApollo;
|
||||
|
||||
use Hyperf\ConfigApollo\Option;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @covers \Hyperf\ConfigApollo\Option
|
||||
*/
|
||||
class OptionTest extends TestCase
|
||||
{
|
||||
public function testBuildUrl()
|
||||
{
|
||||
$option = new Option();
|
||||
$option->setServer('http://127.0.0.1:8080')->setAppid('test')->setCluster('default')->setClientIp('127.0.0.1');
|
||||
$baseUrl = 'http://127.0.0.1:8080/configs/test/default/';
|
||||
$this->assertSame($baseUrl, $option->buildBaseUrl());
|
||||
// Server without 'http://'
|
||||
$option->setServer('127.0.0.1:8080');
|
||||
$this->assertSame($baseUrl, $option->buildBaseUrl());
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user