Uzyskiwanie dostępu do danych zdalnych

Uwaga

Ta książka elektroniczna została opublikowana wiosną 2017 r. i od tego czasu nie została zaktualizowana. Jest wiele w książce, która pozostaje cenna, ale niektóre z materiałów są przestarzałe.

Wiele nowoczesnych rozwiązań internetowych korzysta z usług internetowych hostowanych przez serwery internetowe w celu zapewnienia funkcjonalności zdalnych aplikacji klienckich. Operacje uwidaczniane przez usługę internetową stanowią internetowy interfejs API.

Aplikacje klienckie powinny mieć możliwość korzystania z internetowego interfejsu API bez znajomości sposobu implementacji danych lub operacji udostępnianych przez interfejs API. Wymaga to, aby interfejs API przestrzegał wspólnych standardów, które umożliwiają aplikacji klienckiej i usłudze internetowej uzgodnienie formatów danych do użycia oraz strukturę danych wymienianych między aplikacjami klienckimi a usługą internetową.

Wprowadzenie do reprezentacji transferu stanu

Representational State Transfer (REST) to styl architektury do tworzenia systemów rozproszonych opartych na hipermediach. Główną zaletą modelu REST jest to, że jest on oparty na otwartych standardach i nie wiąże implementacji modelu ani aplikacji klienckich, które uzyskują dostęp do niej do żadnej konkretnej implementacji. W związku z tym można zaimplementować usługę internetową REST przy użyciu platformy Microsoft ASP.NET Core MVC, a aplikacje klienckie mogą być opracowywane przy użyciu dowolnego zestawu narzędzi i języka, które mogą generować żądania HTTP i analizować odpowiedzi HTTP.

Model REST używa schematu nawigacji do reprezentowania obiektów i usług za pośrednictwem sieci, nazywanych zasobami. Systemy implementujące architekturę REST zwykle używają protokołu HTTP do przesyłania żądań w celu uzyskania dostępu do tych zasobów. W takich systemach aplikacja kliencka przesyła żądanie w postaci identyfikatora URI, który identyfikuje zasób, oraz metodę HTTP (np. GET, POST, PUT lub DELETE), która wskazuje operację do wykonania w tym zasobie. Treść żądania HTTP zawiera wszystkie dane wymagane do wykonania operacji.

Uwaga

Interfejs REST definiuje model żądania bezstanowego. W związku z tym żądania HTTP muszą być niezależne i mogą występować w dowolnej kolejności.

Odpowiedź z żądania REST korzysta ze standardowych kodów stanu HTTP. Na przykład żądanie zwracające prawidłowe dane powinno zawierać kod odpowiedzi HTTP 200 (OK), podczas gdy żądanie, które nie może odnaleźć lub usunąć określonego zasobu, powinno zwrócić odpowiedź zawierającą kod stanu HTTP 404 (Nie znaleziono).

Internetowy interfejs API RESTful uwidacznia zestaw połączonych zasobów i udostępnia podstawowe operacje, które umożliwiają aplikacji manipulowanie tymi zasobami i łatwe nawigowanie między nimi. Z tego powodu identyfikatory URI, które stanowią typowy internetowy interfejs API RESTful, są zorientowane na dane, które uwidaczniają, i używają obiektów udostępnianych przez protokół HTTP do działania na tych danych.

Dane zawarte przez aplikację kliencką w żądaniu HTTP i odpowiednie komunikaty odpowiedzi z serwera internetowego mogą być prezentowane w różnych formatach, znanych jako typy multimediów. Gdy aplikacja kliencka wysyła żądanie zwracające dane w treści komunikatu, może określić typy multimediów, które mogą obsłużyć w Accept nagłówku żądania. Jeśli serwer internetowy obsługuje ten typ nośnika, może odpowiedzieć z odpowiedzią zawierającą Content-Type nagłówek określający format danych w treści wiadomości. Następnie aplikacja kliencka odpowiada za analizowanie komunikatu odpowiedzi i odpowiednie interpretowanie wyników w treści komunikatu.

