RabbitMQ, .NET Core, Nancy Fx, MongoDb – przykład kolejkowania zdarzeń.

Siemanko.

Jak już wspominałem we wcześniejszych postach od pewnego czasu staram się zgłebiać wiedzę na temat systemów rozproszonych i podejścia DDD. Im głebiej w las tym bardziej się jaram i zarazem dostrzegam ułomności standardowego monolitycznego podejścia do budowania aplikacji z pseudo warstwami abstrakcji, które i tak w końcu zamieniają się w spaghetti code (niestety). W tym poście opiszę przykład asynchronicznej komunikacji między aplikacjami poprzez serwer RabbitMQ.

Założenia

Dwie osobne aplikacje .NET Core z wykorzystaniem Nancy FX (bo tak, ale może być Mvc). Pierwsza aplikacja po wejściu na „index” publikuje zdarzenie do kolejki RabbitMQ z DateTime.Now jako danymi (może być cokolowiek).

Druga aplikacja po uruchomieniu „rejestruje” się w kolejce RabbitMQ jako subskrybent i zapisuje każde dane z odczytanego zdarzenia (czyli DateTime.Now w moemencie publikacji). Po wejściu na jej „index” wyświetla listę wszystkich DateTime.Now z odczytanych zdarzeń.

Crew propgramu to to, że druga aplikacja wcale nie musi być uruchomiona aby pierwsza mogła działać i robić swoje (czyli w tym przypadku tylko publikować zdarzenia na każde wejście na „index”) i żeby nic nie zostało utracone.  W momencie kiedy druga aplikacja wystartuje odczyta wszystkie wiadomości z kolejki i zapisze je w swojej bazie danych.

rabbit1

Instalacja

  1. Pobrać i zainstalować Erlang (potrzebne do rabita) – http://www.erlang.org/downloads
  2. Pobrac i zainstalować RabbitMQ – https://www.rabbitmq.com/download.html
  3. Po instalacji Rabbita upewnić się czy masz ustawione wszystkie zmienne – https://www.rabbitmq.com/install-windows-manual.html
  4. Odpalić w konsoli – rabbitmq-plugins enable rabbitmq_management
  5. Odpalić w przeglądarce – http://localhost:15672
  6. Zalogować się – login: guest, hasło: guest

Jesli wszystko poszło dobrze to powinieneś zobaczyć panel zarządzania

2

Producent

To co musi zrobić producent to podłączyć się do kolejki (zostanie utworzona jeśli jej nie ma) i opublikować zdarzenie:

private readonly IModel channel;
private readonly IConnection connection;
private readonly string queueName;

public EventSender()
{
   queueName = "helloWorldQueue";
   var factory = new ConnectionFactory() { HostName = "localhost" };
   this.connection = factory.CreateConnection();
   this.channel = connection.CreateModel();
   this.channel.QueueDeclare(queue: queueName,
   durable: false,
   exclusive: false,
   autoDelete: false,
   arguments: null);
}

public void SendEvent(string message)
{
   var body = Encoding.UTF8.GetBytes(message);

   this.channel.BasicPublish(exchange: "",
                             routingKey: queueName,
                             basicProperties: null,
                             body: body);
}

Po uruchomieniu i wejściu na „index”

3

wystarczy wywołać seriwisik publikujący zdarzenie. Po klikukrotnym odświeżeniu zajrzyjmy do rabbita:

4

Jest 8 wiadomości w kolejce, a aplikacja obsługująca te wiadomości nie jest jeszcze uruchomiona – ba! nawet jeszcze jej nie ma ;).

Subskrybent

Aplikacja przy starcie rejestruje się w kolejce

private readonly IMessagesService messagesService;
private IModel channel;
private IConnection connection;
private readonly string queueName;
private EventingBasicConsumer consumer;

		public EventConsumer(IMessagesService messagesService)
		{
			this.messagesService = messagesService;
			this.queueName = "helloWorldQueue";
		}

		public void Start()
		{
			var factory = new ConnectionFactory() { HostName = "localhost" };
			this.connection = factory.CreateConnection();
			this.channel = connection.CreateModel();
			this.channel.QueueDeclare(queue: queueName,
				durable: false,
				exclusive: false,
				autoDelete: false,
				arguments: null);

			this.consumer = new EventingBasicConsumer(this.channel);
			consumer.Received += (model, ea) =>
			{
				var body = ea.Body;
				var message = Encoding.UTF8.GetString(body);

				messagesService.InsertMessage(message);
			};
			channel.BasicConsume(queue: this.queueName,
				noAck: true,
				consumer: consumer);
		}

Niech obiekt tej klasy będzie singletonem aby był ciągle podłączony do kolejki i mógł odczytywać wiadomości na bieżąco. Imlementacja IMessagesService to warstwa dostępu do danych. Każdą odczytaną wiadomość zapisuje do bazy (w tym przypadku użyłem Mongo). Po wejściu na „index” subskrybenta wszystkie zapisane wiadomości zostaną wyświetlone:

6

Podsumowanie

