Zugreifen auf Remotedaten

Hinweis

Dieses eBook wurde im Frühjahr 2017 veröffentlicht und wurde seitdem nicht aktualisiert. Es gibt viel in dem Buch, das wertvoll bleibt, aber einige der Materialien sind veraltet.

Zahlreiche moderne webbasierte Lösungen nutzen auf Webservern gehostete Webdienste, um Funktionen für Remoteclientanwendungen bereitzustellen. Die von einem Webdienst verfügbar gemachten Vorgänge bilden eine Web-API.

Client-Apps sollten die vom Webdienst bereitgestellte API nutzen können, ohne Kenntnis darüber zu haben, wie die von der API offengelegten Daten oder Vorgänge implementiert sind. Dazu muss die API allgemeine Standards einhalten, die es einer Client-App und einem Webdienst ermöglichen, sich bezüglich der zu verwendenden Datenformate und der Struktur der zwischen den Client-Apps und dem Webdienst ausgetauschten Daten zu verständigen.

Einführung in Representational State Transfer (REST)

Representational State Transfer (REST) ist ein Architekturstil zur Entwicklung verteilter Systeme, die auf Hypermedia basieren. Ein wichtiger Vorteil des REST-Modells besteht darin, dass es auf offenen Standards basiert und die Umsetzung des Modells oder der Client-Apps, die darauf zugreifen, nicht an eine bestimmte Implementierung bindet. Daher könnte ein REST-Webdienst mithilfe von Microsoft ASP.NET Core MVC implementiert werden, und Client-Apps könnten mit jeder Beliebigen Sprache und einem Toolset entwickelt werden, die HTTP-Anforderungen generieren und HTTP-Antworten analysieren können.

Das REST-Modell verwendet ein Navigationsschema zur Darstellung von Objekten und Diensten (bezeichnet als „Ressourcen“) über ein Netzwerk. Systeme, die REST implementieren, verwenden in der Regel das HTTP-Protokoll zum Übertragen von Anforderungen für den Zugriff auf diese Ressourcen. In solchen Systemen sendet eine Client-App eine Anforderung in Form eines URI, der eine Ressource identifiziert, und einer HTTP-Methode (z. B. GET, POST, PUT oder DELETE), die angibt, welcher Vorgang für die Ressource ausgeführt werden soll. Der Text der HTTP-Anforderung enthält alle zum Ausführen des Vorgangs benötigten Daten.

Hinweis

REST definiert ein zustandsloses Anforderungsmodell. Daher müssen HTTP-Anforderungen unabhängig sein und können in beliebiger Reihenfolge erfolgen.

In der Antwort für eine REST-Anforderung werden standardmäßige HTTP-Statuscodes verwendet. Beispielsweise sollte eine Anforderung, die gültige Daten zurückgibt, den HTTP-Antwortcode 200 (OK) enthalten, während eine Anforderung, die eine bestimmte Ressource nicht finden oder löschen kann, eine Antwort zurückgeben sollte, die den HTTP-Statuscode 404 (Nicht gefunden) enthält.

Eine RESTful-Web-API macht eine Reihe verbundener Ressourcen verfügbar und stellt die grundlegenden Vorgänge bereit, die es einer Anwendung ermöglichen, diese Ressourcen zu manipulieren und einfach zwischen ihnen zu navigieren. Daher sind die URIs, die eine typische RESTful-Web-API bilden, auf die verfügbar gemachten Daten ausgerichtet und nutzen die von HTTP bereitgestellten Funktionen zur Arbeit mit diesen Daten.

Die von einer Client-App in eine HTTP-Anforderung eingefügten Daten und die zugehörigen Antwortnachrichten des Webservers können in einer Vielzahl von Formaten („Medientypen“) dargestellt werden. Wenn eine Client-App eine Anforderung sendet, die Daten im Textkörper einer Nachricht zurückgibt, kann sie die Medientypen angeben, die Accept sie im Header der Anforderung verarbeiten kann. Wenn der Webserver diesen Medientyp unterstützt, kann er mit einer Antwort antworten, die den Header enthält, der Content-Type das Format der Daten im Textkörper der Nachricht angibt. Es liegt dann in der Verantwortung der Client-App, die Antwortnachricht zu analysieren und die Ergebnisse im Nachrichtentext entsprechend zu interpretieren.

