MSMQ

Niezawodność to jedna z najbardziej wymagających cech, które musi posiadać dzisiejsze oprogramowanie. Ta cecha jest nawet punktem krytycznym dla rozproszonych biznesowych aplikacji. Jeśli jeden człon rozproszonego systemu przestanie działać  cały przepływ akcji  się zawala.

Oczywiście ktoś mądry wymyślił rozwiązanie, które pozwoli nam zabezpieczyć przepływ procesu, nawet gdy jeden z członów przestanie działać, ponieważ na przykład w całym mieście nie ma prądu.

Jest to mechanizm kolejki inaczej zwany “Message queue”* .

Kolejka komunikatów jest asynchronicznym protokołem komunikacyjnym, co oznacza, że odbiorca i nadawca wiadomości nie muszą łączyć się z kolejką w tym samym czasie. Komunikaty przesłane kolejce są przechowywane aż do czasu odebrania przez inny proces.

Duża liczba implementacji kolejek działa tylko w obrębie systemu operacyjnego* lub aplikacji. Takie mechanizmy przeznaczone są tylko dla celów tych systemów.

Zgodnie z definicją z Wikipedii kolejka pozwala nam przetrzymać komunikat, jeśli jeden z członów został uszkodzony. Gdy człon wrócił wtedy wszystkie przetrzymane komunikaty są masowo do niego wysłane.

image

Tworząc aplikację w systemie rozproszonym wybór protokołu transportowego jest ważny i jest dostosowany do do twoich potrzeb. Jeśli stawiamy na niezawodność i gwarancję dostawy wiadomości to bezpośrednie protokoły HTTP czy TCP takie funkcjonalności nam nie zaoferują.  Jeśli nie ma sieci  komunikat nam nie dotrze.

Kolejka natomiast jest wspierana przez swoje repozytorium. Jeśli komunikat nie mógł być wysłany wcześniej to jest on wysłany ponownie gdy n.p połączenie zostało przywrócone.

Istnieje wiele implantacji kolejek i są one nawet wbudowane w bazach danych.

SQL Server ma “service broker-a” ,a Oracle ma technologię “Oracle Adcanced Queuing”.

Implantacja kolejki w platformie .NET nazywa się MSMQ. Nie jest to bardzo dobrze udokumentowana technologia dlatego pomyślałem ,że zrobię o tym rozwiązaniu wpis.

Dlaczego zainteresowałem się MSMQ moja historia znajduję się w poniższym punkcie.

W tym wpisie wyjaśnię jak zaimplementować prostą kolejkę MSMQ jak i spróbuje wytłumaczyć jak ona działa. Warto zaznaczyć ,że wiadomości te nie są domyślnie mocno szyfrowane i ten problem zostawię na inny wpis.

Studencka historia

Moja przygoda z kolejką Microsoftu zaczęła się na studiach. Na studiach PJWST w drugim semestrze trafił mi się przedmiot “Systemy Rozproszone”. Na pierwszy rzut oka wszystko wydawało się okej. Problem polegał na tym jednak ,że przedmiot nie jest w ogóle elastyczny  i ustawiony tylko Jave.

Wiedziałem o tym nawet przed pójściem na tą uczelnie i z tego powodu nawet się zastanawiałem czy w ogóle w niej studiować.

29267469Aby zaliczyć przedmiot trzeba było napisać 4 człony rozproszone przy użyciu jednej z pięciu technologii “CORBA”, “JBoss” ,”RMI””JMS” i usługi sieciowe.  Wszystkie te technologię znajdują się  w platformie Java.

Co gorsza .NET-owy WCF dla ćwiczeniowca wydawał się zbyt prostym rozwiązaniem. Widać brak tutoriali i jak co zrobić w Javie, jak i magia dziwności samej Javy wchodziła w skład zaliczenia tego przedmiotu.

Nie powiem nie lubię grzebać w ustawieniach i konfiguracjach ,a głównie na tym polega programowanie systemów rozproszony. WCF też ma ten problem ,ale  jest przynajmniej bardziej przyjazny dla programisty.

