sobota, 31 października 2015

[HTML|JS|CSS] ES6: Strings, arrays, collections

Strings

Poza kilkoma dodatkowymi metodami na obiekcie stringa, takimi jak includes, startsWith, endsWith, znanymi z innych języków programowania, najważniejsza zmiana to tzw. template strings - wyrażania opakowane w backticki, służące do wygodnego budowania stringów.

let a = 20;
let b = 6;
let c = "JavaScript";
let str = `My age is ${a+b} and I love ${c}`;

console.log(str);

Arrays 

Metoda from przypomina select z C# z możliwością wstawienia opcji jako trzeci parametr. Dodatkowo wprowadzono metodę fill do wstawiania danej wartości w istniejącej już tablicy (z możliwością zdefiniowania granic wstawiania). Kolejne ważne metody to find oraz findIndex przyjmujące predykat.

Collections

W ES5 mieliśmy do dyspozycji tylko tablice. Teraz dochodzą nowe kolekcje, takie jak:
  • Array buffer - wydajna kolekcja przechowująca liczby, nie może rosnąć dynamicznie
  • Typed array - wrapper na array buffer pozwalający w prosty sposób czytać z niej i zapisywać obiekty
  • Set - kolekcja unikalnych wartości dowolnego typu, powtarzające się wartości są wyrzucane
  • Weak Set - w odróżnieniu od set przechowuje jedynie referencje do obiektów
  • Map - kolekcja par klucz  wartość
  • Weak Map - w odróżnieniu od map kluczami mogą być jedynie referencje obiektów

let set = new Set("Hello!!!");
set.add(12);
console.log(set.has("!")); //check if value exists
console.log(set.size);
set.delete(12);
console.log(...set); //H e l o !
set.clear();

let map = new Map();
let o = {n: 1};
map.set(o, "A"); //add
map.set("2", 9);
console.log(map.has("2"));
console.log(map.get(o));
console.log(...map);
map.delete("2"); //remove key with value
map.clear();

[HTML|JS|CSS] ES6: Syntax

Nowa specyfikacja ECMAScript wnosi sporo nowości do JavaScriptu. Nie możemy ich jeszcze w pełni wykorzystywać bez potrzeby transpilacji, ale kwestią czasu jest, kiedy przeglądarki zaimplementują wszystkie nowości w swoich engine'ach. W tym momencie do transpilacji można używać np. babeljs, będącego pakietem node.js. Instalujemy go poprzez wywołanie

npm install -g babel-cli
npm install --save-dev babel-preset-es2015
babel --presets es2015 script_es6.js --out-file script_es5.js

Ostatnie polecenie reprezentuje transpilację z jednego pliku do drugiego. Od kilku dni należy do tego celu wykorzystywać preset es2015, wcześniej transpilacja była domyślną operacją.

Najważniejsze zmiany składniowe w ES2015 (ES6):

let keyword

Deklaruje zmienną typu block-scoped, co jest naturalne w większości języków programowania, w JS nie było, kiedy mieliśmy do wyboru tylko var (function-scoped variables).

function myFunction()
{
  console.log(a);

  let b = 13; //accessible throughout function
  console.log(b);

  if(true)
  {
    let c = 14; //accessible throughout function
    let b = 15;
    console.log(b);
  }

  //console.log(c); //c is not defined
}

Zmienne możemy nadpisywać poprzez let, ale tylko jeżeli są w innym block-scope. Zaleca się używanie let zamiast var, ze względu na zmniejszenie podatności na bugi takiego kodu.

const keyword

Zmienne typu read-only, ostatnia linijka zwróci SyntaxError podczas transpilacji, a docelowo także błąd w run-time. Zmienne typu const są również block-scoped.

const pi = 3.141;
var r = 2;

console.log(pi * r * r); //Output "12.564"

pi = 12; //throws read-only exception

 W przypadku obiektów deklarowanych jako const będziemy mogli modyfikować ich propercje, a błąd dostaniemy tylko przy próbie nadpisania całego obiektu.

default parameter values

