リモート データへのアクセス

Note

この電子ブックは 2017 年の春に発行され、それ以来更新されていません。 貴重なままの本に多くがありますが、資料の一部は時代遅れです。

現代の多くの Web ベース ソリューションでは、Web サーバーによりホストされる Web サービスを使用することで、リモート クライアント アプリケーションの機能を提供します。 Web サービスにより公開される操作によって Web API が構成されています。

クライアント アプリでは、API によって公開されるデータや操作がどのように実装されるかがわかっていなくても、Web API を利用できる必要があります。 そのためには、API が共通基準を順守し、クライアント アプリおよび Web サービスが、使用するデータ形式や、クライアント アプリと Web サービスの間で交換されるデータの構造に同意できるようにする必要があります。

Representational State Transfer の概要

Representational State Transfer (REST) は、ハイパーメディアに基づき分散システムを構築するためのアーキテクチャ スタイルです。 REST モデルの主な利点は、オープン スタンダードに基づいており、それにアクセスするモデルやクライアント アプリの実装を、特定の実装に結びつけないという点です。 そのため、MICROSOFT ASP.NET Core MVC を使用して REST Web サービスを実装でき、クライアント アプリは、HTTP 要求を生成して HTTP 応答を解析できる任意の言語とツールセットを使用して開発できます。

REST モデルでは、リソースと呼ばれる、ネットワーク経由のオブジェクトおよびサービスを表すために、ナビゲーション スキームを使用します。 通常、REST を実装するシステムでは、これらのリソースにアクセスするための要求を送信するのに HTTP プロトコルを使用します。 そのようなシステムでは、クライアント アプリにより、リソースを識別する URI、およびそのリソースで実行する操作を示す HTTP メソッド (GET、POST、PUT、DELETE など) の形式で要求が送信されます。 HTTP 要求の本文には、操作を実行するのに必要なデータが含まれています。

注意

REST ではステートレス要求モデルを定義します。 したがって、HTTP 要求は独立している必要があり、任意の順序で発生する可能性があります。

REST 要求からの応答では標準 HTTP 状態コードが使用されます。 たとえば、有効なデータを返す要求には HTTP 応答コード 200 (OK) を含める必要があり、指定したリソースの確認および削除に失敗した要求は HTTP 状態コード 404 (Not Found) を含む応答を返す必要があります。

RESTful Web API では一連の接続されたリソースを公開し、アプリでこれらのリソースを操作してその間を簡単に移動できるようにする主要な操作が提供されます。 このため、一般的な RESTful Web API を構成する URI は、公開するデータ指向であり、このデータを操作するために HTTP によって提供される機能を使用します。

HTTP 要求でクライアント アプリによって含められるデータや、Web サーバーからの対応する応答メッセージは、さまざまな形式 (メディアの種類ともいう) で表示できます。 クライアント アプリは、メッセージの本文でデータを返す要求を送信するときに、要求のヘッダーで処理できるメディアの種類を Accept 指定できます。 Web サーバーがこのメディアの種類をサポートしている場合は、メッセージの本文内のデータの形式を Content-Type 指定するヘッダーを含む応答で応答できます。 応答メッセージを解析し、メッセージ本文内の結果を適切に解釈することはクライアント アプリの役割です。

REST の詳細については、「 API の設計と API実装」を参照してください。

RESTful API の使用

eShopOnContainers モバイル アプリは Model-View-ViewModel (MVVM) パターンを使用し、パターンのモデル要素はアプリで使用されるドメイン エンティティを表します。 eShopOnContainers 参照アプリケーションのコントローラーおよびリポジトリ クラスでは、これらのモデル オブジェクトの多くを受け入れて返します。 そのため、モバイル アプリとコンテナー化されたマイクロサービスの間で渡されるすべてのデータを保持するデータ転送オブジェクト (DTO) として使用されます。 DTO を使用して Web サービスとの間でデータを送受信する主な利点は、1 回のリモート呼び出しでより多くのデータを送信することで、アプリで行う必要があるリモート呼び出しの数を減らせることです。

