WIADOMOŚCI

Dzienniki komunikacji

Published on:11 / December / 2015

Zakres

Platforma komunikacyjna firmy Infobip obsługuje około 250 mln transakcji dziennie. Każda z tych transakcji obejmuje otrzymanie wiadomości SMS od klienta, rozliczenie jej, przetrasowanie, zaadaptowanie w razie potrzeby, przesłanie do operatora telekomunikacyjnego, przetworzenie raportu doręczenia oraz przesłanie wiadomości z powrotem do klienta. Oznacza to przynajmniej 6 par żądanie/odpowiedź w sieci wykorzystujących różne API, (czasami) w 4 całkowicie różnych protokołach, tylko po to, aby przetworzyć jedną wiadomość. I to tylko wtedy, gdy wszystko pójdzie dobrze i nie trzeba podejmować ponownych prób. A mówimy na razie tylko o komunikacji zewnętrznej, nie uwzględniając komunikacji międzyusługowej. Ponadto niektóre wiadomości są ze sobą powiązane i spojrzenie na pojedynczą wiadomość nie daje pełnego obrazu.

Śledzenie tych wszystkich operacji sieciowych stanowi ogromne wyzwanie. Dział wsparcia musi szybko reagować na zapytania ze strony klienta i operatora. Potrzebne jest do tego narzędzie umożliwiające wyszukanie wszystkich par żądanie/odpowiedź wydanych podczas przetwarzania wiadomości, ponieważ punktem wyjścia do debugowania czy rozwiązywania jakiegokolwiek problemu jest sprawdzenie, co klient przesłał i jak dokładnie zostało to przekazane do operatora.

Rozproszone rejestrowanie zdarzeń

Słowo „rozproszone” brzmi dobrze. Jest trendy. Jakoś instynktownie chcemy, żeby wszystko było rozproszone. Zobaczmy jednak, jak rzeczywiście wygląda system rozproszonego rejestrowania zdarzeń.

Mamy tu osobną usługę dla każdego protokołu wiadomości po stronie klienta i operatora. Każda usługa obsługuje własne dzienniki, zazwyczaj przechowując je w pamięci, zapisując je na dysku w formie bufora kołowego. Ponieważ usługi są opracowywane przez różne zespoły, ujednolicenie wyglądu interfejsów sieciowych do zarządzania dziennikami wymaga pewnego wysiłku. Aby ułatwić życie pracownikom działu wsparcia, usługi ujawniają dzienniki za pośrednictwem swoich API, a także wprowadzono centralny interfejs administracyjny, za pośrednictwem którego można obejrzeć wszystkie dzienniki ze wszystkich usług. Wypychanie dzienników przez wszystkie usługi do systemu przechowywania rejestrów zdarzeń, takiego jak Graylog, to ważny krok ku ujednoliceniu – i centralizacji. Graylog jest fajny. Dobrze skaluje i działa. Opiera się na klastrze elasticsearch. Przechowuje wiadomość dziennika ze zindeksowanymi metadanymi wartości kluczowych. Interfejs sieciowy przy pierwszym użyciu trochę przeszkadza, ale można się do niego przyzwyczaić. Do dyspozycji użytkownika są też przydatne narzędzia analityczne.

Problem polega na tym, że te dzienniki nie są ze sobą powiązane. 6 par żądanie/odpowiedź nie ma ze sobą żadnego związku. Samo połączenie kilku dzienników komunikacji w jedną wiadomość wymaga męczącej analizy i ręcznego pozyskania identyfikatorów korelacji z bazy danych platformy przetwarzania wewnętrznego. Nie wspominając o znalezieniu dzienników komunikacji dla powiązanych wiadomości. Wynika to z tego, że strona klienta nie jest sparowana ze stroną operatora. Nie mają ani jednego wspólnego identyfikatora korelacji.

  

