従来、マイクロサービスのコレクションの前に、追加のレイヤーが提案されています-いわゆるAPIゲートウェイは、いくつかの問題を一度に解決します(それらは後でリストされます)。 この記事の執筆時点では、このようなゲートウェイのオープンソース実装はほとんどないため、Lumenマイクロフレームワーク(Laravelの一部)を使用してPHPで独自に記述することにしました。
この記事では、このタスクが最新のPHPにとっていかに簡単かを示します。
ゲートウェイAPIとは何ですか?
要するに、 APIゲートウェイは、ユーザーと任意の数のサービス(API)の間のスマートプロキシサーバーであるため、名前です。
この層の必要性は、マイクロサービスパターンに切り替えるとすぐに現れます。
- 単一のアドレスは、数百(Netflixには600以上)の個々のAPIアドレスよりもはるかに便利です。
- 「入力」の1か所でユーザーデータ(トークン)をチェックすることは論理的です。
- 単一の場所でリクエストの数に制限を実装すると便利です。
- システム全体がより柔軟になります-少なくとも毎日、内部構造を変更できます。 APIの古いバージョンのサポートは簡単になりつつあります。
- 応答をキャッシュまたは変更できます。
- ユーザー(またはフロントエンド開発者)の便宜のために、異なるサービスからの応答を組み合わせることができます。 Facebookは長い間この機能を提供してきました。
さらに利点があります-これらは、10〜20秒で思い浮かんだものです。
Nginxは、マイクロサービスとAPIゲートウェイ専用の優れた無料の電子書籍をリリースしました。このパターンに興味のあるすべての人に読むことをお勧めします。
既存のオプション
- Umbrella API 、Lua;
- コング 、ルア;
- AWS API Gatewayは、Amazonの有料サービスです。
上で言ったように、選択肢は非常に少なく、比較的最近登場しました。 それらにはまだ多くの機会がありません。
なぜPHPとLumenなのか?
バージョン7のリリースで、PHPは高性能になり、LaravelやSymfonyなどのフレームワークの出現により、PHPは美しく機能することが世界に証明されました。 Laravelの「クリーン」な高速バージョンであるLumenは、フルスタックアプリケーションのセッション、テンプレート、またはその他の機能を必要としないため、ここでの使用に最適です。
さらに、PHPとLumenの経験が豊富であり、作成されたアプリケーションをDockerを介してデプロイする場合、将来のユーザーは作成される言語をまったく気にしません。 これはその役割を果たす単なるレイヤーです!
選択された用語
次のアーキテクチャと対応する用語を提案します。 コードでは、混乱しないようにこれらの用語を厳守します。
ロシア語の「ゲート」はほとんど「ゲートウェイ」であり、世界にはロシア語の名前を持つアプリケーションがないため、アプリケーション自体がVrataを呼び出すことにしました)
「ゲート」のすぐ後ろには、N個のマイクロサービス(Web要求に応答できるAPIサービス)の数があります。 各サービスは任意の数のインスタンスを持つことができるため、APIゲートウェイは、いわゆるサービスレジストリを通じて特定のインスタンスを選択します。
各サービスは一定量のリソースを(REST言語で)提供し、各リソースはいくつかの可能なアクションを持つことができます。 経験豊富なRESTプログラマー向けの非常にシンプルで論理的な構造。
Vrataの要件
コードを開始する前に、将来のアプリケーションの要件をすぐに判断できます。
- ゲートウェイは2016年であり、誰もが水平方向に拡張したいため、水平方向に拡張する必要があります。 したがって、アプリケーションの状態はありません。
- ゲートウェイは、リクエストを結合し、マイクロサービスを非同期的に起動できる必要があります。
- ゲートウェイは、時間間隔ごとの要求の数を制限できる必要があります。
- ゲートウェイは、認証トークンの信頼性を検証できる必要があります。 APIゲートウェイが認証を実行し、その下に隠されたマイクロサービスがリソースに対して許可を実行することが伝統的に提案されています。
- ゲートウェイは、利用可能なリソースをマイクロサービスから自動的にインポートできる必要があります。 まず、今日世界で最も人気のあるものとしてSwagger形式を選択します。
- ゲートウェイは、マイクロサービスの応答を変更(変更)できる必要があります。
- 最後に、ゲートウェイはDockerイメージから直接正常に実行され、環境変数を使用して構成する必要があります。 追加のリポジトリ、展開スクリプトなどは必要ありません!
ほとんどの項目はすでに機能しており、それらを実装するのは非常に簡単でした。 結局のところ、彼らは真実を語っています-私たちはプログラマーにとって最高の時代に生きています!
実装
認証
私はほとんどこの方向で作業する必要はありませんでした-Laravel Passport for Lumenを修正するだけで十分であり、JWTを含むすべての最新のOAuth2機能のサポートを得ました。 私の小さなパッケージポートはGitHub / Packagistで公開されており、誰かが既にインストールしています。
ルートとコントローラー
マイクロサービスからの下位ルートはすべて、JSON形式の構成ファイルからVrataにインポートされます。 起動時に、これらのルートがサービスプロバイダーに追加されます。
// – $registry = $this->app->make(RouteRegistry::class); // Lumen , $registry->bind(app());
一方、ルートデータベースでは:
/** * @param Application $app */ public function bind(Application $app) { // - Lumen // // middleware OAuth2, $this->getRoutes()->each(function ($route) use ($app) { $method = strtolower($route->getMethod()); $app->{$method}($route->getPath(), [ 'uses' => 'App\Http\Controllers\GatewayController@' . $method, 'middleware' => [ 'auth', 'helper:' . $route->getId() ] ]); }); }
現在、マイクロサービスからの各パブリック(および構成で許可されている)ルートは、APIゲートウェイへのルートに対応しています。 さらに、このゲートウェイにのみ存在する合成クエリまたは結合クエリも追加されました。 すべてのリクエストは同じコントローラーに送られます:
これは、コントローラーがGET要求を処理する方法です。
/** * @param Request $request * @param RestClient $client * @return Response */ public function get(Request $request, RestClient $client) { // , - $parametersJar = $request->getRouteParams(); // N $output = $this->actions->reduce(function($carry, $batch) use (&$parametersJar, $client) { // N $responses = $client->asyncRequest($batch, $parametersJar); // $parametersJar = array_merge($parametersJar, $responses->exportParameters()); // - array reduce return array_merge($carry, $responses->getResponses()->toArray()); }, []); // . JSON return $this->presenter->format($this->rearrangeKeys($output), 200); }
GuzzleがHTTPクライアントとして選択されました。これは非同期リクエストに完全に対応し、統合テスト用の既製のツールも備えています。
複合クエリ
複雑な複合クエリはすでに機能しています-これは、異なるマイクロサービス上の任意の数のルートがゲートウェイ上の1つのルートに対応する場合です。 これが実際の例です:
// Boolean-, 'aggregate' => true, 'method' => 'GET', // , "jar" 'path' => '/v2/devices/{mac}/extended', // 'actions' => [ 'device' => [ // 'service' => 'core', 'method' => 'GET', 'path' => 'devices/{mac}', // 'sequence' => 0, // - 'critical' => true ], 'ping' => [ 'service' => 'history', // 'output_key' => false, 'method' => 'POST', 'path' => 'ping/{mac}', 'sequence' => 0, 'critical' => false ], 'settings' => [ 'service' => 'core', // JSON- 'output_key' => 'network.settings', 'method' => 'GET', // , 'device' 'path' => 'networks/{device%network_id}', 'sequence' => 1, 'critical' => false ] ]
ご覧のとおり、複雑なルートはすでに利用可能であり、優れた機能セットを備えています-重要なルートを選択したり、並列リクエストを作成したり、あるサービスの応答を別のサービスへのリクエストで使用したりできます。 とりわけ、出力は優れたパフォーマンスです-合計応答を受信するのにわずか56ミリ秒(Lumenと3つのバックグラウンドクエリ、すべてのデータベースを持つマイクロサービスをロードする)。
サービスレジストリ
これはこれまでで最も弱い部分です-非常に簡単な方法が1つだけ実装されています:DNS。 その原始性にもかかわらず、プロバイダー自体がサービスのグループを監視し、DNSレコードを動的に編集するDocker CloudやAWSなどの環境で正常に機能します。
現時点では、Vrataはサービスのホスト名を取得するだけで、クラウドか物理コンピューターかを詳しく調べることはありません。 おそらく今日の最も人気のあるレジストリはConsulであり、以下を追加する価値があります。
レジストリの本質は非常にシンプルです。サービスのライブインスタンスとデッドインスタンスのテーブルを保存し、必要に応じて特定のインスタンスのアドレスを提供する必要があります。 AWSとDocker Cloud(および他の多く)がこれを行うことができ、常に機能する1つの「魔法の」ホスト名を提供します。
Dockerイメージ
マイクロサービスについては、ここ数年で最もホットなテクノロジーの1つであるDockerに言及する必要があります。 原則として、マイクロサービスはDockerイメージとまったく同じようにテストおよびデプロイされます。これは標準的な手法になっているため、Docker Hubでパブリックイメージをすばやく準備しました。
OS X、Windows、またはLinuxマシンのターミナルに1つのコマンドを入力すると、Vrataゲートウェイが機能します。
$ docker run -d -e GATEWAY_SERVICES=... -e GATEWAY_GLOBAL=... -e GATEWAY_ROUTES=... pwred/vrata
設定全体は、JSON形式の環境変数で渡すことができます。
あとがき
アプリケーション(ゲートウェイ)は、私が働いている会社ですでに実際に使用されています。 GitHubのリポジトリ内のすべてのコード。 誰でも開発に参加したい場合-どういたしまして:)
理論と実装の両方での複合クエリは、FacebookのGraphQLクエリ形式(RESTとは対照的)に非常に似ているため、将来の優先機能の1つはGraphQLクエリのサポートです。