Aby uzyskać więcej informacji na temat architektury REST, zobacz Projektowanie interfejsu API i implementacja interfejsu API.

Korzystanie z interfejsów API RESTful

Aplikacja mobilna eShopOnContainers używa wzorca Model-View-ViewModel (MVVM), a elementy modelu wzorca reprezentują jednostki domeny używane w aplikacji. Klasy kontrolera i repozytorium w aplikacji referencyjnej eShopOnContainers akceptują i zwracają wiele z tych obiektów modelu. W związku z tym są one używane jako obiekty transferu danych (DTO), które przechowują wszystkie dane przekazywane między aplikacją mobilną a konteneryzowanymi mikrousługami. Główną zaletą używania jednostek DTO do przekazywania danych i odbierania danych z usługi internetowej jest to, że przesyłając więcej danych w jednym wywołaniu zdalnym, aplikacja może zmniejszyć liczbę zdalnych wywołań, które należy wykonać.

Tworzenie żądań internetowych

Aplikacja mobilna eShopOnContainers używa HttpClient klasy do przesyłania żądań za pośrednictwem protokołu HTTP, a kod JSON jest używany jako typ nośnika. Ta klasa zapewnia funkcje asynchronicznego wysyłania żądań HTTP i odbierania odpowiedzi HTTP z zidentyfikowanego zasobu identyfikatora URI. Klasa HttpResponseMessage reprezentuje komunikat odpowiedzi HTTP odebrany z interfejsu API REST po wykonaniu żądania HTTP. Zawiera on informacje o odpowiedzi, w tym kod stanu, nagłówki i dowolną treść. Klasa HttpContent reprezentuje treść HTTP i nagłówki zawartości, takie jak Content-Type i Content-Encoding. Zawartość może być odczytywana przy użyciu dowolnej metody ReadAs , takiej jak ReadAsStringAsync i ReadAsByteArrayAsync, w zależności od formatu danych.

Tworzenie żądania GET

Klasa CatalogService służy do zarządzania procesem pobierania danych z mikrousługi wykazu. W metodzie RegisterDependenciesViewModelLocator w klasie CatalogService klasa jest rejestrowana jako mapowanie typów na typ z ICatalogService kontenerem iniekcji zależności autofac. Następnie po utworzeniu CatalogViewModel wystąpienia klasy konstruktor akceptuje ICatalogService typ, który zostanie rozpoznany przez funkcję Autofac, zwracając wystąpienie CatalogService klasy. Aby uzyskać więcej informacji na temat wstrzykiwania zależności, zobacz Wprowadzenie do wstrzykiwania zależności.

Rysunek 10–1 przedstawia interakcję klas odczytujących dane wykazu z mikrousługi wykazu do wyświetlania CatalogViewprzez element .

Retrieving data from the catalog microservice

Rysunek 10–1. Pobieranie danych z mikrousługi wykazu

Po przejściu CatalogView do OnInitialize metody w CatalogViewModel klasie wywoływana jest metoda . Ta metoda pobiera dane wykazu z mikrousługi wykazu, jak pokazano w poniższym przykładzie kodu:

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

Ta metoda wywołuje metodę GetCatalogAsyncCatalogService wystąpienia, które zostało wprowadzone do CatalogViewModel klasy Autofac. Poniższy przykład kodu przedstawia metodę 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();            
}

Ta metoda tworzy identyfikator URI, który identyfikuje zasób, do którego zostanie wysłane żądanie, i używa RequestProvider klasy do wywołania metody HTTP GET w zasobie przed zwróceniem wyników do CatalogViewModelklasy . Klasa RequestProvider zawiera funkcje, które przesyłają żądanie w postaci identyfikatora URI identyfikującego zasób, metodę HTTP wskazującą operację do wykonania na tym zasobie, oraz treść zawierającą wszelkie dane wymagane do wykonania operacji. Aby uzyskać informacje na temat sposobu RequestProvider wstrzykiwania klasy do CatalogService classklasy , zobacz Wprowadzenie do wstrzykiwania zależności.

