Während Web Services schon ihren 20ten Geburtstag gefeiert haben, waren sie lange Zeit in der IBM i Welt nicht sehr bekannt und verbreitet. Mit Sicherheit einer der Gründe, warum die Plattform von immer mehr Menschen als veraltet und rückständig angesehen wurde, denn die restliche Welt, außerhalb des IBM i Universums hat sich in dieser Zeit sehr intensiv mit diesem Thema beschäftigt und die Entwicklung ist nicht stehen geblieben.

Durch die Anforderungen im Bereich mobiler Apps, Web (Shop) Integration und Internet of Things sind in den letzten Jahren jedoch immer mehr IBM i-Entwickler mehr oder weniger freiwillig auf den Web Service Zug aufgesprungen oder besser gesagt geschupst worden. Doch wie schon gesagt, ist die Entwicklung im Bereich der Web Services nicht stehen geblieben und so hat die Evolution ihren Lauf genommen und die sog. Microservices (kurz MS) hervorgebracht.

Leider ist es auch bei diesem Schlagwort wieder, wie so oft: Alle reden von Microservices und jeder meint etwas anderes. Vorab sei gesagt, dass es meiner Meinung nach derzeit keine feste und klare Definition von Microservices gibt. Manch einer meint damit das SOA (Services Oriented Architecture) Konzept, welches nun endlich realisierbar sei.

Nicht ganz falsch, aber auch nicht ganz richtig, denn das Konzept von Microservices geht noch etwas über das ursprüngliche SOA Konzept hinaus, zumal sich die Anforderungen in den letzten 20 Jahren durchaus verändert haben. Manch einer meint damit kleinste Programmmodule, die via HTTP REST Service miteinander kommunizieren.

Nicht ganz falsch, aber auch nicht ganz richtig, denn wie Microservices miteinander kommunizieren ist unabhängig von HTTP,  REST oder anderen Technologien. Genauso gut sollte man keine maximale Größe für ein Modul festlegen, welches man als Microservice einsetzen will, denn damit macht man sich nur selbst stress. Die wichtigsten Eigenschaften, die ein Microservice (MS) erfüllen sollte sind:

  • Ein MS sollte immer nur eine Funktion umfassen, also z.B. eine Preisberechnung oder eine Währungsumrechnung. Niemals sollte ein Service zwei dieser Dinge beinhalten.
  • Der Quellcode sollte vom Umfang so sein, dass er leicht und schnell auch für fremde Entwickler überschaubar ist.
  • Ein MS muss problemlos durch einen anderen mit gleicher Schnittstelle zu ersetzen sein.
  • Die Plattform und Programmiersprache, in welcher der MS entwickelt wird, darf keine Rolle spielen.
  • Nach Möglichkeit sollte ein MS „stateless“ sein, also Zustandslos, damit er einfach zu skalieren ist.

Die Liste ist je nach Ansicht entsprechend lang. Wie immer und überall, gibt es Entwicklerkollegen, die – meiner Meinung nach – alles übertreiben müssen und dementsprechend eine Definition von Microservices haben, dass sie fast gar keinen Sinn mehr für den praktischen Einsatz ergeben – aber das ist eine andere Geschichte.

ILE für alle

Wer die IBM i kennt und sich mit dem „Integrated Language Environment“ – kurz ILE – ein wenig beschäftigt hat, wird beim Durchlesen der o.g. Eigenschaften schnell bemerken, dass es sich um lauter Eigenschaften handelt, die IBM bereits vor mehr als 24 Jahren für das ILE Konzept definiert hat. Genau das hat sich auch Niels Liisberg gesagt und kurzerhand den Web- und Applikationsserver IceBreak so erweitert, dass er sich als optimaler Microservice Server für IBM i ILE Sprachen eignet.

Bei IceBreak handelt es sich um den einzigen, nativen Web- und Applikationsserver für die IBM i, da IceBreak selbst in ILE programmiert und kompiliert wurde. Dabei wurden die Programmiersprachen RPG und C/C++ verwendet. Da IceBreak auch in einer Community Edition, kostenlos erhältlich ist, sowie innerhalb weniger Minuten heruntergeladen, installiert und Einsatzbereit ist, bietet es sich für jeden Entwickler an, sich einmal damit zu beschäftigen.

Von dem abgesehen, dass die damit entwickelten Web Anwendungen, Web Services, sowie Microservices schneller sind, als alles, was Sie sonst auf der IBM i in diesem Bereich gesehen haben, da die Anwendungen keinerlei PASE Overhead (Apache, CGI, Java, Websphere, etc.) einsetzen, sondern alles nativ in ILE abläuft. D.h. jeder Microservice, der mit IceBreak entwickelt wird, ist ein natives ILE Objekt im IBM i und wird nach außen auch native zur Verfügung gestellt.

