MassTransit laboratorium 2017 T. Goluch, K.M. Ocetkiewicz

Podobne dokumenty
namespace HostedReceiver { public class Receiver: IConfigureThisEndpoint, AsA_Server {

Service Bus: MassTransit laboratorium 2017 K.M. Ocetkiewicz, T. Goluch

Wprowadzenie do projektu QualitySpy

xmlns:prism= c. <ContentControl prism:regionmanager.regionname="mainregion" />

Programowanie obiektowe

Dokumentacja do API Javy.

Instrukcja obsługi certyfikatów w programie pocztowym MS Outlook Express 5.x/6.x

Obiekt klasy jest definiowany poprzez jej składniki. Składnikami są różne zmienne oraz funkcje. Składniki opisują rzeczywisty stan obiektu.

Ćwiczenie 1. Kolejki IBM Message Queue (MQ)

KLASA UCZEN Uczen imię, nazwisko, średnia konstruktor konstruktor Ustaw Wyswietl Lepszy Promowany

Wykład 8: klasy cz. 4

KLASA UCZEN Uczen imię, nazwisko, średnia konstruktor konstruktor Ustaw Wyswietl Lepszy Promowany

Opis protokołu RPC. Grzegorz Maj nr indeksu:

Aplikacje RMI

Narzędzia i aplikacje Java EE. Usługi sieciowe Paweł Czarnul pczarnul@eti.pg.gda.pl

Projektowanie aplikacji internetowych laboratorium

PHP 5 język obiektowy

Interfejsy. Programowanie obiektowe. Paweł Rogaliński Instytut Informatyki, Automatyki i Robotyki Politechniki Wrocławskiej

Wykład 4 Delegat (delegate), właściwości indeksowane, zdarzenie (event) Zofia Kruczkiewicz

Ciekawym rozwiązaniem służącym do obsługi zdarzeń dla kilku przycisków w ramach jednej aktywności może być następujący kod:

Rozdział 4 KLASY, OBIEKTY, METODY

Programowanie obiektowe

Zadanie polega na stworzeniu bazy danych w pamięci zapewniającej efektywny dostęp do danych baza osób.

Programowanie obiektowe

PWSG Ćwiczenia 12. Wszystkie ukończone zadania należy wysłać na adres: lub

Instrukcja tworzenia aplikacji EE na bazie aplikacji prezentowanej na zajęciach lab.4 z PIO umożliwiająca przez sieć dostęp wielu użytkownikom.

Programowanie obiektowe

Wykład 5: Klasy cz. 3

.NET Klasy, obiekty. ciąg dalszy

Wykład 12. Programowanie serwera MS SQL 2005 w C#

Aplikacje internetowe i rozproszone - laboratorium

Zaawansowane aplikacje WWW - laboratorium

Techniki programowania INP001002Wl rok akademicki 2018/19 semestr letni. Wykład 3. Karol Tarnowski A-1 p.

MVVM Light Toolkit. Julita Borkowska

D:\DYDAKTYKA\ZAI_BIS\_Ćwiczenia_wzorce\04\04_poprawiony.doc 2009-lis-23, 17:44

Marcin Luckner Politechnika Warszawska Wydział Matematyki i Nauk Informacyjnych

Metody Metody, parametry, zwracanie wartości

Aplikacje w środowisku Java

JAVA W SUPER EXPRESOWEJ PIGUŁCE

Instrukcja laboratoryjna

Zaawansowane aplikacje internetowe - laboratorium

Klasy i obiekty cz II

Klasy cd. Struktury Interfejsy Wyjątki

Kurs WWW. Paweł Rajba.

Programowanie obiektowe

Zaawansowane aplikacje internetowe

1. Czynności przygotowujące aplikację działającą na platformie Java SE Biblioteka5 (należy ją pobrać z załącznika z p.1)

Informatyka I. Klasy i obiekty. Podstawy programowania obiektowego. dr inż. Andrzej Czerepicki. Politechnika Warszawska Wydział Transportu 2018

Dzisiejszy wykład. Wzorce projektowe. Visitor Client-Server Factory Singleton

Komponenty sterowane komunikatami

1 Atrybuty i metody klasowe

UML a kod w C++ i Javie. Przypadki użycia. Diagramy klas. Klasy użytkowników i wykorzystywane funkcje. Związki pomiędzy przypadkami.

Builder (budowniczy) Cel: Przykład:

Singleton. Cel: Przykład: Zastosowanie: Zapewnienie, że klasa ma tylko jedną instancję i dostarczenie globalnego dostępu do niej.

Aplikacje w Javie- wykład 11 Wątki-podstawy

Laboratorium 6 DIAGRAM KLAS (Class Diagram)

Klasy abstrakcyjne i interfejsy

Tworzenie i wykorzystanie usług sieciowych

Programowanie obiektowe

Konstruktory. Streszczenie Celem wykładu jest zaprezentowanie konstruktorów w Javie, syntaktyki oraz zalet ich stosowania. Czas wykładu 45 minut.

Specyfikacja techniczna. mprofi Interfejs API

Programowanie zaawansowane

Diagram klas UML jest statycznym diagramem, przedstawiającym strukturę aplikacji bądź systemu w paradygmacie programowania obiektowego.

1 LINQ. Zaawansowane programowanie internetowe Instrukcja nr 1

Klasy Obiekty Dziedziczenie i zaawansowane cechy Objective-C

Klasy. dr Anna Łazińska, WMiI UŁ Podstawy języka Java 1 / 13

Politechnika Gdańska Katedra Optoelektroniki i Systemów Elektronicznych

Programowanie obiektowe

LINQ TO XML. Autor ćwiczenia: Marcin Wolicki

Microsoft.NET: ASP.NET MVC + Entity Framework (Code First)

Wykład 4: Klasy i Metody

Wprowadzenie do Doctrine ORM

Języki i techniki programowania Ćwiczenia 2

Instrukcja laboratoryjna nr.2

PHP: bloki kodu, tablice, obiekty i formularze

1. Które składowe klasa posiada zawsze, niezależnie od tego czy je zdefiniujemy, czy nie?

Zaawansowane aplikacje internetowe laboratorium REST

Aplikacje w środowisku Java

Obiektowe programowanie rozproszone Java RMI. Krzysztof Banaś Systemy rozproszone 1

Instrukcja 10 Laboratorium 13 Testy akceptacyjne z wykorzystaniem narzędzia FitNesse

Korzystanie z Certyfikatów CC Signet w programie MS Outlook 98

Fragmenty są wspierane od Androida 1.6

Wykład 7: Pakiety i Interfejsy

Java Zadanie 1. Aby poprawnie uruchomić aplikację desktopową, należy zaimplementować główną metodę zapewniającą punkt wejścia do programu.

Instrukcja implementacji sterownika wirtualnego portu szeregowego dla systemu Android. Opracowanie: Elzab Soft sp. z o.o.

akademia androida Składowanie danych część VI

Baza danych sql. 1. Wprowadzenie. 2. Repozytaria generyczne

Instrukcja laboratoryjna nr.4

Backend Administratora

Instrukcja laboratoryjna cz.3

akademia androida Intencje oraz URI część III

PWŚG Ćwiczenia 13. Ukończoną pracę należy przesłać na adres lub

Kurs programowania. Wykład 2. Wojciech Macyna. 17 marca 2016

Kolejkowanie wiadomości Standard MQ (JMS)

Tworzenie i wykorzystanie usług

TEMAT : KLASY DZIEDZICZENIE

Wykład 5 Okna MDI i SDI, dziedziczenie

Transkrypt:

MassTransit laboratorium 2017 T. Goluch, K.M. Ocetkiewicz 1. Wstęp 1 Mass Transit jest framework iem przeznaczonym do tworzenia rozproszonych aplikacji biznesowych opartych o.net. Jest przeznaczony dla korporacji w celu ułatwienia budowy oprogramowania pracującego w jej ramach. Dostarcza mechanizmu subskrybowania wiadomości według typu. Pozwala, poprzez łączenie różnych węzłów przetwarzania przy pomocy tego mechanizmu, budować spójną sieć usług. Współpracuje z takimi systemami kolejkowania jak MSMQ, RabbitMQ i ActiveMQ, co pozwala na integrację ze środowiskiem Mono. Aplikacja oparta o MT jest zbiorem obiektów wysyłających i obsługujących różne rodzaje komunikatów za pomocą kolejki. Jest to technologia lekka, podobnie jak WCF pozwala na samohostowanie w procesach. 2. Instalacja Najprostszym sposobem zainstalowania framework a Mass Transit jest skorzystanie z systemu dystrybucji bibliotek NuGet. Wystarczy w wybranym projekcie wydać w oknie Package Manager Console 2 wydać komendę: albo: install-package MassTransit install-package MassTransit.RabbitMQ jeśli zdecydujemy się na kolejkowanie wiadomości przy użyciu RabbitMQ. Należy pamiętać o odpowiedniej wersji frameworka.net, przykładowo wersja MassTransit.RabbitMQ 3.5.7 wymaga.net a w wersji 4.5.2. Biblioteki zostaną zainstalowane w domyślnym projekcie jeśli chcemy dokonać instalacji w wybranym projekcie musimy explicite podać jego nazwę: install-package MassTransit.RabbitMQ -ProjectName <nazwaprojektu> Klikając PPM 3 w węźle References projektu można wybrać opcję Manage NuGet Package, pozwala ona na zarządzanie pakietami w oknie dialogowym 4. 1 http://masstransit.readthedocs.org/en/latest/overview/backstory.html 2 TOOLS Library Package Manager Package Manager Console 3 PPM prawy przycisk myszy 4 https://docs.nuget.org/docs/start-here/managing-nuget-packages-using-the-dialog T. Goluch, K.M. Ocetkiewicz C91E19C4-8B08-4D63-9B7C-0913AAB2A7CEMassTransitC91E19C4-8B08-4D63-9B7C-0913AAB2A7CE 1

Rysunek 1 - okno menedżera pakietów NuGet W przypadku kiedy pakiet NuGet okaże się niezainstalowany można go zainstalować przy pomocy menedżera dodatków: TOOLS Extensions and Updates. 3. Wiadomości Wiadomość w MT to obiekt klasy lub interfejsu.net. Może zawierać dowolne pola (typy proste, złożone, kolekcje.net), które powinny być właściwościami np.: public class BasicRequest public string Text get; set; Wiadomości powinny być zaimplementowane w osobnym komponencie w celu łatwego współdzielenia ich kodu. Uwaga, projekty wysyłający/publikujący wiadomość oraz odbierający/subskrybujący muszą korzystać z tego samego komponentu zawierającego klasy wiadomości. W innym przypadku komunikacja nie powiedzie się! Wiadomości mogą być wysyłane przy pomocy metod szyny (Bus).GetEndpoint(new Uri(" ")).Send( ) albo publikowane.publish( ), PublishRequest( ). W celu ich odebrania należy dokonać subskrypcji przy pomocy metody.subscribe( ) do której przekazujemy delegata, metodę anonimową bądź wyrażenie lambda obsługującą odebrany komunikat. Możemy również przesłać wiadomość zwrotną przy pomocy metody.respond( ). Dokładniej o wysyłaniu, publikowaniu, subskrybowaniu i odpowiadaniu na wiadomości opowiemy sobie w następnych punktach instrukcji. 4. Komunikacja Publish-Subscribe 5 Model komunikacji wydawca-odbiorca polega na tym, że zarówno ani wydawca ani odbiorca nic o sobie nie wiedzą. Wydawca publikuje wiadomości pewnego typu, natomiast abonent subskrybuje pewne typy. Całą wiedzę o tym do kogo ma trafić, która widomość posiada szyna (w naszym przypadku MassTransit Bus). W celu budowy aplikacji MT opartej o wzorzec publish-subscribe realizującej komunikację po RabbitMQ należy utworzyć projekt wiadomości oraz dwa projekty konsolowe nadawcy i odbiorcy z zainstalowanym MassTransit.RabbitMQ. Kod prostej aplikacji widoczny jest poniżej (proces publikujący): public class Program public static void Main() Bus.Initialize(sbc => sbc.usemsmq(configurator => 5 http://docs.masstransit-project.com/en/latest/configuration/quickstart.html T. Goluch, K.M. Ocetkiewicz C91E19C4-8B08-4D63-9B7C-0913AAB2A7CEMassTransitC91E19C4-8B08-4D63-9B7C-0913AAB2A7CE 2

configurator.verifymsmqconfiguration(); configurator.usemulticastsubscriptionclient(); ); sbc.receivefrom("msmq://localhost/publisher"); ); System.Threading.Thread.Sleep(3.Seconds()); Bus.Instance.Publish(new BasicRequest Text = "Hi"); Bus.Initialize( ); inicjalizacja szyny, przyjmuje wyrażenie lambda w celu konfiguracji. Jeśli nie chcemy korzystać z singletona możemy stworzyć obiekt szyny: var bus = ServiceBusFactory.New( ),.UseMsmq(); informacja o jaki transport będzie oparta aplikacja MT, metody: VerifyMsmqConfiguration() i VerifyMsDtcConfiguration() mają za zadanie potwierdzić, że RabbitMQ i DTC 6 są zainstalowane poprawnie i ewentualnie dodać brakujące komponenty,.receivefrom("msmq://localhost/test_queue"); wiadomości będą odbierane z lokalnej, prywatnej kolejki o nazwie: test_queue. Dla celów developerskich można wykorzystać domyślnie dostępny protokół loopback: sbc.receivefrom("loopback://localhost/queue"), Publish( ); opublikowanie wiadomości. Należy dać wydawcy czas na zarejestrowanie się u niego subskrybenta. W tym celu po zainicjalizowaniu szyny należy odczekać pewien czas (można wykorzystać metodę.readkey(), lub.sleep(3.seconds()) 7 ). Kod odbiorcy wiadomości (subskrybenta) jest podobny, z tą różnicą że nie publikujemy żadnych wiadomości 8, a w zamian dokonujemy subskrypcji interesujących nas typów wiadomości: public class Program public static void Main() Bus.Initialize(sbc => sbc.usemsmq(configurator => configurator.verifymsmqconfiguration(); configurator.usemulticastsubscriptionclient(); ); sbc.receivefrom("msmq://localhost/subscriber"); sbc.subscribe(subs => subs.handler<basicrequest>(m => Console.WriteLine("odebrano: 0", m.text)); ); ); sbc.subscribe( ); subskrypcja uchwytu (wyrażenie lambda) do wiadomości typu Your Message, który, dla każdej z nich, będzie wypisywał w oknie konsoli zawartość właściwości Text. 5. Komunikacja Publish/Request-Subscribe/Response 9 Jeśli chcemy otrzymać odpowiedź od abonenta to musimy dodać do projektu komunikatów typ odpowiedzi, a następnie skorzystać z metody PublishRequest( ): 6 http://msdn.microsoft.com/en-us/library/ms684146(v=vs.85).aspx 7 Jeśli chcemy określić interwał czasowy przy pomocy wyrażenia XX.Seconds() musimy skorzystać z przestrzeni nazw: Magnum.Extensions. 8 Oczywiście nic nie stoi na przeszkodzie aby wydawca był równocześnie subskrybentem. 9 http://docs.masstransit-project.com/en/latest/overview/request.html T. Goluch, K.M. Ocetkiewicz C91E19C4-8B08-4D63-9B7C-0913AAB2A7CEMassTransitC91E19C4-8B08-4D63-9B7C-0913AAB2A7CE 3

Bus.Instance.PublishRequest(new BasicRequest() Text = "asd", x => x.handle<basicresponse>(message => Console.WriteLine(message.Text)); x.settimeout(5.seconds()); ); Metoda PublishRequest( ), w porównaniu do zwykłego Publish( ) dodatkowo, jako drugi parametr przyjmuje obiekt konfigurujący wywołanie zwrotne. Jego metoda generyczna Handle< >( ) przyjmuje funkcję, która będzie wywoływana podczas przetwarzania wiadomości. Ponieważ PublishRequest( ) jest metodą synchroniczną należy ustawić timeout przy pomocymetody SetTimeout( ), która po przekroczeniu odpowiedniego czasu rzuca wyjątek: MassTransit.Exceptions.RequestTimeoutException. W procesie abonenta musimy podczas subskrypcji do metody szablonowej Handler< >( ) przekazać jako pierwszy, dodatkowy parametr reprezentujący kontekst pozwalający na wysłanie odpowiedzi przy pomocy metody Respond(): sbc.subscribe(sub => sub.handler<basicrequest>((cxt, msg) => Console.WriteLine("odebrano 0", msg.text); cxt.respond(new BasicResponse Text = "RESP" + msg.text ); ); ); 6. Konfiguarcja 10 MT podczas serializacji wspiera formaty JSON, BSON, XML, binarny oraz własny. Można dokonać zmiany: ServiceBusFactory.New(sbc => sbc.usebinaryserializer(); sbc.usebsonserializer(); sbc.usejsonserializer(); sbc.useversiononexmlserializer(); sbc.usexmlserializer(); ); //if you would like to implement your own. sbc.setdefaultserializer<tserializer>(); jednak jest to posunięcie opcjonalne, ponieważ domyślnie wykorzystywane są formaty preferowane przez daną warstwę transportową. W trybie produkcyjnym można dokonać tuningu szyny: ServiceBusFactory.New(sbc => sbc.setconcurrentconsumerlimit(2); sbc.setdefaulttransactiontimeout(5.minutes()); ); sbc.afterconsumingmessage(() => ); sbc.beforeconsumingmessage(() => );.SetConcurrentConsumerLimit( ); maksymalna liczba wątków przetwarzających wiadomości, 10 http://docs.masstransit-project.com/en/latest/configuration/config_api.html T. Goluch, K.M. Ocetkiewicz C91E19C4-8B08-4D63-9B7C-0913AAB2A7CEMassTransitC91E19C4-8B08-4D63-9B7C-0913AAB2A7CE 4

.SetDefaultTransactionTimeout( ); domyślny timeout dla transakcji,.after/beforeconsumingmessage( ); polecenia wykonywane przed/po przetworzeniu wiadomości, Aby sprawdzić konfiguracje szyny można wykorzystać metodę Probe() : var probe = bus.probe(); Można również wypisać konfigurację na konsoli: bus.writeintrospectiontofile("a_file.txt"); lub zapisać do pliku: bus.writeintrospectiontoconsole(); 7. Przetwarzanie wiadomości W MT można przetwarzać wiadomości na wiele możliwych sposobów 11 : Handlers dotychczas przez nas wykorzystywany, najprostszy i najmniej elastyczny sposób. Jest to dowolna metoda (również anonimowa albo wyrażenie lambda) przyjmująca pojedynczy argument typu wiadomość i zwracająca typ void: static public void Handler(BasicRequest m) Po otrzymaniu wiadomości MT wywoła powyższą metodę przekazując wiadomość jako argument. W tym przypadku nie istnieją żadne mechanizmy związane z zarządzaniem czasem życia wiadomości. Można się spotkać z sytuacją, że kilka wiadomości będzie równolegle przetwarzanych przez różne wątki. Aby temu zapobiec można jedynie skonfigurować szynę na obsługę wiadomości w pojedynczym wątku. Instances obiekt klasy implementującej jeden bądź więcej interfejsów Consumes, z których każdy przyjmuje argument generyczny (musi być typu referencyjnego) deklarujący typ wiadomości obsługiwany przez tę instancję: public class MyClass : Consumes<BasicRequest>.All, Consumes<OtherBasicRequest>.All public void Consume(BasicRequest message) Console.WriteLine("odebrano 0", message.text); public void Consume(OtherBasicRequest message) Console.WriteLine("dodatkowo odebrano 0", message.text); Subskrybcji instancji dokonujemy przy pomocy metody Instance( ): sbc.subscribe(sub => sub.instance(new MyClass()); Interfejs Consumes może być zdefiniowany na cztery sposoby: 11 http://docs.masstransit-project.com/en/latest/overview/keyideas.html T. Goluch, K.M. Ocetkiewicz C91E19C4-8B08-4D63-9B7C-0913AAB2A7CEMassTransitC91E19C4-8B08-4D63-9B7C-0913AAB2A7CE 5

o o o.all wymaga implementacji metody Consume( ) która jako parametr przyjmuje wiadomość. Służy do przetwarzania wszystkich wiadomości danego typu..selected dodatkowo wymaga implementacji metody Accept( ), która stwierdza, zwracając wartość logiczną true, czy dana wiadomość ma zostać przetworzona..for< > pozwala na korelacje wiadomości 12. Przyjmuje parametr generyczny będący typem identyfikatora. Zaleca się wykorzystanie typu Guid, który zapewnia globalną unikalność. Wiadomości, które podlegają korelacji muszą implementować interfejs CorrelatedBy< >: public class CorrelatedRequest : CorrelatedBy<Guid> ( ) public Guid CorrelationId get; set; Przykładowo, obiekt konsumenta posiadający właściwość: public Guid CorrelationId get return new Guid("CFA84E50-9CA2-4861-A6CA-ADA17C5AEE76"); Będzie przetwarzał tylko wiadomości, opublikowane z tym samym numerem Guid: Bus.Instance.Publish(new CorrelatedRequest(), CorrelationId = new Guid("CFA84E50-9CA2-4861-A6CA-ADA17C5AEE76") ); o Context w tym przypadku metoda Consume(), przyjmuje cały kontekst wiadomości, która jest jedną z jego właściwości. Podobnie jak w przypadku zwykłych uchwytów tutaj również możemy odpowiedzieć procesowi publikującemu (przy pomocy metody PublishRequest( )) wykorzystując metodę Respond( ): public void Consume(IConsumeContext<RequestResponse> message) Console.WriteLine("odebrano wiadomość wraz z kontekstem 0", message.message.text); message.respond<basicresponse>(new BasicResponse() Text = "res" ); Consumers Deklarowane są w identyczny sposób jak instancje, przy pomocy interfejsów Consumes, jednak subskrypcja wymaga podania fabryki obiektów. W momencie odbierania widomości MT tworzy obiekt konsumenta, przy pomocy wspomnianej fabryki, na którym wywołuje metodę Consume( ) do której przekazuje wiadomość jako parametr. Wykorzystanie fabryki pozwala na obsługę cyklu życia obiektu konsumenta. Może to być proste wywołanie konstruktora albo pozyskanie obiektu konsumenta wraz z jego zależnościami. Dodatkowo fabryka może opakować wołania konsumenta dodatkowym kodem wywoływanym przed i po przetworzeniu wiadomości. Saga Pozwala wysyłanie wiadomości w trybie długotrwałej transakcji, ale o tym później. 8. Wersjonowanie 13 Kiedy system się rozwija, usługi ewoluują, zmieniają się wymagania możemy być pewni, że czeka nas zmiana zawartości wiadomości. W takim przypadku może pojawić się problem, kiedy część systemu pracuje już z nową a część jeszcze ze starą wersją komunikatu. Z pomocą przychodzi mechanizm ich wersjonowania. 12 http://docs.masstransit-project.com/en/latest/overview/correlation.html 13 http://docs.masstransit-project.com/en/latest/overview/versioning.html T. Goluch, K.M. Ocetkiewicz C91E19C4-8B08-4D63-9B7C-0913AAB2A7CEMassTransitC91E19C4-8B08-4D63-9B7C-0913AAB2A7CE 6

Wiadomości powinny implementować pewien kontrakt/interfejs, jeśli nowa wersja serwisu będzie wymagać zmian w dotychczasowym kontrakcie, możemy zaimplementować nowy interfejs i opublikować jedną wiadomość implementującą obydwa, stary i nowy. MT zadba o to aby do abonentów trafiły wiadomości implementujące interesujący je interfejs. Przykładowe kontrakty/interfejsy i wspólna wiadomość mogą mieć następującą postać: public interface IBasicRequest string Text get; public interface IBasicRequestV2 Uri Adress get; public class BasicRequest : IBasicRequest, IBasicRequestV2 public string Text get; set; public Uri Adress get; set; Wysłanie wiadomości wymaga implementacji wszystkich pół: Bus.Instance.Publish(new BasicRequest() Text = "asd", Adress = new Uri("http://localhost") ); Pobranie wiadomości w starym procesie (Handler): static public void Handler(IBasicRequest m) Console.WriteLine("odebrano: 0", m.text); i w nowym (Instancja/Konsument): public class MyClass : Consumes<IBasicRequestV2>.All public void Consume(IBasicRequestV2 message) Console.WriteLine("odebrano Uri: 0", message.adress.tostring()); 9. Szyfrowanie 14 MT pozwala na łatwe szyfrowanie wiadomości, w tym celu należy wykorzystać nakładkę PreSharedKeyEncryptedMessageSerializer() na serializator, która implementuje szyfrowanie Rijndael 15 : Bus.Initialize(sbc => ( ) sbc.setdefaultserializer(new PreSharedKeyEncryptedMessageSerializer( "qwertyuiop[]asdfghjkl;'zxcvbnm,.", new XmlMessageSerializer())); ); 14 http://docs.masstransit-project.com/en/latest/overview/valueadd.html?highlight=encryption 15 https://en.wikipedia.org/wiki/rijndael_key_schedule T. Goluch, K.M. Ocetkiewicz C91E19C4-8B08-4D63-9B7C-0913AAB2A7CEMassTransitC91E19C4-8B08-4D63-9B7C-0913AAB2A7CE 7

Podczas inicjalizacji szyny należy do metody SetDefaultSerializer() obiektu konfiguracyjnego przekazać jako domyślny serializator obiekt PreSharedKeyEncryptedMessageSerializer z przestrzeni nazw MassTransit.Serialization, który z kolei wymaga podania klucza składającego się z 32 znaków ASCII oraz obiektu bazowego serializatora. W naszym przypadku XmlMessageSerializer(). 10. Saga 16 Saga w MT może być definiowana na dwa sposoby. Pierwsze podejście podobne jest do konsumenta, klasa sagi wykorzystuje odpowiednie interfejsy do definiowania wiadomości pozwalających na jej inicjalizację (interfejs InitiatedBy), orchestrację (interfejs Orchestrates) bądź może obserwować pewne wiadomości. Od zwykłych instancji odróżnia ją fakt, że jest zarządzana przez framework MT, który dba o to aby odpowiednie wiadomości trafiły do skorelowanej z nimi instancji sagi. Odbywa się to z wykorzystaniem interfejsu CorrelatedID naturalnie wykorzysującego do tego celu typ Guid. Sagi mogą również obserwować wiadomości, które nie są z nimi bezpośrednio skorelowane (jednak powinniśmy uważać na potencjalne niebezpieczeństwo związane z powiązaniem wiadomości z olbrzymią liczbą różnych instancji sagi). public class MySaga : ISaga, InitiatedBy<MyInitialMessage>, Orchestrates<MyFollowUpMessage> public Guid CorrelationId get; set; public void Consume(MyInitialMessage message) public void Consume(MyFollowUpMessage message) public IServiceBus Bus get; set; Drugie podejście polega na stworzeniu maszyny stanów w skład której wchodzą zdarzenia, stany i akcje. W tym celu należy stworzyć klasę dziedziczącą po interfejsie SagaStateMachine<T> typizowanym wiadomością inicjalizującą nową instancję sagi. Przykładowa pusta saga reprezentująca aukcję mogła by wyglądać następująco: public class AuctionSaga : SagaStateMachine<AuctionSaga>, ISaga public AuctionSaga(Guid guid) CorrelationId = guid; static AuctionSaga() Define(() => // the state machine behavior is defined here ); public Guid CorrelationId get; set; public IServiceBus Bus get; set; W momencie tworzenia nowego obiektu sagi do właściwości CorrelationId przypisywany jest nowy Guid. Właściwość IServiceBus będzie zainicjalizowana zanim dowolna metoda sagi zostanie wywołana, pozwala to na wykorzystanie obiektu szyny przez zdarzenia zdefiniowane w danej sadze. Istnieją również dwa predefiniowane stany: Initial i Completed, które muszą być uwzględnione podczas definicji sagi. Przy pomocy statycznych właściwości State możemy zadeklarować dodatkową liczbę stanów: 16 http://docs.masstransit-project.com/en/latest/overview/saga.html?highlight=sagas T. Goluch, K.M. Ocetkiewicz C91E19C4-8B08-4D63-9B7C-0913AAB2A7CEMassTransitC91E19C4-8B08-4D63-9B7C-0913AAB2A7CE 8

public static State Initial get; set; public static State Completed get; set; public static State Open get; set; public static State Closed get; set; Aby mieć możliwość przechodzenia pomiędzy stanami musimy zdefiniować jakieś zdarzenia: public static Event<ICreateAuction> Create get; set; public static Event<IPlaceBid> Bid get; set; public static Event<ICloseAuction> Timeout get; set; Zdarzenia, tak jak stany, definiowane są przy pomocy statycznych właściwości podatkowo parametryzowanych typem skojarzonych z nimi wiadomości. W momencie kiedy saga jest subskrybowana przez szynę, subskrybcja dotyczy to również tych wiadomości. W momencie utworzenia aukcji wysyłane jest polecenie/wiadomość CreateAuction na endpoint w którym zarejestrowana jest saga: public interface CreateAuction : CorrelatedBy<Guid> string Title get; string OwnerEmail get; decimal OpeningBid get; Dla każdej wiadomości typu Bid (Oferta) istnieje osobny unikalny identyfikator BidId typu Guid. public interface PlaceBid Guid BidId get; Guid AuctionId get; decimal MaximumBid get; string BidderEmail get; W celu powiązania wiadomości z odpowiadającą jej instancją sagi wykorzystano właściwość AuctionId, również typu Guid. Posiadając komunikaty powiązane z odpowiadającymi im zdarzeniami zdefiniowanymi w sadze, musimy określić jak i kiedy te zdarzenia będą obsługiwane. Opisujemy to jako akcję przekazywaną metody statycznej metody Define w statycznym konstruktorze klasy: static AuctionSaga() Define(() => Initially( When(Create)); During(Open, When(Bid)); ); Zdarzenie Create jest akceptowane wyłącznie kiedy saga jest w stanie Initial (jest ono domyślne dla nowych instancji sagi). Jeśli dla danego komunikatu nie istnieje odpowiadająca mu instancja sagi to zostaje takowa powołana. Metoda Initially() jest równoważna wołaniu During(Initial). Wyrażenie During() definiuje które zdarzenia są akceptowane w danym stanie. W naszym przypadku zdarzenie Bid jest akceptowane jeśli saga jest w stanie Open. Zdarzenie Bid jest przypadkiem specjalnym, ponieważ nie jest skorelowana poprzez Guid. Aby dostarczyć wiadomości do odpowiadających im instancji sagi należy zdefiniować odpowiednie powiązanie, które definiujemy przy pomocy metody Correlate(): static AuctionSaga() T. Goluch, K.M. Ocetkiewicz C91E19C4-8B08-4D63-9B7C-0913AAB2A7CEMassTransitC91E19C4-8B08-4D63-9B7C-0913AAB2A7CE 9

Define(() => Correlate(Bid).By((saga, message) => saga.correlationid == message.auctionid); ); Zauważmy, że dla wiadomości CreateAuction wykorzystana domyślna korelacja (poprzez Guid). W następnym kroku należy zdefiniować zachowanie odpowiadające danemu zdarzeniu. static AuctionSaga() Define(() => Initially( When(Create).Then((saga, message) => saga.openingbid = message.openingbid; saga.owneremail = message.owneremail; saga.title = message.title; ).TransitionTo(Open)); ); Powyżej zdefiniowano dwa proste etapy zachowania. Pierwszy inicjalizuje kilka właściwości sagi wykorzystując otrzymaną wiadomość. Drugi powoduje zmianę stanu sagi do Open. Do poprawnego działania należy dodać do klasy sagi następujące właściwości: public decimal OpeningBid get; set; public string OwnerEmail get; set; public string Title get; set; Dla zdarzenia Bid definicja zachowania wygląda następująco: static AuctionSaga() Define(() => During(Open, When(Bid).Then((saga, message) => saga.placenewbid(message))); ); Wywoływana jest metoda Handle(). Jeśli otrzymana stawka jest wyższa niż aktualna wartość oferty. Aktualizowana jest aktualna wartość oferty, jej identyfikator oraz informacja o właścicielu. void Handle(PlaceBid bid) if (!CurrentBid.HasValue bid.maximumbid > CurrentBid) if (HighBidder!= null) Bus.Publish(new Outbid(HighBidId)); CurrentBid = bid.maximumbid; HighBidder = bid.bidderemail; HighBidId = bid.bidid; else T. Goluch, K.M. Ocetkiewicz C91E19C4-8B08-4D63-9B7C-0913AAB2A7CEMassTransitC91E19C4-8B08-4D63-9B7C-0913AAB2A7CE 10

throw new UnderBidException(); public decimal? CurrentBid get; set; public string HighBidder get; set; public Guid HighBidId get; set; T. Goluch, K.M. Ocetkiewicz C91E19C4-8B08-4D63-9B7C-0913AAB2A7CEMassTransitC91E19C4-8B08-4D63-9B7C-0913AAB2A7CE 11

public static void Main() Bus.Initialize(sbc => sbc.usemsmq(cfg => cfg.verifymsmqconfiguration(); cfg.usemulticastsubscriptionclient(); ); sbc.receivefrom("msmq://localhost/my_saga_bus"); sbc.subscribe(subs => subs.saga<auctionsaga>(new InMemorySagaRepository<AuctionSaga>()).Permanent(); ); ); Console.WriteLine("naciśnij klawisz aby zakończyć"); Console.ReadKey(); Subskrypcja sagi wygląda następująco: Kod klienta jest identyczny jak w przypadku zwykłego procesu publikującego i subskrybującego komunikaty. Podsumowując, schemat sagi dla serwisu aukcyjnego powinien wyglądać następująco: Projekt auctiontimer zaczyna odliczać czas po zainicjalizowaniu komunikatem NewAuction, dla kolejnych komunikatów NewOdder jeśli wartość aukcji uległa zmianie zeruje licznik zakończenia aukcji. Lepszym rozwiązaniem jest resetowanie licznika aukcji po odebraniu komunikatu OutBid (proszę to zmienić). T. Goluch, K.M. Ocetkiewicz C91E19C4-8B08-4D63-9B7C-0913AAB2A7CEMassTransitC91E19C4-8B08-4D63-9B7C-0913AAB2A7CE 12

Przykładowy kod timera: static void Main(string[] args) Bus.Initialize(sbc => sbc.usemsmq(cfg => cfg.verifymsmqconfiguration(); cfg.usemulticastsubscriptionclient(); ); sbc.receivefrom("msmq://localhost/timer"); sbc.subscribe(sub => sub.handler<icreateauction>(handler); sub.handler<iplacebid>(handler); ); ); Console.WriteLine("naciśnij klawisz aby zakończyć"); Console.ReadKey(); static int timercounter; static Guid LatestCorrelationId; const int onebidtime = 10000; const int sleeptime = 1000; private static void Handler(IPlaceBid m) LatestCorrelationId = m.auctionid; timercounter =+ onebidtime; Console.WriteLine("Nowa oferta w aukcji: 0", m.maximumbid); private static void Handler(ICreateAuction m) LatestCorrelationId = m.correlationid; Console.WriteLine("Uruchomiono timer"); StartTimer(oneBidTime); private static void StartTimer(int onebidtime) timercounter = onebidtime; while (timercounter > 0) System.Threading.Thread.Sleep(sleepTime); timercounter -= sleeptime; Console.WriteLine("Do końca aukcji pozostało: 0 sek.", timercounter / 1000); Bus.Instance.Publish<ICloseAuction>(new CloseAuction() CorrelationId = LatestCorrelationId ); Wynik powinien wyglądać podobnie jak zamieszczony niżej: T. Goluch, K.M. Ocetkiewicz C91E19C4-8B08-4D63-9B7C-0913AAB2A7CEMassTransitC91E19C4-8B08-4D63-9B7C-0913AAB2A7CE 13

A. NuGet 17 W celu automatyzacji procesu pobierania, instalacji, konfiguracji oraz usuwania projektów NServiceBus w projektach VS można wykorzystać menedżera pakietów NuGet Package Manager. Przydatne komendy: get-package pozwala przejrzeć listę zainstalowanych pakietów w ramach projektu, get-package -remote pozwala przejrzeć listę wszystkich pakietów. Należy uważać na długi czas wykonania. Lepiej zastosować odpowiedni filtr: get-package remote filter <nazwapakietu> gdzie nazwa pakietu to np. MassTransit, install-package <nazwapakietu> pozwala zainstalować dany pakiet wraz z zależnościami (innymi wymaganymi pakietami) w domyślnym projekcie, automatycznie zostaną dodane referencje, zaktualizowane zostaną pliki konfiguracyjne oraz dodany zostanie (w katalogu głównym solucji) folder packages zawierający zainstalowane projekty, install-package <nazwapakietu> -ProjectName <nazwaprojektu> pozwala na instalację pakietu w wybranym projekcie, uninstall-package -remote pozwala odinstalować dany pakiet. 17 https://docs.nuget.org/docs/start-here/using-the-package-manager-console http://msdn.microsoft.com/pl-pl/library/nuget--system-dystrybucji-bibliotek.aspx (po polsku) T. Goluch, K.M. Ocetkiewicz C91E19C4-8B08-4D63-9B7C-0913AAB2A7CEMassTransitC91E19C4-8B08-4D63-9B7C-0913AAB2A7CE 14

T. Goluch, K.M. Ocetkiewicz C91E19C4-8B08-4D63-9B7C-0913AAB2A7CEMassTransitC91E19C4-8B08-4D63-9B7C-0913AAB2A7CE 15