Web 要求を作成する

eShopOnContainers モバイル アプリでは、 クラスを HttpClient 使用して HTTP 経由で要求を行い、JSON がメディアの種類として使用されます。 このクラスでは、HTTP 要求を非同期に送信し、URI で識別されたリソースから HTTP 応答を受信するための機能が提供されます。 クラスは HttpResponseMessage 、HTTP 要求が行われた後に REST API から受信した HTTP 応答メッセージを表します。 これには、状態コード、ヘッダー、本文など、応答に関する情報が含まれます。 クラスは HttpContent 、 や などの Content-TypeContent-EncodingHTTP 本文とコンテンツ ヘッダーを表します。 コンテンツは、データの形式に応じて、ReadAsStringAsyncReadAsByteArrayAsync などの ReadAs メソッドのいずれかを使用して読み取ることができます。

GET 要求の作成

CatalogService クラスは、カタログ マイクロサービスからのデータ取得プロセスを管理するために使用されます。 クラスの RegisterDependenciesViewModelLocator メソッドでは、 CatalogService クラスは Autofac 依存関係挿入コンテナーを使用して型に ICatalogService 対する型マッピングとして登録されます。 次に、 クラスのインスタンスが CatalogViewModel 作成されると、そのコンストラクターは型を ICatalogService 受け入れます。この型は Autofac によって解決され、 クラスのインスタンスが返されます CatalogService 。 依存関係の挿入の詳細については、「 依存関係の挿入の概要」を参照してください。

図 10-1 は、 によって表示されるカタログ マイクロサービスからカタログ データを読み取るクラスの相互作用を CatalogView示しています。

サービスからのデータの取得

図 10-1: カタログ マイクロサービスからのデータの取得

CatalogView 移動すると、 クラスの OnInitializeCatalogViewModel メソッドが呼び出されます。 このメソッドでは、次のコード例に示すように、カタログ マイクロサービスからカタログ データを取得します。

public override async Task InitializeAsync(object navigationData)  
{  
    ...  
    Products = await _productsService.GetCatalogAsync();  
    ...  
}

このメソッドは、 GetCatalogAsync Autofac によって に挿入されたインスタンスの CatalogService メソッドを CatalogViewModel 呼び出します。 次のコード例は、GetCatalogAsync メソッドを示しています。

public async Task<ObservableCollection<CatalogItem>> GetCatalogAsync()  
{  
    UriBuilder builder = new UriBuilder(GlobalSetting.Instance.CatalogEndpoint);  
    builder.Path = "api/v1/catalog/items";  
    string uri = builder.ToString();  

    CatalogRoot catalog = await _requestProvider.GetAsync<CatalogRoot>(uri);  
    ...  
    return catalog?.Data.ToObservableCollection();            
}

このメソッドでは、要求が送信されるリソースを識別する URI を構築し、RequestProvider クラスを使用してリソースに対して GET HTTP メソッドを呼び出してから、CatalogViewModel に結果を返します。 RequestProvider クラスには、リソースを識別する URI の形式で要求を送信する機能、そのリソースに対して実行する操作を示す HTTP メソッド、および操作を実行するために必要なデータを含む本文が含まれています。 クラスを にCatalogService class挿入するRequestProvider方法の詳細については、「依存関係の挿入の概要」を参照してください。

次のコード例は、RequestProvider クラスの GetAsync メソッドを示しています。

public async Task<TResult> GetAsync<TResult>(string uri, string token = "")  
{  
    HttpClient httpClient = CreateHttpClient(token);  
    HttpResponseMessage response = await httpClient.GetAsync(uri);  

    await HandleResponse(response);  
    string serialized = await response.Content.ReadAsStringAsync();  

    TResult result = await Task.Run(() =>   
        JsonConvert.DeserializeObject<TResult>(serialized, _serializerSettings));  

    return result;  
}

