TechArch

Architektura i jej aspekty technologiczne

Usługi w świecie Microsoft WCF,REST, .NET Service Bus, Workflow Services - materiały z sesji na MTS 2009

clock 1 października, 2009 06:26 przez author tkopacz

W tym poście zamieszczona jest prezentacja i przykłady z sesji Usługi w świecie Microsoft WCF,REST, .NET Service Bus, Workflow Services. Celem sesji było przedstawienie subiektywnie wybranych elementów technologii WCF (i pokrewnych).

Agenda:

  1. Kontrakty (i WSDL)
  2. Konfiguracja
  3. Hostowanie
  4. Rozszerzanie WCF
  5. Bezpieczeństwo (krótko)
  6. Sesja
  7. Kolejki
  8. Strumienie
  9. Asynchroniczność
  10. .Koncepcja REST
  11. Windows Azure i .NET Service Bus
  12. WCF 4.0
  13. WASE (Windows Application Server Extensions)

Podczas prezentacji było mało czasu na szczegółowe omówienie każdego z tych tematów i w przykładach omawiane były tylko kluczowe elementy rozwiązania. Z tego powodu zachęcam także do samodzielnego rzucenia okiem na kod. Cześć przykładów zakłada, że zdefiniowane są odpowiednie aplikacje w IIS. Nazwa aplikacji jest wtedy taka sama jak danego folderu.

Przykład WCF_Security_Certificate\WCFTCPCertificateRoles\WCFTCPCertificateRoles\WCFTCPCertificateRoles.sln zakłada, że zdefiniowana jest baza danych z użytkownikami i rolami (wpis w pliku web.config)

<add name="MyLocalSQLServer" connectionString="Initial Catalog=aspnetdb;data source=.\SqlExpress;Integrated Security=SSPI;" />

Można ją utworzyć wywołując: C:\Windows\Microsoft.NET\Framework\v2.0.50727\aspnet_regsql.exe.

Zdecydowałem się także pokazać bardziej rozbudowaną wersję prezentacji - zawierającą dodatkowe informacje (m. innymi na temat .NET Service Bus oraz WCF 4.0).

Prezentacja: MTS2009 - Usługi w świecie Microsoft WCF,REST, .NET Service Bus, Workflow Services (Tomasz Kopacz).zip (1,20 mb)

Przykłady: MTS2009_Tomasz_Kopacz_WCF.zip (4,39 mb)

Aktualnie oceniony na 1.5 (2)

  • Currently 1,5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


Użycie WCF do separacji kodu w ramach jednego AppDomain (lub nawet assembly)

clock 22 stycznia, 2009 10:17 przez author tkopacz

Pisząc rozwiązanie, nie zawsze wydzielane są formalne warstwy z fizyczną separacją komponentów. Czasami taka separacja ma miejsce na poziomie klas (czy modułów) w środku aplikacji. W takim scenariuszu warto mieć narzędzie, które zapewni dodatkową infrastrukturę – typu transakcje, sesja, bezpieczeństwo itp. Dodatkowo, warto móc sprawdzić jak taka komunikacja wygląda, ile kosztuje odwołanie do takiego modułu – czy po prostu pozwoli na diagnostykę “środka” aplikacji.

Aby to uzyskać, można użyć Windows Communication Foundation (WCF) – ale w taki sposób że “serwer” i “klient” będą się znajdowały w jednym AppDomain (albo nawet w jednym Assembly)

Do komunikacji najwygodniej jest wykorzystać binding <netNamedPipeBinding>, który jest zoptymalizowany pod kątem komunikacji międzyprocesowej, ale może tez być użyty jako narzędzie do komunikacji “InProc” – w ramach jednego AppDomain. Standardowo, wykorzystuje on mechanizm WS-ReliableMessaging (by zapewnić pewność dostarczenia komunikatu i synchronizację), bezpieczeństwo na poziomie transportu, przesyłane informacje serializuje w sposób binarny a do komunikacji wykorzystuje nazwane potoki (precyzyjniej – IPC).