Poniższy przykład kodu przedstawia metodę GetAsyncRequestProvider w klasie:

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

Ta metoda wywołuje metodę CreateHttpClient , która zwraca wystąpienie HttpClient klasy z odpowiednim zestawem nagłówków. Następnie przesyła asynchroniczne żądanie GET do zasobu zidentyfikowanego przez identyfikator URI z odpowiedzią przechowywaną w wystąpieniu HttpResponseMessage . HandleResponse Następnie wywoływana jest metoda, która zgłasza wyjątek, jeśli odpowiedź nie zawiera kodu stanu HTTP powodzenia. Następnie odpowiedź jest odczytywana jako ciąg, konwertowana z formatu JSON na CatalogRoot obiekt i zwracana do obiektu CatalogService.

Metoda jest pokazana CreateHttpClient w poniższym przykładzie kodu:

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

Ta metoda tworzy nowe wystąpienie HttpClient klasy i ustawia Accept nagłówek wszystkich żądań wysyłanych przez HttpClient wystąpienie na application/jsonwartość , co wskazuje, że oczekuje zawartości dowolnej odpowiedzi sformatowanej przy użyciu formatu JSON. Następnie, jeśli token dostępu został przekazany jako argument do CreateHttpClient metody, jest dodawany do Authorization nagłówka wszystkich żądań wysyłanych przez HttpClient wystąpienie, poprzedzony ciągiem Bearer. Aby uzyskać więcej informacji na temat autoryzacji, zobacz Autoryzacja.

GetAsync Gdy metoda w RequestProvider klasie wywołuje HttpClient.GetAsyncmetodę w Items klasie w CatalogController projekcie Catalog.API, która jest wyświetlana w poniższym przykładzie kodu:

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

Ta metoda pobiera dane wykazu z bazy danych SQL przy użyciu elementu EntityFramework i zwraca je jako komunikat odpowiedzi zawierający kod stanu HTTP powodzenia oraz kolekcję wystąpień sformatowanych CatalogItem w formacie JSON.

Tworzenie żądania POST

Klasa BasketService służy do zarządzania procesem pobierania i aktualizowania danych za pomocą mikrousługi koszyka. W metodzie RegisterDependenciesViewModelLocator w klasie BasketService klasa jest rejestrowana jako mapowanie typów na typ z IBasketService kontenerem iniekcji zależności autofac. Następnie po utworzeniu BasketViewModel wystąpienia klasy konstruktor akceptuje IBasketService typ, który zostanie rozpoznany przez funkcję Autofac, zwracając wystąpienie BasketService klasy. Aby uzyskać więcej informacji na temat wstrzykiwania zależności, zobacz Wprowadzenie do wstrzykiwania zależności.

Rysunek 10–2 przedstawia interakcje klas, które wysyłają dane koszyka BasketViewwyświetlane przez element , do mikrousługi koszyka.

Sending data to the basket microservice

Rysunek 10–2. Wysyłanie danych do mikrousługi koszyka

Po dodaniu elementu do koszyka zakupów wywoływana ReCalculateTotalAsync jest metoda w BasketViewModel klasie . Ta metoda aktualizuje łączną wartość elementów w koszyku i wysyła dane koszyka do mikrousługi koszyka, jak pokazano w poniższym przykładzie kodu:

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

Ta metoda wywołuje metodę UpdateBasketAsyncBasketService wystąpienia, które zostało wprowadzone do BasketViewModel klasy Autofac. Poniższa metoda przedstawia metodę 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;  
}

Ta metoda tworzy identyfikator URI, który identyfikuje zasób, do którego zostanie wysłane żądanie, i używa RequestProvider klasy do wywołania metody HTTP POST w zasobie przed zwróceniem wyników do BasketViewModelklasy . Należy pamiętać, że token dostępu uzyskany z serwera IdentityServer podczas procesu uwierzytelniania jest wymagany do autoryzowania żądań do mikrousługi koszyka. Aby uzyskać więcej informacji na temat autoryzacji, zobacz Autoryzacja.