Jakby nie patrzeć przedmiot dla każdego programisty był dziecinie prosty ,ale w sumie przez 3/4 semestru ustalałem z ćwiczeniówce alternatyw do tych technologii. Tak udało mi się znaleźć alternatywę dla technologii CORBA i zrobię o tym wpis, bo samo rozwiązanie jest bardziej przyjazne dla platformy .NET i o wiele lepsze.

Z taką historią wychodzą na maniaka .NET-owego, który nienawidzi Javy ,ale więźcie mi tak nie jest. Po prostu  wiem ,że technologię są już bardzo stare.

Odpowiednikiem RMI w .NET-cię jest technologia  .NET Remoting. Microsoft sam oficjalnie powiedział ,że ta technologia nie powinna być już używana i powinna być ona zastąpiona WCF.

29277464CORBA  ma jeszcze sens dla starych systemów, które gadają z COBOLEM. Problem polega na tym ,że w Polsce  takich systemów prawie nie ma.

JMS z tego zrozumiałem tworzy mechanizm subskrypcji informacji tak zwane topiki. Tutaj w .NET znowu pojawia się WCF i mechanizm duplex. Dużo roboty trzeba przyznać. Tutaj nawet pojawiłby się problem z nazewnictwem technologii.Nawet wam nie chcę opowiadać jak musiałem przekonywać ćwiczeniowca ,że usługa SOAP i REST różni się jak dzień i noc, mimo iż w .NET to wciąż jest to WCF.  W sumie JMS mógłby się nauczyć.

No i…kolejki. Odpowiednikiem JBoss-a jest kolejka MSMQ. O tym wam dzisiaj opowiem.

Jeśli chodzi o tą historię to dostałem bardzo pozytywną ocenę i byłem zwolniony z egzaminu. Po tych wszystkich ustaleniach ćwiczeniowiec docenił moje starania i nawet przymkną oko na to ,że usługa SOAP przez przypadek mi się zepsuła w czasie obrony projektu.

Jednym słowem jak zawsze tutaj nie są winne konkretne osoby ,ale sam system nauczania, który się zestarzał.

Zaczynamy: Włączenie funkcji MSMQ

Zanim zaczniesz swoją przygodę z kolejką Microsoftu musisz ją najpierw włączyć. Windows 7 i 8 ta funkcja istnieje więc nie ma problemu z pisaniem aplikacji w celach studenckich bądź testowych.

Wejdź do panelu sterowania i wybierz “dodaj i usuń programy” potem po lewej stronie znajdź hiperłącze “Włącz lub wyłącz funkcję systemu Windows”. Z okna dialogowe zaznacz opcję “Serwer usługi MSMQ”.

image

Jeśli nie aktywujesz tej funkcji aplikacja MSMQ w czasie wysłania komunikatu wyrzuci wyjątek.

Uwaga

WykrzyknikAby kolejka i protokół MSMQ działał musi on być zainstalowany i po stronie serwera i po stronie klienta.

Tworzenie projektu

Na potrzeby tego wpisu stworze dwa projekty. Projekt WCF Service Library będzie hostował naszą usługę MSMQ na serwerze. Projekt WPF stworzy nam klienta aplikacji, który może być uruchomiony na innym bądź tym samym komputerze.

Najpierw stworzyłem aplikację kliencką.

image

Potem już do istniejącej solucj dodałem nowy projekt WCF Service Library.

image

Oto jak powinien wyglądać twój projekt.

Widzimy ,że Visual Studio do projektu usługi sieciowej dodało dwa pliki. IService określa kontrakt naszej usługi ,a Service ją implementuję.

image

Pamiętaj o tym ,że przy zmianie nazwy tych klas i interfejsów  skorzystać z opcji “Rename”. Tak aby zaoszczędzić sobie problemów później w pliku konfiguracyjnym.

image

Skasowałem przykładowy kod w plikach IService.cs i Service i potem dopisałem swój własny.

Kontrakt, czyli interfejs tej usługi zawiera w sobie dwie zadeklarowane metody. Obie te metody będą przyjmować prostą wiadomość tekstową, jak i datę, która określa, kiedy komunikat został wysłany.