このメソッドでは、適切なヘッダーが設定された HttpClient クラスのインスタンスを返す CreateHttpClient メソッドを呼び出します。 その後、URI によって識別されるリソースに非同期 GET 要求を送信し、応答がインスタンスに HttpResponseMessage 格納されます。 その後、HandleResponse メソッドが呼び出されます。応答に成功 HTTP 状態コードが含まれていない場合は例外がスローされます。 次に、応答が文字列として読み取られ、JSON から CatalogRoot オブジェクトに変換され、CatalogService に返されます。

CreateHttpClient メソッドを次のコード例に示します。

private HttpClient CreateHttpClient(string token = "")  
{  
    var httpClient = new HttpClient();  
    httpClient.DefaultRequestHeaders.Accept.Add(  
        new MediaTypeWithQualityHeaderValue("application/json"));  

    if (!string.IsNullOrEmpty(token))  
    {  
        httpClient.DefaultRequestHeaders.Authorization =   
            new AuthenticationHeaderValue("Bearer", token);  
    }  
    return httpClient;  
}

このメソッドは、 クラスの新しいインスタンスをHttpClient作成し、インスタンスによってHttpClient行われた要求のヘッダーを にapplication/json設定Acceptします。これは、応答の内容が JSON を使用して書式設定されることを想定していることを示します。 その後、アクセス トークンが CreateHttpClient メソッドの引数として渡された場合、HttpClient インスタンスによって行われた要求の Authorization ヘッダーに追加され、文字列 Bearer が付加されます。 承認の詳細については、「承認」を参照してください。

クラスの メソッドが GetAsyncRequestProvider呼び出HttpClient.GetAsyncItemsすと、Catalog.API プロジェクトの CatalogController クラス内の メソッドが呼び出されます。これは、次のコード例に示されています。

[HttpGet]  
[Route("[action]")]  
public async Task<IActionResult> Items(  
    [FromQuery]int pageSize = 10, [FromQuery]int pageIndex = 0)  
{  
    var totalItems = await _catalogContext.CatalogItems  
        .LongCountAsync();  

    var itemsOnPage = await _catalogContext.CatalogItems  
        .OrderBy(c=>c.Name)  
        .Skip(pageSize * pageIndex)  
        .Take(pageSize)  
        .ToListAsync();  

    itemsOnPage = ComposePicUri(itemsOnPage);  
    var model = new PaginatedItemsViewModel<CatalogItem>(  
        pageIndex, pageSize, totalItems, itemsOnPage);             

    return Ok(model);  
}

このメソッドは、EntityFramework を使用して SQL データベースからカタログ データを取得し、成功した HTTP 状態コードと JSON 形式 CatalogItem のインスタンスのコレクションを含む応答メッセージとして返します。

POST 要求の作成

BasketService クラスは、バスケット マイクロサービスでデータの取得と更新のプロセスを管理するために使用されます。 クラスの RegisterDependenciesViewModelLocator メソッドでは、 BasketService クラスは Autofac 依存関係挿入コンテナーを使用して型に IBasketService 対する型マッピングとして登録されます。 次に、 クラスのインスタンスが BasketViewModel 作成されると、そのコンストラクターは型を IBasketService 受け入れます。この型は Autofac によって解決され、 クラスのインスタンスが返されます BasketService 。 依存関係の挿入の詳細については、「 依存関係の挿入の概要」を参照してください。

図 10-2 は、 によって BasketView表示されるバスケット データをバスケット マイクロサービスに送信するクラスの相互作用を示しています。

送信 バスケット マイクロサービスへのデータの送信

図 10-2: バスケット マイクロサービスへのデータの送信

アイテムが買い物かごに追加されると、BasketViewModel クラスの ReCalculateTotalAsync メソッドが呼び出されます。 このメソッドでは、次のコード例に示すように、バスケット内のアイテムの合計値を更新し、バスケット データをバスケット マイクロサービスに送信します。