Poniższy przykład kodu przedstawia jedną z PostAsync metod w RequestProvider klasie:

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

Ta metoda wywołuje metodę CreateHttpClient , która zwraca wystąpienie HttpClient klasy z odpowiednim zestawem nagłówków. Następnie przesyła asynchroniczne żądanie POST do zasobu zidentyfikowanego przez identyfikator URI z serializowanymi danymi koszyka wysyłanymi w formacie JSON i odpowiedzią przechowywaną w wystąpieniu HttpResponseMessage . HandleResponse Następnie wywoływana jest metoda, która zgłasza wyjątek, jeśli odpowiedź nie zawiera kodu stanu HTTP powodzenia. Następnie odpowiedź jest odczytywana jako ciąg, konwertowana z formatu JSON na CustomerBasket obiekt i zwracana do obiektu BasketService. Aby uzyskać więcej informacji na temat CreateHttpClient metody, zobacz Tworzenie żądania GET.

PostAsync Gdy metoda w RequestProvider klasie wywołuje HttpClient.PostAsyncmetodę , Post wywoływana jest metoda w klasie w BasketController projekcie Basket.API, która jest wyświetlana w poniższym przykładzie kodu:

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

Ta metoda używa wystąpienia RedisBasketRepository klasy do utrwalania danych koszyka w pamięci podręcznej Redis Cache i zwraca je jako komunikat odpowiedzi zawierający kod stanu HTTP powodzenia oraz wystąpienie sformatowane CustomerBasket w formacie JSON.

Tworzenie żądania USUWANIA

Rysunek 10–3 przedstawia interakcje klas, które usuwają dane koszyka z mikrousługi koszyka dla elementu CheckoutView.

Deleteing data from the basket microservice

Rysunek 10–3. Usuwanie danych z mikrousługi koszyka

Po wywołaniu procesu wyewidencjonowania wywoływana CheckoutAsync jest metoda w CheckoutViewModel klasie . Ta metoda tworzy nowe zamówienie przed wyczyszczeniem koszyka zakupów, jak pokazano w poniższym przykładzie kodu:

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

Ta metoda wywołuje metodę ClearBasketAsyncBasketService wystąpienia, które zostało wprowadzone do CheckoutViewModel klasy Autofac. Poniższa metoda przedstawia metodę 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);  
}

Ta metoda tworzy identyfikator URI, który identyfikuje zasób, do którego zostanie wysłane żądanie, i używa RequestProvider klasy do wywołania metody HTTP DELETE w zasobie. Należy pamiętać, że token dostępu uzyskany z serwera IdentityServer podczas procesu uwierzytelniania jest wymagany do autoryzowania żądań do mikrousługi koszyka. Aby uzyskać więcej informacji na temat autoryzacji, zobacz Autoryzacja.

Poniższy przykład kodu przedstawia metodę DeleteAsyncRequestProvider w klasie:

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

Ta metoda wywołuje metodę CreateHttpClient , która zwraca wystąpienie HttpClient klasy z odpowiednim zestawem nagłówków. Następnie przesyła asynchroniczne żądanie DELETE do zasobu zidentyfikowanego przez identyfikator URI. Aby uzyskać więcej informacji na temat CreateHttpClient metody, zobacz Tworzenie żądania GET.

DeleteAsync Gdy metoda w RequestProvider klasie wywołuje HttpClient.DeleteAsyncmetodę , Delete wywoływana jest metoda w klasie w BasketController projekcie Basket.API, która jest wyświetlana w poniższym przykładzie kodu:

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

Ta metoda używa wystąpienia RedisBasketRepository klasy do usunięcia danych koszyka z pamięci podręcznej Redis Cache.

Buforowanie danych

Wydajność aplikacji można poprawić przez buforowanie często używanych danych do szybkiego magazynu znajdującego się w pobliżu aplikacji. Jeśli szybki magazyn znajduje się bliżej aplikacji niż oryginalne źródło, buforowanie może znacznie poprawić czas odpowiedzi podczas pobierania danych.