W ES5 nie musieliśmy przekazywać wszystkich argumentów wywołując funkcję. Nieprzekazane zmienne przyjmowały wartość undefined. Teraz możemy od razu zadeklarować dla każdego parametru domyślne wartości.

function myFunction(x = 1, y = 2, z = 3){
 console.log(x, y, z); //Output "6 7 3"
}

myFunction(6, 7);

spread operator

Operator ten reprentują ... , a jego zadaniem jest rozdzielenie obiektu typu iterable na pojedyncze elementy. Po transpilacji otrzymamy metodę apply (przyjmuje listę parametrów jako tablicę) wołaną na funkcji, ale docelowo wykorzystane będą znacznie szybsze mechanizmy w runtime.

function myFunction3(a, b)
{
 return a + b;
}

let data = [1, 4];
let result = myFunction3(...data);
console.log(result); //Output "5"

Można go wykorzystać także przy budowaniu tablicy z innej tablicy.

rest parameter

Trzech kropek można także użyć do obsługi funkcji ze zmienną liczbą argumentów. Rest parameter zmapuje nadmiarowe zmienne jako tablicę.

function myFunction(a, b, ...args)
{
  console.log(args); //Output "3, 4, 5"
}

myFunction(1, 2, 3, 4, 5);

array destructuring assignment

Pozwala na przypisanie wartości do wielu zmiennych za pomocą jednego wyrażenia. Prawą stroną musi być obiekt typu iterable. Array destructuring pozwala na ignorowanie niektórych elementów oraz użycie spread operator-a.

let myArray2 = [1, 2, 3];
let [g, h, i] = myArray2;
let [j,  , k] = myArray2;
let [l, ...m] = myArray2;

object destructuring assignment

W analogiczny sposób możemy wykonywać destructuring na obiektach.

let object = {"name" : "John", "age" : 23};
let {name, age} = object; //object destructuring assignment syntax

arrow function

Przypomina dobrze znane np. z C# lambda expression. Więcej niż jedną linię należy opakowywać w nawiasy klamrowe.

let circleArea = (pi, r) => pi * r * r;
let result = circleArea(3.14, 3);

let circleArea2 = (pi, r) => {
 let area = pi * r * r;
 return area;
};
let result = circleArea2(3.14, 3);


enhanced object literals

Wprowadzono kilka udogodnien składniowych przy budowie obiektów. Mamy do dyspozycji skróconą składnię przy definiowaniu propercji (również tych z wyliczanymi nazwami) oraz metod, 

let x = 1, y = 2;
let object = { x, y };

console.log(object.x); //output "1"

let object = {
  ["first" + "Name"]: "Eden",
};

//extract
console.log(object["first" + "Name"]); //Output "Eden"

let object = {
  myFunction(){
    console.log("Hello World!!!"); //Output "Hello World!!!"
  }
}

czwartek, 29 października 2015

[MessageQueues] RabbitMQ

RabbitMQ jest open-source'owym message brokerem zbudowanym w oparciu o standard AMQP w języku Erlang. Jego główne cechy wyróżniające go spośród innych rozwiązan tej klasy to:
- high availability
- clustering
- zarządzianie (web UI)
- security (autentykacja, uprawnienia)

Aby rozpocząć pracę z RMQ należy pobrać i zainstalować go ze strony. Aby uruchomić instalator należy wcześniej pobrać i zainstalować dystrybucję Erlanga. Obie instalacje warto przeprowadzać jako administrator systemu (unikniemy problemów z cookie Erlanga). RabbitMQ dostarcza przyjazny interfejs webowy na porcie 15672. Aby móc z niego skorzystać należy wywołać polecenie:
rabbitmq-plugins enable rabbitmq_management

AMQP (Advanced Message Queue Protocol) jest otwartym standarded dla messagingu, niezależnym od producenta. Opiera się o następujące koncepcje:
- message broker - scentralizowany serwer pośredniczący
- exchanges :
 - direct (do konkretnej kolejki)
 - fan-out (do wielu kolejek)
 - topic (routing na podstawie wyrażen tekstowych)
 - headers (routing na podstawie nagłówków)