Cały kodzik obu aplikacji dostępny tutaj.

Pjona!

Azure Function czyli po co Ci serwer?

Siemanko

Ostatnio (dopiero?) podczas zgłębiania wiedzy o mikroserwisach usłyszałem o serverless – czyli architekturze webowej, która nie wymaga klasycznego serwera. Opiera się ona na wywoływaniu funkcji będących w chmurze. Oczywiście my te funkcje musimy napisać i wrzucić do chmury. Obsługę funkcji wg mojej wiedzy oferuje Amazon, Azure i Google Cloud. Jako entuzjasta Microsoft’u założyłem konto na Azure z darmowymi 170cioma euro do wykorzystania przez pierwsze 30 dni. W kilka minut (łącznie z rejestracją) napisałem prostą funckje, która zwraca htmla (wszystko w aplikacji Azure’a w przeglądarce).

using System;
using System.Net;
using System.Net.Http.Headers;

public static async Task Run(HttpRequestMessage req, TraceWriter log)
{
    log.Info("C# HTTP trigger function processed a request.");
    await Task.FromResult(true);

    var pageTemplate = "{0}"; //html document template in string which can't be pasted here
    var pageBody = "Hello World from Azure Function which was made in less time than your coffe!";

    var response = new HttpResponseMessage();
    response.Content = new StringContent(String.Format(pageTemplate, pageBody));
    response.Content.Headers.ContentType = new MediaTypeHeaderValue("text/html");

    return response;
}

Wraz z kliknięciem „Uruchom”

uruchom

Funkcję już można odpalać pod tym adresem.

Dla mnie bomba!

Więcej na stronie Azure’a.

Pjona!

Nancy – czemu my się dopiero poznaliśmy?

Siemanko

Ostatnio zakupiłem polecaną przez kilka znanych osobistości w polskim community książkę „Microservices in .NET Core” gdyż temat mnie interesuje i postanowiłem przyjrzeć sie temu bliżej. We wstępie autor informuje iż w stacku technologicznym wykorzystywanym w przykladach w książce góruje Nancy, której autor jest współtwórcą. Czym jest Nancy mniej więcej wiedziałem bo dość często przewija się to słowo w artykułach, które zdarza mi się czasem przeczytać 😉 Jednak to „mniej więcej” oznaczało jedynie tyle, że wiedziałem o istnieniu tego czegoś ale niekoniecznie co to coś robi ;). Z racji zamiaru przyswojenia całej wiedzy z w.w. książki postanowiłem dziś przyjrzeć się Nancy i po raz pierwszy spróbowac użyć.

Bajecznie prosta i lekka aplikacja webowa

public class IndexModule : NancyModule 
    {
        public IndexModule() 
        {
            Get["/"] = parameters => 
            {
                return View["index"];
            };
        }
    }

Powiedzmy, że to jest odpowiednik kontrolera w Asp.Net.

Teraz w klasie „Program.cs” wystartujmy naszą aplikacje webową

static void Main(string[] args) 
       {
            var uri = new Uri("http://localhost:8500");

            var config = new HostConfiguration();
            config.UrlReservations.CreateAutomatically = true;

            using (var host = new NancyHost(config, uri)) 
            {
                host.Start();

                Console.ReadLine();
            }
        }

Teraz po uderzeniu pod adres http://localhost:8500 (u mnie) dostaniemy stronkę z htmlem z pliczku „index.sshtml”. To wszystko!

Proste? Proste! I jakie lekkie!

nancy

Zużycie pamięci działającej aplikacji Nancy

Zrobiłem dla porównania na szybko aplikacje Asp.Net Core z jednym kontrolerem i widokiem (z template’u z Visual Studio). Różnica w zasobach jest znacząca.

asp

Zużycie pamięci aplikacji Asp.Net Core z jednym kontrolerem i widokiem

 

[EDIT Start 24.03.2017]

W komentarzach zarzuca mi się, że porównuje dwie wersje frameworka. Oczywiście nie miałem tego na celu tylko chciałem pokazać  jaka jest różnica w zasobach między aplikacją webową zrobioną „na szybko” z temaplate’u z VS (i tu poprostu padło na .core), a aplikacją „na szybko”, która można zrobić z Nancy.

W celu sprostowania zrobiłem pustą aplikację Asp.Net Core z dodanym Mvc oraz drugą pustą aplikację z dodanym Owin’em i Nancy. Aplikacje zwracają tylko jeden widok „Hello Wolrd”. Różnica w zasobach jest rzędu 50% na korzyść aplikacji z wykorzystaniem Nancy. Zajmę się tym konkretniej i opiszę to w kolejnym poście.

[Edit End]

 

Zakochałem się! 😉

Pjona!

Paintball Arena. Prototyp – kolizja gracza z przeszkodą. #dajsiepoznac

Siemanko

W poprzedniej części poruszaliśmy trochę graczem. Jednak wtedy gracz mógł przechodzić również przez przeszkody co jest oczywiście zjawiskiem niepożądanym. Dziś temu zaradzimy.

Komponent Collider2D