Najczęstszą formą buforowania jest buforowanie odczytu, w którym aplikacja pobiera dane, odwołując się do pamięci podręcznej. Jeśli dane nie znajdują się w pamięci podręcznej, zostaną pobrane z magazynu danych i dodane do pamięci podręcznej. Aplikacje mogą implementować buforowanie odczytu za pomocą wzorca z odkładaniem do pamięci podręcznej. Ten wzorzec określa, czy element znajduje się obecnie w pamięci podręcznej. Jeśli element nie znajduje się w pamięci podręcznej, jest odczytywany z magazynu danych i dodawany do pamięci podręcznej. Aby uzyskać więcej informacji, zobacz Wzorzec odkładania do pamięci podręcznej.

Napiwek

Buforuj dane, które są często odczytywane i które zmieniają się rzadko. Te dane można dodać do pamięci podręcznej na żądanie przy pierwszym pobraniu przez aplikację. Oznacza to, że aplikacja musi pobrać dane tylko raz z magazynu danych, a kolejny dostęp może zostać spełniony przy użyciu pamięci podręcznej.

Aplikacje rozproszone, takie jak aplikacja referencyjna eShopOnContainers, powinny zawierać zarówno następujące pamięci podręczne, jak i obie z następujących pamięci podręcznych:

  • Udostępniona pamięć podręczna, do której można uzyskać dostęp przez wiele procesów lub maszyn.
  • Prywatna pamięć podręczna, w której dane są przechowywane lokalnie na urządzeniu z uruchomioną aplikacją.

Aplikacja mobilna eShopOnContainers używa prywatnej pamięci podręcznej, gdzie dane są przechowywane lokalnie na urządzeniu, na którym uruchomiono wystąpienie aplikacji. Aby uzyskać informacje o pamięci podręcznej używanej przez aplikację referencyjną eShopOnContainers, zobacz .NET Microservices: Architecture for Containerized .NET Applications (Mikrousługi platformy .NET: architektura konteneryzowanych aplikacji .NET).

Napiwek

Pamięć podręczna jest uważana za przejściowy magazyn danych, który może zniknąć w dowolnym momencie. Upewnij się, że dane są przechowywane w oryginalnym magazynie danych, a także w pamięci podręcznej. Szanse na utratę danych są następnie zminimalizowane, jeśli pamięć podręczna stanie się niedostępna.

Zarządzanie wygaśnięciem danych

Niepraktyczne jest oczekiwanie, że buforowane dane będą zawsze zgodne z oryginalnymi danymi. Dane w oryginalnym magazynie danych mogą ulec zmianie po ich buforowanej pamięci podręcznej, co powoduje, że dane z pamięci podręcznej staną się nieaktualne. W związku z tym aplikacje powinny wdrożyć strategię, która pomaga zapewnić, że dane w pamięci podręcznej są tak aktualne, jak to możliwe, ale mogą również wykrywać i obsługiwać sytuacje, które pojawiają się, gdy dane w pamięci podręcznej staną się nieaktualne. Większość mechanizmów buforowania umożliwia skonfigurowanie pamięci podręcznej w celu wygaśnięcia danych, a tym samym skrócenie okresu, w którym dane mogą być nieaktualne.

Napiwek

Ustaw domyślny czas wygaśnięcia podczas konfigurowania pamięci podręcznej. Wiele pamięci podręcznych implementuje wygaśnięcie, co unieważnia dane i usuwa je z pamięci podręcznej, jeśli nie jest uzyskiwany dostęp przez określony okres. Należy jednak zachować ostrożność podczas wybierania okresu wygaśnięcia. Jeśli będzie zbyt krótki, dane wygasną zbyt szybko, a korzyści z buforowania zostaną zmniejszone. Jeśli jest on zbyt długi, ryzyko związane z danymi staje się nieaktualne. W związku z tym czas wygaśnięcia powinien być zgodny ze wzorcem dostępu dla aplikacji korzystających z danych.

