Posts tagged WCF
Tunel SOAP w SOAP i WCF
Jun 10th
Podczas wstępnego projektowania systemu, nad którym teraz pracuje, natknęliśmy się na dosyć interesujący problem. Polega on na tym, iż docelowe środowisko wdrożeniowe nie pozwala na komunikację między serwerem WWW, a serwerem aplikacyjnym. Komunikacja odwrotna jest możliwa. Sytuację tę przedstawia poniższy diagram.
Te raczej mocne obostrzenia podyktowane są (podobno) polityką bezpieczeństwa. Niestety są one zabójcze dla naszego systemu, ponieważ ma on służyć do monitorowania i zarządzania procesami uruchomionymi na serwerze aplikacyjnym. Jak więc monitorować i sterować czymś, z czym nie można się połączyć?
Tunel
Odpowiedzią jest tunel SOAP w SOAP wykorzystujący fakt, że komunikacja odwrotna (serwer aplikacyjny wysyła requesty do serwera WWW) jest, jak najbardziej, możliwa. Jak działa taki tunel? Tunel składa się z dwóch końców. Koniec kliencki umiejscowiony jest na maszynie WWW i przyjmuje żądania, które następnie są kolejkowane w pamięci. Serwerowy koniec tunelu jest aplikacją uruchomioną na serwerze aplikacyjnym, która okresowo (raz na kilka sekund) odpytuje (wywołując operację Fetch) koniec kliencki, czy są jakieś zakolejkowane żądania. Jeśli tak, pierwsze żądanie z kolejki jest zwracane w odpowiedzi na wywołanie Fetch. Przesłane żądanie jest następnie forwardowane do odpowiedniego procesu serwerowego. Prześledźmy to na przykładzie.
Tunel w działaniu
- Serwer WWW wysyła żądanie. Adresatem (nagłówek “To” w SOAP) jest serwer aplikacyjny, jednak żądanie jest fizycznie (na poziomie TCP) wysyłane na adres klienckiego końca tunelu.
- Generowany jest unikalny identyfikator żądania. Żądanie, wzbogacone o dodatkowy nagłówek zawierający wygenerowany identyfikator, jest kolejkowane w oczekiwaniu na wywołanie Fetch.
- Serwerowy koniec tunelu wywołuje operację Fetch celem pobrania pierwszego oczekującego żądania. Operacja ta zdefiniowana jest w ten sposób, że request jest pusty, a w odpowiedzi przesyłany jest dowolny komunikat SOAP.
- W odpowiedzi na Fetch żądanie (przesyłane jako odpowiedź) trafia do serwerowego końca tunelu.
- Serwerowy koniec tunelu forwarduje je (bez jakiejkolwiek ingerencji bądź analizy) do nasłuchującego lokalnie procesu serwera.
- Proces serwera zwraca odpowiedź do serwerowego końca tunelu.
- Odpowiedź jest forwardowana do końca klienckiego poprzez wywołanie operacji Reply, która pozwala na przesłanie dowolnego komunikatu SOAP (w odpowiedzi na Reply przesyłany jest pusty komunikat). Przed wysłanie do komunikatu dodawany jest nagłówek zawierający identyfikator, który zawierało żądanie.
- Kliencki koniec tunelu, na podstawie przekazanego identyfikator kojarzy przesłaną za pomocą Reply odpowiedź z oczekującym żądaniem, a następnie odpowiada klientowi przesyłając mu komunikat otrzymany od serwerowego końca tunelu.
Szczegóły implementacyjne
Jest kilka kwestii, o których trzeba pamiętać budując rozwiązanie tego typu. Pierwszą z nich jest konieczność użycia WS-Addressing (czyli de facto WSHttpBinding lub pochodnego). W przeciwnym wypadku informacje o adresacie nie będą przeźroczyście transportowane od klienta do serwera. Nie stanowi to problemu w przypadku prostych zastosowań (no security), jednak uniemożliwia np. stworzenie pewnej sesji (reliable session).
Kolejną kwestią techniczną jest konieczność każdorazowego kopiowania komunikatów WCF przed ich forwardowaniem. Specyfika WCF jest taka, że obiekt Message reprezentujący otrzymany komunikat SOAP może być odczytany co najwyżej raz. Jeśli potrzebujemy więcej razy — musimy utworzyć kopię.
Ustawienie właściwości Action i ReplyAction w atrybucie OperationContract na “*” powoduje, że serwis będzie operował na dowolnych komunikatach SOAP. Jest tylko jedna pułapka. Tylko jedna operacja w kontrakcie może mieć ustawione “*”, ponieważ inaczej nie byłoby wiadomo do której operacji skierować komunikat.
Wyjaśnienie
Nie zachęcam was, broń Boże, to stosowania takich zabawek dla samej idei. Jest to bardzo szczególne rozwiązanie bardzo szczególnego problemu, który zapewne nie występuje zbyt często (oby). Jest to właściwie obejście procedur bezpieczeństwa i, jako takie, powinno być stosowane z rozwagą. W końcu ktoś te procedury w jakimś celu ustalał, prawda?
Windows Communication Foundation 4.0
May 28th
Druga część mojej sobotniej prezentacji dotyczyła nowości w Windows Communication Foundation. Aby zmieścić się w założonych piętnastu minutach, spośród wielu ciekawostek o których można poczytać np. u Nicholasa Allena, wybrałem dwie:
- RoutingService
- WS-Discovery
Dlaczego akurat te dwie? Otóż dlatego, że udało mi się wymyślić jeden wspólny temat, pod szyldem którego mógłbym je prezentować — usługę Event Brokera. Jeśli ktoś nie kojarzy tego wzorca, Event Broker to pośrednik, który forwarduje informacje o zdarzeniach pochodzące od nadawcy (źródła) do dowolnej liczby odbiorców. Celem tego wzorca jest zmniejszenie powiązania miedzy nadawcą i odbiorcami.
Projekt
Poniższa prezentacja (6 slajdów) pokazuje sposób działania Event Brokera. Pozostaje jedynie mapowanie poszczególnych funkcji na dostępne technologie. Dzięki nowemu WCF, wszystko czego potrzebujemy znajduje się już na naszym komputerze.
Funkcjonalność rejestracji subskrybentów (otrzymujących komunikaty) w Event Brokerze zapewnia nam obsługa WS-Discovery. Filtrowanie i routowanie przychodzących komunikatów do zainteresowanych odbiorców to funkcje nowego, wbudowanego w WCF, RoutingService’u. Wszystko, co musimy zrobić własnoręcznie to sprzęgnąć te dwie technologie tak, aby reakcja na ogłoszenia WS-Discovery powodowała odpowiednią modyfikację tablicy routingu.
Routing
RoutingService to gotowa do użycia usługa dostarczana wraz z nowym WCF. Jej użycie wymaga trzech prostych kroków: stworzenia nowego hosta, skonfigurowania rodzaju routingu oraz podpięcia odpowiedniego zachowania. W kodzie wygląda to tak:
var routerServiceHost = new ServiceHost(typeof (RoutingService)); routerServiceHost.AddServiceEndpoint(typeof (ISimplexDatagramRouter), new BasicHttpBinding(), RouterAddress); routerServiceHost.Description.Behaviors.Add(new RoutingBehavior(subscriptionManager.RoutingConfiguration));
Konfiguracja rodzaju routingu polega na utworzeniu endpoint’u odpowiedniego typu. Ja wykorzytsałem ISimplexDatagramRouter ponieważ potrzebuje przekazywać pakiety w jednym kierunku. Możliwy jest jednak także routing komunikacji request-response (z tym, że raczej już bez multicast’u).
Do konstruktora RoutingBehavior przekazujemy obiekt RoutingConfiguration, który m.in. zawiera tablicę routingu. Tablica ta może być modyfikowana z zewnątrz w dowolnym momencie, a dokonane zmiany zostaną uwzględnione podczas routowania następnego komunikatu. Kod modyfikujący tablicę wygląda np. tak:
var contract = ContractDescription.GetContract(typeof (ISimplexDatagramRouter));
var subscriber = new ServiceEndpoint(contract, new BasicHttpBinding(), address);
RoutingConfiguration.FilterTable.Add(new ActionMessageFilter(actions), new List<ServiceEndpoint> {subscriber});
WS-Discovery
Mój Event Broker wykorzystuje protokół WS-Discovery poprzez nasłuchiwanie na ogłoszenia (announcements) o pojawieniu się lub zniknięciu usług. Aby dla danej usługi WCF włączyć wysyłanie tych ogłoszeń, wystarczy zmodyfikować jej konfiguracje według poniższego wzorca:
<services>
<service behaviorConfiguration="discoveryBehavior"
name="Subscriber.SayHelloService">
<endpoint name="udpDiscoveryEpt" kind="udpDiscoveryEndpoint" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="discoveryBehavior">
<serviceMetadata/>
<serviceDiscovery>
<announcementEndpoints>
<endpoint kind="udpAnnouncementEndpoint" />
</announcementEndpoints>
</serviceDiscovery>
</behavior>
</serviceBehaviors>
</behaviors>
Konieczne jest dodanie nowego endpointu oraz dodanie do usługi odpowiedniego zachowania. W kodzie nie są potrzebne żadne zmiany. Modyfikacja ta sprawi, że subskrybenci będą wysyłać ogłoszenie w momencie przejścia w tryb on-line. Kolejnym krokiem jest dodanie do Event Brokera kodu, który będzie nasłuchiwał na te ogłoszenia i, zgodnie z ich treścią, modyfikował tablicę routingu. Funkcjonalność tą realizuje poniższy fragment:
var subscriptionListener = new SubscriptionListener(subscriptionManager); var announcementService = new AnnouncementService(); announcementService.OnlineAnnouncementReceived += (s, a) => subscriptionListener.Subscribe(a); announcementService.OfflineAnnouncementReceived += (s, a) => subscriptionListener.Unsubscribe(a); var announcementServiceHost = new ServiceHost(announcementService); announcementServiceHost.AddServiceEndpoint(new UdpAnnouncementEndpoint());
Jak widać, usługa WS-Discovery oferuje dwa eventy, pod które możemy podpiąć się z własnym kodem. Jest to bardzo wygodny sposób pracy.
Podsumowanie
Zapraszam do ściągnięcia całego przykładu z galerii MSDN. Mam nadzieję, że powyższy post pozwoli Wam łatwiej zrozumieć kod. Specjalnie nie omawiałem klasy obsługującej parsowanie WSDL-a, która jest niezbędna w ostatecznym rozwiązaniu, jednak nie jest ważna z punktu widzenia funkcjonalności Event Brokera. WCF 4 — polecam każdemu!
Building an embedded MVC server
Feb 4th
During yesterday’s blog reading session I came across two interesting posts. First one was about building a web server using WCF. The other one was about testing ASP.NET MVC as a whole, not only controllers, by hosting ASP.NET in process.
I realized that by combining these two not very common usages of these technologies I can build an even more uncommon one. I could use WCF to receive requests and send response and ASP.NET MVC to generate content. The goal would be to get it to the state, when I could reference an assembly (a class library) containing controllers and views and start a web server using these in my console application or Windows service.
Now, probably, you want to ask, why on earth would I need such a feature? Isn’t IIS the place to host your web applications? My scenario, which I think is quite interesting, it to add HTTP/HTML based diagnostics to a Windows service. Want to check in log paths are OK? Just connect to it using browser. Want to run some diagnostics against disk share paths or database table permissions? Just write some code to generate a report and make it accessible via a controller.
I encountered some interesting challenges I want to share with you. First of all, how ASP.NET MVC is quite dependent on ASP.NET infrastructure. Some request/context related classes are not a big problem thanks to System.Web.Abstractions dll. The case if far worse with default view engine (aspx/ascx) which uses directly System.Web. I wasn’t able to find out how could I use the compilation feature without starting ASP.NET runtime in AppDomain, so I decided to give up and start the ASP.NET.
(I am not very happy with this solution and I will try to use another view engine to check whether there are other hard ASP.NET dependencies. If not, I will happily get rid of it, since it complicates stuff, a lot — read on)
ASP.NET runtime could be started only in a brand new AppDomain to which you don’t have to access before it starts. It is a big problem because when you start the runtime, you pass it a custom type which is instantiated ‘on the other side’ and lets you configure and manipulate the domain. By default ASP.NET searches for dll in the bin subdirectory of main app directory, so if you want to run it in context of console application, you have to create bin directory where you have your dlls and put there one containing that aforementioned ‘integration’ type. At least it works and this is the trick this article uses.
Mine was to add assembly load directives to web config:
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="bin;"/>
</assemblyBinding>
</runtime>
The ‘;’ character is critical. Because of it this private path means ’search in bin subdirectory of application base dir and in the application base dir‘. The latter is represented by an empty string, ‘;’ is just a separator.
Other elements of solution include custom virtual path provided (using aspx/ascx files embedded in an assembly) and some hackyish code I am not very proud of. But remember, that is only a very quick and very dirty proof-of-concept.
You can download the code here. It requires ASP.NET MVC 1.0 to run. If you have any ideas (besides that code is horrible
), please leave a comment.
WCF, a dobre zachowanie
Jul 16th
O co chodzi? Wyobraźmy sobie dwie aplikację. Pierwsza z nich, nazwana przeze mnie umownie Klientem, chce do drugiej aplikacji (nazywanej dalej Serwerem) przekazać zachowanie. Nie dane, ale zachowanie. To znaczy nie
ale raczej
Zastanawiając się nad tym, który z wzorców znanych ze swej użyteczności w problemach komunikacji, wykorzystać możemy przypomnieć sobie o Wizytatorze. Zdefiniowanie interfejsu wizytatora w tym samym, współdzielonym między Serwerem, a Klientem assembly, które zawiera klasy komunikatów , nic nas nie kosztuje w kontekście coupling-u. Implementacja interfejsu znajduje się wyłącznie po stronie serwera, więc z tym też nie ma problemu. Jedynym minusem jest fakt, że dodanie nowego komunikatu powoduje konieczność rozbudowy wizytatora o nową metodę. I tak za każdym razem.
Ale jest też inne rozwiązanie. Wykorzystuje ono fakt, że mamy do czynienia z granicą międzyprocesową oraz, ja się zapewne domyślacie z tytułu, WCF-a. Chociaż WCF to za dużo powiedzianie. Generalnie chodzi o DataContractSerializer i jego pojęcie równoważności typów (data contract equivalence). W skrócie, oznacza ono, że dopóki serializowany i deserializowany typ są reprezentowane przez identycznie wyglądający schemat, dopóty możliwe jest wysyłanie obiektów jednego typu, a odbieranie – drugiego.
Jak to się ma do naszego problemu? Ano tak, że możemy mieć po stronie Klienta i po stronie Serwera dwa równoległe zestawy klas komend. Klasy te parami będą nazywać się tak samo oraz zawierać te same pola danych. Różnica polegać będzie jedynie na tym, że klasy po stronie Serwera będą zawierać zachowanie, które ma zostać wykonane po odebraniu. Aby nie kopiować kodu, oba zestawy klas mogą dziedziczyć z trzeciego, którego klasy definiowałyby sam zakres danych przekazywany w komendach. Unikniemy dzięki temu duplikacji kodu. Dodatkowo, jeśli po stronie Klienta komendy nie mają w ogóle zachowania, można ich w ogóle nie tworzyć tylko polegać na współdzielonych klasach definiujących dane.
W tym wypadku, logicznie, to serializacja zapewnia nam mechanizm mapowania kontenerów na dane na zachowanie z nimi związane.
Stąd możecie pobrać solution VS, które zawiera aplikację demonstrującą to podejście. Zaimplementowany przykład zawiera komendy, które mają zachowanie zarówno po stronie Klienta, jak i Serwera.