Weitere Informationen zu REST finden Sie unter API-Design und API-Implementierung.

Verwenden von RESTful-APIs

Die mobile eShopOnContainers-App verwendet das Model-View-ViewModel (MVVM)-Muster, und die Modellelemente des Musters stellen die in der App verwendeten Domänenentitäten dar. Die Controller- und Repositoryklassen in der eShopOnContainers-Referenzanwendung akzeptieren viele dieser Modellobjekte und geben sie zurück. Daher werden sie als Datenübertragungsobjekte (Data Transfer Objects, DTOs) verwendet, die alle Daten enthalten, die zwischen der mobilen App und den containerisierten Microservices übergeben werden. Der Hauptvorteil der Verwendung von Datenübertragungsobjekten für die Übermittlung von Daten an den Dienst sowie den Empfang von Daten von einem Webdienst besteht darin, dass die App durch die Übertragung von mehr Daten in einem einzigen Remoteaufruf die Anzahl der erforderlichen Remoteaufrufe verringern kann.

Ausführen von Webanforderungen

Die mobile eShopOnContainers-App verwendet die HttpClient Klasse, um Anforderungen über HTTP zu stellen, wobei JSON als Medientyp verwendet wird. Diese Klasse bietet die Funktionalität zum asynchronen Senden von HTTP-Anforderungen und zum Empfangen von HTTP-Antworten von einer per URI identifizierten Ressource. Die HttpResponseMessage Klasse stellt eine HTTP-Antwortnachricht dar, die von einer REST-API empfangen wurde, nachdem eine HTTP-Anforderung gestellt wurde. Sie enthält Informationen zur Antwort, einschließlich Statuscode, Headern und beliebigem Text. Die HttpContent-Klasse steht für den HTTP-Textkörper und die Inhaltsheader, wie etwa Content-Type und Content-Encoding. Der Inhalt kann je nach Format der Daten mit einer beliebigen der ReadAs-Methoden (beispielsweise ReadAsStringAsync und ReadAsByteArrayAsync) gelesen werden.

Durchführen einer GET-Anforderung

Mithilfe der Klasse CatalogService wird der Datenabruf aus dem Katalog-Microservice verwaltet. In der Methode in der RegisterDependencies ViewModelLocator Klasse wird die CatalogService Klasse als Typzuordnung für den ICatalogService Typ mit dem Container für die Autofac-Abhängigkeitseinfügung registriert. Wenn dann eine Instanz der CatalogViewModel Klasse erstellt wird, akzeptiert der Konstruktor einen ICatalogService Typ, der von Autofac aufgelöst wird, und gibt eine Instanz der CatalogService Klasse zurück. Weitere Informationen zur Abhängigkeitsinjektion finden Sie in der Einführung in Abhängigkeitsinjektion.

Abbildung 10-1 zeigt die Interaktion von Klassen, die Katalogdaten aus dem Katalog-Microservice für die Anzeige durch den CatalogViewKatalog lesen.

Abrufen von Daten aus dem Katalog-Microservice

Abbildung 10-1: Abrufen von Daten aus dem Katalog-Microservice

Wenn die CatalogView Methode in der Klasse navigiert wird, wird die OnInitialize Methode in der CatalogViewModel Klasse aufgerufen. Diese Methode ruft Katalogdaten aus dem Katalog-Microservice ab, wie im folgenden Codebeispiel gezeigt wird:

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

Diese Methode ruft die GetCatalogAsync Methode der CatalogService Instanz auf, die von Autofac in die CatalogViewModel Autofac eingefügt wurde. Die GetCatalogAsync-Methode wird in folgendem Codebeispiel veranschaulicht:

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();            
}

Diese Methode erstellt den URI zur Identifizierung der Ressource, an die die Anforderung gesendet wird, und verwendet die Klasse RequestProvider, um die GET-HTTP-Methode für die Ressource aufzurufen, bevor die Ergebnisse an CatalogViewModel zurückgegeben werden. Die Klasse RequestProvider umfasst Funktionalität, um eine Anforderung in Form einer URI zur Identifizierung einer Ressource zu übermitteln, eine HTTP-Methode zum Angeben des Vorgangs, der für diese Ressource ausgeführt werden soll, und einen Textteil, der alle für die Ausführung des Vorgangs benötigten Daten enthält. Informationen dazu, wie die RequestProvider Klasse in die CatalogService classKlasse eingefügt wird, finden Sie unter Einführung in Abhängigkeitsinjektion.

