Acesso a dados remotos

Observação

Este eBook foi publicado na primavera de 2017 e não foi atualizado desde então. Há muito no livro que permanece valioso, mas parte do material está desatualizado.

Muitas soluções modernas baseadas na Web usam serviços Web, hospedados por servidores Web, para fornecer funcionalidades para aplicativos de cliente remoto. As operações que um serviço Web expõe constituem uma API da Web.

Os aplicativos cliente devem conseguir usar a API Web sem saber como os dados ou as operações expostas pela API são implementadas. Isso requer que a API opere em conformidade com padrões comuns que permitem que um serviço Web e aplicativos cliente concordem sobre quais formatos de dados usar e a estrutura dos dados que são trocados entre aplicativos cliente e o serviço Web.

Introdução à Transferência de Estado Representacional

REST (Transferência de Estado Representacional) é um estilo arquitetural para a criação de sistemas distribuídos com base em hipermídia. Uma vantagem principal do modelo REST é que ele é baseado em padrões abertos e não vincula a implementação do modelo ou os aplicativos cliente que o acessam a nenhuma implementação específica. Portanto, um serviço Web REST pode ser implementado usando o Microsoft ASP.NET Core MVC, e os aplicativos cliente podem estar se desenvolvendo usando qualquer idioma e conjunto de ferramentas que possa gerar solicitações HTTP e analisar respostas HTTP.

O modelo de REST usa um esquema de navegação para representar objetos e serviços em uma rede, mencionados como funcionalidades. Sistemas que implementam REST geralmente usam o protocolo HTTP para transmitir solicitações para acessar esses recursos. Nesses sistemas, um aplicativo cliente envia uma solicitação na forma de um URI que identifica um recurso e um método HTTP (como GET, POST, PUT ou DELETE) que indica a operação a ser executada nesse recurso. O corpo da solicitação HTTP contém quaisquer dados necessários para executar a operação.

Observação

REST define um modelo de solicitação sem estado. Portanto, as solicitações HTTP devem ser independentes e podem ocorrer em qualquer ordem.

A resposta de uma solicitação REST usa códigos de status HTTP padrão. Por exemplo, uma solicitação que retorna dados válidos deve incluir o código de resposta HTTP 200 (OK), enquanto uma solicitação que não consegue localizar ou excluir um recurso especificado deve retornar uma resposta que inclui o código de status HTTP 404 (Não Encontrado).

Uma API Web RESTful expõe um conjunto de recursos conectados e fornece as principais operações que permitem que um aplicativo manipule esses recursos e navegue facilmente entre eles. Por esse motivo, os URIs que constituem uma API da Web RESTful típica são orientados para os dados que ela expõe, além de usar os recursos fornecidos pelo HTTP para realizar operações com esses dados.

Os dados incluídos por um aplicativo cliente em uma solicitação HTTP e as mensagens de resposta correspondentes do servidor da Web podem ser apresentados em vários formatos, conhecidos como tipos de mídia. Quando um aplicativo cliente envia uma solicitação que retorna dados no corpo de uma mensagem, ele pode especificar os tipos de mídia que ele pode manipular no Accept cabeçalho da solicitação. Se o servidor Web der suporte a esse tipo de mídia, ele poderá responder com uma resposta que inclui o Content-Type cabeçalho que especifica o formato dos dados no corpo da mensagem. É responsabilidade do aplicativo cliente analisar a mensagem de resposta e interpretar os resultados no corpo da mensagem adequadamente.

Para obter mais informações sobre REST, consulte Design de API e implementação de API.

Consumo de APIs RESTful

O aplicativo móvel eShopOnContainers usa o padrão MVVM (Model-View-ViewModel) e os elementos de modelo do padrão representam as entidades de domínio usadas no aplicativo. As classes de controlador e repositório no aplicativo de referência eShopOnContainers aceitam e retornam a maioria desses objetos de modelo. Portanto, eles são usados como DTOs (objetos de transferência de dados) que contêm todos os dados passados entre o aplicativo móvel e os microsserviços conteinerizados. O principal benefício de usar DTOs para passar e receber dados de um serviço Web é que, ao transmitir mais dados em uma única chamada remota, o aplicativo pode reduzir o número de chamadas que precisam ser feitas.

Como fazer solicitações da Web

