Open Source im professionellen Einsatz
Linux-Magazin 09/2015
1003

Willkürliche Kontrolle

Lädt Elixir ein Modul, ruft der Supervisor standardmäßig die Funktion »start_link()« in Listing 7 (Zeile 2) auf. In der nächsten Zeile erzeugt die Funktion »compile()« einen Router, indem sie das Modul Cowboy_router aufruft. Sie speichert den Router in der Variablen »dispatch« . Hier beantwortet er, analog zu den Plugs »:match« und »:dispatch« aus Listing 4, HTTP-Anfragen.

Listing 7

htdist/lib/htdist.ex (Proxy-Modul)

01 defmodule Htdist do
02   def start_link() do
03     dispatch = :cowboy_router.compile([{:_, [{"/[...]", Distr, []}]}])
04     {:ok, _} = :cowboy.start_http(:http, 100, [{:ip, {127, 0, 0, 2}}, {:port, 80}], [{ :env, [{:dispatch, dispatch}]}])
05   end
06 end

Seine Konfiguration holt sich der Router aus der Liste am Ende von Zeile 3. Mit ihr wertet er Anfragen für verschiedene IP-Adressen und Portnummern aus. Das Muster »:_« im ersten Tupel passt auf alle denkbaren IP-Adressen und Portnummern, das Muster »/[...]« auf sämtliche URLs. Schließlich ruft Zeile 3 das Modul Dist auf. In Zeile 4 von Listing 7 bringt »start_http()« den Webserver auf Trab. Über die anfangs zugewiesene Variable »dispatch« landet der Router nebst IP-Adresse und Portnummer in der Parameterliste.

Das Modul Distr, dessen vollständigen Code Listing 8 zeigt, wird aktiv, sobald die Zeile 3 in Listing 7 es aufruft. Es erstellt die HTTP-Antwort nach dem Vorbild eines Load Balancer. Die Funktionen »init()« und »terminate()« des Moduls sind rein obligatorisch, denn tatsächlich kümmert sich die Rückruffunktion »handle()« um HTTP-Anfragen (Zeilen 10 bis 18). Bei jeder eintreffenden HTTP-Anfrage legt sie das Anfrage-Objekt in der Variablen »req« und den Status der Anfragebearbeitung in »state« ab.

Listing 8

htdist/lib/distr.ex (Load Balancer)

01 defmodule Distr do
02   def init(_type, req, []) do
03     {:ok, req, :no_state}
04   end
05
06   def terminate(reason, request, state) do
07     :ok
08   end
09
10   def handle(req, state) do
11     {url, _} = :cowboy_req.url(req)
12     {code, body, headers} = case HTTPoison.request (:get, next_url(url)) do
13       {:ok, resp} -> {resp.status_code, resp.body, resp.headers}
14       {:error, _} -> {500, "<h1>500 - Internal Server Error</h1>", []}
15     end
16     {:ok, reply} = :cowboy_req.reply(code, headers, body, req)
17     {:ok, reply, state}
18   end
19
20   def next_url(url) do
21     purl = URI.parse(url)
22     "#{purl.scheme}://#{next_host()}#{purl.path}"
23   end
24
25   def next_host() do
26     :random.seed(:os.timestamp)
27     [next|_] = Enum.shuffle(["127.0.0.3", "127.0.0.4"])
28     next
29   end
30 end

Zeile 11 kopiert die angeforderte URL im Anfrage-Objekt und speichert den Wert in der Variablen »url« . Zeile 12 reicht die HTTP-Anfrage an einen lokalen HTTP-Server weiter. Dazu ruft sie »request()« aus dem Modul Httpoison auf, wobei sich der Proxy auf Get-Anfragen (»:get« ) beschränkt.

Die anschließend aufgerufene Funktion »next_url()« befindet sich in den Zeilen 20 bis 23. Sie übersetzt die URL der HTTP-Anfrage in eine Adresse im lokalen Netzwerk. In Zeile 21 zerlegt die Funktion »parse()« aus dem Standardmodul URI die eingegangene URL in ihre Adressenbestandteile. Die nachfolgende Zeile reproduziert die URL anhand der Adressenbestandteile wieder, allerdings ohne den IP-Anteil.

Diesen wiederum ersetzt die Funktion »next_host()« (Zeilen 25 bis 29) durch die IP-Adresse eines HTTP-Servers im lokalen Netzwerk, den sie per Zufall bestimmt. Den Zufall erzeugt ein Pseudozufallszahlen-Generator, den »seed()« in Zeile 26 initialisiert. In der darauf folgenden Zeile nimmt die Variable »next« den Kopf der mit »shuffle()« zufällig umsortierten Liste aus Zieladressen auf, Zeile 28 gibt im Anschluss das Ergebnis zurück. Das Ergebnis werten dann wiederum die Zeilen 13 und 14 aus. Diese unterziehen die Antwort der HTTP-Anfrage einer Case-Fallunterscheidung, die zwei mögliche Ergebnisse zeitigt.

