Tag: messages

Karafka – Ruby micro-framework for building Apache Kafka message-based applications

What is Karafka?

Karafka is a microframework used to simplify Apache Kafka based Ruby applications development. Up until now there was only a sending library called Poseidon and its extension called Poseidon Cluster that could be used to work with Kafka clusters. Unfortunately there was no Sinatra "like" framework to rapidly develop message based applications. Karafka goes beyond simple sending and receiving. It provides an environment to work with multiple topics and groups (for load balancing) in a MVC like way.

What is Apache Kafka?

A high-throughput distributed messaging system. Kafka is designed to allow a single cluster to serve as the central data backbone for a large organization. It can be elastically and transparently expanded without downtime. (description taken from Apache Kafka website).

Why even bother with messages when there is HTTP and REST?

Because HTTP does not provide broadcasting and Apache Kafka does. This means, that we can create multiple applications that "live" by consuming messages generated by hundreds of other applications inside of one ecosystem. And those applications generate their messages that can be consumed and processed by other endpoints.

Also, without a message broker, you have to make changes to both applications - one that produces messages (to send to a new app) and to the one that receives (obviously).

karafka1However, if you have a message broker, you can easily add more and more applications that consume and produce messages that could be used by any number of applications, without any additional changes to the producer. That way they are less dependent on each other.

karafka2(1)

The next huge advantage of messaging is that producer app can be replaced by other, as long as they send understandable data (same format) into same topics.  You can also use multiple applications that deliver information into the same topic, for example about users activities across all of your systems. You could have a single topic called users_activities that would track any user action.

Karafka ecosystem

Karafka framework is combined from 3 parts:

  • Karafka - Framework used to build Kafka messages receiving applications in a similar way like Sinatra or Rails - with controllers and params
  • WaterDrop - Library used to send messages to Apache Kafka from any Ruby based application (in a standard or aspect oriented way)
  • SidekiqGlass - Sidekiq worker wrapper that provides optional timeout and after failure (reentrancy)

Karafka framework components

Apart of the implementation details, Karafka is combined from few logical parts:

  • Messages Consumer (Karafka::Connection::Consumer)
  • Router (Karafka::Routing::Router)
  • Controller (Karafka::BaseController)
  • Worker (Karafka::Worker)

They all act together to consume, route and process incoming messages:karafka3

Why Sidekiq?

Performing business logic for each message can be time and resource consuming, so instead of doing it in the same process as data consumption, we use Sidekiq to schedule background tasks that will perform it. Also it is worth mentioning, that Sidekiq is well known, well tested and pretty stable. What do you need to do to enqueue Karafka tasks into Sidekiq? Nothing. Everything happens automatically, so you don't need to define any workers or schedule anything. Your controller and Karafka::Worker will do that for you. You just need to execute Sidekiq worker by running bundle exec rake karafka:sidekiq task. There's one more advantage in Sidekiq favour: scalability and performance. Sidekiq workers can be easily scaled both by number of threads and number of processed (and we can distribute them across multiple machines).

Scalability

Apart from scaling Sidekiq workers, Kafka also takes advantage of Poseidon Cluster library features. With a bit of magic it allows to create processes (applications) groups. You can think of them as of separate namespaces for processes. Kafka will ensure that a single message is delivered to a single process from each namespace (group). That way you can spin up multiple Kafka processes with "auto load balancing" support build in. Technically we could process everything in the Kafka app (without Sidekiq) although it would require building many more features that are already built into Sidekiq. There's just no need for wheel re-inventing.

Reentrancy

Karafka supports reentrancy for any task that is being executed in Sidekiq. You can read more about reentrancy here:

Rails like before filtering and params

Kafka allows you to preprocess messages before they are enqueued to Sidekiq. We use ActiveSupport::Callbacks to provide you with before_enqueue callbacks. They act in a similar way as before_action for Rails framework. There's just one difference: the callbacks chain will stop if there's a false value returned - which means that the Sidekiq task won't be schedule. This gives you possibility to filter out any "noise" that comes from Kafka.

before_enqueue do
  # Don't enqueue task for message that has a counter less than 1 and without a flag
  params[:counter] > 0 && params[:flag]
end

The next thing that is similar to Rails is params usage. The params object (Karafka::Params::Params) is a ActiveSupport::HashWithIndifferentAccess descendant. Thanks to that, you can use the same approach as with any of your Rails/Sinatra applications:

def perform
  EventTracker.create(params[:event])
end

What Karafka is missing?