Das folgende Codebeispiel zeigt die Methode GetAsync in der Klasse RequestProvider:

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;  
}

Diese Methode ruft die Methode CreateHttpClient auf, die eine Instanz der Klasse HttpClient mit den entsprechenden Headern zurückgibt. Anschließend sendet sie eine asynchrone GET-Anforderung an die vom URI identifizierte Ressource, wobei die Antwort in der HttpResponseMessage Instanz gespeichert wird. Dann wird die Methode HandleResponse aufgerufen, die eine Ausnahme auslöst, wenn die Antwort keinen erfolgreichen HTTP-Statuscode enthält. Die Antwort wird als Zeichenfolge gelesen, von JSON in ein CatalogRoot-Objekt konvertiert und an den CatalogService zurückgegeben.

Die Methode CreateHttpClient wird im folgenden Codebeispiel gezeigt:

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;  
}

Diese Methode erstellt eine neue Instanz der HttpClient Klasse und legt den Accept Header aller Anforderungen fest, die von der HttpClient Instanz application/jsonvorgenommen werden, was angibt, dass der Inhalt einer antwort mit JSON formatiert wird. Wenn ein Zugriffstoken als Argument an die CreateHttpClient-Methode übergeben wurde, wird dieses in den Authorization-Header aller von der HttpClient-Instanz gesendeten Anforderungen eingefügt und mit der Zeichenfolge Bearer als Präfix versehen. Weitere Informationen zur Autorisierung finden Sie unter Autorisierung.

Wenn die GetAsync Methode in der RequestProvider Klasse aufgerufen wird HttpClient.GetAsync, wird die Items Methode in der CatalogController Klasse im Catalog.API-Projekt aufgerufen, die im folgenden Codebeispiel dargestellt wird:

[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);  
}

Diese Methode ruft die Katalogdaten aus der SQL-Datenbank mithilfe von EntityFramework ab und gibt sie als Antwortnachricht zurück, die einen Erfolgs-HTTP-Statuscode und eine Sammlung von JSON-formatierten CatalogItem Instanzen enthält.

Senden einer POST-Anforderung

Die Klasse BasketService wird verwendet, um den Datenabruf und den Aktualisierungsprozess mit dem Microservice „Basket“ zu verwalten. In der Methode in der RegisterDependencies ViewModelLocator Klasse wird die BasketService Klasse als Typzuordnung für den IBasketService Typ mit dem Container für die Autofac-Abhängigkeitseinfügung registriert. Wenn dann eine Instanz der BasketViewModel Klasse erstellt wird, akzeptiert der Konstruktor einen IBasketService Typ, der von Autofac aufgelöst wird, und gibt eine Instanz der BasketService Klasse zurück. Weitere Informationen zur Abhängigkeitsinjektion finden Sie in der Einführung in Abhängigkeitsinjektion.

Abbildung 10-2 zeigt die Interaktion von Klassen, die die Korbdaten senden, die vom BasketViewKorb microservice angezeigt werden.

Senden von Daten an den Korb microservice

Abbildung 10-2: Senden von Daten an den Korb microservice

Wenn dem Warenkorb ein Element hinzugefügt wird, wird die ReCalculateTotalAsync-Methode in der BasketViewModel-Klasse aufgerufen. Diese Methode aktualisiert den Gesamtwert der Artikel im Warenkorb und sendet die Warenkorbdaten an den Microservice „Basket“, wie im folgenden Codebeispiel gezeigt:

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

Diese Methode ruft die UpdateBasketAsync Methode der BasketService Instanz auf, die von Autofac in die BasketViewModel Autofac eingefügt wurde. Der folgende Code zeigt die UpdateBasketAsync-Methode:

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;  
}

Diese Methode erstellt den URI zur Identifizierung der Ressource, an die die Anforderung gesendet wird, und verwendet die Klasse RequestProvider, um die POST-HTTP-Methode für die Ressource aufzurufen, bevor die Ergebnisse an BasketViewModel zurückgegeben werden. Beachten Sie, dass ein Zugriffstoken, das während des Authentifizierungsprozesses von IdentityServer abgerufen wird, erforderlich ist, um Anforderungen an den Basket Microservice zu autorisieren. Weitere Informationen zur Autorisierung finden Sie unter Autorisierung.

