Przykład strumieniowych kanałów informacyjnych
W przykładzie StreamingFeeds pokazano, jak zarządzać kanałami informacyjnymi syndykacji zawierającymi dużą liczbę elementów. Na serwerze przykład pokazuje, jak opóźnić tworzenie poszczególnych SyndicationItem obiektów w kanale informacyjnym do momentu natychmiastowego zapisania elementu do strumienia sieciowego.
Na kliencie przykład pokazuje, jak można użyć niestandardowego formatatora źródła danych syndykacji do odczytywania poszczególnych elementów ze strumienia sieciowego, dzięki czemu odczytywanie kanału informacyjnego nigdy nie jest w pełni buforowane do pamięci.
Aby najlepiej zademonstrować możliwości przesyłania strumieniowego interfejsu API syndykacji, w tym przykładzie użyto nieco mało prawdopodobnego scenariusza, w którym serwer uwidacznia źródło danych zawierające nieskończoną liczbę elementów. W takim przypadku serwer kontynuuje generowanie nowych elementów do kanału informacyjnego, dopóki nie ustali, że klient odczytał określoną liczbę elementów ze źródła danych (domyślnie 10). Dla uproszczenia zarówno klient, jak i serwer są implementowane w tym samym procesie i używają obiektu współużytkowanego ItemCounter
, aby śledzić liczbę elementów generowanych przez klienta. Typ ItemCounter
istnieje tylko w celu umożliwienia czystego zakończenia przykładowego scenariusza i nie jest podstawowym elementem wzorca, który jest pokazany.
Pokaz korzysta z iteratorów języka Visual C# (przy użyciu konstrukcji słowa kluczowego yield return
). Aby uzyskać więcej informacji na temat iteratorów, zobacz temat "Korzystanie z iteratorów" w witrynie MSDN.
Usługa
Usługa implementuje podstawowy WebGetAttribute kontrakt składający się z jednej operacji, jak pokazano w poniższym kodzie.
[ServiceContract]
interface IStreamingFeedService
{
[WebGet]
[OperationContract]
Atom10FeedFormatter StreamedFeed();
}
Usługa implementuje ten kontrakt przy użyciu ItemGenerator
klasy w celu utworzenia potencjalnie nieskończonego strumienia SyndicationItem wystąpień przy użyciu iteratora, jak pokazano w poniższym kodzie.
class ItemGenerator
{
public IEnumerable<SyndicationItem> GenerateItems()
{
while (counter.GetCount() < maxItemsRead)
{
itemsReturned++;
yield return CreateNextItem();
}
}
...
}
Gdy implementacja usługi tworzy źródło danych, dane wyjściowe ItemGenerator.GenerateItems()
są używane zamiast buforowanej kolekcji elementów.
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();
}
W związku z tym strumień elementu nigdy nie jest w pełni buforowany do pamięci. To zachowanie można zaobserwować, ustawiając punkt przerwania w yield return
instrukcji wewnątrz ItemGenerator.GenerateItems()
metody i zauważając, że ten punkt przerwania występuje po raz pierwszy po tym, jak usługa zwróciła wynik StreamedFeed()
metody.
Klient
Klient w tym przykładzie używa niestandardowej SyndicationFeedFormatter implementacji, która opóźnia materializację poszczególnych elementów w kanale informacyjnym zamiast buforowania ich w pamięci. Wystąpienie niestandardowe StreamedAtom10FeedFormatter
jest używane w następujący sposób.
XmlReader reader = XmlReader.Create("http://localhost:8000/Service/Feeds/StreamedFeed");
StreamedAtom10FeedFormatter formatter = new StreamedAtom10FeedFormatter(counter);
SyndicationFeed feed = formatter.ReadFrom(reader);
Zwykle wywołanie polecenia ReadFrom(XmlReader) nie zwraca się do momentu odczytania całej zawartości kanału informacyjnego z sieci i buforowania do pamięci. StreamedAtom10FeedFormatter
Jednak przesłonięcia ReadItems(XmlReader, SyndicationFeed, Boolean) obiektu w celu zwrócenia iteratora zamiast buforowanej kolekcji, jak pokazano w poniższym kodzie.
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();
}
W związku z tym każdy element nie jest odczytywany z sieci, dopóki aplikacja kliencka nie będzie ReadItems()
mogła jej używać. To zachowanie można zaobserwować, ustawiając punkt przerwania w yield return
instrukcji wewnątrz StreamedAtom10FeedFormatter.DelayReadItems()
instrukcji i zauważając, że ten punkt przerwania występuje po raz pierwszy po zakończeniu wywołania ReadFrom()
.
Poniższe instrukcje pokazują, jak skompilować i uruchomić przykład. Należy pamiętać, że mimo że serwer przestaje generować elementy po odczytaniu przez klienta 10 elementów, dane wyjściowe pokazują, że klient odczytuje znacznie więcej niż 10 elementów. Wynika to z faktu, że powiązanie sieciowe używane przez przykład przesyła dane w segmentach czterech kilobajtów (KB). W związku z tym klient otrzymuje 4 KB danych elementu, zanim będzie miał możliwość odczytania nawet jednego elementu. Jest to normalne zachowanie (wysyłanie przesyłanych strumieniowo danych HTTP w rozsądnych segmentach zwiększa wydajność).
Aby skonfigurować, skompilować i uruchomić przykład
Upewnij się, że wykonano procedurę instalacji jednorazowej dla przykładów programu Windows Communication Foundation.
Aby skompilować wersję rozwiązania w języku C# lub Visual Basic .NET, postępuj zgodnie z instrukcjami w temacie Building the Windows Communication Foundation Samples (Tworzenie przykładów programu Windows Communication Foundation).
Aby uruchomić przykład w konfiguracji pojedynczej lub między maszynami, postępuj zgodnie z instrukcjami w temacie Uruchamianie przykładów programu Windows Communication Foundation.