Po wygaśnięciu buforowanych danych należy je usunąć z pamięci podręcznej, a aplikacja musi pobrać dane z oryginalnego magazynu danych i umieścić je z powrotem w pamięci podręcznej.

Istnieje również możliwość, że pamięć podręczna może zostać wypełniona, jeśli dane mogą pozostać zbyt długo. W związku z tym żądania dodania nowych elementów do pamięci podręcznej mogą być wymagane do usunięcia niektórych elementów w procesie znanym jako eksmisji. usługa buforowania zwykle eksmituje dane z najmniej ostatnio używanych danych. Istnieją jednak inne zasady eksmisji, w tym ostatnio używane i pierwszy na pierwszym wyjęcie. Aby uzyskać więcej informacji, zobacz wskazówki Buforowanie.

obrazy Buforowanie

Aplikacja mobilna eShopOnContainers korzysta ze zdalnych obrazów produktów, które korzystają z buforowania. Te obrazy są wyświetlane przez kontrolkę Image i kontrolkę CachedImage udostępnioną przez bibliotekę FFImageLoading .

Kontrolka Xamarin.FormsImage obsługuje buforowanie pobranych obrazów. Buforowanie jest domyślnie włączona i będzie przechowywać obraz lokalnie przez 24 godziny. Ponadto można skonfigurować czas wygaśnięcia za CacheValidity pomocą właściwości . Aby uzyskać więcej informacji, zobacz Pobieranie Buforowanie obrazu.

Kontrolka CachedImage FFImageLoading jest zamiennikiem Xamarin.FormsImage kontrolki, zapewniając dodatkowe właściwości, które umożliwiają dodatkowe funkcje. Wśród tej funkcji kontrolka zapewnia konfigurowalne buforowanie, a jednocześnie obsługuje błędy i ładowanie symboli zastępczych obrazu. Poniższy przykład kodu pokazuje, jak aplikacja mobilna eShopOnContainers używa CachedImage kontrolki w elemecie ProductTemplate, który jest szablonem danych używanym przez kontrolkę ListView w elemecie CatalogView:

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

Kontrolka CachedImage ustawia LoadingPlaceholder właściwości i ErrorPlaceholder na obrazy specyficzne dla platformy. Właściwość LoadingPlaceholder określa obraz, który ma być wyświetlany podczas pobierania obrazu określonego Source przez właściwość, a ErrorPlaceholder właściwość określa obraz, który ma być wyświetlany, jeśli podczas próby pobrania obrazu określonego Source przez właściwość wystąpi błąd.

Jak wskazuje nazwa, kontrolka CachedImage buforuje zdalne obrazy na urządzeniu przez czas określony przez wartość CacheDuration właściwości. Jeśli ta wartość właściwości nie zostanie jawnie ustawiona, zostanie zastosowana wartość domyślna 30 dni.

Zwiększanie odporności

Wszystkie aplikacje komunikujące się z usługami zdalnymi i zasobami muszą być wrażliwe na błędy przejściowe. Przejściowe błędy obejmują chwilową utratę łączności sieciowej z usługami, tymczasową niedostępność usługi lub przekroczenia limitu czasu, gdy usługa jest zajęta. Te błędy są często samonaprawiające, a jeśli akcja jest powtarzana po odpowiednim opóźnieniu, prawdopodobnie zakończy się powodzeniem.

Błędy przejściowe mogą mieć ogromny wpływ na postrzeganą jakość aplikacji, nawet jeśli została dokładnie przetestowana we wszystkich przewidywalnych okolicznościach. Aby zapewnić niezawodne działanie aplikacji komunikującej się z usługami zdalnymi, musi być w stanie wykonać wszystkie następujące czynności:

  • Wykrywaj błędy, gdy wystąpią, i ustal, czy błędy mogą być przejściowe.
  • Spróbuj ponownie wykonać operację, jeśli ustali, że błąd może być przejściowy i śledzić liczbę ponownych prób wykonania operacji.
  • Użyj odpowiedniej strategii ponawiania, która określa liczbę ponownych prób, opóźnienie między poszczególnymi próbami i akcje do wykonania po nieudanej próbie.