Das folgende Codebeispiel zeigt eine der PostAsync-Methoden in der RequestProvider-Klasse:

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;  
}

Diese Methode ruft die Methode CreateHttpClient auf, die eine Instanz der Klasse HttpClient mit den entsprechenden Headern zurückgibt. Anschließend wird eine asynchrone POST-Anforderung an die durch den URI identifizierte Ressource gesendet, wobei die serialisierten Warenkorbdaten im JSON-Format übermittelt werden und die Antwort in der HttpResponseMessage-Instanz gespeichert wird. Dann wird die Methode HandleResponse aufgerufen, die eine Ausnahme auslöst, wenn die Antwort keinen erfolgreichen HTTP-Statuscode enthält. Anschließend wird die Antwort als Zeichenfolge gelesen, von JSON in ein CustomerBasket Objekt konvertiert und an das BasketServiceObjekt zurückgegeben. Weitere Informationen zur CreateHttpClient Methode finden Sie unter Erstellen einer GET-Anforderung.

Wenn die PostAsync Methode in der RequestProvider Klasse aufgerufen wird HttpClient.PostAsync, wird die Post Methode in der BasketController Klasse im Basket.API-Projekt aufgerufen, die im folgenden Codebeispiel dargestellt wird:

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

Diese Methode verwendet eine Instanz der Klasse RedisBasketRepository, um die Warenkorbdaten im Redis-Cache zu speichern. Anschließend gibt sie eine Antwortnachricht zurück, die einen erfolgreichen HTTP-Statuscode und eine JSON-formatierte CustomerBasket-Instanz enthält.

Erstellen einer DELETE-Anforderung

Abbildung 10-3 zeigt die Interaktionen von Klassen, die Korbdaten aus dem Korb microservice löschen, für die CheckoutView.

Löschen von Daten aus dem Korb microservice

Abbildung 10-3: Löschen von Daten aus dem Korb microservice

Beim Aufruf des Bezahlvorgangs wird die Methode CheckoutAsync in der Klasse CheckoutViewModel aufgerufen. Diese Methode erstellt eine neue Bestellung, bevor der Warenkorb gelöscht wird, wie im folgenden Codebeispiel gezeigt:

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

Diese Methode ruft die ClearBasketAsync Methode der BasketService Instanz auf, die von Autofac in die CheckoutViewModel Autofac eingefügt wurde. Der folgende Code zeigt die ClearBasketAsync-Methode:

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);  
}

Diese Methode erstellt den URI, der die Ressource identifiziert, an die die Anforderung gesendet wird, und verwendet die RequestProvider Klasse, um die DELETE-HTTP-Methode für die Ressource aufzurufen. Beachten Sie, dass ein Zugriffstoken, das während des Authentifizierungsprozesses von IdentityServer abgerufen wird, erforderlich ist, um Anforderungen an den Basket Microservice zu autorisieren. Weitere Informationen zur Autorisierung finden Sie unter Autorisierung.

Das folgende Codebeispiel zeigt die Methode DeleteAsync in der Klasse RequestProvider:

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

Diese Methode ruft die Methode CreateHttpClient auf, die eine Instanz der Klasse HttpClient mit den entsprechenden Headern zurückgibt. Anschließend wird eine asynchrone DELETE-Anforderung an die vom URI identifizierte Ressource gesendet. Weitere Informationen zur CreateHttpClient Methode finden Sie unter Erstellen einer GET-Anforderung.

Wenn die DeleteAsync Methode in der RequestProvider Klasse aufgerufen wird HttpClient.DeleteAsync, wird die Delete Methode in der BasketController Klasse im Basket.API-Projekt aufgerufen, die im folgenden Codebeispiel dargestellt wird:

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

Diese Methode verwendet eine Instanz der RedisBasketRepository-Klasse, um die Warenkorbdaten aus dem Redis-Cache zu löschen.

Zwischenspeichern von Daten