Aby taki mechanizm wykorzystać, należy:

  1. Utworzyć kontrakty (interfejs)
  2. Zaimplementować usługę
  3. Zdefiniować proxy klienckie - klasę dziedziczącą z ClientBase<T> i zwykle implementującą interfejs kontraktu (ale to nie jest wymagane)
  4. Określić binding (zwykle w pliku konfiguracyjnym)
  5. Uruchomić proces hostujący – ServiceHost
  6. Wywołać daną funkcjonalność za pośrednictwem proxy

W naszym przypadku zdefiniujemy kontrakt, który będzie miał 2 metody. Jedna (CalculateF) wylicza liczbę Fibonacciego. Druga, (Add) dodaje dwie liczby i będzie używana do pokazania narzutu jaki daje taki sposób komunikacji (narzut – uprzedzając dalsze rozważania, jest w praktyce pomijalny).

Kontrakt ma postać następującą:

    [ServiceContract]
    interface ISimpleService {
        [OperationContract]
        long CalculateF(long n);
        [OperationContract]
        int Add(int a,int b);
    }

Implementacja kontraktu też nie jest niczym nadzwyczajnym:

    class MySimpleService:ISimpleService {
        #region ISimpleService Members
        public long CalculateF(long n) {
            if (n <= 1) return n;
            return (CalculateF(n - 1) + CalculateF(n - 2));
        }
        public int Add(int a, int b) {
            return a + b;
        }
        #endregion
    }

Definicja proxy ma następującą postać:

    partial class MySimpleServiceClient : ClientBase<ISimpleService>, ISimpleService {
        public MySimpleServiceClient () { }
        public MySimpleServiceClient(string configurationName): base(configurationName) { }
        #region ISimpleService Members
        public long CalculateF(long n) {
            return Channel.CalculateF(n);
        }
        public int Add(int a, int b) {
            return Channel.Add(a, b);
        }
        #endregion
    }

W tym przypadku nie wykorzystujemy mechanizmu automatycznego generowania proxy, tylko piszemy je ręcznie. Proszę zauważyć, że dziedziczenie z ClientBase<T> daje nam od razu obiekt Channel który pozwala komunikować się z usługą. Zwykle, dla wygody impelementuje się jeszcze interfejs kontraktu – by klient po prostu wywoływał konkretną metodę.

Kolejnym ważnym elementem jest zdefiniowanie bindingów – zarówno dla klienta jak i dla serwisu(w jednym app.config):

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <services>
      <service name="WCF35SP1_InsideExe.MySimpleService">
        <endpoint address="net.pipe://localhost/MySimpleService" binding="netNamedPipeBinding" contract="WCF35SP1_InsideExe.ISimpleService" />
      </service>
    </services>
    <client>
      <endpoint address="net.pipe://localhost/MySimpleService" binding="netNamedPipeBinding" contract="WCF35SP1_InsideExe.ISimpleService" />
    </client>
  </system.serviceModel>
</configuration>

Proszę zauważyć, że definiujemy zarówno <service> jak i <client> – wskazując odpowiednie klasy i interfejsy w naszym EXE (WCF35SP1_InsideExe to nazwa projektu i przestrzeni w tym przykładzie)

Należy jeszcze pamiętać, że “host” musi być fizycznie uruchomiony. Tu zwykle wykorzystywany jest ServiceHost. Nie ma potrzeby definiować opcji bindingu (bo są one zdefiniowane w app.config), należy tylko wskazać jaki typ będzie hostowany. Dzięki temu ServiceHost wyszukuje definicję stosownego endpointu i uruchamia “nasłuch”.

ServiceHost service1 = new ServiceHost(typeof(MySimpleService));
service1.Open();

Po tych krokach można  wykonać kod, na przykład taki:

using (MySimpleServiceClient clt = new MySimpleServiceClient()) {
    Console.WriteLine(clt.Add(10, 20)); 
    Console.WriteLine(clt.CalculateF(5)); 
    Console.WriteLine(clt.CalculateF(10));
    Console.WriteLine(clt.CalculateF(15)); 
}

W ten sposób zdefiniowana została i wywołana warstwa “serwisu” w środku jednego pliku EXE.