Im Erfolgsfall passt das Muster der ersten Fallunterscheidung. Als Reaktion kopiert daraufhin die Zeile 13 den Statuscode, den Körper und alle übrigen Meta-Angaben aus der HTTP-Antwort in die drei Variablen »code« , »body« und »headers« (Zeile 12). Im Fehlerfall belegt hingegen der Code in Zeile 14 die drei Variablen mit konstanten Werten, wobei einmal mehr der »_« -Operator eine zentrale Rolle spielt.

Aus den drei Variablen erstellt schließlich der Aufruf der Funktion »reply()« in Zeile 16 eine HTTP-Antwort und speichert den Wert in der gleichnamigen Variablen »reply« . Die nachfolgende Zeile gibt »reply()« zusammen mit dem Literal »:ok« und dem Status (»state« ) als Antwort an den Webserver zurück.

Der Entwickler startet die Proxyanwendung, indem er wieder eine Shell aufruft und zunächst die Abhängigkeiten mittels »mix deps.get« lädt. Er kompiliert sie dann mit dem Kommando »mix deps.compile« , um daraufhin den Befehl »sudo iex -S mix« abzusetzen. Um die Anwendung aus Abbildung 1 zu vervollständigen, initialisiert er über eine weitere Shell einen zusätzlichen HTTP-Server mit der neuen IP-Adresse 127.0.0.4: Dazu wechselt er in das Verzeichnis »httpd« und gibt »sudo iex -S mix« gefolgt von

Plug.Adapters.Cowboy.http Httpd, [], ip:  {127, 0, 0, 4}, port: 80

ein. Abbildung 5 zeigt rechts den Load Balancer im Praxiseinsatz unter Ubuntu 14.04. Auf Anfrage von »http://127.0.0.2/info« antwortet die Anwendung unter Firefox mit der aus Abbildung 4 bekannten Replik. Jedoch variiert der Wert des Feldes »host« zufällig zwischen den IP-Adressen der beiden Webserver.

Abbildung 5: Die beiden Shells links oben starten die Webserver, die unten den Proxy. Rechts zeigt sich der Load Balancer in Aktion.

Fazit

Mit Elixir bauen Entwickler verteilte, fehlertolerante und skalierbare Anwendungen. Dank der Kompatibilität lässt sich das Ökosystem von Erlang auf komfortable Weise mitnutzen. Zugleich verzichtet Elixir auf das akademische Ideengestrüpp, das den Zugang zu funktionalen Sprachen meist überwuchert. Somit lassen sich damit schnell wartbare und fehlerresistente Anwendungen schreiben. Darüber hinaus erweitern Makros den Sprachumfang von Elixir und vereinfachen den Programmcode zusätzlich. So ungefähr könnte sich das Programmieren von morgen anfühlen.

Diesen Artikel als PDF kaufen

Express-Kauf als PDF

Umfang: 5 Heftseiten

Preis € 0,99
(inkl. 19% MwSt.)

Linux-Magazin kaufen

Einzelne Ausgabe
 
Abonnements
 
TABLET & SMARTPHONE APPS
Bald erhältlich
Get it on Google Play

Deutschland

Ähnliche Artikel

  • Elixir in Version 1.3 mit Kalendermodul

    Änderungen am Buildtool und am Test-Framework stechen als Neuerungen an Version 1.3 von Elixir ebenso hervor wie jene an der Sprache selbst. Neu ist auch ein Kalendermodul.

  • Phoenix 1.0: Web-Framework auf Elixir-Basis

    Anderthalb Jahre haben Entwickler an einem Web-Framework gearbeitet, das auf der funktionalen Programmiersprache Elixir basiert. Nun ist Phoenix in Version 1.0 zu haben.

  • Ruby on Rails

    Läuft alles nach Plan, setzen die Ruby-on-Rails-Macher Ende April 2017 die neue Version 5.1 aufs Gleis. Die liebt Javascript und hat unter anderem den flinken Javascript-Paketmanager Yarn an Bord. Zudem kann sie dank eigener Javascript-Bibliothek erstmals auf Jquery als Default-Option verzichten.

  • Crystal

    Das Open-Source-Projekt Crystal will das Beste zweier Welten vereinen: Die Einfachheit einer weitgehend zu Ruby ähnlichen Sprachsyntax mit der Geschwindigkeit und den Möglichkeiten der LLVM-Plattform.

  • Docker 1.0

    Dieser Workshop zeigt am Beispiel des Cloudstack Owncloud, wie stark die Entwickler das Integrieren etablierter Linux-Technologien wie LXC und Cgroups in Docker 1.0 abstrahiert und vereinfacht haben und wie Admins komplexe Szenarien in Containerumgebungen zähmen.

comments powered by Disqus

Ausgabe 08/2017

Digitale Ausgabe: Preis € 6,40
(inkl. 19% MwSt.)

Artikelserien und interessante Workshops aus dem Magazin können Sie hier als Bundle erwerben.