Die Leistung einer App kann verbessert werden, indem häufig aufgerufene Daten in einem schnellem Speicher in der Nähe der App zwischengespeichert werden. Wenn sich dieser schnelle Datenspeicher näher an der App befindet als die ursprüngliche Datenquelle, kann das Zwischenspeichern die Antwortzeiten beim Abrufen von Daten erheblich verbessern.

Die gebräuchlichste Form der Zwischenspeicherung ist das Read-Through-Caching, bei dem eine App Daten durch Referenzierung des Caches abruft. Wenn die Daten nicht im Cache enthalten sind, werden sie aus dem Datenspeicher abgerufen und dem Cache hinzugefügt. Apps können das Read-Through-Caching mit dem cachefremden Programmierschema implementieren. Dieses Muster bestimmt, ob sich das Element derzeit im Cache befindet. Wenn das Element nicht im Cache enthalten ist, wird es aus dem Datenspeicher gelesen und dem Cache hinzugefügt. Weitere Informationen finden Sie im Cache-Aside-Muster .

Tipp

Daten, die häufig gelesen werden, zwischenspeichern und die sich selten ändern. Diese Daten können dem Cache bei Bedarf hinzugefügt werden, wenn sie zum ersten Mal von einer App abgerufen werden. Dadurch muss die App die Daten nur ein einziges Mal aus dem Datenspeicher abrufen, und spätere Zugriffe können über den Cache abgewickelt werden.

Verteilte Anwendungen, wie z. B. die Referenzanwendung eShopOnContainers, sollten einen oder beide der folgenden Caches bereitstellen:

  • Ein gemeinsam genutzter Cache, auf den mehrere Prozesse oder Computer zugreifen können.
  • Ein privater Cache, bei dem die Daten lokal auf dem Gerät gespeichert werden, auf dem die App ausgeführt wird.

Die mobile eShopOnContainers-App verwendet einen privaten Cache, in dem Daten lokal auf dem Gerät gespeichert werden, auf dem eine Instanz der App ausgeführt wird. Weitere Informationen zum verwendeten Cache der Referenzanwendung eShopOnContainers finden Sie unter .NET-Microservices: Architektur für containerisierte .NET-Anwendungen.

Tipp

Betrachten Sie den Cache als einen flüchtigen Datenspeicher, der jederzeit entfernt werden könnte. Stellen Sie sicher, dass die Daten sowohl im ursprünglichen Datenspeicher als auch im Cache verwaltet werden. Dadurch wird das Risiko eines Datenverlusts minimiert, falls der Cache nicht mehr zugänglich ist.

Verwalten des Datenablaufs

Es sollte nicht erwartet werden, dass zwischengespeicherte Daten immer mit den Originaldaten übereinstimmen. Die Daten im ursprünglichen Datenspeicher können sich nach dem Zwischenspeichern ändern, wodurch die Daten im Cache veralten. Daher sollten Apps eine Strategie implementieren, mit der sichergestellt wird, dass die Daten im Cache möglichst aktuell sind. Gleichzeitig sollten sie jedoch auch Situationen erkennen und handhaben können, in denen die Daten im Cache veraltet sind. Die meisten Mechanismen für die Zwischenspeicherung bieten die Möglichkeit, für die Daten im Cache ein Ablaufdatum festzulegen. Dadurch wird der Zeitraum verkürzt, in dem Daten veralten können.

Tipp

Legen Sie beim Konfigurieren eines Caches eine Standardablaufzeit fest. Viele Caches implementieren einen Ablauf, mit dem die Daten ungültig gemacht und aus dem Cache entfernt werden, wenn für einen angegebenen Zeitraum nicht darauf zugegriffen wurde. Bei der Auswahl des Ablaufzeitraums ist jedoch Vorsicht geboten. Bei einem zu kurzen Zeitraum laufen die Daten zu schnell ab, und die Vorteile der Zwischenspeicherung werden verringert. Werden die Daten zu lange aufbewahrt, besteht die Gefahr, dass sie veraltet sind. Daher sollte die Ablaufzeit dem Zugriffsmuster der Apps entsprechen, die die Daten nutzen.

Wenn zwischengespeicherte Daten ablaufen, sollten sie aus dem Cache entfernt werden. Die App muss die Daten dann aus dem ursprünglichen Datenspeicher abrufen und wieder in den Cache einfügen.

