Skip to content

Commit

Permalink
feat: add confd nacos driver (#129)
Browse files Browse the repository at this point in the history
* feat: add confd nacos driver

---------

Co-authored-by: greezen <[email protected]>
Co-authored-by: Deeka Wong <[email protected]>
  • Loading branch information
3 people authored Feb 15, 2023
1 parent a3b7458 commit 9b82b75
Show file tree
Hide file tree
Showing 6 changed files with 187 additions and 4 deletions.
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
"hyperf/db-connection": "~3.0.0",
"hyperf/di": "~3.0.0",
"hyperf/engine": "^1.3|^2.0",
"hyperf/etcd": "~3.0.0",
"hyperf/filesystem": "~3.0.0",
"hyperf/framework": "~3.0.0",
"hyperf/grpc-server": "~3.0.0",
Expand Down Expand Up @@ -57,7 +56,9 @@
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^3.0",
"hyperf/etcd": "~3.0.0",
"hyperf/ide-helper": "~3.0.0",
"hyperf/nacos": "~3.0.0",
"mockery/mockery": "^1.0",
"phpstan/phpstan": "^1.0",
"phpunit/phpunit": "^9.5",
Expand Down
6 changes: 5 additions & 1 deletion src/confd/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,14 @@ The confd component for Hyperf.

```shell
composer require friendsofhyperf/confd
composer require friendsofhyperf/etcd
# or
composer require friendsofhyperf/nacos
```

## Command

Fetch configs from etcd/consul and upgrade `.env`.
Fetch configs from etcd/nacos and upgrade `.env`.

```shell
php bin/hyperf.php confd:env
Expand Down Expand Up @@ -62,6 +65,7 @@ class ConfigChangedListener implements ListenerInterface
## Support

- [x] Etcd
- [x] Nacos
- [ ] Consul

## Sponsor
Expand Down
5 changes: 4 additions & 1 deletion src/confd/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
"hyperf/command": "~3.0.0",
"hyperf/config-center": "~3.0.0",
"hyperf/di": "~3.0.0",
"hyperf/etcd": "~3.0.0",
"hyperf/event": "~3.0.0",
"hyperf/framework": "~3.0.0",
"hyperf/utils": "~3.0.0"
Expand All @@ -29,5 +28,9 @@
"hyperf": {
"config": "FriendsOfHyperf\\Confd\\ConfigProvider"
}
},
"suggest": {
"hyperf/nacos": "For nacos driver.",
"hyperf/etcd": "For etcd driver."
}
}
32 changes: 32 additions & 0 deletions src/confd/publish/confd.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,38 @@
'/test/foo',
],
],
'nacos' => [
'driver' => \FriendsOfHyperf\Confd\Driver\Nacos::class,
'client' => [
'host' => '127.0.0.1',
'port' => 8848,
'username' => 'nacos',
'password' => 'nacos',
'guzzle' => [
'config' => ['timeout' => 3, 'connect_timeout' => 1],
],
],
'listener_config' => [
'mysql' => [
'tenant' => 'framework',
'data_id' => 'mysql',
'group' => 'DEFAULT_GROUP',
'type' => 'json',
],
],
'mapping' => [
'mysql' => ['charset' => 'DB_CHARSET'],
'redis' => ['port' => 'REDIS_PORT'],
],
'watches' => [
'test' => [
'tenant' => 'framework',
'data_id' => 'test',
'group' => 'DEFAULT_GROUP',
'type' => 'text',
],
],
],
],

'env_path' => BASE_PATH . '/.env',
Expand Down
2 changes: 1 addition & 1 deletion src/confd/src/Driver/Etcd.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public function getChanges(): array
->filter(fn ($kv) => in_array($kv['key'], $watches))
->mapWithKeys(fn ($kv) => [$kv['key'] => $kv['value']])
->toArray();
$changes = array_diff($values, $this->origins);
$changes = array_diff_assoc($values, $this->origins);

if (! $this->origins) { // Return [] when first run.
return tap([], fn () => $this->origins = $values);
Expand Down
143 changes: 143 additions & 0 deletions src/confd/src/Driver/Nacos.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
<?php

declare(strict_types=1);
/**
* This file is part of friendsofhyperf/components.
*
* @link https://github.com/friendsofhyperf/components
* @document https://github.com/friendsofhyperf/components/blob/3.x/README.md
* @contact [email protected]
*/
namespace FriendsOfHyperf\Confd\Driver;

use Hyperf\Contract\ConfigInterface;
use Hyperf\Contract\StdoutLoggerInterface;
use Hyperf\Nacos\Application;
use Hyperf\Nacos\Config;
use Hyperf\Utils\Codec\Json;
use Hyperf\Utils\Codec\Xml;
use Psr\Container\ContainerInterface;

class Nacos implements DriverInterface
{
private Application $client;

private array $origins = [];

public function __construct(private ContainerInterface $container, private ConfigInterface $config, private StdoutLoggerInterface $logger)
{
$this->client = make(Application::class, [
'config' => $this->pendingNacosConfig(),
]);
}

/**
* get all listener config from nacos server.
*/
public function fetch(): array
{
$listener = $this->config->get('confd.drivers.nacos.listener_config', []);
$mapping = (array) $this->config->get('confd.drivers.nacos.mapping', []);

$config = [];
foreach ($listener as $key => $item) {
$config = collect($this->pullConfig($item))
->filter(fn ($item, $k) => isset($mapping[$key][$k]))
->mapWithKeys(fn ($item, $k) => [$mapping[$key][$k] => is_array($item) ? implode(',', $item) : $item])
->merge($config)
->toArray();
}
return $config;
}

/**
* check watch config is chaged.
* @throws \GuzzleHttp\Exception\GuzzleException
* @throws \Hyperf\Utils\Exception\InvalidArgumentException
*/
public function getChanges(): array
{
$watches = (array) $this->config->get('confd.drivers.nacos.watches', []);

$config = [];
foreach ($watches as $item) {
$configKey = sprintf('%s.%s.%s', $item['group'], $item['tenant'] ?? 'default', $item['data_id']);
$config = collect($this->pullConfig($item))
->mapWithKeys(fn ($value) => [$configKey => is_array($value) ? implode(',', $value) : $value])
->merge($config)
->toArray();
}

if (! $this->origins) { // Return [] when first run.
return tap([], fn () => $this->origins = $config);
}

$changes = array_diff_assoc($config, $this->origins);

return tap($changes, function ($changes) use ($config) {
if ($changes) {
$this->logger->debug('[confd#nacos] Config changed.');
}

$this->origins = $config;
});
}

protected function decode(string $body, ?string $type = null): mixed
{
$type = strtolower((string) $type);
switch ($type) {
case 'json':
return Json::decode($body);
case 'yml':
case 'yaml':
return yaml_parse($body);
case 'xml':
return Xml::toArray($body);
default:
return $body;
}
}

/**
* get nacos client config from confd setting.
*/
protected function pendingNacosConfig(): Config
{
$clientConfig = $this->config->get('confd.drivers.nacos.client', []);

if (! empty($clientConfig['uri'])) {
$baseUri = $clientConfig['uri'];
} else {
$baseUri = sprintf('http://%s:%d', $clientConfig['host'] ?? '127.0.0.1', $clientConfig['port'] ?? 8848);
}

return new Config([
'base_uri' => $baseUri,
'username' => $clientConfig['username'] ?? null,
'password' => $clientConfig['password'] ?? null,
'guzzle_config' => $clientConfig['guzzle']['config'] ?? null,
]);
}

/**
* pull fresh config from nacos server.
* @throws \GuzzleHttp\Exception\GuzzleException
* @throws \Hyperf\Utils\Exception\InvalidArgumentException
*/
protected function pullConfig(array $listenerConfig): array|string
{
$dataId = $listenerConfig['data_id'];
$group = $listenerConfig['group'];
$tenant = $listenerConfig['tenant'] ?? null;
$type = $listenerConfig['type'] ?? null;
$response = $this->client->config->get($dataId, $group, $tenant);

if ($response->getStatusCode() !== 200) {
$this->logger->error(sprintf('The config of %s.%s.%s read failed from Nacos.', $group, $tenant, $dataId));
return [];
}

return $this->decode((string) $response->getBody(), $type);
}
}

0 comments on commit 9b82b75

Please sign in to comment.