O aplicativo móvel eShopOnContainers usa a HttpClient classe para fazer solicitações por HTTP, com JSON sendo usado como o tipo de mídia. Essa classe fornece funcionalidade para enviar solicitações e receber respostas HTTP de maneira assíncrona um recurso identificado por URI. A HttpResponseMessage classe representa uma mensagem de resposta HTTP recebida de uma API REST depois que uma solicitação HTTP é feita. Ela contém informações sobre a resposta, incluindo o código de status, cabeçalhos e qualquer corpo. A HttpContent classe representa o corpo HTTP e os cabeçalhos de conteúdo, como Content-Type e Content-Encoding. O conteúdo pode ser lido usando qualquer um dos métodos de ReadAs, como ReadAsStringAsync e ReadAsByteArrayAsync, dependendo do formato dos dados.

Fazendo uma solicitação GET

A classe CatalogService é usada para gerenciar o processo de recuperação de dados do microsserviço de catálogo. RegisterDependencies No método na ViewModelLocator classe , a CatalogService classe é registrada como um mapeamento de tipo em relação ao ICatalogService tipo com o contêiner de injeção de dependência autofac. Em seguida, quando uma instância da CatalogViewModel classe é criada, seu construtor aceita um ICatalogService tipo, que o Autofac resolve, retornando uma instância da CatalogService classe . Para obter mais informações sobre injeção de dependência, consulte Introdução à injeção de dependência.

A Figura 10-1 mostra a interação de classes que leem dados de catálogo do microsserviço de catálogo para exibição pelo CatalogView.

Recuperando dados do microsserviço de catálogo

Figura 10-1: Recuperando dados do microsserviço de catálogo

Quando o CatalogView é navegado para, o OnInitialize método na CatalogViewModel classe é chamado. Esse método recupera dados correspondentes do microsserviço de catálogo, conforme demonstrado no exemplo de código a seguir:

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

Esse método chama o GetCatalogAsync método da CatalogService instância que foi injetado no CatalogViewModel pelo Autofac. O seguinte exemplo de código mostra o método 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();            
}

Esse método cria o URI que identifica o recurso para o qual a solicitação será enviada e usa a classe RequestProviderpara invocar o método HTTP GET no recurso, antes de retornar os resultados para o CatalogViewModel. A classe RequestProvider contém a funcionalidade que envia uma solicitação na forma de um URI que identifica um recurso, um método HTTP que indica a operação a ser executada nesse recurso e um corpo que contém todos os dados necessários para executar a operação. Para obter informações sobre como a RequestProvider classe é injetada no CatalogService class, consulte Introdução à injeção de dependência.

O exemplo de código a seguir mostra o método GetAsync na classe 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;  
}

Esse método chama o método CreateHttpClient, que retorna uma instância da classe HttpClient com os cabeçalhos apropriados definidos. Em seguida, ele envia uma solicitação GET assíncrona para o recurso identificado pelo URI, com a resposta sendo armazenada na HttpResponseMessage instância. O método HandleResponse é invocado, o que lança uma exceção se a resposta não incluir um código de status HTTP de sucesso. A resposta então é lida como uma cadeia de caracteres, convertida de JSON para um objeto de CatalogRoot e retornada para o CatalogService.

O método CreateHttpClient é mostrado no seguinte exemplo de código:

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

Esse método cria uma nova instância da HttpClient classe e define o Accept cabeçalho de todas as solicitações feitas pela instância como application/json, o HttpClient que indica que ele espera que o conteúdo de qualquer resposta seja formatado usando JSON. Então, se um token de acesso foi passado como um argumento para o método CreateHttpClient, ele é adicionado ao cabeçalho Authorization de todas as solicitações feitas pela instância HttpClient, prefixado com a cadeia de caracteres Bearer. Para obter mais informações sobre autorização, confira Autorização.

Quando o GetAsync método na RequestProvider classe chama HttpClient.GetAsync, o Items método na CatalogController classe no projeto Catalog.API é invocado, o que é mostrado no exemplo de código a seguir:

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

Esse método recupera os dados de catálogo do banco de dados SQL usando EntityFramework e os retorna como uma mensagem de resposta que inclui um código http status de êxito e uma coleção de instâncias formatadas CatalogItem em JSON.

Fazendo uma solicitação POST