Wenn die Zeitspanne bis zum Ablauf der Daten zu lang ist, kann der Cache auch überlaufen. In diesem Fall müssen bei jeder Anforderung zum Hinzufügen neuer Elemente einige ältere Elemente aus dem Cache entfernt werden. Bei Cachediensten werden Daten in der Regel nach dem Prinzip der geringsten Nutzung entfernt. Es gibt jedoch andere Eviction-Richtlinien, einschließlich der zuletzt verwendeten und first-in-first-out. Weitere Informationen finden Sie unter Zwischenspeicherungsleitfaden.

Zwischenspeichern von Bildern

Die mobile eShopOnContainers-App nutzt Remoteproduktbilder, die von der Zwischenspeicherung profitieren. Diese Bilder werden vom Image Steuerelement und dem CachedImage von der FFImageLoading-Bibliothek bereitgestellten Steuerelement angezeigt.

Das Xamarin.FormsImage Steuerelement unterstützt das Zwischenspeichern von heruntergeladenen Bildern. Das Zwischenspeichern ist standardmäßig aktiviert und speichert das Bild 24 Stunden lang lokal. Darüber hinaus kann die Ablaufzeit mit der CacheValidity Eigenschaft konfiguriert werden. Weitere Informationen finden Sie unter "Zwischenspeicherung heruntergeladener Bilder".

Das Steuerelement von FFImageLoading CachedImage ist ein Ersatz für das Xamarin.FormsImage Steuerelement, das zusätzliche Eigenschaften bereitstellt, die zusätzliche Funktionen ermöglichen. Unter diesen Funktionen bietet das Steuerelement konfigurierbare Zwischenspeicherung, während Fehler und Laden von Bildplatzhaltern unterstützt werden. Das folgende Codebeispiel zeigt, wie die mobile eShopOnContainers-App das CachedImage Steuerelement im ProductTemplateSteuerelement verwendet , das die Datenvorlage ist, die vom Steuerelement in der ListView Datei CatalogViewverwendet wird:

<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>

Das CachedImage Steuerelement legt die und ErrorPlaceholder die LoadingPlaceholder Eigenschaften auf plattformspezifische Bilder fest. Die LoadingPlaceholder Eigenschaft gibt das Bild an, das angezeigt werden soll, während das durch die Source Eigenschaft angegebene Bild abgerufen wird, und die ErrorPlaceholder Eigenschaft gibt das anzuzeigende Bild an, wenn beim Versuch, das durch die Source Eigenschaft angegebene Bild abzurufen, ein Fehler auftritt.

Wie der Name schon sagt, speichert das CachedImage Steuerelement Remoteimages für die vom Wert der CacheDuration Eigenschaft angegebene Zeit auf dem Gerät zwischen. Wenn dieser Eigenschaftswert nicht explizit festgelegt wird, wird der Standardwert von 30 Tagen angewendet.

Erhöhung der Resilienz

Alle Apps, die mit Remotediensten und -ressourcen kommunizieren, müssen auf vorübergehende Störungen reagieren können. Zu diesen vorübergehende Störungen gehören der temporäre Verlust der Netzwerkverbindung mit Diensten, die vorübergehende Nichtverfügbarkeit eines Diensts oder Timeouts, die auftreten, wenn ein Dienst ausgelastet ist. Diese Fehler werden oft automatisch behoben, und wenn der Vorgang nach einer angemessenen Zeitspanne wiederholt wird, kann er wahrscheinlich erfolgreich ausgeführt werden.

Vorübergehende Fehler können eine enorme Auswirkung auf die wahrgenommene Qualität einer App haben, selbst wenn diese unter allen vorhersehbaren Umständen gründlich getestet wurde. Um die zuverlässige Funktion einer App zu gewährleisten, die mit Remotediensten kommuniziert, muss sie alle folgenden Aufgaben erfüllen können:

  • Erkennung von Störungen bei deren Auftreten, und Ermittlung, ob es sich wahrscheinlich um vorübergehende Fehler handelt.
  • Wiederholung des Vorgangs, wenn ein vorübergehender Fehler vermutet wird, und Aufzeichnung der Anzahl der Wiederholungsversuche für den Vorgang.
  • Anwendung einer geeigneten Wiederholungsstrategie, mit der die Anzahl der Wiederholungsversuche, die Verzögerung zwischen den einzelnen Versuchen und die nach einem fehlgeschlagenen Versuch zu ergreifenden Maßnahmen festgelegt werden.

