BaratineのJavaサービスでの非同期の強制

マイクロサービス用のバラチンサーバーは、私が取り組んできた最も珍しいプラットフォームの一つです。 このサーバーの設計は、いくつかの補完的な原則に基づいています。









Baratineのマイクロサービスは、インターフェースによって記述されます。 インターフェイスは、サービスによって提供される操作を定義します。 非同期インターフェイスの機能は、Futureオブジェクトのように、インターフェイスメソッドが非同期で結果を返すことです。







たとえば、クレジットカード支払いトランザクションの使い慣れたインターフェイスは次のようになります。







public interface CreditService { PaymentStatus pay(int amount, CreditCard card); }
      
      





このメソッドは支払い時に結果を返し、それを使用するコードは次のようになります。







 CreditService _creditService; PaymentStatus executePayment(int amount, Client client) { return _creditService.pay(amount, client.getCreditCard()); }
      
      





非同期インターフェイスは結果を返しませんが、Futureオブジェクトを非同期的に入力します。







 public interface CreditService { void pay(int amount, CreditCard card, Result<PaymentStatus> result); }
      
      





このようなインターフェイスのユーザーコードは次のようになります。







 CreditService _creditService; void executePayment(int amount, Client client, Result<PaymentStatus> result) { return _creditService.pay(amount, client.getCreditCard(), result.then()); }
      
      





このクライアントコードの機能は、コードがresult.then()を使用してFutureオブジェクトを最終的な支払いサービスに渡すことです。







クライアントが結果をさらに処理する必要がある場合、結果の入力時に呼び出されるラムダを使用できます。







  void executePayment(int amount, Client client, Result<PaymentStatus> result) { _creditService.pay(amount, client.getCreditCard(), result.then( status -> { log(status); return status; } )); }
      
      





一見したところ、非同期インターフェースはあまり便利ではないように思えるかもしれませんが、このようなコードの編成により、スレッドを迅速に解放して次のタスクを実行でき、クライアントは準備ができたら結果を取得できます。







単一のスレッドでサービス呼び出しを行う



Baratineのマイクロサービスは、このサービスに割り当てられた1つのスレッドで実行されます。 フローは、コールが発生するとすぐにサービスに割り当てられます。 一般的に、サービスへの呼び出しは多くのクライアントから来ます。 呼び出しはキューに入れられ、1つの専用スレッドによって順番に実行されます。







このコンテキストでは、操作の完了を待機しているスレッドを占有しないようにサービスを記述する必要があることに注意してください。 これには、io.baratine.service.ResultタイプのFutureオブジェクトが使用されます(上記を参照)。 これらにより、高価なブロッキング操作の呼び出しの結果の処理を将来に転送できます。







たとえば、小切手アカウントを使用した支払いには数時間かかる場合があり、カスタムの支払い開始コードはミリ秒の何分の一かでリアルタイムに実行されます。







 CheckingService _checkingService = ...; void executePayment(int amount, Client client, Result<PaymentStatus> result) { _checkingPayment.pay(amount, client.getCheckingAccInfo(), result.then( status-> { log(status); if (status.isAppoved()) { shipGoods(); } else { handleFailedPayment(status); } } )); ); }
      
      





上記のコードでは、then()呼び出しのラムダの実行は、_checkingServiceが支払い結果を返すまで遅延され、executePayment()メソッドは次の呼び出しですぐに使用可能になります。







シングルスレッドで実行すると、コンテキストスイッチの数が減り、プロセッサキャッシュとの一貫性が向上するため、パフォーマンスにプラスの効果があります。







分割されていないデータの所有権



マスターコピーへの書き込みアクセスの所有は、 Baratineのマイクロサービスアーキテクチャの特徴の1つです。 マイクロサービスは呼び出しを並列ではなく順次処理するため、データはサービスの単一インスタンスのメモリに保存でき、常に最後の最も関連性の高いデータのコピーになります。







(この場合、「コピー」という言葉の使用は完全に適切ではなく、慣用的に使用されます)。







マイクロデータサービスのライフサイクルは延長されており、サービスが使用される前に、Baratineは@OnLoadアノテーションでサービスメソッドを実行するか、Baratineの一部である非同期オブジェクトデータベース(Kraken)からインスタンスフィールドをロードします。







データに裏打ちされたマイクロサービスは、次のようにシステムのユーザーを表すことができます。







 @Asset public class User { @Id private IdAsset _id; private UserData _data; }
      
      





上記のコードでは、ユーザーデータを含むUserDataインスタンスがKrakenからロードされます。







非同期Web



非同期サービスとの速度とより良いインターフェイスを実現するために、非同期の原則によりWeb要求の実行が抑制されました。 これは、Future応答オブジェクトを使用して実現されます。







io.baratine.service.Resultのようなio.baratine.web.RequestWebは、応答のデータが準備できるまで応答の入力を延期する機能を提供します。







たとえば、RESTプロトコルを使用したリクエストのコードは次のようになります。







 @Service public class QuoteRestService { QuoteService _quotes; @Get public void quote(@Query("symbol") String symbol, RequestWeb requestWeb) { _quotes.quote(symbol, requestWeb.then(quote -> quote)); } }
      
      





上記のコードでは、quote()メソッドがGetアノテーションでマークされており、これによりメソッドがWeb要求で使用可能になります。 Baratineプラットフォームでは、サービスの単一インスタンスが、このサービスに割り当てられた単一スレッド内のすべての着信要求に応答します。 このようなアーキテクチャでは、要求に応じて操作をQuotes(QuoteService)に責任のあるサービスに委任することで、quote()メソッドからすばやく戻ることでパフォーマンスが達成されます。







非同期サービス実行プラットフォーム



プラットフォームで作業する過程で、プラットフォームコンポーネントへの非同期性の広がりの傾向が自然に結晶化し始めました。 したがって、プラットフォームが提供するすべての組み込みサービスは非同期です。







したがって、システムの開発の結果として、データベースサービス(Kraken)、スケジューリング、イベント、パイプ、Web; そして、彼らはすべて重力の規則を非同期に修復しました。







このシステムの開発者の1人として、バラチンに関するhabrコミュニティの意見を知りたいと思います。








All Articles