Tę przejściową obsługę błędów można osiągnąć, opakowując wszystkie próby uzyskania dostępu do usługi zdalnej w kodzie, który implementuje wzorzec ponawiania prób.

Wzorzec ponawiania prób

Jeśli aplikacja wykryje błąd podczas próby wysłania żądania do usługi zdalnej, może obsłużyć błąd w dowolny z następujących sposobów:

  • Ponów próbę wykonania operacji. Aplikacja może natychmiast ponowić próbę żądania zakończonego niepowodzeniem.
  • Ponów próbę wykonania operacji po opóźnieniu. Aplikacja powinna poczekać odpowiedni czas przed ponowieniu próby żądania.
  • Anulowanie operacji. Aplikacja powinna anulować operację i zgłosić wyjątek.

Strategia ponawiania prób powinna być dostosowana do wymagań biznesowych aplikacji. Na przykład ważne jest, aby zoptymalizować liczbę ponownych prób i interwał ponawiania prób do próby wykonania operacji. Jeśli operacja jest częścią interakcji użytkownika, interwał ponawiania prób powinien być krótki i tylko kilka ponownych prób próbowało uniknąć oczekiwania użytkowników na odpowiedź. Jeśli operacja jest częścią długotrwałego przepływu pracy, w którym anulowanie lub ponowne uruchomienie przepływu pracy jest kosztowne lub czasochłonne, należy poczekać dłużej między próbami i ponowić próbę więcej razy.

Uwaga

Agresywna strategia ponawiania z minimalnym opóźnieniem między próbami i dużą liczbą ponownych prób może obniżyć wydajność zdalnej usługi, która jest uruchomiona blisko lub w pojemności. Ponadto taka strategia ponawiania prób może również mieć wpływ na czas odpowiedzi aplikacji, jeśli stale próbuje wykonać operację kończącą się niepowodzeniem.

Jeśli żądanie nadal kończy się niepowodzeniem po wielu ponownych próbach, lepiej jest zapobiec dalszym żądaniom przechodzącym do tego samego zasobu i zgłaszać błąd. Następnie po upływie określonego okresu aplikacja może wysyłać do zasobu co najmniej jedno żądanie, aby sprawdzić, czy zakończyły się powodzeniem. Aby uzyskać więcej informacji, zobacz Circuit Breaker Patern (Wzorzec wyłącznika).

Napiwek

Nigdy nie implementuj mechanizmu nieskończonego ponawiania prób. Użyj skończonej liczby ponownych prób lub zaimplementuj wzorzec wyłącznika , aby umożliwić usłudze odzyskiwanie.

Aplikacja mobilna eShopOnContainers nie implementuje obecnie wzorca ponawiania próby podczas tworzenia żądań internetowych RESTful. Jednak kontrolka CachedImage dostarczana przez bibliotekę FFImageLoading obsługuje obsługę błędów przejściowych przez ponowienie próby załadowania obrazu. Jeśli ładowanie obrazu nie powiedzie się, zostaną wykonane dalsze próby. Liczba prób jest określana przez RetryCount właściwość i ponawianie prób nastąpi po opóźnieniu określonym przez RetryDelay właściwość. Jeśli te wartości właściwości nie są jawnie ustawione, ich wartości domyślne są stosowane — 3 dla RetryCount właściwości i 250 ms dla RetryDelay właściwości. Aby uzyskać więcej informacji na temat kontrolkiCachedImage, zobacz Buforowanie Images (Obrazy).

Aplikacja referencyjna eShopOnContainers implementuje wzorzec ponawiania prób. Aby uzyskać więcej informacji, w tym omówienie sposobu łączenia wzorca ponawiania z klasąHttpClient, zobacz .NET Microservices: Architecture for Containerized .NET Applications (Mikrousługi platformy .NET: architektura konteneryzowanych aplikacji .NET).

Aby uzyskać więcej informacji na temat wzorca ponawiania, zobacz Wzorzec ponawiania prób .