Diese Verarbeitung vorübergehender Fehler kann erreicht werden, indem alle Versuche zum Zugriff auf einen Remotedienst mit Code umschlossen werden, der das Wiederholungsmuster implementiert.

Wiederholungsmuster

Wenn eine App beim Senden einer Anforderung an einen Remotedienst einen Fehler feststellt, kann sie diesen Fehler auf eine der folgenden Arten behandeln:

  • Wiederholung des Vorgangs: Die App führt die fehlgeschlagene Anforderung sofort erneut aus.
  • Wiederholung des Vorgangs nach einer Verzögerung: Die App wartet einen angemessenen Zeitraum, bevor sie die Anforderung wiederholt.
  • Abbruch des Vorgangs: Die App bricht den Vorgang ab und meldet eine Ausnahme.

Die Wiederholungsstrategie sollte auf die geschäftlichen Anforderungen der App abgestimmt sein. Beispielsweise ist es wichtig, die Anzahl der Wiederholungsversuche und das Wiederholungsintervall auf den jeweiligen Vorgang abzustimmen. Wenn der Vorgang Teil einer Benutzerinteraktion ist, sollte das Wiederholungsintervall kurz sein, und es sollten nur wenige Wiederholungsversuche unternommen werden, damit die Benutzer nicht auf eine Antwort warten müssen. Wenn der Vorgang Teil eines zeitintensiven Workflows ist, bei dem ein Abbruch oder Neustart des Workflows kostspielig oder zeitaufwändig ist, ist es sinnvoll, zwischen den Versuchen länger zu warten und mehrere Wiederholungsversuche durchzuführen.

Hinweis

Eine aggressive Wiederholungsstrategie mit minimaler Verzögerung zwischen den Versuchen und einer großen Anzahl von Wiederholungsversuchen kann einen Remotedienst, der seine Kapazitätsgrenze nahezu oder ganz erreicht hat, zusätzlich beeinträchtigen. Darüber hinaus kann sich eine solche Wiederholungsstrategie auch auf die Reaktionsfähigkeit der App auswirken, wenn ständig versucht wird, einen fehlgeschlagenen Vorgang auszuführen.

Wenn eine Anforderung nach mehreren Wiederholungsversuchen weiterhin fehlschlägt, ist es für die App besser, weitere Anforderungen an dieselbe Ressource zu verhindern und einen Fehler zu melden. Nach Ablauf einer bestimmten Zeitspanne kann die App dann eine oder mehrere Anforderungen an die Ressource senden, um zu ermitteln, ob diese erfolgreich durchgeführt werden können. Weitere Informationen finden Sie unter Circuit Breaker Pattern (Schutzschaltermuster).

Tipp

Implementieren Sie niemals einen endlosen Wiederholungsmechanismus. Verwenden Sie eine begrenzte Anzahl von Wiederholungsversuchen, oder implementieren Sie das Muster Trennschalter, damit der Dienst wiederhergestellt werden kann.

Die mobile eShopOnContainers-App implementiert derzeit nicht das Wiederholungsmuster beim Erstellen von RESTful-Webanforderungen. Das CachedImage von der FFImageLoading-Bibliothek bereitgestellte Steuerelement unterstützt jedoch die vorübergehende Fehlerbehandlung durch erneutes Laden von Bildern. Wenn das Laden von Bildern fehlschlägt, werden weitere Versuche unternommen. Die Anzahl der Versuche wird durch die RetryCount Eigenschaft angegeben, und Wiederholungsversuche erfolgen nach einer verzögerung, die von der RetryDelay Eigenschaft angegeben wird. Wenn diese Eigenschaftswerte nicht explizit festgelegt werden, werden ihre Standardwerte angewendet – 3 für die RetryCount Eigenschaft und 250 ms für die RetryDelay Eigenschaft. Weitere Informationen zum CachedImage Steuerelement finden Sie unter Zwischenspeichern von Bildern.

Die Referenzanwendung eShopOnContainers implementiert das Wiederholungsmuster. Weitere Informationen, einschließlich einer Erläuterung zur Kombination des Wiederholungsmusters mit der HttpClient Klasse, finden Sie unter .NET Microservices: Architecture for Containerized .NET Applications.