Exchange transferuje wiadomość do kolejek. Każda kolejka ma przypisany jakiś exchange poprzez mechanizm bindingu.

Kolejki i exchange można zakładać zarówno w proceduralnym kodzie jak i w UI administracyjnym, wyklikując odpowiednie opcje:



W projekcie .NET-owym należy zainstalować nugetem pakiet klienta RabbitMQ:



Najprostszy kod wstawiający do nazwanej kolejki poprzez domyślny exchange wygląda następująco:


var connectionFac = new ConnectionFactory()
{
    HostName = "192.168.1.5",
    UserName = "admin",
    Password = "admin1",
    VirtualHost = "/"
};

var connection = connectionFac.CreateConnection();
var model = connection.CreateModel();

var properties = model.CreateBasicProperties();
properties.SetPersistent(true);

var bytes = Encoding.UTF8.GetBytes("my message");

var queue = "myqueue";
var exchange = ""; //default

model.BasicPublish(exchange, queue, properties, bytes);

Console.ReadKey();
W przypadku połączen spoza localhost musimy założyć swojego usera (takiego jak admin) i nadać mu uprawnienia do vhosta.

Przykład kodu czytającego wiadomości:

model.BasicQos(0, 1, false); //przetwarzany po jednej wiadomości

var consumer = new QueueingBasicConsumer();
model.BasicConsume("myqueue", false, consumer);

while (true)
{
    var delArgs = consumer.Queue.Dequeue();
    byte[] bytes = delArgs.Body;
    Console.WriteLine(Encoding.UTF8.GetString(bytes));
    model.BasicAck(delArgs.DeliveryTag, false);
}

Procesów słuchających można uruchomić wiele przypisanych do tej samej kolejki. Będą wtedy rywalizowały o pobranie wiadomości. Jeśli chcemy, by kolejka działało jako publish / subscribe, to powinniśmy zbindować nasze kolejki do odpowiedniego exchange (typu fanout). Po założeniu takiego exchange'a możemy to skonfigurować w UI administracyjnym:

W kodzie po stronie wstawiającej należy ustawić odpowiedni, nazwany exchange, routing key tym razem nie będzie nazwą kolejki, ale pustym stringiem.

Po stronie odbierającej wiadomości mamy kilka zmian:

model.QueueBind("sub1", "my_fanout_exchange", "");
var subscriber = new EventingBasicConsumer(model);

subscriber.Received += (mod, ea) =>
{
    var body = ea.Body;
    var message = Encoding.UTF8.GetString(body);
    Console.WriteLine(" [x] {0}", message);
};

model.BasicConsume(queue: "sub1", noAck: true, consumer: subscriber);
break;

Pozostałe wzorce komunikacyjne w RabbitMQ:

  1. RPC -  wiadomość jest wysyłana do kolejki przez default Exchange, ale zawiera resposne queue, odbiorca dostaje wiadomość i umieszcza odpowiedź w reponse queue
  2. Routing - consumers zbindowani po kluczu, producent wysyła wiadomość z kluczem, a następnie na podstawie tego klucza wiadomość jest przekazywana do jednej lub więcej kolejek.
  3. Topic może zawierać wyrażenie tekstowe a'la regex wyliczane na podstawie znaków * (dowolny znak) i # (zero lub więcej słów). Wyrażeniem tym przetwarzany jest RoutingKey. Przykładowe topiki to: *.high.* lub corporate.#.
  4. Headers: nazwany exchange, wiadomość zawiera nagłówki (tekstowe), warunkiem może być all lub any. Umożliwia matchowanie po jednym lub każdym headerze (własności wiadomości). Analogia do tagowania postów
  5. Scatter Gather - producent wstawia wiadomość do kolejki, tworzy tymczasową kolejkę odpowiedzi. Wielu przetwarzających pobiera dane, przetwarza i wstawia swoje odpowiedzi do response queue. 

czwartek, 22 października 2015

[MessageQueues] ZeroMQ

Jest to technologia open source, cross platformowa, wbudowana w proces hosta (in-memory). API bazuje na programowaniu socketów. Nazwa ma reprezentować ideę "zero broker" - bez middleware w komunikacji.