Collider2D to komponent, który pozwala na kolizję między obiektami gry i ich obsługę. Na tę chwilę wymagane jest aby gracz nie mógł przejść przez przeszkode czyli aby się na niej zatrzymał. Jest kilka klas Collidera w zależności od kształtu obiektu gry. Dla obiektu gracza użyłem CircleCollider2D natomiast dla przeszkód BoxCollider2D. Nazwy mówią same za siebie. W naszym przypadku ważne jest aby collidery obiektów przeszkód miały ustawioną wartość IsKinematic na true. Jeśli tego nie zrobimy to to obiekty przeszkód zaczną się przemieszczać po kolizji, a tego nie chcemy bo balony są przecież mocno zaśledziowane w ziemii 😉

[RequireComponent(typeof(BoxCollider2D))]
public class BunkerBase : Unit
{
	private BoxCollider2D BoxCollider { get { return GetComponent(); } }
	protected override void Start()
	{
		Rigid.isKinematic = true;
		base.Start();
	}
}

Podsumowanie

Możemy już się poruszać i chować za balonami. To może teraz by coś postrzelać? 🙂

Pjona!

Paintball Arena. Prototyp – poruszanie gracza. #dajsiepoznac

Siemanko

Dziś poruszamy trochę tą czerwoną kropką, tffu, graczem 🙂

Klasa MonoBehaviour

MonoBehaviour to klasa, która w Unity jest bazową klasą dla obiektów gry i zawiera wiele metod, które są wywoływane przez silnik Unity w trakcie wykonywanie programu. Jedną z nich jest metoda Update, która jest wywoływana przy każdej klatce gry.

Komponent RigidBody2d

Jest to komponent, który po dodaniu go do obiektu gry umożliwia np. poruszanie nim. My oczywiście chcemy się poruszać w każdym kierunku za pomocą strzałek i ew. klawiszami WSAD. W tym celu zaimplementowałem metodę Update w skrypcie dla gracza, w której zmieniam „velocity” komponentu RigidBody2d w opowiedzi na naciśnięte przyciski. Kod jest bardzo krótki.

private float speed = 100;
protected void Update()
{
	var vector = new Vector2(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical"));
	vector *= Time.deltaTime;
	vector *= speed;

	Rigid.velocity = vector;
}

Dzięki temu zabiegowi obiektem możemy poruszać.

www-gifcreator-me_iw9dbt

Podsumowanie

W następnym wpisie kolizja z balonem.

Pjona!

Paintball Arena. Prototyp – scena i obiekty gry. #dajsiepoznac

Siemanko.

W dzisiejszym odcinku scena i obiekty gry. W repo jest branch „Prototype” gdzie będę wrzucał wszystkie próbne kody itp. Nie będzie to ani ładne, ani zgrabne ale od tego jest prototyp. Jak ogarnę wszystkie funkcjonalności to wtedy utworzę nowy projekt, który będę już pisał „produkcyjnie” i kod będzie lądował na masterze.

Scena

Jest to wszystko co widzi gracz. Tło, obiekty ruchome jak i nieruchome to wszystko jest sceną.

Obiekty gry

Wszystko co dodamy do sceny będzie to „game object”. Każdy game object może posiadać wiele komponentów, z których najwazniejszy to script, który nada życiu obiektowi.

scene

go

Game object i jego kilka komponentów

Demo

A tak wyglada na tę chwilę gra. Przepiękna nieprawdaż? 🙂

gra

Aaach gdyby na wszystkich polach w PL była taka zielona trawka 😉

Na scenie z zielonym tłem jest 8 obiektów gry (game objects) z czego 7 to przeszkody, a jeden (czerwona kropka) to zawodnik.

Podsumowanie

W następnym wpisie wprawimy tę czerwoną kropkę w ruch! 🙂

Pjona!

Opis sceny – Paintball Arena #dajsiepoznac

Siemanko

Dziś przybliżę nieco jak będzie wyglądać scena gry.

Perspektywa

Widok będzie z góry w formacie 4:3 gdzie na dole i na górze sceny będą bazy (miejsca startu drużyn). Na polu będą rozmieszczone obiekty przeszkód (balony) o różnych wymiarach i kształtach. W realu balony są niższe i wyższe co przy widoku z góry będzie słabo widoczne ale grający w paintball będą doskonale wiedzieli. To oczywiście sprawia problem bo na jednej przeszkodzie możemy być całkowicie zasłonięci, natomiast na innej nie, więc będę musiał jakoś sprytnie to ogarnąć żeby przy widoku z góry kulki odbijały się od jednej przeszkody natomiast przez inną przelatywały i trafiały gracza. Druga sprawa to pozycja gracza – stojącą, kucająca czy wreszcie leżąca na wężu (podłużny balon) – jeszcze nie mam pomysłu ale jestem pewien, że wyjdzie „w praniu”.

wpbo

Zdjęcie pochodzi z profilu fb organizacji WPBO

 

Tak mniej więcej będzie wyglądać scena – oczywiście będą na niej zawodnicy.

Pjona!