Поделиться через


Пример потоковых веб-каналов

В примере StreamingFeeds показано, как управлять веб-каналами синдикации, содержащими большое количество элементов. На сервере в примере показано, как отложить создание отдельных объектов SyndicationItem в ленте непосредственно перед записью элемента в сетевой поток.

На стороне клиента приведен пример того, как можно использовать настраиваемый модуль форматирования потока синдикации для чтения отдельных элементов из сетевого потока, чтобы читаемый поток никогда не буферизировался полностью в память.

Чтобы лучше всего продемонстрировать возможности потоковой передачи API синдикации, в этом примере используется несколько маловероятный сценарий, в котором сервер предоставляет канал, содержащий бесконечное количество элементов. В этом случае сервер продолжает создавать новые элементы в веб-канале, пока не определит, что клиент считывает указанное количество элементов из веб-канала (по умолчанию 10). Для простоты клиент и сервер реализуются в одном процессе и используют общий ItemCounter объект для отслеживания количества созданных клиентом элементов. ItemCounter Тип существует только для того, чтобы разрешить примеру сценария завершиться чисто и не является основным элементом демонстрируемого шаблона.

Демонстрация использует итераторы Visual C# (с помощью ключевого слова yield return). Дополнительные сведения об итераторах см. в разделе "Использование итераторов" в MSDN.

Услуга

Служба реализует базовый WebGetAttribute контракт, состоящий из одной операции, как показано в следующем коде.

[ServiceContract]
interface IStreamingFeedService
{
    [WebGet]
    [OperationContract]
    Atom10FeedFormatter StreamedFeed();
}

Служба реализует этот контракт с помощью ItemGenerator класса для создания потенциально бесконечного SyndicationItem потока экземпляров с помощью итератора, как показано в следующем коде.

class ItemGenerator
{
    public IEnumerable<SyndicationItem> GenerateItems()
    {
        while (counter.GetCount() < maxItemsRead)
        {
            itemsReturned++;
            yield return CreateNextItem();
        }

    }
    ...
}

Когда реализация службы создает веб-канал, выходные данные ItemGenerator.GenerateItems() используются вместо буферизованной коллекции элементов.

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

В результате поток элементов никогда не полностью буферизуется в память. Вы можете наблюдать это поведение, установив точку останова на операторе yield return внутри метода ItemGenerator.GenerateItems() и отметив, что эта точка останова срабатывает впервые после того, как служба возвращает результат метода StreamedFeed().

Клиент

Клиент в этом примере использует пользовательскую SyndicationFeedFormatter реализацию, которая задерживает материализацию отдельных элементов в ленте путем отсрочки, вместо буферизации их в памяти. Пользовательский StreamedAtom10FeedFormatter экземпляр используется следующим образом.

XmlReader reader = XmlReader.Create("http://localhost:8000/Service/Feeds/StreamedFeed");
StreamedAtom10FeedFormatter formatter = new StreamedAtom10FeedFormatter(counter);

SyndicationFeed feed = formatter.ReadFrom(reader);

Как правило, вызов ReadFrom(XmlReader) не возвращается до тех пор, пока все содержимое веб-канала не загружается из сети и сохраняется в память. Однако объект StreamedAtom10FeedFormatter переопределяет ReadItems(XmlReader, SyndicationFeed, Boolean), чтобы возвращать итератор вместо буферной коллекции, как показано в следующем коде.

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

В результате каждый элемент не считывается из сети, пока клиентское приложение, просматривающее результаты ReadItems(), не будет готово их использовать. Это поведение можно наблюдать, задав точку останова в yield return инструкции внутри StreamedAtom10FeedFormatter.DelayReadItems() и отметив, что эта точка останова встречается впервые после завершения вызова ReadFrom().

В следующих инструкциях показано, как создать и запустить пример. Обратите внимание, что хотя сервер перестает создавать элементы после того, как клиент прочитал 10 элементов, выходные данные показывают, что клиент считывает более 10 элементов. Это связано с тем, что сетевая привязка, используемая примером, передает данные в сегментах размером четыре килобайта (КБ). Таким образом, клиент получает 4 КБ данных элемента, прежде чем может прочитать хотя бы один элемент. Это нормальное поведение (отправка потоковых данных HTTP в сегментах с разумным размером увеличивает производительность).

Настройка, сборка и запуск примера

  1. Убедитесь, что вы выполнили процедуру настройки One-Time для образцов Windows Communication Foundation.

  2. Чтобы создать версию решения на C# или Visual Basic .NET, следуйте инструкциям по сборке примеров Windows Communication Foundation .

  3. Чтобы запустить пример в конфигурации с одним или несколькими компьютерами, следуйте инструкциям в запуска примеров Windows Communication Foundation.

См. также