Wzorzec wyłącznika

W niektórych sytuacjach błędy mogą wystąpić z powodu przewidywanych zdarzeń, które potrwają dłużej. Te błędy mogą wahać się od częściowej utraty łączności do całkowitej awarii usługi. W takich sytuacjach nie ma sensu, aby aplikacja ponawiała próbę wykonania operacji, która prawdopodobnie nie powiedzie się, i zamiast tego powinna zaakceptować, że operacja zakończyła się niepowodzeniem i odpowiednio obsłuży tę awarię.

Wzorzec wyłącznika może uniemożliwić aplikacji wielokrotne wykonywanie operacji, która prawdopodobnie zakończy się niepowodzeniem, a jednocześnie umożliwić aplikacji wykrywanie, czy błąd został rozwiązany.

Uwaga

Przeznaczenie wzorca wyłącznika różni się od wzorca ponawiania. Wzorzec ponawiania umożliwia aplikacji ponowienie próby wykonania operacji w oczekiwaniu, że zakończy się powodzeniem. Wzorzec wyłącznika uniemożliwia aplikacji wykonywanie operacji, która prawdopodobnie zakończy się niepowodzeniem.

Wyłącznik działa jako serwer proxy dla operacji, które mogą zakończyć się niepowodzeniem. Serwer proxy powinien monitorować liczbę ostatnich awarii i użyć tych informacji, aby zdecydować, czy zezwolić operacji na kontynuowanie, czy natychmiast zwrócić wyjątek.

Aplikacja mobilna eShopOnContainers nie implementuje obecnie wzorca wyłącznika. Jednak eShopOnContainers nie. Aby uzyskać więcej informacji, zobacz .NET Microservices: Architecture for Containerized .NET Applications (Mikrousługi platformy .NET: architektura konteneryzowanych aplikacji .NET).

Napiwek

Połącz wzorce ponawiania prób i wyłącznika. Aplikacja może połączyć wzorce ponawiania prób i wyłącznika przy użyciu wzorca ponawiania w celu wywołania operacji za pośrednictwem wyłącznika. Jednak logika ponawiania powinna uwzględniać ewentualne wyjątki zwracane przez wyłącznik i przerwać ponawianie prób, jeśli wyłącznik wskazuje, że błąd nie jest przejściowy.

Aby uzyskać więcej informacji na temat wzorca wyłącznika, zobacz wzorzec wyłącznika .

Podsumowanie

Wiele nowoczesnych rozwiązań internetowych korzysta z usług internetowych hostowanych przez serwery internetowe w celu zapewnienia funkcjonalności zdalnych aplikacji klienckich. Operacje uwidaczniane przez usługę internetową stanowią internetowy interfejs API, a aplikacje klienckie powinny mieć możliwość korzystania z internetowego interfejsu API bez znajomości sposobu implementacji danych lub operacji udostępnianych przez interfejs API.

Wydajność aplikacji można poprawić przez buforowanie często używanych danych do szybkiego magazynu znajdującego się w pobliżu aplikacji. Aplikacje mogą implementować buforowanie odczytu za pomocą wzorca z odkładaniem do pamięci podręcznej. Ten wzorzec określa, czy element znajduje się obecnie w pamięci podręcznej. Jeśli element nie znajduje się w pamięci podręcznej, jest odczytywany z magazynu danych i dodawany do pamięci podręcznej.

Podczas komunikowania się z internetowymi interfejsami API aplikacje muszą być wrażliwe na błędy przejściowe. Przejściowe błędy obejmują chwilową utratę łączności sieciowej z usługami, tymczasową niedostępność usługi lub przekroczenia limitu czasu, gdy usługa jest zajęta. Te błędy są często samonaprawiające, a jeśli akcja jest powtarzana po odpowiednim opóźnieniu, prawdopodobnie zakończy się powodzeniem. W związku z tym aplikacje powinny opakowywać wszystkie próby uzyskania dostępu do internetowego interfejsu API w kodzie, który implementuje mechanizm obsługi błędów przejściowych.