private async Task ReCalculateTotalAsync()  
{  
    ...  
    await _basketService.UpdateBasketAsync(new CustomerBasket  
    {  
        BuyerId = userInfo.UserId,   
        Items = BasketItems.ToList()  
    }, authToken);  
}

このメソッドは、 UpdateBasketAsync Autofac によって に挿入されたインスタンスの BasketService メソッドを BasketViewModel 呼び出します。 次のメソッドは UpdateBasketAsync メソッドを示しています。

public async Task<CustomerBasket> UpdateBasketAsync(CustomerBasket customerBasket, string token)  
{  
    UriBuilder builder = new UriBuilder(GlobalSetting.Instance.BasketEndpoint);  
    string uri = builder.ToString();  
    var result = await _requestProvider.PostAsync(uri, customerBasket, token);  
    return result;  
}

このメソッドでは、要求が送信されるリソースを識別する URI をビルドし、RequestProvider クラスを使用してリソースに対して POST HTTP メソッドを呼び出してから、BasketViewModel に結果を返します。 認証プロセス中に IdentityServer から取得されたアクセス トークンは、バスケット マイクロサービスへの要求を承認するために必要であることに注意してください。 承認の詳細については、「承認」を参照してください。

次のコード例は、RequestProvider クラスの PostAsync メソッドの 1 つを示しています。

public async Task<TResult> PostAsync<TResult>(  
    string uri, TResult data, string token = "", string header = "")  
{  
    HttpClient httpClient = CreateHttpClient(token);  
    ...  
    var content = new StringContent(JsonConvert.SerializeObject(data));  
    content.Headers.ContentType = new MediaTypeHeaderValue("application/json");  
    HttpResponseMessage response = await httpClient.PostAsync(uri, content);  

    await HandleResponse(response);  
    string serialized = await response.Content.ReadAsStringAsync();  

    TResult result = await Task.Run(() =>  
        JsonConvert.DeserializeObject<TResult>(serialized, _serializerSettings));  

    return result;  
}

このメソッドでは、適切なヘッダーが設定された HttpClient クラスのインスタンスを返す CreateHttpClient メソッドを呼び出します。 その後、URI によって識別されたリソースに非同期 POST 要求を送信し、シリアル化されたバスケット データは JSON 形式で送信され、応答は HttpResponseMessage インスタンスに格納されます。 その後、HandleResponse メソッドが呼び出されます。応答に成功 HTTP 状態コードが含まれていない場合は例外がスローされます。 次に、応答が文字列として読み取られ、JSON から オブジェクトに CustomerBasket 変換され、 に BasketService返されます。 メソッドの詳細については、「GET 要求のCreateHttpClient作成」を参照してください。

クラスの メソッドが PostAsyncRequestProvider呼び出HttpClient.PostAsyncPostすと、Basket.API プロジェクトの BasketController クラス内のメソッドが呼び出されます。これは、次のコード例に示されています。

[HttpPost]  
public async Task<IActionResult> Post([FromBody]CustomerBasket value)  
{  
    var basket = await _repository.UpdateBasketAsync(value);  
    return Ok(basket);  
}

このメソッドでは、RedisBasketRepository クラスのインスタンスを使用して、バスケット データを Redis Cache に保持し、成功 HTTP 状態コードと、JSON 形式の CustomerBasket インスタンスを含む応答メッセージとして返します。

DELETE 要求の作成

図 10-3 は、 のバスケット マイクロサービスからバスケット データを削除するクラスの相互作用を CheckoutView示しています。

バスケット マイクロサービスからのデータの削除

図 10-3: バスケット マイクロサービスからのデータの削除

チェックアウト プロセスが呼び出されると、CheckoutViewModel クラスの CheckoutAsync メソッドが呼び出されます。 このメソッドは、次のコード例に示すように、買い物かごをクリアする前に新しい注文を作成します。

private async Task CheckoutAsync()  
{  
    ...  
    await _basketService.ClearBasketAsync(_shippingAddress.Id.ToString(), authToken);  
    ...  
}