As for now, Karafka is a quite new tool and it is missing some features:

  • Celluloid listening (already there!)
  • Daemonization
  • Graceful shutdown (in progress) (already there!)
  • Multithreading for controllers groups (already there!)
  • Reloading in development environment (you have to restart the process to see your code changes) (works in console)

How can I use Karafka?

There's a pretty decent HOWTO in Karafka's README file. We're also planning to add an example app soon. I will also blog post with some usage examples of Karafka soon.

How can I test it?

Karafka can be tested with any Ruby test framework. It contains a plain Ruby code and you can just mock and stub anything you need.

Rozsyłanie poczty e-mail wraz z osadzonymi obrazkami (base64)

Rozsyłając e-maile, niejednokrotnie chcielibyśmy je uatrakcyjnić. Sam suchy tekst nie jest ani ładny, ani zbyt wymowny. Najprostszym sposobem na uatrakcyjnienie naszych wiadomości e-mail, jest zastosowanie w nim zamiast suchego tekstu (text/plain) - HTMLa (text/html). Niesie to za sobą jednak kilka problemów:

  • Nie wszystkie readery obsługują HTML (aczkolwiek te nieobsługujące to raczej wyjątki)
  • Nie wszystkie readery obsługują CSS i osadzanie obrazów (np. google reader - aczkolwiek nie zawsze ;) )
  • Część readerów obsługuje tylko część znaczników i selectorów

Jak więc sprawić żeby nasze e-maile były ładne tam gdzie się da oraz czytelne tam gdzie trzeba?

Nie jest to wbrew pozorom aż tak trudna sprawa. Pierwszą rzeczą jaką należy przygotować jest tekst. W naszym przypadku posłużymy się tekstem który już mam. Dotyczy on zmiany adresu e-mail w Susanoo.

Cała wiadomość wygląda tak:

Szanowny Użytkowniku,

Niniejszy e-mail wysłany został ze względu na rozpoczętą procedurę zmiany adresu e-mail konta: XYZ. Jeżeli nie wykonywałeś takiej dyspozycji - zignoruj tę wiadomość.

Aby dokończyć operację, potwierdź ją: potwierdzam
Możesz także skorzystać z poniższego odnośnika, kopiując go do swojej przeglądarki internetowej:

http://localhost:3000/admin/core/account/confirm/jakistamhash

Jeśli nie aktywujesz nowego adresu e-mail w przeciągu 24 godzin, system automatycznie przywróci Twój poprzedni adres e-mail.

Oczywiście tutaj jako cytat wygląda beznadziejnie. Sprawimy więc żeby jako e-mail wyglądała tak jak trzeba. Nie opiszę tutaj całego procesu tworzenia takiego "ładnego" e-maila. Opiszę jednak metodologię abyście sami mogli rozwijać własne wzory e-maili.

Zanim zaczniemy, wyznaczmy sobie cele. Chcemy aby w readerze który jest dość słaby, wiadomość wyglądała tak:

zrzut_ekranu

Jak widać, jest nagłówek, zwrot grzecznościowy oraz treść. Na końcu i na początku są też notki dot. tego że wiadomość rozsyłał automat. Jest przejrzyście i prosto. Ale z drugiej strony, w readerze obsługującym osadzanie styli oraz obrazki w  base64, ta wiadomość wygląda tak:

zrzut_ekranuww

Jak widać, wiadomość prezentuje się całkiem nieźle. Jak osiągnąć taki podwójny efekt? Przede wszystkim należy pamiętać że Gmail nie wspiera ani osadzania CSSa w plikach zewnętrznych, ani też w headerze strony. Jedynym wyjściem są proste style inline. I tak właśnie postąpiłem w jego wypadku. Skorzystałem z prostych styli jak:

  • color
  • padding
  • margin
  • text-decoration
  • font-size

Aby nadać mu podstawowy wygląd. Jednocześnie skorzystałem z pewnej sztuczki. Aby nasze style które osadzimy dla "fajniejszych" readerów, nie gryzły się z tymi dla słabych, należało tak utworzyć CSS aby nie kolidował jeden z drugim. Było to stosunkowo proste. O ile w przypadku Gmaila styl inline wyglądał tak:

<div id="confirm_contener" style="margin-top: 20px; margin-bottom: 20px; padding: 20px 10px;">

O tyle już CSS "normalny" był taki:

padding-bottom:  20px;