A classe BasketService é usada para gerenciar o processo de recuperação e atualização de dados com o microsserviço de cesta. RegisterDependencies No método na ViewModelLocator classe , a BasketService classe é registrada como um mapeamento de tipo em relação ao IBasketService tipo com o contêiner de injeção de dependência autofac. Em seguida, quando uma instância da BasketViewModel classe é criada, seu construtor aceita um IBasketService tipo, que o Autofac resolve, retornando uma instância da BasketService classe . Para obter mais informações sobre injeção de dependência, consulte Introdução à injeção de dependência.

A Figura 10-2 mostra a interação das classes que enviam os dados da cesta exibidos pelo BasketView, para o microsserviço de cesta.

Enviando dados para o microsserviço de cesta

Figura 10-2: Enviando dados para o microsserviço de cesta

Quando um item é adicionado à cesta de compras, o método ReCalculateTotalAsync na classe BasketViewModel é chamado. Esse método atualiza o valor total dos itens na cesta e envia esses dados para o microsserviço da cesta, conforme demonstrado no exemplo de código a seguir:

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

Esse método chama o UpdateBasketAsync método da BasketService instância que foi injetado no BasketViewModel pelo Autofac. O método a seguir mostra o método 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;  
}

Esse método cria o URI que identifica o recurso para o qual a solicitação será enviada e usa a classe RequestProviderpara invocar o método HTTP POST no recurso, antes de retornar os resultados para o BasketViewModel. Observe que um token de acesso, obtido do IdentityServer durante o processo de autenticação, é necessário para autorizar solicitações para o microsserviço de cesta. Para obter mais informações sobre autorização, confira Autorização.

O exemplo de código a seguir mostra um dos métodos PostAsync da classe RequestProvider:

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

Esse método chama o método CreateHttpClient, que retorna uma instância da classe HttpClient com os cabeçalhos apropriados definidos. Ele então envia uma solicitação POST assíncrona para o recurso identificado pelo URI, com os dados serializados da cesta enviados no formato JSON e a resposta armazenada na instância HttpResponseMessage. O método HandleResponse é invocado, o que lança uma exceção se a resposta não incluir um código de status HTTP de sucesso. Em seguida, a resposta é lida como uma cadeia de caracteres, convertida de JSON em um CustomerBasket objeto e retornada para o BasketService. Para obter mais informações sobre o CreateHttpClient método, consulte Fazendo uma solicitação GET.

Quando o PostAsync método na RequestProvider classe chama HttpClient.PostAsync, o Post método na BasketController classe no projeto Basket.API é invocado, o que é mostrado no exemplo de código a seguir:

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

Esse método usa uma instância da classe RedisBasketRepository para manter os dados da cesta no cache Redis e os retorna como uma mensagem de resposta que inclui um código de status HTTP de sucesso e uma instância CustomerBasket formatada em JSON.

Fazendo uma solicitação DELETE

A Figura 10-3 mostra as interações de classes que excluem dados de cesta do microsserviço de cesta, para o CheckoutView.

Excluindo dados do microsserviço de cesta

Figura 10-3: Excluindo dados do microsserviço de cesta

Quando o processo de check-out é invocado, o método CheckoutAsync na classe CheckoutViewModel é chamado. Este método cria um novo pedido antes de limpar a cesta de compras, conforme demonstrado no exemplo de código a seguir:

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

Esse método chama o ClearBasketAsync método da BasketService instância que foi injetado no CheckoutViewModel pelo Autofac. O método a seguir mostra o método 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);  
}

Esse método cria o URI que identifica o recurso para o qual a solicitação será enviada e usa a RequestProvider classe para invocar o método HTTP DELETE no recurso. Observe que um token de acesso, obtido do IdentityServer durante o processo de autenticação, é necessário para autorizar solicitações para o microsserviço de cesta. Para obter mais informações sobre autorização, confira Autorização.

O exemplo de código a seguir mostra o método DeleteAsync na classe RequestProvider:

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

Esse método chama o método CreateHttpClient, que retorna uma instância da classe HttpClient com os cabeçalhos apropriados definidos. Em seguida, ele envia uma solicitação DELETE assíncrona para o recurso identificado pelo URI. Para obter mais informações sobre o CreateHttpClient método , consulte Fazendo uma solicitação GET.

Quando o DeleteAsync método na RequestProvider classe chama HttpClient.DeleteAsync, o Delete método na BasketController classe no projeto Basket.API é invocado, o que é mostrado no exemplo de código a seguir:

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

Esse método usa uma instância da classe RedisBasketRepository para excluir os dados da cesta do cache Redis.