Poza tym, z powodu ładnie zdefiniowanych granic między zakresami usług, usługi po stronie operatora nie wiedzą nic o stronie klienta i na odwrót. Oznacza to, że usługi po stronie operatora nie wiedzą, od którego klienta przyszła wiadomość, którą przetwarzają. To tak zwany rozdział zagadnień – ogólnie dobra rzecz. Tyle tylko, że nie sprawdza się dobrze w przypadku rejestrowania zdarzeń, bo usługi po stronie operatora nie mogą wzbogacić dzienników metadanymi identyfikującymi klienta, które pomogłyby powiązać dzienniki. Poza tym te usługi nic nie wiedzą o powiązaniach wiadomości – bo nie muszą. Tylko platforma przetwarzania wewnętrznego zna te powiązania, ale nie dysponuje możliwością komunikacji zewnętrznej, więc nie tworzy żadnych dzienników. Oznacza to, że z naszymi dziennikami nie są skojarzone żadne metadane.

Jeden sposób na rozwiązanie tego problemu to zobowiązanie usług do tworzenia dzienników i przekazywania ich za pośrednictwem komunikacji wewnętrznej do platformy przetwarzania wewnętrznego. Platforma może następnie wzbogacić dzienniki o wszystkie metadane i wypchnąć je identyfikator – UUID do miejsca przechowywania dzienników. To rozwiązanie mogłoby się sprawdzić, ale jest zwyczajnie nieodpowiednie. Zużywa cenne zasoby przetwarzania i „odwraca uwagę” platformy od zadań biznesowych.

 

Przedstawiamy UUID

Cóż, to żadna rewolucja. Stwierdziliśmy, że każda wiadomość musi mieć od samego początku, czyli od wprowadzenia do systemu, przypisany unikalny identyfikator – UUID (uniwersalny unikalny identyfikator) i że musimy przekazywać ten identyfikator wraz z wiadomością do następnej usługi w łańcuchu przetwarzania. Następnie każda usługa tworząca dzienniki może dołączyć ten identyfikator do metadanych dziennika. Hej, to łatwe! Możemy teraz powiązać szereg dzienników z jedną wiadomością. Gdyby tylko to było takie proste...

Parowanie raportów dostarczenia

Szybko można się zorientować, że mamy problem. Wystarczy spojrzeć na dzienniki, aby zauważyć, że nie ma raportów dostarczenia. Ponownie, problemem jest rozdział zagadnień i ponownie nie chcemy z niego zrezygnować, bo nadal zgadzamy się, że to dobra rzecz. Usługi, które obsługują dany protokół, mogą przesłać wiadomość otrzymaną od platformy i obsłużyć raport dostarczenia. Sparowanie raportu doręczenia z wiadomością jest jednak trudniejszym zadaniem, niż można by się było spodziewać. Ewidentnie jest to zagadnienie dla platformy. Nadal jednak chcemy, żeby usługa, która otrzymała raport doręczenia sporządziła dla niego zapis w dzienniku – to zagadnienie dla tej usługi. A jednak w momencie odbioru doręczenia nie wiadomo jeszcze, z którą wiadomością zostanie ono sparowane (jeśli w ogóle zostanie sparowane!), więc usługa nie wie, który UUID dołączyć do dziennika.

  

Łączenie powiązanych wiadomości

Jak wcześniej wspomniałem, niektóre wiadomości są ze sobą powiązane. Zwykle są to długie wiadomości SMS, przesyłane pojedynczo, ale przetwarzane i ostatecznie wyświetlane na telefonie komórkowym jako jedna wiadomość. Czasami wiąże się to z dzieleniem i scalaniem, więc mamy relację jedno do wielu, wiele do jednego i wiele do wielu. Dobrze by było, gdyby dzienniki dla tych wiadomości też były powiązane, bo czasami musimy spojrzeć na te wiadomości jako na jedną całość. Problem polega na tym, że tylko platforma przetwarzania wewnętrznego wie o związku pomiędzy tymi wiadomościami i właśnie na niej odbywa się dzielenie i scalanie. Usługi obsługujące komunikację zewnętrzną nic nie wiedzą o powiązaniach. Obsługują każdą wiadomość pojedynczo, a każda z wiadomości ma własny UUID.