Jak widać jest to podobny kod, jednak inline ustawia marginesy tylk górny i dolny, zaś nasz "CSSowy" ustawia je wszystkie. W ten sposób uzyskujemy wcięcie z każdej strony. Większość pracy właśnie polega na utworzeniu niekolidujących ze sobą styli, co tak naprawdę nie jest trudne o ile najpierw tworzymy styl zaawansowany który umieszczamy w headerze strony jako:

<style type="text/css">
    jakieś style ...
</style>

Dzięki temu wyświetlanie w klientach jak Gmail nastąpi bez uwzględnienia tych styli. Dla takich readerów wstawiamy CSS inline.

Pozostaje jeszcze kwestia grafik. Tutaj są dwie możliwości:

  1. Grafiki zdalne - doczytywane z naszego serwera
  2. Grafiki inline - zakodowane w samej wiadomości

Ja skłoniłem się ku rozwiązaniu drugiemu. Dlaczego? Otóż wiele klientów webmail (jak i część desktopowych) domyślnie nie ładuje grafik zewnętrznych traktując je jako potencjalnie groźną zawartość zdalną. Pojawia się monit z pytaniem czy przeglądarka ma wyświetlić zawartość zdalną. Nie bardzo mi się to podobało i postanowiłem oszczędzić użytkownikom zbędnych pytań.

Osadzam więc te newielkie graficzki jak tło czy przycisk, jako obrazy inline osadzone w ciele dokumentu. Jak tego dokonać?

Mając obrazek, przykładowo obrazek tła, wchodzimy tutaj:

http://www.motobit.com/util/base64-decoder-encoder.asp

I ładujemy tam nasz plik. Serwis ten umożliwia przekonwertowanie dowolnego pliku na jego odpowiednik zakodowany w base64, dzięki czemu możemy go wstawić do kodu inline. Należy przy tym pamiętać ze konwerter ten, dodaje znaki nowego wiersza, więc trzeba je pousuwać (cały "tekst"| wynikowy musi być w jednej linii). Nasz plik po przekonwertowaniu, stanie się "czytelny" i możliwy do wklejenia. Będzie wyglądał mniej więcej tak (tylko będzie tego trochę więcej):

WC1BY2NvdW50LUtleTogYWNjb3VudDQNClgtVUlETDogR21haWxJZDEyYjQ1Z
WJlYWEwZWNiZWQNClgtTW96aWxsYS1TdGF0dXM6IDAwMDENClgtTW96aW
xsYS1TdGF0dXMyOiAwMDAwMDAwMA0KWC1Nb3ppbGxhLUtleXM6ICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI
CAgICAgICAgICAgICAgICAgICAgICAgICAgDQpEZWxpdmVyZWQtVG86IG1lbn
Nm

Taki "tekst" wstawiamy do kodu. Jeśli chodzi o wstawianie np. tła w CSS, wykonujemy to w taki sposób:

background: url('data:image/gif;base64,R0lGODlhBQAFAJEAABgYGC4uLigoK=');

Jak więc widzicie wystarczy dodać: data:image/gif;base64, a po tym wkleić naszą zwartość. Od tego momentu w wypadku zaawansowanych czytników, będziemy mieli ładne tło dla danego elementu.

Jeśli chodzi zaś o osadzanie elementów klikalnych jak img, sytuacja nie różni się zbytnio od poprzedniej. W przypadku HTMLa wygląda to tak:

<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhE">

Wpisujemy w src data:image/png;base64,, po czym naszą zawartość, zamykamy i już. Jest jednak w tym rozwiązaniu jeden minus. Mianowicie jeśli mamy elementy klikalne, które są podlinkowane, musimy dostarczyć alternatywę dla prostych readerów. Tą alternatywą jest alt. Sam obrazek nie będzie widoczny, jednak zamiast niego będzie tekst który osadzimy właśnie w alt. Dodatkowo dodamy jeszcze prosty styl inline aby obrazki nie miały obramowania. Całość będzie wyglądać następująco:

    <a href="www.dev.mensfeld.pl" id="img_confirm">
        <img src="data:image/png;base64,iVBORw0..." alt=" potwierdzam" style="border:0; margin:0; padding: 0"/>
    </a>

To by było na tyle. Dzięki odpowiedniemu ustawieniu stylów w headerze, stylów inline oraz osadzeniu obrazów wraz z atrybutem alt możemy uzyskać fajnie wyglądające wiadomości e-mail, tam gdzie się da, oraz funkcjonalne i przejrzyste tam gdzie wyświetla się tylko tekst oraz ew. podstawowe style inline.

Copyright © 2024 Closer to Code

Theme by Anders NorenUp ↑