Django (/ˈdʒæŋɡoʊ/ jang-goh) ist ein freies, quelloffenes Web-Anwendungs-Framework, geschrieben in Python. Hier findest du ein Tutorial von Django, falls du es noch nicht kennst und ausprobieren möchtenst: https://docs.djangoproject.com/en/2.1/intro/tutorial01/. Der weitere Beitrag richtet sich an erfahrene Django-Entwickler.

Django und Microservices

Ich nutze Django schon länger zusammen mit der Task-Engine Celery für asynchrone Verarbeitungen verteilt über mehrere Systeme. Neuerdings beschäftige ich mich mit dem Thema "Microservices" und wie diese zusammen mit Django realisiert werden können. (mehr zum Thema Microservices: [1] Medium, [2] microservices.io).
Wir möchten die bestehende Applikation in kleinere Services aufteilen und jeder Service ist eine eigenständige Django Applikation. Eines der Standards der Microservice Architektur ist, dass jeder Service möglichst eine eigene Datenbasis (Datenbank, Schema o.ä.) hat (siehe "[3] Database per Service"), über diese der Service der alleinige Verwalter ist.
Eines der Herausforderungen ist nun, dass die Serviceübergreifenden Daten konsistent sind. z.B. ist eine Bestellung im Service "Order" erst gültig, wenn die Transaktion im Service "Customer" validiert wurde.

Ein Lösungsvorgehen ist das Saga Pattern, welches beim update von Daten Events verschickt. Andere Services können diese Events subscriben und anschliessend z.B ihre Daten aktualisieren.

Publish/Subscribe mit Django

Die Voraussetzung um Celery zu nutzen ist ein Message-Broker wie z.B. RabbitMQ mit dem AMQP-Protokoll. Dieses Protokoll eignet sich auch sehr gut um Messages für bestimmte Themen zu publishen oder zu subscriben. Celery ist zwar gut geeignet als Taskengine, eignet sich aber weniger als Event-Bus, da Celery davon ausgeht, dass jede Nachricht ein bestimmten Task ist, der ausgeführt werden soll. Die Koordination zwischen der Task-Engine und dem Broker übernimmt "Kombu" eine Messaging Library. Diese unterstützt viele verschiedene Broker und Protokolle. Wir interessieren uns für welche die Nachrichtenart "Topic" unterstützen, wie z.B. AMQP.

Exchange: Topic
Eine gute Übersicht der verschiedenen Routing Arten von RabbitMQ gibt cloudamp.

Nachrichten werden an eine oder mehrere Warteschlangen weitergeleitet, basierend auf einer Übereinstimmung zwischen einem Nachrichtenrouting-Schlüssel und dem Routing-Muster.

Topic Exchange

Beispiel:

  • Ein Subscriber von animals.# erhält alle Nachrichten die mit dem Routing animals. beginnen, wie z.B animals.rabbit oder animals.turtle.

Yosun

Yosun ist ein tolles kleines Pub/Sub Utility in Python welches die Kombu Library nutzt. Damit können Nachrichten (als Events) published und durch den Subscriber konsumiert werden. Yosun können wir in Django nutzen um Events zu publishen und in einem anderen Django Service bestimmte Events zu subscriben, welche dann weitere Abläufe im Service auslösen. z.B. können Daten, welche von einem anderen Service benötigt werden via REST API aktualisiert werden.

Installation

pip install yosun

Subscribe

from kombu import Connection, Exchange
from yosun import Yosun

# Verbindung definieren
connection = Connection('amqp://guest:guest@localhost:5672//')

# RabbitMQ Exchange definieren
exchange = Exchange('events', type='topic')

# Yosun initialisieren
yosun = Yosun(connection, exchange)

def on_rabbit(body, message):
    print('Look, a rabbit!')
    print(body)

def on_animal(body, message):
    print('Look, an animal!')
    print(body)

sub = yosun.subscribe('animals.#')

# from now on when a animals.rabbit message arrives, on_rabbit will be called
sub.on('animals.rabbit', on_rabbit)

# when any message matching animals.# arrives, on_animal will be called
sub.all(on_animal)

Publish

from kombu import Connection, Exchange
from yosun import Yosun

# Verbindung definieren
connection = Connection('amqp://guest:guest@localhost:5672//')

# RabbitMQ Exchange definieren
exchange = Exchange('events', type='topic')

# Yosun initialisieren
yosun = Yosun(connection, exchange)

yosun.publish('animals.rabbit', {'name' : 'McTwisp'})

Beispiel: Django mit Signals und Events

Das post_save Signal welches Django beim Update der Daten in Service A schickt nutzen wir, um einen Event mit Yosun zu publishen.

# Service A

from serviceA.models import Animals
from django.db.models.signals import post_save
from django.dispatch import receiver

from kombu import Connection, Exchange
from yosun import Yosun

connection = Connection('amqp://guest:guest@localhost:5672//')
exchange = Exchange('events', type='topic')
yosun = Yosun(connection, exchange)

@receiver(post_save, sender=Animals)
def publish_event(sender, instance, **kwargs):
    yosun.publish('animals.rabbit', {'id': instance.id})

Und in Service B nutzen wir den Event animals.rabbit um den Celery Task on_rabbit asynchron zu starten:

 # Service B

 from serviceB.celery import app

 @app.task
 def on_rabbit(body, message):
     # do something with the rabbit event :)

from kombu import Connection, Exchange
from yosun import Yosun

connection = Connection('amqp://guest:guest@localhost:5672//')
exchange = Exchange('events', type='topic')
yosun = Yosun(connection, exchange)

sub = yosun.subscribe('animals.#')

# from now on when a animals.rabbit message arrives, on_rabbit will be called async as celery task
sub.on('animals.rabbit', on_rabbit.delay)

Previous Post

Kommentar hinzufügen