perf: 增加remote module 请求转发模式

This commit is contained in:
daodao97
2020-06-19 14:49:25 +08:00
parent 46b41b5711
commit 0d5613a33e
9 changed files with 171 additions and 14 deletions

View File

@@ -4,4 +4,11 @@
作业必须基于`App\Util\CronCenter\ClassJobAbstract`, 或`App/Util/CronCenter/CommandJobAbstract.php`抽象类进行实现, 才可进行执行状态的跟踪 作业必须基于`App\Util\CronCenter\ClassJobAbstract`, 或`App/Util/CronCenter/CommandJobAbstract.php`抽象类进行实现, 才可进行执行状态的跟踪
`CronCenter`的实现基于`hyperf-crontab`进行实现, 具体代码在`app/Util/CronCenter`, 更多细节可查看[文档](https://hyperf.wiki/#/zh-cn/crontab) `CronCenter`的实现基于`hyperf-crontab`进行实现, 具体代码在`src/CronCenter`, 更多细节可查看[文档](https://hyperf.wiki/#/zh-cn/crontab)
开启`cron-center` `config/autoload/crontab.php`
```php
[
"enable" => true
]
```

View File

@@ -68,4 +68,20 @@ php bin/hyperf.php
然后将相应的菜单添加到后台即可使用. 然后将相应的菜单添加到后台即可使用.
!> 这里的组件菜单, 后期可以优化成配置文件导入的方式, 会更简单些 !> 这里的组件菜单, 后期可以优化成配置文件导入的方式, 会更简单些
业务组件的db依赖问题, 参见 `src/cron-center/src/ConfigProvider.php``databases`
```php
'databases' => [
'config_center' => db_complete([
'host' => env('CONFIG_CENTER_DB_HOST', env('HYPERF_ADMIN_DB_HOST')),
'database' => env('CONFIG_CENTER_DB_NAME', env('HYPERF_ADMIN_DB_NAME')),
'username' => env('CONFIG_CENTER_DB_USER', env('HYPERF_ADMIN_DB_USER')),
'password' => env('CONFIG_CENTER_DB_PWD', env('HYPERF_ADMIN_DB_PWD')),
]),
],
```
组件可以使用自己单独的库配置, 默认使用 `hyperf_amdin` 的主db配置.

View File

@@ -194,11 +194,9 @@ class MenuController extends AdminAbstractController
'permission|权限标识' => [ 'permission|权限标识' => [
'type' => 'select', 'type' => 'select',
'default' => [], 'default' => [],
'options' => function ($field, $data) {
return $this->permission_service->getSystemRouteOptions();
},
'props' => [ 'props' => [
'multiple' => true, 'multiple' => true,
'selectApi' => '/system/routes?module={module}'
], ],
], ],
'pid|上级类目' => [ 'pid|上级类目' => [
@@ -272,7 +270,7 @@ class MenuController extends AdminAbstractController
[ [
'text' => '加子菜单', 'text' => '加子菜单',
'type' => 'form', 'type' => 'form',
'target' => '/menu/form?pid[]={id}&module={tab_id}', 'target' => '/menu/form?pid[]={id}&module={module}',
'formUi' => [ 'formUi' => [
'form' => [ 'form' => [
'labelWidth' => '80px', 'labelWidth' => '80px',
@@ -330,6 +328,7 @@ class MenuController extends AdminAbstractController
'columns' => [ 'columns' => [
['field' => 'id', 'hidden' => true], ['field' => 'id', 'hidden' => true],
['field' => 'pid', 'hidden' => true], ['field' => 'pid', 'hidden' => true],
['field' => 'module', 'hidden' => true],
[ [
'field' => 'label', 'field' => 'label',
'width' => '250px', 'width' => '250px',

View File

@@ -1,10 +1,9 @@
<?php <?php
namespace HyperfAdmin\Admin\Controller; namespace HyperfAdmin\Admin\Controller;
use HyperfAdmin\Admin\Model\ExportTasks; use Hyperf\Utils\Str;
use HyperfAdmin\Admin\Service\CommonConfig; use HyperfAdmin\Admin\Service\CommonConfig;
use HyperfAdmin\Admin\Service\ExportService; use HyperfAdmin\Admin\Service\ModuleProxy;
use HyperfAdmin\BaseUtils\Constants\ErrorCode;
class SystemController extends AdminAbstractController class SystemController extends AdminAbstractController
{ {
@@ -26,4 +25,19 @@ class SystemController extends AdminAbstractController
return $this->success($config); return $this->success($config);
} }
public function routes()
{
$module_proxy = make(ModuleProxy::class);
if ($module_proxy->needProxy()) {
return $this->success($module_proxy->request()['payload']);
}
$kw = $this->request->input('kw', '');
$routes = $this->permission_service->getSystemRouteOptions();
$routes = array_filter($routes, function ($item) use ($kw) {
return Str::contains($item['value'], $kw);
});
return $this->success(array_values($routes));
}
} }

View File

@@ -5,18 +5,25 @@
*/ */
namespace HyperfAdmin\Admin\Middleware; namespace HyperfAdmin\Admin\Middleware;
use FastRoute\Dispatcher;
use Hyperf\HttpServer\Contract\RequestInterface; use Hyperf\HttpServer\Contract\RequestInterface;
use Hyperf\HttpServer\Contract\ResponseInterface as HttpResponse; use Hyperf\HttpServer\Contract\ResponseInterface as HttpResponse;
use Hyperf\HttpServer\CoreMiddleware; use Hyperf\HttpServer\CoreMiddleware;
use Hyperf\HttpServer\Router\Dispatched;
use Hyperf\Logger\LoggerFactory; use Hyperf\Logger\LoggerFactory;
use HyperfAdmin\Admin\Service\AuthService;
use HyperfAdmin\Admin\Service\CommonConfig;
use HyperfAdmin\Admin\Service\ModuleProxy;
use HyperfAdmin\Admin\Service\PermissionService;
use HyperfAdmin\BaseUtils\AKSK;
use HyperfAdmin\BaseUtils\Constants\ErrorCode;
use HyperfAdmin\BaseUtils\Guzzle;
use HyperfAdmin\BaseUtils\Log;
use HyperfAdmin\BaseUtils\Redis\Redis;
use Psr\Container\ContainerInterface; use Psr\Container\ContainerInterface;
use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface; use Psr\Http\Server\RequestHandlerInterface;
use HyperfAdmin\Admin\Service\PermissionService;
use HyperfAdmin\Admin\Service\AuthService;
use HyperfAdmin\BaseUtils\AKSK;
use HyperfAdmin\BaseUtils\Constants\ErrorCode;
class PermissionMiddleware extends CoreMiddleware class PermissionMiddleware extends CoreMiddleware
{ {
@@ -61,6 +68,18 @@ class PermissionMiddleware extends CoreMiddleware
$uri = $request->getUri(); $uri = $request->getUri();
$path = $uri->getPath(); $path = $uri->getPath();
$method = $request->getMethod(); $method = $request->getMethod();
$module_proxy = make(ModuleProxy::class);
if ($module_proxy->needProxy()) {
$res = $module_proxy->request();
if(isset($res['payload']) && $res['payload'] === []) {
$res['payload'] = (object)[];
}
$response = $this->response->json($res);
Log::get('http')->info('proxy_end', ['module' => $module_proxy->getTargetModule(), 'path' => $path, 'response' => $response]);
return $response;
}
// 其他系统调用走AKSK中间件验证 // 其他系统调用走AKSK中间件验证
$client_token = $request->getHeader('Authorization')[0] ?? ''; $client_token = $request->getHeader('Authorization')[0] ?? '';
if ($client_token) { if ($client_token) {

View File

@@ -0,0 +1,52 @@
<?php
namespace HyperfAdmin\Admin\Service;
use HyperfAdmin\BaseUtils\Guzzle;
use HyperfAdmin\BaseUtils\Redis\Redis;
class ModuleProxy
{
protected $request;
protected $modules;
protected $target_module;
public function __construct()
{
$this->request = request();
$this->modules = Redis::getOrSet('hyperf_admin:system_modules', 500, function () {
$list = CommonConfig::getConfigByName('website_config')['value']['system_module'];
array_change_v2k($list, 'name');
return $list;
});
$this->target_module = $this->request->input('module') ?? (request_header('x-module')[0] ?? '');
}
public function needProxy()
{
$no_proxy = request_header('x-no-proxy')[0] ?? false;
if ($no_proxy) {
return false;
}
if (!isset($this->modules[$this->target_module])) {
return false;
}
if ($this->modules[$this->target_module]['type'] != 'remote') {
return false;
}
return true;
}
public function request()
{
return Guzzle::proxy($this->modules[$this->target_module]['remote_base_uri'] . $this->request->getUri()->getPath(), $this->request);
}
public function getTargetModule()
{
return $this->target_module;
}
}

View File

@@ -243,7 +243,8 @@ class PermissionService
} }
} }
$user_open_apis = $this->getOpenResourceList('user_open_api'); $user_open_apis = $this->getOpenResourceList('user_open_api');
return array_merge($resources, $user_open_apis); $system_user_open = config('system.user_open_resource', ['/system/routes']);
return array_merge($resources, $user_open_apis, $system_user_open);
} }
public function getOpenResourceList($field = 'open_api') public function getOpenResourceList($field = 'open_api')

View File

@@ -38,3 +38,4 @@ register_route('/cconf', CommonConfigController::class, function ($controller) {
}); });
Router::get('/system/config', [SystemController::class, 'config']); Router::get('/system/config', [SystemController::class, 'config']);
Router::get('/system/routes', [SystemController::class, 'routes']);

View File

@@ -3,6 +3,8 @@ namespace HyperfAdmin\BaseUtils;
use GuzzleHttp\Client; use GuzzleHttp\Client;
use Hyperf\Guzzle\ClientFactory; use Hyperf\Guzzle\ClientFactory;
use Psr\Http\Message\ServerRequestInterface;
use GuzzleHttp\Cookie\CookieJar;
class Guzzle class Guzzle
{ {
@@ -76,4 +78,50 @@ class Guzzle
return false; return false;
} }
} }
public static function proxy($url, ServerRequestInterface $request)
{
$client = self::create([
'timeout' => 10.0,
]);
$options = [];
$logger = Log::get('module_proxy');
try {
$options['headers']['X-No-Proxy'] = true;
$options['headers'] = array_merge($options['headers'], array_map(function ($item) {
return $item[0];
}, request_header()));
$parse =parse_url($url);
$domain = isset($parse['port']) ? $parse['host'] . ':' . $parse['port'] : $parse['host'];
$options['cookies'] = CookieJar::fromArray(cookie(), $domain);
if ($query = $request->getQueryParams()) {
$options['query'] = $query;
}
if ($body = (array)json_decode($request->getBody()->getContents(), true)) {
$options['body'] = \GuzzleHttp\json_encode($body);
}
if ($form_data = $request->getParsedBody()) {
$options['form_params'] = $form_data;
}
$request = retry(3, function () use ($client, $request, $url, $options) {
return $client->request($request->getMethod(), $url, $options);
}, 50);
$content = $request->getBody()->getContents();
$logger->info('proxy_success', compact('url', 'options'));
return my_json_decode($content);
} catch (\GuzzleHttp\Exception\GuzzleException $e) {
$logger->error('proxy_fail', compact('url', 'options', 'e'));
throw new \Exception("proxy exception {$e}", 500);
} catch (\Throwable $e) {
$logger->error('proxy_fail', compact('url', 'options', 'e'));
throw new \Exception("proxy exception {$e}", 500);
}
}
} }