Ein Microservice in RPG

Quelle: Markus A. Litters

Bild 2.

Doch lange Rede, kurzer Sinn – legen wir doch direkt mit einem Beispiel los. In Bild 2 sieht man einen vollständigen Microservice, welcher mittels total Free RPG umgesetzt wurde: Vorweg sei angemerkt, dass die Art, wie Sie mit IceBreak RPG Programme schreiben, Ihnen überlassen bleibt. D.h. dieses Beispiel hätte genauso gut als spaltenorientiertes RPG geschrieben worden sein können. IceBreak unterstützt jegliches RPG, welches die IBM Compiler umwandeln können inkl. embedded SQL.

In diesem Beispiel sehen Sie zunächst in Zeile 1 den Programmtyp: SRVPGM. D.h. es handelt sich hier um ein ganz normales RPG Serviceprogramm, welches anschließend in einer Bibliothek mit der Art *SRVPGM auch vorhanden ist!

Grundsätzlich lässt sich so quasi ohne viel Anpassungen jedes bereits vorhandene Serviceprogramm in einen Microservice umwandeln, wenngleich die einzelnen Procedures hier den eigentlichen Microservice darstellen.

Die Includes (Zeile 7 und 8) holen sich spezielle Funktionen, die IceBreak mitliefert in den Programmcode, um in diesem Fall z.B. ein JSON Dokument zu erstellen, welches unser Microservice zurückgibt. Als Eingangsparameter (Zeile 12-14) empfangen wir einen Pointer und geben an, dass wir auch einen zurückgeben.

Da viele RPG Entwickler beim Begriff Pointer direkt die Flucht ergreifen, möchte ich an dieser Stelle direkt darauf hinweisen, dass Pointer im Falle von RPG Microservices mit IceBreak ein reiner „Container“ darstellen, der von den speziellen IceBreak Funktionen (s. u.) verwendet wird. D.h. kein RPG Entwickler muss sich an dieser Stelle mit Pointern auseinandersetzen.

Über den Pointer erhält der MS z.B. eingehende JSON oder XML-Daten. Selbstverständlich könnten hier auch „normale“ Parameter übergeben werden, sofern z.B. nur eine Kundennummer oder Artikelnummer als Eingangsparameter benötigt wird.

In Zeile 21 wird der Rückgabe Pointer mittels der IceBreak Funktion „json_newObject“ als neues JSON Dokument initialisiert und anschließend in den Zeilen 23-25 gefüllt und in Zeile 27 zurück gegeben – ENDE.

Dank der mitgelieferten, umfangreichen JSON Funktionen, ist die Erstellung, sowie Bearbeitung, als auch das Lesen von JSON oder XML Dokumenten innerhalb von RPG oder Cobol sehr einfach.

In Zeile 25 sieht man z.B., dass wir die Ausgabe JSON Eigenschaft „message“ mit dem Inhalt aus dem eingehenden JSON füllen und zwar dem Wert aus der eingehenden Eigenschaft „message“.

D.h. mit json_setStr oder json_setNum (oder setBool, etc.) füllt man JSON Dokumente. Hierbei kann man beliebig tief verschachteln und auch Unterdokumente anlegen, sowie Arrays.

Quelle: Markus A. Litters

Bild 3.

Mit den Pendants json_getStr, getNum, getBool, etc. holt man wiederum Werte aus JSON Dokumenten heraus. Wenn wir diesen Microservice z.B. aus dem Browser aufrufen, erhalten wir ein Ergebnis, wie es in Bild 3 zu sehen ist.

Die Schaltzentrale

Um mittels IceBreak Microservices nutzen zu können, braucht man zunächst einen sog. Router oder auch Controller. D.h. einen zentralen Service, der Anfragen von außen entgegennimmt und diese intern verteilt. Dies ist bei anderen Programmiersprachen, wie z.B. ASP.NET MVC oder Javascript in Node, nicht anders, außer, dass bei IceBreak der Router ebenfalls in RPG oder Cobol geschrieben sein kann. Schauen wir uns einen einfachen Router in RPG also an – Bild 4 zeigt den Code. Dabei handelt es sich um die Hauptroutine des Microservice Routers, welche mit gerade einmal 11 Zeilen echten Code, doch sehr überschaubar ist.

Quelle: Markus A. Litters

Bild 4.

Natürlich findet die eigentliche Arbeit in den Subprocedures statt: Die Procedure „unpackParms“ sorgt, wie der Name schon vermuten lässt, dafür, dass eingehende Parameter „entpackt“ werden. In diesem Beispiel wurde der Router sehr flexibel gehalten, d.h. er prüft mehrere mögliche Wege, wie die Parameter in den Router gelangen können. In der Praxis wird meist ein bestimmter Weg definiert, so dass der Router wesentlich kompakter gehalten werden kann.

