lushdigital / microservice-aggregator-transport
一套方便的类和接口,用于简化从多个微服务聚合数据的操作。
Requires
- guzzlehttp/guzzle: ^6.2
- laravel/lumen-framework: 5.3.*
Requires (Dev)
- phpunit/phpunit: ~5.0
README
一套方便的类和接口,用于简化从多个微服务聚合数据的操作。
该包的目的是提供一种可靠、可测试且易于使用的通信方式,用于在面向服务的架构中与微服务进行交互。
包内容
- 服务接口
- 服务抽象类
- 云服务接口
- 云服务抽象类
- 请求类
安装
按正常方式安装该包
$ composer require lushdigital/microservice-aggregator-transport
将 src/config/transport.php 文件复制到您的应用程序根目录下的 config 文件夹中。
最后,将以下行添加到您的 bootstrap/app.php 文件中
$app->configure('transport');
创建服务
要使用此包,您首先需要创建一个与您的服务交互的类。
此类将扩展此包提供的基类之一;您为要访问的服务每个端点添加自己的方法。
本地服务
当您可以通过某种本地DNS与服务通信时,使用“本地”服务。在这种情况下,您不需要调用互联网来访问服务。例如,您可能正在使用 Kubernetes DNS。
在您创建云服务之前,您需要确保以下配置选项已设置(显式或通过环境变量)
transport.branch- CI分支。例如master。transport.environment- CI环境。例如dev或staging。
然后为每个本地服务,您必须定义
transport.services.local.SERVICE_NAME.uri- 本地服务的URI。
您还可以可选地指定服务的版本
transport.services.local.SERVICE_NAME.version- 本地服务的版本。
要创建本地服务,您需要扩展 \LushDigital\MicroserviceAggregatorTransport\Service 类
<?php /** * @file * Contains \App\Services\MyAwesomeService. */ namespace App\Services; use LushDigital\MicroserviceAggregatorTransport\Service as BaseService; use LushDigital\MicroserviceAggregatorTransport\Request; use App\Models\Thing; /** * Transport layer for my awesome service. * * @package App\Services */ class MyAwesomeService extends BaseService { /** * Save a thing. * * @param Thing $thing * The thing to save. * * @return array */ public function saveAThing(Thing $thing) { // Create the request. $request = new Request('things', 'POST', $thing->toArray()); // Do the request. $this->dial($request); $response = $this->call(); return !empty($response->data->things) ? $response->data->things : []; } }
如你所见,在这个服务中我们创建了一个调用
POST端点来保存某个东西的方法。
云服务
当您需要通过互联网与服务通信时,使用云服务。假设服务是通过某种API网关访问的,并且无法直接访问。
在您创建云服务之前,您需要确保以下配置选项已设置(显式或通过环境变量)
transport.branch- CI分支。例如master。transport.domain- 服务环境的顶级域名。transport.gateway_uri- API网关的URI。transport.environment- CI环境。例如dev或staging。
然后为每个云服务,您必须定义
transport.services.cloud.SERVICE_NAME.uri- 云服务的URI。transport.auth.SERVICE_NAME.email- 用于访问云服务的服务账户的电子邮件地址。transport.auth.SERVICE_NAME.password- 用于访问云服务的服务账户的密码。
您还可以可选地指定服务的版本
transport.services.cloud.SERVICE_NAME.version- 云服务的版本。
然后您可以定义您的服务
<?php /** * @file * Contains \App\Services\MyAwesomeService. */ namespace App\Services; use LushDigital\MicroserviceAggregatorTransport\CloudService; use LushDigital\MicroserviceAggregatorTransport\Request; use App\Models\Thing; /** * Transport layer for my awesome cloud service. * * @package App\Services */ class MyAwesomeCloudService extends CloudService { /** * Save a thing. * * @param Thing $thing * The thing to save. * * @return array */ public function saveAThing(Thing $thing) { // Create the request. $request = new Request('things', 'POST', $thing->toArray()); // Do the request. $this->dial($request); $response = $this->call(); return !empty($response->data->things) ? $response->data->things : []; } }
如您所见,服务看起来与本地服务非常相似。唯一的主要区别是基类。基类处理了认证和API网关路由的重任,因此您不必这样做!
使用服务
一旦您创建了您的服务,就可以像使用任何其他PHP类一样使用它。想想看,就像在数据库环境中使用仓库对象一样。
在控制器中的示例用法
<?php /** * @file * Contains \App\Http\Controllers\MyAwesomeController. */ namespace App\Http\Controllers; use App\Models\Thing; use App\Services\MyAwesomeService; use App\Services\MyAwesomeCloudService; use GuzzleHttp\Exception\BadResponseException; use Illuminate\Http\Request; use Illuminate\Http\Response; use Laravel\Lumen\Routing\Controller as BaseController; use LushDigital\MicroserviceAggregatorTransport\ServiceInterface; class MyAwesomeController extends BaseController { /** * Transport layer for my awesome service. * * @var ServiceInterface */ protected $myAwesomeService; /** * Transport layer for my awesome cloud service. * * @var ServiceInterface */ protected $myAwesomeCloudService; /** * MyAwesomeController constructor. */ public function __construct() { $this->myAwesomeService = new MyAwesomeService(); $this->myAwesomeCloudService = new MyAwesomeCloudService(); } /** * Create a new thing. * * @param Request $request * @return Response */ public function storeThing(Request $request) { // Validate the request. $this->validate($request, ['name' => 'required|string']); try { // Prepare a thing. $thing = new Thing; $thing->fill($request->input()); // Save a thing. $newThing = $this->myAwesomeService->saveAThing($thing); return response()->json($newThing, 200); } catch (BadResponseException $e) { return response()->json(null, 500); } } /** * Create a new thing. * * @param Request $request * @return Response */ public function storeCloudThing(Request $request) { // Validate the request. $this->validate($request, ['name' => 'required|string']); try { // Prepare a thing. $thing = new Thing; $thing->fill($request->input()); // Save a thing. $newThing = $this->myAwesomeCloudService->saveAThing($thing); return response()->json($newThing, 200); } catch (BadResponseException $e) { return response()->json(null, 500); } } }
异步请求
在某些情况下,您可能需要快速连续地发出多个请求。这通常可以通过并发运行请求来改进。该包利用Guzzle的承诺来实现这一点。首先,您需要定义一个具有异步调用的服务
<?php /** * @file * Contains \App\Services\MyAwesomeService. */ namespace App\Services; use LushDigital\MicroserviceAggregatorTransport\Service as BaseService; use LushDigital\MicroserviceAggregatorTransport\Request; use GuzzleHttp\Promise\PromiseInterface; use App\Models\Thing; /** * Transport layer for my awesome service. * * @package App\Services */ class MyAwesomeService extends BaseService { /** * Save a thing. * * @param Thing $thing * The thing to save. * @param callable|null $onFulfilled * Function to run on a successful call. * @param callable|null $onRejected * Function to run on a rejected call. * * @return PromiseInterface */ public function saveAsyncThing(Thing $thing, callable $onFulfilled = null, callable $onRejected = null) { // Create the request. $request = new Request('things', 'POST', $thing->toArray()); // Do the request. $this->dial($request); return $this->callAsync($onFulfilled, $onRejected); } }
然后您可以在您的控制器中使用这个服务
<?php /** * @file * Contains \App\Http\Controllers\MyAwesomeController. */ namespace App\Http\Controllers; use App\Models\Thing; use App\Services\MyAwesomeService; use Illuminate\Http\Request; use Illuminate\Http\Response; use Laravel\Lumen\Routing\Controller as BaseController; use LushDigital\MicroserviceAggregatorTransport\ServiceInterface; use GuzzleHttp\Exception\RequestException; use Psr\Http\Message\ResponseInterface; class MyAwesomeController extends BaseController { /** * Transport layer for my awesome service. * * @var ServiceInterface */ protected $myAwesomeService; /** * Transport layer for my awesome cloud service. * * @var ServiceInterface */ protected $myAwesomeCloudService; /** * MyAwesomeController constructor. */ public function __construct() { $this->myAwesomeService = new MyAwesomeService(); } /** * Create a new thing. * * @param Request $request * @return Response */ public function storeThing(Request $request) { // Validate the request. $this->validate($request, ['things.*.name' => 'required|string']); $promises = []; $things = []; foreach ($request->input('things') as $thingData) { // Prepare a thing. $thing = new Thing; $thing->fill($thingData); // Save a thing. $promises[] = $this->myAwesomeService->saveAsyncThing( $thing, function (ResponseInterface $res) use (&$things) { // Get the JSON response or exit if none. $serviceResponse = json_decode($res->getBody()); if (empty($serviceResponse->data)) { return null; } $things[] = $serviceResponse->data->things[0]; }, function (RequestException $e) { Log::error(sprintf('Could not get thing. Reason: %s', $e->getMessage())); return null; } ); } return response()->json($things, 200); } }