using System;
using System.ServiceModel;

namespace MSMQService
{
    [ServiceContract]
    public interface IMSMQService 
    {
        [OperationContract(IsOneWay = true)]
        void SendLockMessage(string msg, DateTime sendDate);

        [OperationContract(IsOneWay = true)]
        void SendToOutputWindow(string msg, DateTime sendDate);
    }
}

Teraz aby obie te metody działały w stylu MSMQ muszą one mieć atrybut

[OperationContract(IsOneWay=true)].

Ten atrybut określa ,że wywołanie metody nigdy nie zwróci klientowi żadnego komunikatu.Metody więc niczego nie zwracają (void).

Sam odbiór komunikatu byłby nie  określony w czasie więc  klient mógłby otrzymywać asynchroniczne pseudo-komunikat zwrotne nie powiązane do poszczególnych wysłań. Dlatego jedno kierunkowe wiadomości są częścią filozofii kolejek.

Implementacja tych metod wygląda tutaj następująco. Metoda “SendToOutputWindow” wyświetli nam tylko komunikat w oknie Output.  Ilustruje ona prosty odbiór wiadomości.

Natomiast metoda SendLockMessage porusza bardziej złożony problem związany z wątkami.

using System;
using System.Diagnostics;
using System.IO;

namespace MSMQService
{
    public class MSMQService : IMSMQService 
    {
        public void SendToOutputWindow(string msg, DateTime sendDate)
        {
            Debug.WriteLine(msg + " Otrzymana: " + System.DateTime.Now + " Wysłana: " + sendDate);
        }

        private static readonly object SyncObject = new object();
        private static readonly TextWriter W = new StreamWriter("log.txt", true);

        public void SendLockMessage(string msg, DateTime sendDate)
        {
            lock (SyncObject)
            {
                W.WriteLine("Otrzymana: {0} {1}", DateTime.Now.ToLongTimeString(),
                    DateTime.Now.ToLongDateString());
                W.WriteLine("Wysłana {0} {1}", sendDate.ToLongTimeString(),
                    sendDate.ToLongDateString());
                W.WriteLine(" :");
                W.WriteLine(" :{0}", msg);
                W.WriteLine("-------------------------------");
                W.Flush();
            }
        }
    }
}

W trakcie pisanie tego przykładu przypomniałem sobie o bardzo ważnym  zagadnieniu, który jest związany z usługami sieciowymi. Jest to wielowątkowość. Natknąłem się na ten problem dlatego uznałem ,że ktoś inny też mógłby na taką pułapkę wpaść więc postanowiłem ją opisać.

Powiedzmy ,że napisaliśmy prostą metodę, która zapisuje do pliku  pewne informację. Ta metoda jest jest punktem końcowym  mechanizmu kolejki. Aplikacja serwerowa traci kontakt sieciowy ,a aplikacja kliencka wywołuje 3 zapytania do tej metody. Te 3 zapytania są umieszczone w kolejce. Nagle aplikacja serwerowa odzyskuje połączenie i  do niej trafiają te 3 komunikaty.

Te 3 komunikaty nie ustawiają się w kolejce. Są one wykonywane prawie równocześnie.

Teraz co się stanie, gdy 3 procesy będą próbowały zapisać jakąś informację do pliku. Pierwszy proces otworzy plik natomiast pozostałe 2 wyrzucą taki wyjątek.

image

Każda wiadomość dostała oddzielną instancję obiektu klasy MSMQService .

Każdy z tych obiektów próbuje wykonać tą samą metodę. Operacje IO nie są bezpieczne wątkowo więc trzeba napisać dodatkowy mechanizm blokujący wątek.

image

Blokowanie polega na użyciu słowa kluczowego “lock”.

Tworzymy statyczny obiekt tylko do odczytu, który będzie służył nam za informatora określającego czy inny wątek go właśnie używa. Dopóki pierwszy wątek nie zwróci tego obiektu pozostałe wątki muszą na niego czekać . Wątek używający ten obiekt zwróci go dopiero po wykonaniu bloku kodu zwartego w słowie kluczowym “lock”.