このメソッドは、 ClearBasketAsync Autofac によって に挿入されたインスタンスの BasketService メソッドを CheckoutViewModel 呼び出します。 次のメソッドは ClearBasketAsync メソッドを示しています。

public async Task ClearBasketAsync(string guidUser, string token)  
{  
    UriBuilder builder = new UriBuilder(GlobalSetting.Instance.BasketEndpoint);  
    builder.Path = guidUser;  
    string uri = builder.ToString();  
    await _requestProvider.DeleteAsync(uri, token);  
}

このメソッドは、要求の送信先のリソースを識別する URI をビルドし、 クラスを RequestProvider 使用してリソースに対して DELETE HTTP メソッドを呼び出します。 認証プロセス中に IdentityServer から取得されたアクセス トークンは、バスケット マイクロサービスへの要求を承認するために必要であることに注意してください。 承認の詳細については、「承認」を参照してください。

次のコード例は、RequestProvider クラスの DeleteAsync メソッドを示しています。

public async Task DeleteAsync(string uri, string token = "")  
{  
    HttpClient httpClient = CreateHttpClient(token);  
    await httpClient.DeleteAsync(uri);  
}

このメソッドでは、適切なヘッダーが設定された HttpClient クラスのインスタンスを返す CreateHttpClient メソッドを呼び出します。 次に、URI によって識別されるリソースに非同期 DELETE 要求を送信します。 メソッドの詳細については、「GET 要求のCreateHttpClient作成」を参照してください。

クラスの メソッドが DeleteAsyncRequestProvider呼び出HttpClient.DeleteAsyncDeleteすと、Basket.API プロジェクトの BasketController クラス内のメソッドが呼び出されます。これは、次のコード例に示されています。

[HttpDelete("{id}")]  
public void Delete(string id)  
{  
    _repository.DeleteBasketAsync(id);  
}

このメソッドでは RedisBasketRepository クラスのインスタンスを使用して、Redis Cache からバスケット データを削除します。

キャッシュされたデータ

頻繁にアクセスされるデータを、アプリの近くにある高速ストレージにキャッシュすることで、アプリのパフォーマンスを向上させることができます。 高速ストレージが元のソースよりもアプリの近くに配置されている場合、キャッシュを使用すると、データを取得するときの応答時間が大幅に向上する可能性があります。

キャッシュの最も一般的な形式は、読み取りスルー キャッシュです。ここで、アプリではキャッシュを参照してデータを取得します。 データがキャッシュにない場合は、データ ストアから取得され、キャッシュに追加されます。 アプリでは、キャッシュアサイド パターンを使用して読み取りスルー キャッシュを実装できます。 このパターンでは、アイテムが現在キャッシュ内にあるかどうかを判断します。 アイテムがキャッシュ内にない場合は、データ ストアから読み取られ、キャッシュに追加されます。 詳細については、「 Cache-Aside パターン」を参照してください。

ヒント

頻繁に読み取られ、変更頻度の低いデータをキャッシュします。 このデータは、アプリによって初めて取得されるときに、必要に応じてキャッシュに追加できます。 これは、アプリでデータ ストアからデータを取り込む必要があるのは 1 回のみで、以降のアクセスにはキャッシュを利用できることを意味します。

eShopOnContainers 参照アプリケーションなどの分散アプリケーションでは、次のキャッシュのいずれかまたは両方を提供する必要があります。

  • 共有キャッシュ。複数のプロセスまたはコンピューターからアクセスできます。
  • プライベート キャッシュ。データは、アプリを実行しているデバイス上でローカルに保持されます。

eShopOnContainers モバイル アプリはプライベート キャッシュを使用します。このキャッシュでは、アプリのインスタンスを実行しているデバイス上でデータがローカルに保持されます。 eShopOnContainers 参照アプリケーションで使用されるキャッシュについては、「.NET マイクロサービス: コンテナー化された .NET アプリケーションのアーキテクチャ」を参照してください。

ヒント