Armazenando dados em cache

O desempenho de um aplicativo pode ser melhorado transferindo para cache dados acessados com frequência no armazenamento rápido localizado próximo ao aplicativo. Se o armazenamento rápido estiver localizado mais próximo do aplicativo do que da fonte original, o armazenamento em cache pode melhorar significativamente os tempos de resposta na recuperação de dados.

A forma mais comum de cache é o de leitura, em que um aplicativo recupera dados referenciando o cache. Se os dados não estiverem no cache, ele é recuperado do armazenamento de dados e adicionado ao cache. Os aplicativos podem implementar o cache de leitura com o padrão cache-aside. Esse padrão determina se o item está atualmente no cache. Se os item não estiver no cache, ele será recuperado do armazenamento de dados e adicionado a ele. Para obter mais informações, consulte o padrão Cache-Aside .

Dica

Armazenar dados em cache lidos com frequência e que são alterados com pouca frequência. Os dados poderão ser adicionados ao cache sob demanda na primeira vez em que forem recuperados por um aplicativo. Isso significa que o aplicativo precisa buscar os dados apenas uma vez no armazenamento de dados e o acesso subsequente pode ser atendido pelo uso do cache.

Aplicativos distribuídos, como o aplicativo de referência eShopOnContainers, devem fornecer um ou ambos os seguintes caches:

  • Um cache compartilhado, que pode ser acessado por vários processos ou computadores.
  • Um cache privado, em que os dados são mantidos localmente no dispositivo que executa o aplicativo.

O aplicativo móvel eShopOnContainers usa um cache privado, em que os dados são mantidos localmente no dispositivo que está executando uma instância do aplicativo. Para obter informações sobre o cache usado pelo aplicativo de referência eShopOnContainers, confira Microsserviços do .NET: Arquitetura para Aplicativos .NET Conteinerizados.

Dica

Pense no cache como um armazenamento de dados transitório que pode desaparecer a qualquer momento. Verifique se os dados são mantidos no armazenamento de dados original, bem como no cache. As chances de perda de dados serão minimizadas se o cache ficar indisponível.

Gerenciando a expiração de dados

É impraticável esperar que os dados armazenados em cache sempre sejam consistentes com os dados originais. Dados em armazenamentos originais podem ser alterados depois do armazenamento em cache, fazendo com que os dados em cache se tornem obsoletos. Portanto, os aplicativos devem implementar uma estratégia que ajude a garantir que os dados no cache sejam tão atualizados quanto possível, mas também possam detectar e lidar com situações que possam surgir quando os dados no cache se tornarem obsoletos. A maioria dos mecanismos de cache permite que ele seja configurado de modo que os dados expirem, reduzindo, portanto, o potencial período de desatualização dos dados.

Dica

Defina um tempo de expiração padrão ao configurar um cache. Muitos caches implementam a expiração, que invalida os dados e os remove do cache se não forem acessados por um período especificado. No entanto, é necessário ter cuidado ao escolher o período de expiração. Se for muito curto, os dados expirarão muito rapidamente e os benefícios do cache serão reduzidos. Se for muito longo, os dados poderão ficar obsoletos. Portanto, o tempo de expiração deve corresponder ao padrão de acesso em que os aplicativos usam os dados.

Quando os dados armazenados em cache expiram, eles devem ser removidos do cache, e o aplicativo deve recuperá-los do armazenamento de dados original e colocá-los novamente no cache.

Também é possível que um cache fique cheio se os dados permanecerem por um período muito longo. Portanto, as solicitações para adicionar novos itens ao cache podem ser necessárias para remover alguns itens em um processo conhecido como eviction. Os serviços de cache geralmente despejam os dados usados menos recentemente. No entanto, há outras políticas de remoção, incluindo usadas mais recentemente, e primeiro a entrar primeiro a sair. Para obter mais informações, consulte Diretrizes de cache.

Armazenando imagens em cache

O aplicativo móvel eShopOnContainers consome imagens de produtos remotos que se beneficiam de serem armazenadas em cache. Essas imagens são exibidas pelo Image controle e pelo CachedImage controle fornecido pela biblioteca FFImageLoading .

O Xamarin.FormsImage controle dá suporte ao cache de imagens baixadas. O cache está habilitado por padrão e armazenará a imagem localmente por 24 horas. Além disso, o tempo de expiração pode ser configurado com a CacheValidity propriedade . Para obter mais informações, consulte Cache de imagem baixado.

