Merge pull request #2331 from tw2066/master

Optimized Nacos component.
This commit is contained in:
李铭昕 2020-08-26 17:19:49 +08:00 committed by GitHub
commit 339ecd9e2e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 218 additions and 29 deletions

View File

@ -1,5 +1,15 @@
# v2.0.9 - TBD
## Added
- [#2331](https://github.com/hyperf/hyperf/pull/2331) Added auth api for nacos.
- [#2331](https://github.com/hyperf/hyperf/pull/2331) Added config nacos.enable to control the nacos component.
- [#2331](https://github.com/hyperf/hyperf/pull/2331) Added array merge mode for nacos.
## Fixed
- [#2331](https://github.com/hyperf/hyperf/pull/2331) Fixed exception thrown when the service or config was not found for nacos.
# v2.0.8 - 2020-08-24
## Added

View File

@ -9,10 +9,17 @@ declare(strict_types=1);
* @contact group@hyperf.io
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
use Hyperf\Nacos\Constants;
return [
'enable' => true,
// The nacos host info
'host' => '127.0.0.1',
'port' => 8848,
// The nacos account info
'username' => null,
'password' => null,
'config_merge_mode' => Constants::CONFIG_MERGE_OVERWRITE,
// The service info.
'service' => [
'service_name' => 'hyperf',

View File

@ -15,10 +15,12 @@ use GuzzleHttp\Client;
use GuzzleHttp\RequestOptions;
use Hyperf\Contract\ConfigInterface;
use Hyperf\Contract\ContainerInterface;
use Hyperf\Guzzle\ClientFactory;
use Hyperf\Guzzle\CoroutineHandler;
abstract class AbstractNacos
{
use AccessToken;
/**
* @var ContainerInterface
*/
@ -29,14 +31,22 @@ abstract class AbstractNacos
*/
protected $config;
/**
* @var callable
*/
protected $handler;
public function __construct(ContainerInterface $container)
{
$this->container = $container;
$this->config = $container->get(ConfigInterface::class);
$this->handler = new CoroutineHandler();
}
public function request($method, $uri, array $options = [])
{
$token = $this->getAccessToken();
$token && $options[RequestOptions::QUERY]['accessToken'] = $token;
return $this->client()->request($method, $uri, $options);
}
@ -51,10 +61,10 @@ abstract class AbstractNacos
public function client(): Client
{
$factory = $this->container->get(ClientFactory::class);
$headers['charset'] = $headers['charset'] ?? 'UTF-8';
return $factory->create([
return new Client([
'base_uri' => $this->getServerUri(),
'handler' => $this->handler,
RequestOptions::HEADERS => [
'charset' => 'UTF-8',
],

View File

@ -0,0 +1,48 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://hyperf.wiki
* @contact group@hyperf.io
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
namespace Hyperf\Nacos\Api;
trait AccessToken
{
/**
* @var null|string
*/
private $accessToken;
/**
* @var int
*/
private $expireTime = 0;
public function getAccessToken(): ?string
{
$username = $this->config->get('nacos.username');
$password = $this->config->get('nacos.password');
if ($username === null || $password === null) {
return null;
}
if (isset($this->accessToken) && $this->expireTime > time() + 60) {
return $this->accessToken;
}
$api = $this->container->get(NacosAuth::class);
$result = $api->login($username, $password);
$this->accessToken = $result['accessToken'];
$this->expireTime = $result['tokenTtl'] + time();
return $this->accessToken;
}
}

View File

@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://hyperf.wiki
* @contact group@hyperf.io
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
namespace Hyperf\Nacos\Api;
use GuzzleHttp\RequestOptions;
use Hyperf\Nacos\Exception\RequestException;
use Hyperf\Utils\Codec\Json;
class NacosAuth extends AbstractNacos
{
public function login(string $username, string $password): array
{
$response = $this->client()->request('POST', '/nacos/v1/auth/users/login', [
RequestOptions::QUERY => [
'username' => $username,
'password' => $password,
],
]);
$statusCode = $response->getStatusCode();
$contents = $response->getBody()->getContents();
if ($statusCode !== 200) {
throw new RequestException($contents, $statusCode);
}
return Json::decode($contents);
}
}

View File

@ -23,7 +23,13 @@ class NacosConfig extends AbstractNacos
RequestOptions::QUERY => $configModel->toArray(),
]);
return $configModel->parse($response->getBody()->getContents());
$statusCode = $response->getStatusCode();
$contents = $response->getBody()->getContents();
if ($statusCode !== 200) {
return [];
}
return $configModel->parse($contents);
}
public function set(ConfigModel $configModel): array

View File

@ -77,7 +77,7 @@ class NacosInstance extends AbstractNacos
return false;
}
$enabled = array_filter($instance, function ($item) {
return $item['enabled'];
return $item['enabled'] && $item['healthy'];
});
$tactics = strtolower($this->config->get('nacos.load_balancer', 'random'));

View File

@ -12,8 +12,10 @@ declare(strict_types=1);
namespace Hyperf\Nacos\Api;
use GuzzleHttp\RequestOptions;
use Hyperf\Nacos\Exception\RequestException;
use Hyperf\Nacos\Model\ServiceModel;
use Hyperf\Utils\Codec\Json;
use Hyperf\Utils\Str;
class NacosService extends AbstractNacos
{
@ -44,13 +46,23 @@ class NacosService extends AbstractNacos
return $response->getBody()->getContents() === 'ok';
}
public function detail(ServiceModel $serviceModel): array
public function detail(ServiceModel $serviceModel): ?array
{
$response = $this->request('GET', '/nacos/v1/ns/service', [
RequestOptions::QUERY => $serviceModel->toArray(),
]);
return Json::decode($response->getBody()->getContents());
$statusCode = $response->getStatusCode();
$contents = $response->getBody()->getContents();
if ($statusCode !== 200) {
if (Str::contains($contents, 'is not found')) {
return null;
}
throw new RequestException($contents, $statusCode);
}
return Json::decode($contents);
}
public function list(int $pageNo, int $pageSize = 10, ?string $groupName = null, ?string $namespaceId = null): array

View File

@ -84,6 +84,8 @@ class FetchConfigProcess extends AbstractProcess
public function isEnable($server): bool
{
$config = $this->container->get(ConfigInterface::class);
return $server instanceof Server && (bool) $config->get('nacos.config_reload_interval', false);
return $server instanceof Server
&& $config->get('nacos.enable', true)
&& (bool) $config->get('nacos.config_reload_interval', false);
}
}

View File

@ -14,7 +14,9 @@ namespace Hyperf\Nacos\Config;
use Hyperf\Contract\ConfigInterface;
use Hyperf\Event\Contract\ListenerInterface;
use Hyperf\Framework\Event\OnPipeMessage;
use Hyperf\Nacos\Constants;
use Hyperf\Process\Event\PipeMessage as UserProcessPipMessage;
use Hyperf\Utils\Arr;
use Psr\Container\ContainerInterface;
class OnPipeMessageListener implements ListenerInterface
@ -49,7 +51,11 @@ class OnPipeMessageListener implements ListenerInterface
if (property_exists($event, 'data') && $event->data instanceof PipeMessage) {
$root = $this->config->get('nacos.config_append_node');
foreach ($event->data->configurations ?? [] as $key => $conf) {
$this->config->set($root ? $root . '.' . $key : $key, $conf);
$configKey = $root ? $root . '.' . $key : $key;
if (is_array($conf) && $this->config->get('nacos.config_merge_mode') == Constants::CONFIG_MERGE_APPEND) {
$conf = Arr::merge($this->config->get($configKey, []), $conf);
}
$this->config->set($configKey, $conf);
}
}
}

View File

@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://hyperf.wiki
* @contact group@hyperf.io
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
namespace Hyperf\Nacos;
class Constants
{
const CONFIG_MERGE_OVERWRITE = 1;
const CONFIG_MERGE_APPEND = 2;
}

View File

@ -0,0 +1,16 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://hyperf.wiki
* @contact group@hyperf.io
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
namespace Hyperf\Nacos\Exception;
class RequestException extends RuntimeException
{
}

View File

@ -18,9 +18,11 @@ use Hyperf\Framework\Event\MainWorkerStart;
use Hyperf\Nacos\Api\NacosInstance;
use Hyperf\Nacos\Api\NacosService;
use Hyperf\Nacos\Client;
use Hyperf\Nacos\Constants;
use Hyperf\Nacos\Exception\RuntimeException;
use Hyperf\Nacos\Instance;
use Hyperf\Nacos\Service;
use Hyperf\Utils\Arr;
use Psr\Container\ContainerInterface;
class MainWorkerStartListener implements ListenerInterface
@ -55,27 +57,39 @@ class MainWorkerStartListener implements ListenerInterface
if (! $config->get('nacos')) {
return;
}
$nacosService = $this->container->get(NacosService::class);
$service = $this->container->get(Service::class);
$exist = $nacosService->detail($service);
if (! $exist && ! $nacosService->create($service)) {
throw new RuntimeException(sprintf('nacos register service fail: %s', $service));
if (! $config->get('nacos.enable', true)) {
return;
}
$this->logger->info('nacos register service success.', compact('service'));
$instance = $this->container->get(Instance::class);
$nacosInstance = $this->container->get(NacosInstance::class);
if (! $nacosInstance->register($instance)) {
throw new RuntimeException(sprintf('nacos register instance fail: %s', $instance));
}
$this->logger->info('nacos register instance success.', compact('instance'));
try {
$nacosService = $this->container->get(NacosService::class);
$service = $this->container->get(Service::class);
$exist = $nacosService->detail($service);
if (! $exist && ! $nacosService->create($service)) {
throw new RuntimeException(sprintf('nacos register service fail: %s', $service));
}
$this->logger->info('nacos register service success.', compact('service'));
$client = $this->container->get(Client::class);
$config = $this->container->get(ConfigInterface::class);
$appendNode = $config->get('nacos.config_append_node');
foreach ($client->pull() as $key => $conf) {
$config->set($appendNode ? $appendNode . '.' . $key : $key, $conf);
$instance = $this->container->get(Instance::class);
$nacosInstance = $this->container->get(NacosInstance::class);
if (! $nacosInstance->register($instance)) {
throw new RuntimeException(sprintf('nacos register instance fail: %s', $instance));
}
$this->logger->info('nacos register instance success.', compact('instance'));
$client = $this->container->get(Client::class);
$config = $this->container->get(ConfigInterface::class);
$appendNode = $config->get('nacos.config_append_node');
foreach ($client->pull() as $key => $conf) {
$configKey = $appendNode ? $appendNode . '.' . $key : $key;
if (is_array($conf) && $config->get('nacos.config_merge_mode') == Constants::CONFIG_MERGE_APPEND) {
$conf = Arr::merge($config->get($configKey, []), $conf);
}
$config->set($configKey, $conf);
}
} catch (\Throwable $exception) {
$this->logger->critical((string) $exception);
}
}
}

View File

@ -43,6 +43,9 @@ class OnShutdownListener implements ListenerInterface
public function process(object $event)
{
$config = $this->container->get(ConfigInterface::class);
if (! $config->get('nacos.enable', true)) {
return;
}
if (! $config->get('nacos.remove_node_when_server_shutdown', false)) {
return;
}

View File

@ -37,7 +37,7 @@ class InstanceBeatProcess extends AbstractProcess
sleep($config->get('nacos.client.beat_interval', 5));
$send = $nacosInstance->beat($service, $instance);
if ($send) {
$logger && $logger->info('nacos send beat success!', compact('instance'));
$logger && $logger->debug('nacos send beat success!', compact('instance'));
} else {
$logger && $logger->error('nacos send beat fail!', compact('instance'));
}
@ -47,6 +47,6 @@ class InstanceBeatProcess extends AbstractProcess
public function isEnable($server): bool
{
$config = $this->container->get(ConfigInterface::class);
return $config->get('nacos.client.beat_enable', false);
return $config->get('nacos.enable', true) && $config->get('nacos.client.beat_enable', false);
}
}