キャッシュは、いつ消えるかわからない一時的なデータ ストアと考えてください。 データが元のデータ ストアとキャッシュに確実に保持されるようにしてください。 そうすれば、キャッシュが使用できなくなった場合に、データが失われる可能性は最小限に抑えられます。

データの有効期限の管理

キャッシュされたデータが常に元のデータと一致することを期待するのは現実的ではありません。 元のデータ ストア内のデータは、キャッシュされた後に変更され、キャッシュされたデータが古くなる可能性があります。 したがって、アプリでは、キャッシュのデータをできるだけ最新の状態に維持できるようにするための戦略を実装する必要がありますが、キャッシュのデータが古くなったときに発生する状況を検出して対処することもできます。 ほとんどのキャッシュ メカニズムでは、データの有効期限が切れるようキャッシュを構成できるため、データが古くなっている可能性がある期間を短縮できます。

ヒント

キャッシュの構成時に既定の有効期限を設定します。 多くのキャッシュで有効期限が実装され、データが無効になり、指定された期間にアクセスされていない場合はキャッシュから削除されます。 しかし、有効期限を選択するときは注意が必要です。 短すぎると、データの有効期限が早くなりすぎて、キャッシュの利点が減ります。 長すぎると、データが古くなるリスクがあります。 そのため、有効期限は、データを使用するアプリのアクセス パターンと一致する必要があります。

キャッシュされたデータは、有効期限が切れたときにキャッシュから削除する必要があり、アプリで元のデータ ストアからデータを取得してキャッシュに戻す必要があります。

また、データを長期間保持することが許可されている場合は、キャッシュがいっぱいになる可能性もあります。 そのため、キャッシュに新しいアイテムを追加する要求は、''削除'' と呼ばれるプロセスで一部のアイテムを削除するために必要な場合があります。 通常、キャッシュ サービスでは最も長く使われていないデータを削除します。 ただし、最近使用した削除ポリシーや先入れ先出しポリシーなど、他にも削除ポリシーがあります。詳細については、「 キャッシュ ガイダンス」を参照してください。

イメージのキャッシュ

eShopOnContainers モバイル アプリは、キャッシュのメリットを得られるリモート製品イメージを使用します。 これらのイメージは、コントロールとCachedImage、FFImageLoading ライブラリによって提供されるコントロールによって表示されますImage

コントロールは Xamarin.FormsImage 、ダウンロードしたイメージのキャッシュをサポートします。 キャッシュは既定で有効になっており、イメージは 24 時間ローカルに格納されます。 また、 プロパティを使用して有効期限を CacheValidity 構成することもできます。 詳細については、「 ダウンロードしたイメージ キャッシュ」を参照してください。

FFImageLoading の CachedImage コントロールはコントロールの代わり Xamarin.FormsImage であり、補助機能を有効にする追加のプロパティを提供します。 この機能の中でも、コントロールは構成可能なキャッシュを提供し、エラーをサポートし、画像プレースホルダーを読み込みます。 次のコード例は、eShopOnContainers モバイル アプリが で コントロールをCachedImage使用する方法を示しています。これは、 の CatalogViewコントロールによってListView使用されるデータ テンプレートProductTemplateです。

<ffimageloading:CachedImage
    Grid.Row="0"
    Source="{Binding PictureUri}"     
    Aspect="AspectFill">
    <ffimageloading:CachedImage.LoadingPlaceholder>
        <OnPlatform x:TypeArguments="ImageSource">
            <On Platform="iOS, Android" Value="default_campaign" />
            <On Platform="UWP" Value="Assets/default_campaign.png" />
        </OnPlatform>
    </ffimageloading:CachedImage.LoadingPlaceholder>
    <ffimageloading:CachedImage.ErrorPlaceholder>
        <OnPlatform x:TypeArguments="ImageSource">
            <On Platform="iOS, Android" Value="noimage" />
            <On Platform="UWP" Value="Assets/noimage.png" />
        </OnPlatform>
    </ffimageloading:CachedImage.ErrorPlaceholder>
</ffimageloading:CachedImage>