Innym źródłem relacji jest przetwarzanie masowe. Klient może po prostu przesłać kilka wiadomości w jednym żądaniu HTTP. Usługa obsługująca HTTP sparsuje żądanie i prześle kilka wiadomości do platformy, ale zarejestruje tylko jedno żądanie i jedną odpowiedź. Wygeneruje ona jeden UUID do zarejestrowania żądania HTTP i nowy UUID dla każdej wiadomości wygenerowanej przez to żądanie.

  

Grafy na ratunek

To oczywiste, że musimy gdzieś przechowywać powiązania pomiędzy UUID. Gdy wysyłamy zapytanie o dzienniki do jednej wiadomości, musimy być w stanie pozyskać wszystkie powiązane dzienniki. Spróbujmy zobrazować bardziej złożone, ale nierzadko występujące powiązanie. Mamy jedną przesyłkę od klienta (UUID-100), zawierającą dwie długie wiadomości (UUID-200 i UUID-210), które zostały następnie podzielone na więcej wiadomości i przesłane do operatora (UUID-201, UUID-202, UUID-211, UUID-212). Następnie przyszła jedna przesyłka masowa, zawierająca raporty doręczenia dla wszystkich przesłanych części wiadomości (UUID-300).

  

Nic dziwnego, że to graf. Pewnie, że można by się trochę wysilić i przechowywać ją w regionalnej bazie danych albo przekształcić w json do dokumentowej bazy danych, ale w dzisiejszych czasach mamy grafowe bazy danych. Dobierzmy odpowiednie narzędzie do zadania! Za każdym razem, gdy powstaje nowe powiązanie i dochodzi do podziału lub scalenia, wypychamy mały graf do grafowej bazy danych.

  

Wybraliśmy Neo4j ze względu na świetny marketing – są życzliwi na konferencjach, rozdają darmowe książki i koszulki. Oprogramowanie też nie jest złe. Za klastrowanie i HA trzeba zapłacić, ale do dzienników wystarczy nawet darmowa wersja. Pojedyncza instancja Neo4j na maszynie z 16-rdzeniowym procesorem obsługuje 100 mln węzłów ze 150 mln powiązań, które zajmują tylko 15 GB. Problem z Neo4j tutaj polega na tym, że usunięcie węzłów nie zwalnia pamięci ani dysku, więc od czasu do czasu trzeba czyścić dane. Gdy potrzebujemy pozyskać dzienniki dla danej wiadomości, bierzemy UUID tej wiadomości, pozyskujemy wszystkie powiązane UUID z Neoj4, a następnie pozyskujemy dzienniki z Graylog poprzez te UUID. Ważne jest, aby pozyskać jedynie istotne UUID, nie cały graf. Na przykład, jeśli chcemy dzienniki komunikacji dla wiadomości UUID-200, musimy tylko pozyskać rejestry dziennika UUID-100, UUID-200, UUID-201, UUID-202 i UUID-300. Odgałęzienie UUID-21x jest nieistotne. Sztuka polega na tym, żeby użyć różnych typów powiązań przy łączeniu mas i rozłączaniu części wiadomości. Następnie wykorzystuje się proste zapytanie Neo4j w Cypher do pozyskania powiązanych UUID.

W rezultacie każda usługa wypycha własne dzienniki obsługiwanej przez siebie komunikacji. Ponadto część systemu, która stworzyła nowe powiązanie, jest odpowiedzialna za opublikowanie tej informacji. Zagadnienia są rozdzielone, a dzienniki i powiązania są przechowywane w dedykowanych systemach, z których mogą być pozyskane. (Autor: Milan Mimica, inżynier oprogramowania / lider zespołu)