Dzięki takiej konfiguracji, możemy na przykład dokładnie zobaczyć koszty i komunikację pomiędzy “warstwami”. Można w pliku konfiguracyjnym ustawić:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.diagnostics>
    <sources>
      <source name="System.ServiceModel.MessageLogging" switchValue="Warning, ActivityTracing">
        <listeners>
          <add type="System.Diagnostics.DefaultTraceListener" name="Default">
            <filter type="" />
          </add>
          <add name="ServiceModelMessageLoggingListener">
            <filter type="" />
          </add>
        </listeners>
      </source>
      <source name="System.ServiceModel" switchValue="Warning, ActivityTracing"
        propagateActivity="true">
        <listeners>
          <add type="System.Diagnostics.DefaultTraceListener" name="Default">
            <filter type="" />
          </add>
          <add name="ServiceModelTraceListener">
            <filter type="" />
          </add>
        </listeners>
      </source>
    </sources>
    <sharedListeners>
      <add initializeData="C:\TS\PREZENTACJE\WCF35SP1_InsideExe\WCF35SP1_InsideExe\App_messages.svclog"
        type="System.Diagnostics.XmlWriterTraceListener, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
        name="ServiceModelMessageLoggingListener" traceOutputOptions="LogicalOperationStack, DateTime, Timestamp, ProcessId, ThreadId, Callstack">
        <filter type="" />
      </add>
      <add initializeData="C:\TS\PREZENTACJE\WCF35SP1_InsideExe\WCF35SP1_InsideExe\App_tracelog.svclog"
        type="System.Diagnostics.XmlWriterTraceListener, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
        name="ServiceModelTraceListener" traceOutputOptions="LogicalOperationStack, DateTime, Timestamp, ProcessId, ThreadId, Callstack">
        <filter type="" />
      </add>
    </sharedListeners>
  </system.diagnostics>
  <system.serviceModel>
    <diagnostics>
      <messageLogging logMalformedMessages="true" logMessagesAtTransportLevel="true" />
    </diagnostics>
    <services>
      <service name="WCF35SP1_InsideExe.MySimpleService">
        <endpoint address="net.pipe://localhost/MySimpleService" binding="netNamedPipeBinding" contract="WCF35SP1_InsideExe.ISimpleService" />
      </service>
    </services>
    <client>
      <endpoint address="net.pipe://localhost/MySimpleService" binding="netNamedPipeBinding" contract="WCF35SP1_InsideExe.ISimpleService" />
    </client>
  </system.serviceModel>
</configuration>

Analogiczne operacje można wykonać po wybraniu Tools – WCF Service Configuration Editor (wtedy jest prościej):

image