Zero MQ jest wbudowaną biblioteką do messagingu. Jest łatwa do zintegrowania, pasuje do małych rozwiązań, ma niski koszt uruchomienia.

Jedną z wad jest to, że nie dostarcza wbudowanej serializacji, kompresji, szyfrowania i autentykacji. Nie może również zapewnić persystowania wiadomości na dysk. Dużą zaletą będzie szybkość przesyłania wiadomości, a co za tym idzie duża przepustowość.

Z poziomu języka C# mamy do dyspozycji clrzmq.dll (wrapper na libzmq.dll napisanej w C++). Bibliotekę najlepiej zainstalować za pomocą NuGeta.


Przykładowy kod wstawiający wiadomości do kolejki:

var context = new Context();

var socket = context.Socket(SocketType.PUSH);
socket.Connect("tcp://localhost:5400");

for(var i = 0; i < 1000; i++)
{
    socket.Send("message #" + i, Encoding.UTF8);
    System.Threading.Thread.Sleep(100);
}

Kod jest bardzo prosty, jednak metoda Send przyjmuje tylko stringi lub tablice bajtów, stąd konieczność serializacji obiektów "we własnym zakresie". Wiadomości przechowywane będą w pamięci procesu hostującego. Podobnie wygląda to po stronie odbierającej wiadomości:

var context = new Context();
var socket = context.Socket(SocketType.PULL);
socket.Bind("tcp://127.0.0.1:5400");

while (true)
{
    var message = socket.Recv(Encoding.UTF8);
    Console.WriteLine("processed " + message);
}

Warto zwrócić uwagę na to, że Bind nie działa dla adresu localhost, dlatego należy wpisać IP.

Context powinien być Singletonem. Tworzy on sockety i zapewnia komunikację międzyprocesową. Socket definiuje pattern komunikacyjny (PUSH,PULL,REQ,REP itd).

środa, 14 października 2015

[MessageQueues] MSMQ

Mechanizm ten zintegrowany jest z systemami z rodziny Windows i Windows Server. Kolejkowanie włączamy jako feature Windowsa. Przykładowo na Win 10:

Kolejki możemy zakładać konfigurować i podglądać po przejściu do ekranu zarządzana komputerem (PPM na Komputer i wybór Zarządzaj).


Biblioteka kliencka wbudowana w jest .NET Framework i przestrzen nazw System.Messaging, Kolejki pracują w sposób synchroniczny (FIFO). Na każdej kolejce można ustawić transakcyjność oraz persystencję (MSMQ może zapisywać wiadomości na dysku)

Kolejki mogą być prywatne lub publiczne. Główna różnica polega na tym, że w przypadku publicznych kolejkek, możemy je zintegrować z Active Directory i mechanizmami security Windowsa.

Kolejki prywatne mogą być adresowane na dwa sposoby:
PATH:
- {machine}\private$\{queueName}, np:  .\private$\myqueue

DIRECT:
- DIRECT={protocol}:{address}\private$\{queueName}, np: DIRECT=TCP:192.168.2.140\private$\myqueue

Po założeniu kolejki możemy do niej wstawiać elementy w następujący sposób:

[HttpPost]
public ActionResult SendMessage(MessageEntity entity)
{
    string address = entity.UseTransaction ? ".\\private$\\transactionalcontactmessages" : ".\\private$\\contactmessages";
    using (var queue = new MessageQueue(address))
    {
        var message = new Message(new ContactMessage() { Content = entity.Content });
        message.Recoverable = entity.StoreOnDisk;

        if (entity.UseTransaction)
        {
            using (var transaction = new MessageQueueTransaction())
            {
                transaction.Begin();
                queue.Send(message, transaction);
                queue.Send(new Message("anotherMsg"), transaction);
                transaction.Commit();
            }
        }
        else
        {
            queue.Send(message);
        }
    }
    return new RedirectResult("/");
}