コントロールは CachedImageLoadingPlaceholder プロパティと ErrorPlaceholder プロパティをプラットフォーム固有のイメージに設定します。 プロパティは LoadingPlaceholder 、 プロパティで Source 指定されたイメージの取得中に表示されるイメージを指定し ErrorPlaceholder 、 プロパティで指定されたイメージを取得しようとしたときにエラーが発生した場合に表示されるイメージを Source 指定します。

名前が示すように、コントロールは、 プロパティの CachedImage 値で指定された時間、デバイス上のリモート イメージを CacheDuration キャッシュします。 このプロパティ値が明示的に設定されていない場合、既定値の 30 日が適用されます。

回復性の向上

リモート サービスおよびリソースと通信するすべてのアプリは、一時的な障害に特別な注意を払う必要があります。 一時的な障害には、サービスへのネットワーク接続の瞬間的な喪失、サービスの一時的な使用不可、サービスがビジー状態のときに発生するタイムアウトなどがあります。 多くの場合、これらの障害は自己修正され、しばらくしてから操作を繰り返せば、成功する可能性があります。

予測しうるあらゆる状況下で十分にテストされていても、一時的な障害がアプリの感性品質に大きく影響する場合があります。 リモート サービスと通信するアプリが確実に動作するように、次のすべてを実行できる必要があります。

  • 障害が発生したときにその障害を検出し、障害が一時的なものである可能性があるかどうかを判断する。
  • 一時的な障害である可能性があると判断した場合、操作を再試行し、操作の再試行回数を把握する。
  • 再試行回数、試行間隔、試行失敗後の措置を指定する適切な再試行戦略を使用する。

この一時的な障害の処理は、再試行パターンを実装するコード内のリモート サービスへのアクセスのすべての試行をラップすることによって実現できます。

再試行パターン

アプリでリモート サービスに要求を送信しようとしたときに障害が検出された場合は、次のいずれかの方法で障害を処理できます。

  • 操作を再試行する。 アプリで失敗した要求を直ちに再試行できます。
  • しばらくしてから操作を再試行する。 アプリは、要求を再試行する前に適切な時間、待機する必要があります。
  • 操作を取り消す。 アプリケーションでは操作を取り消し、例外を報告する必要があります。

再試行戦略は、アプリのビジネス要件と一致するように調整する必要があります。 たとえば、再試行する操作の再試行回数と再試行間隔を最適化することが重要です。 操作がユーザー操作の一部である場合は、再試行間隔を短くし、ユーザーに応答を待たせないように再試行の回数を少なく抑える必要があります。 操作が実行時間の長いワークフローの一部であり、ワークフローの取り消しや再起動にコストがかかる場合や時間がかかる場合は、試行の間隔を長めにし、再試行回数を増やすのが妥当です。

注意

最短の待ち時間で何回も再試行を行う積極的な再試行戦略は、フル稼働しているかその状態に近いリモート サービスを低下させる可能性があります。 さらに、このような再試行戦略は、失敗した操作を継続的に再試行しようとした場合、アプリの応答性にも影響を与える可能性があります。

何回再試行しても要求が失敗する場合は、アプリで同じリソースに要求がそれ以上送信されないようにし、障害を報告することをお勧めします。 その後、設定された期間が経過すると、アプリではリソースに対して 1 つまたは複数の要求を行って、それらが成功したかどうかを確認できます。 詳細については、「 サーキット ブレーカー パターン」を参照してください。

ヒント

再試行が際限なく繰り返される設計は確実に避けてください。 再試行回数を制限するか、サーキット ブレーカー パターンを実装してサービスが回復できるようにします。