private static readonly object SyncObject = new object();
private static readonly TextWriter W = new StreamWriter("log.txt", true);

public void SendLockMessage(string msg, DateTime sendDate)
{
    lock (SyncObject)
    {
        W.WriteLine("Otrzymana: {0} {1}", DateTime.Now.ToLongTimeString(),
            DateTime.Now.ToLongDateString());
        W.WriteLine("Wysłana {0} {1}", sendDate.ToLongTimeString(),
            sendDate.ToLongDateString());
        W.WriteLine(" :");
        W.WriteLine(" :{0}", msg);
        W.WriteLine("-------------------------------");
        W.Flush();
    }
}

W wielkim skrócie tak działa słowo kluczowe “lock”. Nie ważne ile komunikatów będzie czekać w kolejce. Wszystkie  one  zostaną  zapisane do pliku.

Komunikat zapisane  do pliku mogą mieć inną kolejność niż z w tej, w jakiej zostały wysłane.

Tworzenie kolejki

Zanim przejdziemy do konfiguracji nasze usługi sieciowej MSMQ trzeb a najpierw stworzyć kolejkę.

Znajdź ikonkę komputera i z menu kontekstowego wybierz “zarządzaj”.

image

Jeśli zainstalowałeś MSMQ jak to było opisane  w poprzednim pod punkcie  po lewej stronie powinieneś odnaleźć “Kolejkowanie komunikatów” w zakładce “Usługi i aplikację”.

Kliknij prawym przyciskiem myszki na folder “Kolejki prywatne” i z menu wybierz “Nowy->Kolejka prywatna”

image

Swoją kolejkę nazwałem  “mojakolejka” oraz upewnij się ,że  zaznaczyłem opcję “Transakcyjna” . Ta opcja jest wymagana dla funkcji MSMQ.

image

Teraz rozszerzają folder “Kolejki prywatne” możesz zobaczyć swoją nową utworzoną kolejkę. Klikając na nią możesz zobaczyć umieszczone w niej wiadomości. Naturalnie jest ona obecnie pusta.

Konfigurowanie  usługi MSMQ

Konfigurowanie usług sieciowym nie kojarzy mi się z niczym pozytywnym. Kod można napisać ,ale magia tych plików jest bardziej twórco błędna niż komukolwiek się to wydaję. Nie martw się jednak postaram się przeprowadzić przez ten proces.

Mój plik konfiguracyjny “app.config” przed zmianami wygląda następująco. Trzeba w nim dużo zmienić.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>

  <system.web>
    <compilation debug="true" />
  </system.web>
  <!-- When deploying the service library project, the content of the config file must be added to the host's 
  app.config file. System.Configuration does not support config files for libraries. -->
  <system.serviceModel>
    <services>
      <service name="MSMQService.MSMQService">
        <endpoint address="" binding="wsHttpBinding" contract="MSMQService.IMSMQService">
          <identity>
            <dns value="localhost" />
          </identity>
        </endpoint>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:8732/Design_Time_Addresses/MSMQService/Service1/" />
          </baseAddresses>
        </host>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <!-- To avoid disclosing metadata information, 
          set the value below to false and remove the metadata endpoint above before deployment -->
          <serviceMetadata httpGetEnabled="True"/>
          <!-- To receive exception details in faults for debugging purposes, 
          set the value below to true.  Set to false before deployment 
          to avoid disclosing exception information -->
          <serviceDebug includeExceptionDetailInFaults="False" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>

</configuration>

Musimy się upewnić czy nazwa usługi jest zgodna z pełną ścieżka do obiektu klasy.

<service name="MSMQService.MSMQService">

Jeśli tak nie będzie później otrzymamy błąd  “no metadata”. Następnie wszystkie informacje zwarte w tagu “service” kasujemy i piszemy następujące tagi.

<host>
  <baseAddresses>
    <add baseAddress="http://panniebieski:8080/MSMQService/"/>
  </baseAddresses>