Transakcyjność ustawiamy w momencie zakładania kolejki. Wiadomości serializowane są do XML-a jako typ danych lub obiekt. Można wstawiać je także w dowolnym innym formacie, np. JSON ustawiając Stream na propercji Body. Wiadomości bez właściwości Recoverable zapisywane będą około 10 razy szybciej. Transakcja jest kolejnym narzutem czasowym przy zapisie, ale można dzięki niej wstawiać jednokrotnie wiele wiadomości.

Najprostszy kod przetwarzający może wyglądać w następujący sposób:

using (var queue = new MessageQueue(".\\private$\\contactmessages"))
{
    while (true)
    {
        var message = queue.Receive();
        Console.WriteLine("Processing");
    }
}

Podobnie można objąć całą operację w MessageQueueTransaction.

Request - Response

Zgodnie z tym wzorcem tworzone są dwie kolejki. Jedna strona komunikacji wstawia do pierwszej kolejki wiadomości, ustawiając property ResponseQueue. Dzięki temu, druga strona po przetworzeniu wiadomości wstawia odpowiedź do podanej kolejki. Dla każdej wiadomości można tworzyć nową kolejkę odpowiedzi.

private void RequestResponse(string queueAddress, string content)
{
    var responseQueue = MessageQueue.Create(GetPrivateQueueAddress());
    using (var queue = new MessageQueue(queueAddress))
    {
        var message = new Message(new ContactMessage() { Content = content });
        message.ResponseQueue = responseQueue;
        queue.Send(message);

        //Waiting for message
        var response = responseQueue.Receive();
    }
}

private string GetPrivateQueueAddress()
{
    return string.Format(".\\private$\\{0}", Guid.NewGuid().ToString().Substring(0, 6));
}


Kod po drugiej stronie może wyglądać w następujący sposób:

while (true)
{
    var message = queue.Receive();
    if(message.ResponseQueue != null)
    {
        using(var responseQueue = message.ResponseQueue)
        {
            var msg = new Message();
            msg.Label = "Processed";
            responseQueue.Send(msg);
        }                        
    }

    Console.WriteLine("Processing");
}

Kolejki mogą także działać w modelu Publish-Subscribe. Ich adres ustawiamy wtedy na wirtualny IP, na którym nasłuchują odbiorcy. Przykładowy adres takiej kolejki to: "FormatName:MULTICAST=234.1.1.2:8001". Wszyscy subskrybenci muszą mieć ustawione taką wartość w property MulticastAddress.

niedziela, 4 października 2015

[MessageQueues] Wprowadzenie

Kolejki komunikatów umożliwiają asynchroniczną komunikację pomiędzy komponentami. Poprawia to wydajność i skalowalność w przypadku systemów, w których wykonywane są intensywne obliczeniowo / komunikacyjnie operacje. Stanowią alternatywę dla synchronicznej komunikacji. Dzięki nim użytkownik końcowy korzysta z responsywnego UI, ponieważ nie musi czekać, aż operacje, które zleca przetworzą się synchronicznie.

Mechanizm działania kolejek jest następujący: klient wstawia wiadomość do kolejki, w osobnym procesie działa worker, który pobiera i przetwarza takie wiadomości. Komunikacja pomiędzy systemami jest bezstanowa. Wiadomość zazwyczaj zostaje w kolejce tak długo, aż zostanie przetworzona.

Messaging Patterns:
- Fire and Forget (klient wstawia wiadomości do kolejki)
- Request - Response (istnieją dwie kolejki: system do jednej z nich wstawia requesty, a z drugiej pobiera asynchronicznie odpowiedzi od innego systemu)
- Publish - Subscribe (broadcast, kolejka forwarduje wiadomość do wielu subskrybentów)

Obsługa błędów (wzorce):
- Retries (nieudana wiadomość jest blokowana do momentu jej przetworzenia) - problem jest zatruta wiadomość, której nie da się przetworzyć - będzie blokowała kolejkę
- Dead-Letter Queue - specjalna kolejka dla nieprzetworzonych wiadomości, jeżeli przetwarzanie jakiejś wiadomości zakończy się błędem, to trafia ona do DLQ, gdzie może być przetwarzana przez inny mechanizm niż zwykłe wiadomości