eShopOnContainers モバイル アプリでは、RESTful Web 要求を行うときの再試行パターンは現在実装されていません。 ただし、CachedImageFFImageLoading ライブラリによって提供されるコントロールは、イメージの読み込みを再試行することによって一時的な障害処理をサポートします。 イメージの読み込みに失敗した場合は、さらに試行されます。 試行回数は プロパティで指定され、 プロパティで RetryCount 指定された遅延後に再試行が RetryDelay 行われます。 これらのプロパティ値が明示的に設定されていない場合は、既定値 (プロパティの場合は 3 RetryCount 、プロパティの場合は 250 ミリ秒) が RetryDelay 適用されます。 コントロールの詳細については、「イメージのCachedImageキャッシュ」を参照してください。

eShopOnContainers 参照アプリケーションでは再試行パターンを実装します。 再試行パターン HttpClient と クラスを組み合わせる方法の説明など、詳細については、「 .NET マイクロサービス: コンテナー化された .NET アプリケーションのアーキテクチャ」を参照してください。

再試行パターンの詳細については、「 再試行パターン」 を参照してください。

遮断器のパターン

場合によっては、修正に時間がかかる予想されるイベントが原因で障害が発生することがあります。 これらの障害は、接続の部分的な喪失からサービスの完全な障害までの範囲で発生する可能性があります。 このような状況では、成功する可能性が低い操作をアプリで再試行するのは無意味です。そうではなく、操作が失敗したことを受け入れ、それに応じてこの障害を処理する必要があります。

サーキット ブレーカー パターンを使用すると、失敗する可能性がある操作をアプリで繰り返し試行することを防ぐことができ、同時にアプリで障害が解決されたかどうかを検出することもできます。

注意

サーキット ブレーカー パターンの目的は、再試行パターンとは異なります。 再試行パターンでは、アプリで成功を見込んで操作を再試行することができます。 サーキット ブレーカー パターンにより、失敗する可能性がある操作がアプリで実行されなくなります。

サーキット ブレーカーは、失敗する可能性のある操作のプロキシとして機能します。 プロキシでは、最近発生した障害の数を監視し、この情報を使用して、操作を続行できるか、すぐに例外を返すかを判断します。

eShopOnContainers モバイル アプリでは、現在サーキット ブレーカー パターンは実装されていません。 しかし、eShopOnContainers ではそうします。 詳細については、「NET マイクロサービス: コンテナー化された .NET アプリケーションのアーキテクチャ」を参照してください。

ヒント

再試行およびサーキット ブレーカー パターンを組み合わせます。 アプリでは、再試行パターンを使用してサーキット ブレーカーを介して操作を呼び出すことによって、再試行およびサーキット ブレーカー パターンを組み合わせることができます。 ただし、再試行ロジックは、サーキット ブレーカーによって返されるすべての例外から大きな影響を受け、エラーが一時的なものではないことが示されると、再試行回数を破棄します。

サーキット ブレーカー パターンの詳細については、「サーキット ブレーカー パターン」を参照してください。

まとめ

現代の多くの Web ベース ソリューションでは、Web サーバーによりホストされる Web サービスを使用することで、リモート クライアント アプリケーションの機能を提供します。 Web サービスによって公開される操作では Web API が構成され、クライアント アプリでは、API によって公開されるデータや操作がどのように実装されるかがわかっていなくても、Web API を利用できる必要があります。

頻繁にアクセスされるデータを、アプリの近くにある高速ストレージにキャッシュすることで、アプリのパフォーマンスを向上させることができます。 アプリでは、キャッシュアサイド パターンを使用して読み取りスルー キャッシュを実装できます。 このパターンでは、アイテムが現在キャッシュ内にあるかどうかを判断します。 アイテムがキャッシュ内にない場合は、データ ストアから読み取られ、キャッシュに追加されます。

Web API と通信する場合、アプリは一時的な障害に特別な注意を払う必要があります。 一時的な障害には、サービスへのネットワーク接続の瞬間的な喪失、サービスの一時的な使用不可、サービスがビジー状態のときに発生するタイムアウトなどがあります。 多くの場合、これらの障害は自己修正され、しばらくしてから操作が繰り返されれば、成功する可能性があります。 したがって、アプリでは、一時的な障害処理メカニズムを実装するコード内の Web API へのアクセスのすべての試行をラップする必要があります。