Ein Weg ist z.B. über die URL. D.h. derjenige, der einen bestimmten Microservice aufrufen möchte, übergibt seine Parameter über die URL Adresse direkt:

http://192.168.1.1:7900/tklrouter?payload={“action“:“tklmicro1.Hello“, “message“:“Hallo TKL Leser“}

Alternativ kann der Dienst auch wie folgt aufgerufen werden:

http://192.168.1.1:7900/tklrouter/tklmicro1/Hello?payload={“message“:“Hallo TKL Leser“}

Und als dritte Variante versteht unser Router auch noch die Übergabe der Parameter als JSON Dokument im Body der HTTP Nachricht (siehe Bild 4). Dabei ist ein Aufruf via SoapUI zu sehen. Hier wird zunächst die HTTP Methode „Post“ gewählt und Body Bereich (links unten zu sehen) das JSON Dokument definiert. Rechts im Bild ist die Antwort unseres RPG Serviceprogramms als JSON Dokument zu sehen.

Allen gemein ist, dass über die JSON Eigenschaft „action“ definiert wird, welcher Microservice aufgerufen wird und über die Eigenschaft „message“ eine Nachricht übergeben wird. Diese Definition ist vollkommen frei, d.h. wie Sie das in Ihren Projekten nachher umsetzen, bleibt Ihnen überlassen. Damit haben Sie die Flexibilität Ihre RPG und Cobol Microservices in bestehende Strukturen des Unternehmens zu integrieren.

Einzigartig daran ist auch, dass Sie zur Laufzeit dynamische Prozeduraufrufe durchführen können. In einigen Projekten haben wir dies so implementiert, dass wir die Microservices auch innerhalb der IBM i aufrufen aus anderen RPG Programmen. Der Vorteil ist, dass man eine Dependency Injection-Infrastruktur mit RPG damit realisieren kann, wie man sie sonst nur aus anderen Sprachen wie c#, Typescript oder Java kennt.

Bild 5.

In der Subprocedure in Bild 5 sieht man wieder einige IceBreak spezifische Funktionen, die dem ILE Entwickler das Leben sehr erleichtern. So wird mittels „SetContentType“ in Zeile 98 zunächst festgelegt, was wir zurück liefern wollen (in unserem Fall JSON Daten). In Zeile 102 schauen wir nach, ob in der URL eine Variable Namens „payload“ existiert. Wenn ja, füllen wir in Zeile 103 den Pointer pPayload mit den JSON Daten, die in diesem Parameter definiert wurden (s. Beispiel des Aufrufs 1. und 2.).

Wenn nicht, prüfen wir in Zeile 104, ob unser Router über die HTTP „POST“ Methode aufgerufen wurde und wenn ja, füllen wir in Zeile 105 unseren pPayload Pointer mit dem JSON Inhalt, der im Body des HTTP Post’s übergeben wurde. Sollten beide Wege nicht zutreffen, wird der Pointer pPayload auf *NULL gesetzt, welches in unserer Hauptroutine in Zeile 26 als erstes abgefragt wird.

Sollte ein Fehler beim Auslesen des Inhalts passieren, z.B. weil keine korrekten JSON Daten geliefert wurden, wird eine Nachricht mit der Fehlermeldung an den Aufrufer zurück gegeben (Zeile 110-114). Am Ende geben wir den Pointer pPayload mit seinem jeweiligen Inhalt zurück an die Hauptroutine, die je nach Inhalt entscheidet, was als nächstes geschieht.

Den Rest des Routers schauen wir uns im nächsten Teil an. Der Router wird jedoch auch allen IceBreak Anwendern direkt mit zur Verfügung gestellt und steht u.a. in meinem GitHub Repository bereit.

Wie man unschwer erkennen kann, ist dank IceBreak auch die IBM i in der Lage einfache und schnelle Microservices als ILE Programme zur Verfügung zu stellen. So lassen sich bereits vorhandene RPG und/oder Cobol, sowie C / C++, als auch CL Programme direkt in einer Microservice Architektur verwenden und z.B. via Tools wie z.B. Kubernetes in komplexere Architekturen einbinden.

Bei meinen bisherigen Projekten in diesem Umfeld war der einzige „Knackpunkt“, dass die Microservices auf der IBM i mit Abstand die Schnellsten waren. D.h. die Softwarearchitekten, die zunächst glaubten, die IBM i könne in ihr wunderschönes, neues Microservice Konzept gar nicht eingebunden werden, standen alle mit offenem Mund da, nachdem sie gesehen hatten, wie schnell wir diese umgesetzt haben und richtig Blass sind sie geworden, als sie die Geschwindigkeit zur Laufzeit gesehen haben.

Markus A. Litters

edvberatung.litters

Kontakt über die Mailadresse: mal@mlitters.com