</host>

Następnie zmianami nasz bazowy adres. Jak zapewnię zauważyłeś adres nie zawiera w sobie słowa “localhost”. Natomiast zawiera ona w sobie nazwę mojego komputera “panniebieski”.  Naturalnie nazwa twojego komputera jest zapewne inne więc ją tutaj wstaw.

Teraz musimy dodać endpoint-a do naszej usługi MSMQ .Endpoint ten ma  zupełnie inny  adres niż inne klasyczne usługi sieciowe.

Endpointy określają swój adres, w który mogą być odnalezione jak i określają protokół w jakim klient ma się z nim komunikować. (BasicHttp, netMsmqBinding, wsHttpBinding, NetTcpBinding .)

Endpointy także wskazuje na kontrakt, czyli interfejs określający klientowi, jakie on metody może wykonać.

<endpoint address="net.msmq://panniebieski/private/mojakolejka"
          binding="netMsmqBinding" bindingConfiguration="MyBinding"
          contract="MSMQService.IMSMQService">
  <identity>
    <dns value="localhost" />
  </identity>
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />

Adres endpointu wskazuje na wcześniej utworzoną kolejkę prywatną, jak i na komputer, na którym ta kolejka się znajduję. Wiązanie dla MSMQ to “netMsmqBidning” Pełna ścieżka do kontraktu to “MSMQService.IMSMQService”.

Na koniec do endpointu dodajemy nazwę jego konfiguracji, którą stworzymy za chwilę.

Skasuj wszystko to co znajduje się pomiędzy tagami </services> a </system.serviceModel> i dodaj następujący kod XML.

lt;behaviors>
  <serviceBehaviors>
    <behavior>
      <serviceMetadata httpGetEnabled="True" />
      <serviceDebug includeExceptionDetailInFaults="False" />
    </behavior>
  </serviceBehaviors>
</behaviors>
<bindings>
  <netMsmqBinding>
    <binding name="MyBinding"  >
      <security mode="None"/>
    </binding>
  </netMsmqBinding>
</bindings>

Jak widzisz obecnie nasze powiązanie nie zawiera w sobie żadnych zabezpieczeń.

Końcowy plik konfiguracyjny App.config wygląda tak.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>

  <system.web>
    <compilation debug="true" />
  </system.web>
  <system.serviceModel>
    <services>
      <service name="MSMQService.MSMQService">
        <host>
          <baseAddresses>
            <add baseAddress="http://panniebieski:8080/MSMQService/"/>
          </baseAddresses>
        </host>
        <endpoint address="net.msmq://panniebieski/private/mojakolejka"
                  binding="netMsmqBinding" bindingConfiguration="MyBinding"
                  contract="MSMQService.IMSMQService">
          <identity>
            <dns value="localhost" />
          </identity>
        </endpoint>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <serviceMetadata httpGetEnabled="True" />
          <serviceDebug includeExceptionDetailInFaults="False" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <bindings>
      <netMsmqBinding>
        <binding name="MyBinding"  >
          <security mode="None"/>
        </binding>
      </netMsmqBinding>
    </bindings>
  </system.serviceModel>

</configuration>

Teraz możemy uruchomić naszą aplikację WCF MSMQ i sprawdzić czy nie popełniliśmy żadnych literówek.

Jeśli otrzymujesz błędy  sprawdź czy masz uruchomione Visual Studio jako administrator oraz  sprawdź czy w przestrzeniach nazw, które określimy   w pliku konfiguracyjnym nie ma żadnych błędów.

W opcjach solucji możesz dla ułatwienia wybrać ,że chcesz debugować tylko obecnie zaznaczony projekt.

image

Jeśli wszystko jest w porządku twoim oczom powinien ukazać się testowy klient WCF. Możesz w nim nawet wywołać dane metody i sprawdzić czy się uruchamiają.

image

Tworzymy klienta

Teraz gdy najtrudniejszą cześć mamy już za sobą dopracujmy naszego  klienta WPF.