O controle de CachedImage FFImageLoading é uma substituição para o Xamarin.FormsImage controle, fornecendo propriedades adicionais que habilitam a funcionalidade suplementar. Entre essa funcionalidade, o controle fornece cache configurável, ao mesmo tempo em que dá suporte a espaços reservados para erros e carregamento de imagens. O exemplo de código a seguir mostra como o aplicativo móvel eShopOnContainers usa o CachedImage controle no ProductTemplate, que é o modelo de dados usado pelo ListView controle no 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>

O CachedImage controle define as LoadingPlaceholder propriedades e ErrorPlaceholder como imagens específicas da plataforma. A LoadingPlaceholder propriedade especifica a imagem a ser exibida enquanto a imagem especificada pela Source propriedade é recuperada e a ErrorPlaceholder propriedade especifica a imagem a ser exibida se ocorrer um erro ao tentar recuperar a imagem especificada pela Source propriedade .

Como o nome indica, o CachedImage controle armazena em cache imagens remotas no dispositivo pelo tempo especificado pelo valor da CacheDuration propriedade. Quando esse valor de propriedade não é definido explicitamente, o valor padrão de 30 dias é aplicado.

Aumentando a resiliência

Todos os aplicativos que se comunicam com serviços e recursos remotos são suscetíveis a falhas transitórias. As falhas transitórias incluem a perda momentânea da conectividade de rede e serviços, a indisponibilidade temporária de um serviço ou tempos limite que surgem quando um serviço está ocupado. Essas falhas geralmente são corrigidas automaticamente. Se a ação for repetida após um tempo razoável, é provável que seja bem-sucedida.

As falhas transitórias podem ter um enorme impacto na qualidade percebida de um aplicativo, mesmo que ele tenha sido integralmente testado sob todas as circunstâncias previsíveis. Para garantir que um aplicativo que se comunique com serviços remotos opere de forma confiável, ele deve ser capaz de fazer o seguinte:

  • Detectar falhas quando elas ocorrem e determine se as falhas provavelmente serão transitórias.
  • Repetir a operação se for determinado que a falha provavelmente será transitória e acompanhar o número de vezes que a operação foi repetida.
  • Usar uma estratégia de repetição apropriada, que especifique o número de tentativas, o atraso entre cada tentativa e as ações a serem executadas após uma tentativa com falha.

Esse tratamento de falha transitória pode ser obtido encapsulando todas as tentativas de acessar um serviço remoto no código que implementa o padrão de repetição.

Padrão de Repetição

Se um aplicativo detectar uma falha ao tentar enviar uma solicitação para um serviço remoto, ele poderá lidar com a falha de uma das seguintes maneiras:

  • Repetindo a operação. O aplicativo pode repetir a solicitação com falha imediatamente.
  • Repetindo a operação após um atraso. O aplicativo deve esperar por um tempo adequado antes de tentar executar novamente a solicitação.
  • Cancelando a operação. O aplicativo deve cancelar a operação e relatar uma exceção.

A estratégia de repetição deve ser ajustada para corresponder às demandas comerciais do aplicativo. Por exemplo, é importante otimizar a contagem de repetições e o intervalo de repetição para a operação que está sendo tentada. Se a operação fizer parte de uma interação do usuário, o intervalo de repetição deve ser curto e ter apenas algumas tentativas para evitar que os usuários esperem por uma resposta. Se a operação fizer parte de um fluxo de trabalho de execução prolongada, onde cancelar ou reiniciar o fluxo de trabalho for caro ou demorado, é apropriado esperar mais tempo entre as tentativas e repetir mais vezes.

Observação

Uma estratégia de repetição agressiva com atraso mínimo entre as tentativas e um grande número de tentativas pode degradar um serviço remoto que está sendo executado próximo ou no limite da capacidade. Adicionalmente, essa estratégia de repetição também pode afetar a capacidade de resposta do aplicativo se ele estiver tentando executar uma operação com falha de maneira contínua.

Se uma solicitação ainda falhar após várias tentativas, é melhor para o aplicativo impedir que outras solicitações sejam enviadas para o mesmo recurso e relatar uma falha. Então, após um período definido, o aplicativo pode fazer uma ou mais solicitações ao recurso para verificar se houve sucesso na tentativa. Para obter mais informações, consulte Padrão de disjuntor.

