Exemplo de feeds de streaming
O exemplo StreamingFeeds demonstra como gerenciar feeds de distribuição que contêm um grande número de itens. No servidor, o exemplo demonstra como atrasar a criação de objetos individuais SyndicationItem dentro do feed até imediatamente antes que o item seja gravado no fluxo de rede.
No cliente, o exemplo mostra como um formatador de feed de distribuição personalizado pode ser usado para ler itens individuais do fluxo de rede para que o feed que está sendo lido nunca seja totalmente armazenado em buffer na memória.
Para demonstrar melhor a capacidade de streaming da API de distribuição, este exemplo usa um cenário um tanto improvável no qual o servidor expõe um feed que contém um número infinito de itens. Nesse caso, o servidor continua gerando novos itens no feed até determinar que o cliente leu um número especificado de itens do feed (por padrão, 10). Para simplificar, o cliente e o servidor são implementados no mesmo processo e usam um objeto compartilhado ItemCounter
para controlar quantos itens o cliente produziu. O ItemCounter
tipo existe apenas com a finalidade de permitir que o cenário de amostra termine de forma limpa e não é um elemento central do padrão que está sendo demonstrado.
A demonstração faz uso de iteradores Visual C# (usando a construção de yield return
palavra-chave). Para obter mais informações sobre iteradores, consulte o tópico "Usando iteradores" no MSDN.
Serviço
O serviço implementa um contrato básico WebGetAttribute que consiste em uma operação, conforme mostrado no código a seguir.
[ServiceContract]
interface IStreamingFeedService
{
[WebGet]
[OperationContract]
Atom10FeedFormatter StreamedFeed();
}
O serviço implementa esse contrato usando uma ItemGenerator
classe para criar um fluxo potencialmente infinito de SyndicationItem instâncias usando um iterador, conforme mostrado no código a seguir.
class ItemGenerator
{
public IEnumerable<SyndicationItem> GenerateItems()
{
while (counter.GetCount() < maxItemsRead)
{
itemsReturned++;
yield return CreateNextItem();
}
}
...
}
Quando a implementação do serviço cria o feed, a saída do ItemGenerator.GenerateItems()
é usada em vez de uma coleção de itens em buffer.
public Atom10FeedFormatter StreamedFeed()
{
SyndicationFeed feed = new SyndicationFeed("Streamed feed", "Feed to test streaming", null);
//Generate an infinite stream of items. Both the client and the service share
//a reference to the ItemCounter, which allows the sample to terminate
//execution after the client has read 10 items from the stream
ItemGenerator itemGenerator = new ItemGenerator(this.counter, 10);
feed.Items = itemGenerator.GenerateItems();
return feed.GetAtom10Formatter();
}
Como resultado, o fluxo de itens nunca é totalmente armazenado em buffer na memória. Você pode observar esse comportamento definindo um ponto de interrupção na yield return
instrução dentro do método e observando que esse ponto de ItemGenerator.GenerateItems()
interrupção é encontrado pela primeira vez depois que o serviço retornou o resultado do StreamedFeed()
método.
Cliente
O cliente neste exemplo usa uma implementação personalizada SyndicationFeedFormatter que atrasa a materialização de itens individuais no feed em vez de armazená-los em buffer na memória. A instância personalizada StreamedAtom10FeedFormatter
é usada da seguinte maneira.
XmlReader reader = XmlReader.Create("http://localhost:8000/Service/Feeds/StreamedFeed");
StreamedAtom10FeedFormatter formatter = new StreamedAtom10FeedFormatter(counter);
SyndicationFeed feed = formatter.ReadFrom(reader);
Normalmente, uma chamada para ReadFrom(XmlReader) não retorna até que todo o conteúdo do feed tenha sido lido da rede e armazenado em buffer na memória. No entanto, o StreamedAtom10FeedFormatter
objeto substitui ReadItems(XmlReader, SyndicationFeed, Boolean) para retornar um iterador em vez de uma coleção em buffer, conforme mostrado no código a seguir.
protected override IEnumerable<SyndicationItem> ReadItems(XmlReader reader, SyndicationFeed feed, out bool areAllItemsRead)
{
areAllItemsRead = false;
return DelayReadItems(reader, feed);
}
private IEnumerable<SyndicationItem> DelayReadItems(XmlReader reader, SyndicationFeed feed)
{
while (reader.IsStartElement("entry", "http://www.w3.org/2005/Atom"))
{
yield return this.ReadItem(reader, feed);
}
reader.ReadEndElement();
}
Como resultado, cada item não é lido da rede até que o aplicativo cliente que atravessa os resultados esteja ReadItems()
pronto para usá-lo. Você pode observar esse comportamento definindo um ponto de interrupção na yield return
instrução dentro de e percebendo que esse ponto de interrupção é encontrado pela primeira vez após a conclusão da StreamedAtom10FeedFormatter.DelayReadItems()
chamada ReadFrom()
.
As instruções a seguir mostram como criar e executar o exemplo. Observe que, embora o servidor pare de gerar itens depois que o cliente leu 10 itens, a saída mostra que o cliente lê significativamente mais de 10 itens. Isso ocorre porque a ligação de rede usada pelo exemplo transmite dados em segmentos de quatro kilobytes (KB). Como tal, o cliente recebe 4KB de dados do item antes de ter a oportunidade de ler até mesmo um item. Este é um comportamento normal (enviar dados HTTP transmitidos em segmentos de tamanho razoável aumenta o desempenho).
Para configurar, compilar e executar o exemplo
Certifique-se de ter executado o procedimento de instalação única para os exemplos do Windows Communication Foundation.
Para criar a edição C# ou Visual Basic .NET da solução, siga as instruções em Criando os exemplos do Windows Communication Foundation.
Para executar o exemplo em uma configuração de máquina única ou cruzada, siga as instruções em Executando os exemplos do Windows Communication Foundation.