Do projektu musimy dodać referencję usługi MSMQ. Zanim wybierzesz z menu kontekstowego tą opcję upewnij się ,że masz uruchomioną aplikację MSMQ, którą przed chwilą napisaliśmy.

image

W oknie dialogowym kliknij na przycisk “Discover” i nazwij odpowiednio swoją referencję. Jeśli nie możesz jej znaleźć sprawdź najpierw czy jest ona w ogóle uruchomiona. W przeciwnym wypadku  sprawdź swoje ustawienia firewall bądź go tymczasowo wyłącz.

image

Do projektu powinny zostać dodane odpowiednie referencję do dll-ek, jak i folder “Service References”.

image

Visual Studio także automatycznie za ciebie napisał kod konfiguracyjny.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <system.serviceModel>
        <bindings>
            <netMsmqBinding>
                <binding name="NetMsmqBinding_IMSMQService">
                    <security mode="None" />
                </binding>
            </netMsmqBinding>
        </bindings>
        <client>
            <endpoint address="net.msmq://panniebieski/private/mojakolejka"
                binding="netMsmqBinding" bindingConfiguration="NetMsmqBinding_IMSMQService"
                contract="MSMQServiceReference.IMSMQService" name="NetMsmqBinding_IMSMQService">
                <identity>
                    <dns value="localhost" />
                </identity>
            </endpoint>
        </client>
    </system.serviceModel>
</configuration>

W tym pliku możesz zmienić parę informacji. W tym pliku możesz ustawić jak długo klient ma próbować ponownie wysyłać wiadomości jak i maksymalną wielkość wysłanej wiadomości. Dla ułatwienie tego wpisu nie zmieniłem zupełnie domyślnych ustawień.

Interfejs aplikacji klienckiej zawiera w sobie pole tekstowe oraz prosty przyciski.

image

Oszczędzę ci zbędne pisanie kodu XAML. Opis tego interfejsu znajduje się poniżej.

<Window x:Class="MSMQClient.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Grid.Background>
            <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                <GradientStop Color="#E7B882" Offset="0" />
                <GradientStop Color="#FFE7DF82" Offset="1" />
            </LinearGradientBrush>
        </Grid.Background>
        <Button Content="Wyślij!" Height="35" 
                HorizontalAlignment="Right" Margin="0,0,119,101" 
                Name="buttonSendMsg" VerticalAlignment="Bottom" Width="141" 
                Background="#E6FF8787"  FontFamily="Segoe UI" 
                Click="buttonSendMsg_Click" BorderBrush="#FFFF7000" Foreground="Black"></Button>
        <TextBox Height="34" HorizontalAlignment="Right" Margin="0,129,119,0" Name="textBox1" 
                 VerticalAlignment="Top" Width="272" AcceptsReturn="True" FontWeight="Bold" FontFamily="Segoe UI" 
                 Foreground="#65BFE0" FontSize="24" />
        <Label Content="Wpisz tutaj swoją wiadomość:" 
               Height="43" HorizontalAlignment="Left" Margin="112,88,0,0" VerticalAlignment="Top" Width="272" FontWeight="Bold" 
               FontFamily="Segoe UI" FontSize="18" />
    </Grid>
</Window>

W kodzie pobocznym tworzymy instancje klienta i odbierają tekstową informację od użytkownika zapisaną w textbox-ie po czym wysyłamy tą informację przy pomocy dwóch metod SendLockMessage i SendToOutputWindows.

using System;
using System.Windows;
using MSMQClient.MSMQServiceReference;

namespace MSMQClient
{
    public partial class MainWindow : Window {
        private MSMQServiceClient client;

        public MainWindow()
        {
            InitializeComponent();
            client = new MSMQServiceClient();
        }

        private void buttonSendMsg_Click(object sender, RoutedEventArgs e)
        {
            client.SendLockMessage(textBox1.Text,DateTime.Now);
            client.SendToOutputWindow(textBox1.Text,DateTime.Now);
        }
    }
}<

Wszystko jest już gotowe. Teraz możesz już ze swojego komputera wysłać wiadomości, które trafia do kolejki.