(w tym przypadku włączone są wszystkie elementy poza komunikatami serwisowymi (które przydają się tylko przy bardzo specyficznej diagnostyce). Logowane informacje są zapisywane w 2 plikach App_messages.svclog (fizyczne komunikaty przesyłane przez WCF) i App_tracelog.svclog (śledzenie aktywności).

Poniżej można zobaczyć graf wywołań:

image 
przesyłane komunikaty (z czasem serializacji i deserializacji)

image

czy po prostu listę aktywności (z czasem)

image

Narzędzie do analizy plików *.svclog znajduje się w <programfiles>\Microsoft SDKs\Windows\v6.0A\bin\SvcTraceViewer.exe. Tam także znajduje się plik z dokumentacją (SvcTraceViewer.chm) którą warto przejrzeć.

Wydajność

Warto pamiętać, że taka technika nie nadaje się do zastępowania każdego wywołania metody w kodzie. Może być zastosowana pomiędzy logicznymi granicami w aplikacji. Użycie WCF “kosztuje” na jednym wywołaniu metody około 3 ms. Dokładniej, na procesorze Intel T7500 samo wywołanie metody w klasie C# trwa 0,00002 ms. Wywołanie przez WCF zajmuje “aż” 2,8691 ms.

Ale – proszę zobaczyć, że jeżeli ta metoda liczy coś bardziej skomplikowanego (na przykład – tu ciąg Fibbonaciego), to się okazuje że ten “narzut” na wywołanie nie jest aż tak bardzo istotny. Bezpośrednie wywołanie to na przykład 69,763 ms, a przy użyciu WCF czas wzrasta do (średnio) 74,357 ms.

Natomiast warto pamiętać, że aby dodać kontekst bezpieczeństwa, sesje, pilnować by nie powstało “za dużo” obiektów danego typu – to konieczne jest samodzielne zakodowanie stosownych algorytmów – które w WCF są gotowe, a dodatkowo ta implementacja jest bardzo wydajna (i skalowalna). Oczywiście, nie zachęcam by w całej aplikacji wymienić wszystkie wywołania na takie z wykorzystaniem WCF. Ale – tam gdzie mamy jakieś granice (typu moduł, czy jakiś blok funkcjonalny w kodzie), warto wykorzystać taką technologię, bo będzie łatwiej chociażby monitorować aplikacje

Pełne wyniki prostego testu:

Proste dodawanie, dla 100000 iteracji 
Wywołanie bezpośrednie: 2 ms 
Wywołanie bezpośrednie/sztukę: 0,00002 ms 
Wywołanie proxy: 286910 ms 
Wywołanie jednej metody: 2,8691 ms 
Ciąg Fibbonaciego, dla 1000 iteracji 
Wywołanie bezpośrednie: 69763 ms 
Wywołanie bezpośrednie/sztukę: 69,763 ms 
Wywołanie proxy: 74357 ms 
Wywołanie jednej metody: 74,357 ms 

Pełny przykład:

WCF_InsideExe.zip (83,06 kb)

Pierwszy oceń post!

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


PDC 2008, Oslo (wstęp), dzień 2

clock 30 listopada, 2008 19:00 przez author tkopacz
Oslo to nowa platforma do modelowania przeznaczona do budowy dowolnego typu modeli w oparciu o koncepcję języków DSL (Domain Specific Language).
Oslo zawiera dwa narzędzia do pracy z modelem. Jedno, to po prostu edytor tekstu. Język M pozwala zdefiniować model, ew. gramatykę (która przełoży wyrażenie w języku naturalnym na coś, co będzie np. zgodne z modelem danych). Drugie narzędzie to edytor, Quadrant (napisany w WPF) który może równolegle być używany do pracy z modelem.
Modelować możemy schemat (postać), dane, przepływy, ograniczenia nakładane na dane czy operacje. Ale - w samym języku nie definiujemy "operacji" - to zalezy już od gramatyki i transformacji z MSchema na np. SQL czy C#. Na PDC był pokazywany przykład gdzie na podstawie struktury modelu generowana była tabela, czy - usługa WCF.
Ponieważ M jest zintegrowany z VS.NET - model można "śledzić", wykonywać krok po kroku.
Dostępne jest centralne repozytorium modeli. Oslo to nie jest narzędzie przeznaczone dla Visual Studio czy Biztalk - jest to w pewnym sensie "niezależna" platforma do modelowania, z której może korzystać inne narzędzie.
Oslo będzie zintegrowane z Entity Framework (czyli model encji aplikacyjnych będzie częścią "uniwersalnego" wymodelowanego świata). Obsługuje także przepływy Workflow Foudation (obecnie jest nawet gotowa gramatyka, i można w M pisać przepływy). Inne narzędzia też będą integrowane z "Oslo". Tak naprawdę, Oslo ma 3 elementy
- narzędzia do definiowania modelu (graficzne lub tekstowe)
- repozytorium w bazie
- warstwę runtime (Dublin, WCF, WF, SQL), która realizuje to co jest opisane modelem.
Obecnie można ściągnąć CTP SDK do Oslo. Sama specyfikacja języka M jest dostępna w ramach licencji otwartej licencji OSP.

Aktualnie oceniony na 1.0 (1)

  • Currently 1/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


O autorze

Tomasz Kopacz, Microsoft

Zajmuje się współpracą z architektami oraz projektantami systemów wykorzystujących, między innymi, technologie Microsoft. W ramach współpracy doradza przy wyborze właściwych elementów pozwalających opracować rozwiązanie informatyczne. Zajmuje się również prezentacją wzorców architektonicznych (ze szczególnym naciskiem na koncepcję rozwiązań opartych o SOA – zorientowanych na usługi) oraz szeroko pojętym wykorzystaniem technologii .NET, serwerów Microsoft i różnych narzędzi wspierających prowadzenie projektów.

Zaloguj