Weitere Informationen zum Wiederholungsmuster finden Sie im Wiederholungsmuster .

Trennschalter-Muster

In bestimmten Situationen können Fehler aufgrund von vorhersehbaren Ereignissen auftreten, deren Behebung länger dauert. Diese Fehler reichen von einem teilweisen Verlust der Konnektivität bis hin zum vollständigen Ausfall eines Diensts. In solchen Situationen hat es keinen Sinn, dass eine App einen Vorgang wiederholt, der wahrscheinlich nicht erfolgreich ausgeführt werden kann. Stattdessen sollte die App akzeptieren, dass der Vorgang fehlgeschlagen ist und diesen Fehler entsprechend behandeln.

Das Trennschaltermuster kann verhindern, dass eine App wiederholt versucht, einen Vorgang auszuführen, der wahrscheinlich fehlschlägt. Gleichzeitig kann die App erkennen, ob der Fehler behoben wurde.

Hinweis

Der Zweck des Trennschaltermuster unterscheidet sich vom Wiederholungsmuster. Mithilfe des Wiederholungsmusters kann eine App einen Vorgang in der Annahme wiederholen, dass dieser erfolgreich ausgeführt wird. Das Trennschaltermuster hindert eine App daran, einen Vorgang auszuführen, bei dem es wahrscheinlich zu einem Fehler kommt.

Ein Trennschalter fungiert als Proxy für Vorgänge, bei denen möglicherweise Fehler auftreten. Der Proxy sollte die Anzahl der zuletzt aufgetretenen Fehler überwachen und anhand dieser Informationen entscheiden, ob der Vorgang fortgesetzt werden darf, oder ob sofort eine Ausnahme zurückgegeben werden soll.

Die mobile eShopOnContainers-App implementiert derzeit nicht das Schaltkreistrennmuster. eShopOnContainers hingegen schon. Weitere Informationen finden Sie unter .NET-Microservices: Architektur für containerisierte .NET-Anwendungen.

Tipp

Kombinieren Sie das Wiederholungs- und Trennschaltermuster. Eine Anwendung kann beide Muster kombinieren, indem sie das Wiederholungsmuster nutzt, um einen Vorgang über das Trennschaltermuster aufzurufen. Die Wiederholungslogik sollte jedoch auf Ausnahmen reagieren, die vom Trennschalter zurückgegeben werden, und Wiederholungsversuche abbrechen, wenn der Trennschalter anzeigt, dass ein Fehler nicht vorübergehend ist.

Weitere Informationen zum Schaltkreisbrechermuster finden Sie im Schaltkreisbrechermuster .

Zusammenfassung

Zahlreiche moderne webbasierte Lösungen nutzen auf Webservern gehostete Webdienste, um Funktionen für Remoteclientanwendungen bereitzustellen. Die von einem Webdienst verfügbar gemachten Vorgänge definieren eine Web-API, und Client-Apps sollten die Web-API nutzen können, ohne Kenntnis darüber zu haben, wie die von der API offengelegten Daten oder Vorgänge implementiert sind.

Die Leistung einer App kann verbessert werden, indem häufig aufgerufene Daten in einem schnellem Speicher in der Nähe der App zwischengespeichert werden. Apps können das Read-Through-Caching mit dem cachefremden Programmierschema implementieren. Dieses Muster bestimmt, ob sich das Element derzeit im Cache befindet. Wenn das Element nicht im Cache enthalten ist, wird es aus dem Datenspeicher gelesen und dem Cache hinzugefügt.

Bei der Kommunikation mit Web-APIs müssen Apps auf vorübergehende Störungen reagieren können. Zu diesen vorübergehende Störungen gehören der temporäre Verlust der Netzwerkverbindung mit Diensten, die vorübergehende Nichtverfügbarkeit eines Diensts oder Timeouts, die auftreten, wenn ein Dienst ausgelastet ist. Diese Fehler werden oft automatisch behoben, und wenn der Vorgang nach einer angemessenen Zeitspanne wiederholt wird, kann er wahrscheinlich erfolgreich ausgeführt werden. Apps sollten daher alle Versuche zum Zugriff auf eine Web-API mit Code umschließen, der einen Mechanismus zur Behandlung vorübergehender Fehler implementiert.