Uruchom obie aplikacje na raz i je przetestuj.

image

Możesz w aplikacji po stronie serwera postawić breakpointa by zaobserwować jak to wszystko działa.

image

Jak widać wiadomość została wyświetlona w oknie “Output” Visual Studio.

image

W katalogu bin aplikacji serwerowej został także stworzony plik log.txt.

image

Plik ten został także odpowiednio uzupełniony.

Otrzymana: 09:58:00 30 października 2012
Wysłana 09:58:00 30 października 2012
  :
  :Wiadomość testowa
-------------------------------

Kolejka w akcji

No dobra ,ale gdzie ta cała kolejka na razie to wszystko działa jak zwykła usługa sieciowa. Aby zaobserwować jak działa kolejka trzeba uruchomić tylko aplikację kliencką i kliknąć parę razy na przycisk wyślij.

image

Normalnie aplikacja kliencka by zwróciła wyjątek, ponieważ serwer nie jest dostępny ,ale w tym wypadu oczywiście tak nie będzie.

Endpoint “<endpoint address="net.msmq://panniebieski/private/mojakolejka" określił ,że klient ma wysłać wiadomości do komputera “panniebieski” i do jego prywatnej kolejki “mojakolejka”. Jeśli aplikacją nie wstanie połączyć się z tą kolejką wtedy wiadomości są przetrzymywane do “kolejki wychodzącej” i będzie próbować ,aż do pewnego momentu umieszczanie wiadomości do kolejki prywatnej.

Aplikacja kliencka nie ma problemu z połączeniem się do mojej prywatnej kolejki dlatego naturalnie wiadomości będę w niej przetrzymywane.

image

Klikając nad folder swojej kolejki możesz obejrzeć ile komunikatów zostało zarchiwowanych i nie dotarło do serwera. Wiadomości w kolejce nie są wieczne i zostaną wysłane ponownie po określony czasie. Ten czas jest określony w bindingu jako retryCycleDelay.

Jeśli wiadomość wciąż nie może być wysłana wtedy ona znowu trafia kolejki jakby w ogóle ona nie zniknęła. Jeśli prób przekroczy parametr “ReciveRetryCount wtedy wiadomości są przemieszczone do kolejki “retry”.

Potem w kolejce “retry” po czasie określonym w parametrze “retryCycleDelay” wiadomość znowu trafia do kolejki prywatnej, która jest określona dla niego w endpointcie.

Cały ten cykl powtarza się zgodnie z parametrem “maxRetryCycle”. Jeżeli wiadomość wciąż nie może być wysłana wtedy wiadomość jest uznana za trującą.

Gdy tak się stanie zgodnie z enumatorem ReceiveErrorHandling możemy taką wiadomość wysłać do kolejki martwych listów, odrzucić, przenieść bądź zgłosić jako błąd w logu Windows.

Ten przykład oczywiście dużo za prostszy aby to zobrazować ,ale mniej więcej taki jest cykl życia komunikatów znajdujących się wewnątrz kolejki.

image

Jeśli uruchomisz serwer ponownie może zobaczyć jak okno OutPut wyświetla  oczekujące komunikaty.

Info #1 Otrzymana: 2012-10-30 10:03:27 Wysłana: 2012-10-30 10:03:18
Info #2 Otrzymana: 2012-10-30 10:03:27 Wysłana: 2012-10-30 10:03:21

Dzięki rozwiązaniu lock, o którym pisałem wcześniej natura  wielowątkowość komunikatów nie psuje nam zapisu do pliku. Komunikaty zostały poprawnie zapisane.

Otrzymana: 10:03:27 30 października 2012
Wysłana 10:03:21 30 października 2012
  :
  :Info #2
-------------------------------
Otrzymana: 10:03:27 30 października 2012
Wysłana 10:03:18 30 października 2012
  :
  :Info #1
-------------------------------

Jak jednak możesz zauważyć z powodu asynchronicznej natury komunikatów nie powinieneś oczekiwać ,że wykonają się one z taką samą kolejność, z jaką zostały one wysłane.