Dica

Nunca implemente um mecanismo de repetição infinita. Use um número finito de novas tentativas ou implemente o padrão como Disjuntor para permitir a recuperação de um serviço.

No momento, o aplicativo móvel eShopOnContainers não implementa o padrão de repetição ao fazer solicitações da Web RESTful. No entanto, o CachedImage controle, fornecido pela biblioteca FFImageLoading , dá suporte ao tratamento de falhas transitórias repetindo o carregamento de imagens. Se o carregamento de imagens falhar, novas tentativas serão feitas. O número de tentativas é especificado pela propriedade e as RetryCount novas tentativas ocorrerão após um atraso especificado pela RetryDelay propriedade . Se esses valores de propriedade não forem definidos explicitamente, seus valores padrão serão aplicados – 3 para a RetryCount propriedade e 250ms para a RetryDelay propriedade. Para obter mais informações sobre o CachedImage controle, consulte Cache de imagens.

O aplicativo de referência eShopOnContainers implementa o padrão de repetição. Para obter mais informações, incluindo uma discussão sobre como combinar o padrão de repetição com a HttpClient classe , consulte Microsserviços do .NET: Arquitetura para aplicativos .NET em contêineres.

Para obter mais informações sobre o padrão de repetição, consulte o padrão de repetição .

Padrão de disjuntor

Em algumas situações, falhas podem ocorrer devido a eventos previstos que levam mais tempo para serem corrigidos. Essas falhas podem variar de uma perda parcial de conectividade até a falha completa de um serviço. Nessas situações, aplicativo tentar novamente uma operação que provavelmente não será bem-sucedida será inútil e, alternativamente, ele deve aceitar que a operação falhou e lidar com essa falha de acordo.

O padrão do disjuntor pode impedir que um aplicativo tente executar uma operação com probabilidade de falha repetidamente, além de permitir que o aplicativo detecte se a falha foi resolvida.

Observação

A finalidade do padrão do disjuntor é diferente do padrão de repetição. O padrão de Repetição permite que um aplicativo volte a tentar executar uma operação na expectativa que haverá êxito. O padrão do disjuntor impede que um aplicativo execute uma operação que provavelmente falhará.

Um disjuntor atua como um proxy para operações que podem falhar. O proxy deve monitorar o número de falhas recentes ocorridas e usar essas informações para decidir se deve permitir que a operação continue ou retorne uma exceção imediatamente.

No momento, o aplicativo móvel eShopOnContainers não implementa o padrão de disjuntor. No entanto, o eShopOnContainers o implementa. Para obter mais informações, confira Microsserviços do .NET: Arquitetura do .NET para Aplicativos Conteinerizados.

Dica

Combine os padrões de repetição e do disjuntor. Um aplicativo pode combinar os padrões de repetição e do disjuntor usando o padrão de repetição para invocar uma operação por meio de um disjuntor. No entanto, a lógica de repetição deverá ser sensível às exceções retornadas pelo disjuntor e abandonar as novas tentativas se o disjuntor indicar que uma falha não é transitória.

Para obter mais informações sobre o padrão de disjuntor, consulte o padrão disjuntor .

Resumo

Muitas soluções modernas baseadas na Web usam serviços Web, hospedados por servidores Web, para fornecer funcionalidades para aplicativos de cliente remoto. As operações que um serviço Web expõe constituem uma API Web, e os aplicativos cliente devem conseguir usar a API Web sem saber como os dados ou operações que a API expõe são implementados.

O desempenho de um aplicativo pode ser melhorado transferindo para cache dados acessados com frequência no armazenamento rápido localizado próximo ao aplicativo. Os aplicativos podem implementar o cache de leitura com o padrão cache-aside. Esse padrão determina se o item está atualmente no cache. Se os item não estiver no cache, ele será recuperado do armazenamento de dados e adicionado a ele.

Ao se comunicar com APIs Web, os aplicativos devem conseguir detectar falhas transitórias. As falhas transitórias incluem a perda momentânea da conectividade de rede e serviços, a indisponibilidade temporária de um serviço ou tempos limite que surgem quando um serviço está ocupado. Essas falhas geralmente são corrigidas automaticamente. Se a ação for repetida após um tempo razoável, é provável que seja bem-sucedida. Portanto, os aplicativos devem encapsular todas as tentativas de acessar uma API Web no código que implementa um mecanismo transitório de tratamento de falhas.