Project Reactor és a szálkezelés

A Project Reactor egy Springhez közel álló reaktív könyvtár. Erre épül a Spring Framework 5-ben megjelent WebFlux webes keretrendszer reaktív webes alkalmazások készítésére. Ez nagyban hasonlít a Spring MVC-re, azonban reaktív módon működik. Ezzel találkozhatunk akkor is, ha WebClient-et használunk REST webszolgáltatások hívására (ez a RestTemplate-et hivatott leváltani, de úgy tűnik, hogy mégis szükség van egy szinkron megvalósításra is, ezért jelent meg a 6.1-es Spring Frameworkben a RestClient). A reaktív elvekről és keretrendszerekről már írtam a Reaktív programozás posztomban.

A Reactor ún. concurrency-agnostic, ami azt jelenti, hogy nem erőltet ránk semmilyen párhuzamossági modellt. Azonban a fejlesztőnek lehetőséget ad a szálak használatára.

Ez a poszt azzal foglalkozik, hogy lehet szálakat használni, hogyan befolyásolja a szálak használatát a publishOn és subscribeOn operátor (Mono és Flux osztályokban lévő metódusok).

A REST API hypertext-driven legyen

Bár erős vitákat tapasztalok a REST körül, valahogy a hypertext-driven API-val ritkán találkozom.

Roy T. Fielding, a REST megálmodója a gyakran idézett REST APIs must be hypertext-driven cikkjében a következőt mondja:

In other words, if the engine of application state (and hence the API) is not being driven by hypertext, then it cannot be RESTful and cannot be a REST API. Period.

Erre erősít rá a Richardson Maturity Model is, mely három lépésben mutatja be a REST alapvető elemeit:

  • Level 1 - Resources
  • Level 2 - HTTP Verbs
  • Level 3 - Hypermedia Controls

És egy API csak akkor nevezhető RESTfulnak, ha az összes szinten leírtakat teljesíti, igen a hypermedia control használatát is.

De mit is jelent ez? Az állítás egyszerű: a kliensnek minden előzetes tudás nélkül igénybe kell tudnia venni a RESTful API mögötti szolgáltatásokat, és ez linkek segítségével valósulhat meg.

Pl. az előző Workflow REST API-n posztomban szereplő Issue Tracker alkalmazás a következőképp adja vissza a hibajegyek listáját:

{
  "_embedded": {
    "jtl:issueResourceList": [
      {
        "id": 1,
        "title": "Write a post about REST",
        "state": "NEW",
        "_links": {
          "self": {
            "href": "http://localhost:8080/api/issues/1"
          },
          "actions": {
            "href": "http://localhost:8080/api/issues/1/actions"
          }
        }
      }
    ]
  },
  "_links": {
    "self": {
      "href": "http://localhost:8080/api/issues"
    }
  }
}

Látható a hibajegyek listáját reprezentáló URL-jét tartalmazó self link, valamint hibajegyenként a hibajegyet reprezentáló URL-jének linkje szintén self névvel, valamint a hibajegyhez tartozó lépéseket reprenzentáló URL actions névvel.

Ez a Hypermedia as the engine of application state (HATEOAS), melyet szintén Roy T. Fielding vezetett be a disszertációjában. A HATEOAS szerint a szerver a kliens számára linkekkel jelzi, hogy mit tehet.

Az elv mögötte az, hogy a REST API-t is úgy lehessen használni, mint a webet. Ahol vannak oldalak és linkek és semmilyen előzetes információra nincs szükségem a használatához.

Hogy hogyan viszonyul ez az OpenAPI-hoz (Swaggerhez)? Elvileg a cél ugyanaz, és míg a HATEOAS a REST gonolatiságához illeszkedik, az OpenAPI inkább az RPC-hez.

Workflow REST API-n

Bár kétségtelenül a REST a legelterjedtebb kommunikációs mód, akár ugyanolyan platformon fejlesztett alkalmazások között is, REST API tervezéskor nekem sok kérdés merül fel, melyre nem kapok megnyugtató választ, és sok olyan megoldással találkozom, amelyek nem felelnek meg a REST elveknek, vagy egyszerűen csak nem tetszenek.

Sokáig próbáltam megfogalmazni, hogy mi az alapvető problémám, és talán most sikerült közelebb kerülni. Vegyünk két microservice-t, melyek mindegyike Javaban készült, Spring Boot használatával. Mindkét oldalon a programozási nyelv alapeleme az osztályok, ahol érvényesül az egységbezárás, azaz az attribútumok mellett ott vannak a metódusok, a megfelelő paraméterekkel és visszatérési értékekkel. Ez a legtöbb fejlesztőnek triviális.

A REST a kettő között viszont egy teljesen más absztrakció. Bár a REST nem mondja ki, hogy csak HTTP-vel együtt használható, más protokollal működni még nem láttam. A REST alapfogalma az erőforrás, és az azon végzett műveletek, melyeket a HTTP metódusok valósítanak meg, mint a GET, POST, PUT és DELETE. Ez nehezen feleltethető meg az objektumorientált világnak. Egy objektum létrehozására tipikusan konstruktort használunk, ebből nem feltétlen egy van. Ezen kívül egy objektumnak lehet több metódusa, amik nem feltétlenül feleltethetőek meg a HTTP metódusoknak, hiszen nem csak ezeket az alapműveleteket használjuk, valamint lehet belőle sokkal több is, mint négy. Míg a Java nyelv szabvány, addig a REST csak egy ajánlás, és az sincs megfelelően definiálva, hogy hogyan kéne implementálni, az meg aztán pláne nincs, hogy a kettőt hogyan feleltessük meg egymásnak. És ennek az az eredménye, hogy minden projekten teljesen más megoldásokat látok, a legtöbb helyen kompromisszumokkal.

Absztrakciók

Az RPC alapú kommunikáció esetén igazából nincs ilyen probléma, mert nem szükséges átfordítás, nincs szükség az absztrakció váltására. Hiszen ott is eljárások vannak, paraméterekkel és visszatérési értékekkel. A Spring Frameworkben volt is ilyen lehetőség, hogy egy távoli eljáráshívás történt, azonban a protokollt kódolás nélkül cserélni lehetett alatta, pl. választhattunk RMI-t, vagy ki emlékszik még a Hessian és Burlap protokollokra. Sajnos ez azóta eltűnt. A SOAP esetén sem volt akkora probléma az átfordítás, hiszen ott is vannak az operációk (eljárásnak felel meg), és a paraméter, valamint a visszatérési érték is egy-egy (XML) dokumentum. Vannak modernebb RPC alapú kommunikációs protokollok is, mint pl. JSON-RPC, vagy az egyre inkább elterjedő multiplatform gRPC, bináris Protocol Buffers formátummal.

Amennyiben CRUD alkalmazásról van szó, a REST használata talán triviális. De ha egy olyan alkalmazásról beszélünk, amiben komoly üzleti logika van (és hiszem, hogy a legtöbb alkalmazásnak ilyennek kéne lennie, csak a fejlesztők “butítják le” CRUD alkalmazássá, amely végső soron oda is vezethet, hogy borzasztó felhasználói felületek kerülnek kifejlesztésre), akkor már több kérdés merül fel.

Vegyünk például egy hibajegykezelő alkalmazást (issue tracker), mely rendelkezzen egy minimális üzleti logikával. Ha hibajegy kerül felvételre, az új (new) állapottal kerül létrehozásra. Amennyiben valaki elkezd rajta dolgozni, átkerül folyamatban (in_progress) állapotba. Amennyiben elkészült, átkerül megoldott (resolved) állapotba. Bár a legtöbb hibajegykezelő alkalmazás lehetővé teszi, hogy egyedi munkafolyamatot lehessen benne létrehozni, ettől most tekintsünk el, mert akkor megint más problémák merülnek fel. Ezt egy egyszerű állapotdiagrammal lehet a legegyszerűbben szemléltetni.

Absztrakciók

Talán egy hibajegy felvételét még el tudom képzelni REST API-n (bár már ott is vannak kérdéseim), azonban a különböző munkafolyamat lépések implementálásakor már bizonytalanabb vagyok. Ez a poszt ezt a problémakört járja körbe, forráskódokkal alátámasztva.

Hőmérséklet monitorozása

Nagyon régóta érdekel az IoT (Internet of Things, azaz Internetre köthető kütyük) világa, ugyanis ezek azok az eszközök, melyek összekötik a virtuális világót a való világgal. Már régóta rendelkezem egy Raspberry PI számítógéppel, melyet azóta is lelkesen használok egy alacsony fogyasztású home serverként. Telepítve van rá a Prometheus és Grafana, mely a DevOps világban egy kvázi standard monitorozó eszköz. Mindkettő ingyenesen használható, nyílt forráskódú eszköz. A Prometheus különösen alkalmas idősorok hatékony tárolására, gyors lekérdezésére, aggregált műveletek végrehajtására. Az idősorok olyan megfigyelések, melyeket egymást követő időpontokban (időszakokban) regisztrálták, és ez az időbeliség az adatok fontos tulajdonsága. Ilyen például egy szerverrel kapcsolatban a bizonyos időpontokban lekérdezett CPU és memóriahasználat, vagy a háttértárakon lévő szabad hely mérete. A Grafana használatával ezeket tudjuk vizualizálni, gyönyörű dashboardokat létrehozni és akár riasztásokat beállítani.

De melyik is lehet az az IoT eszköz, melyet a legegyszerűbben, lehetőleg barkácsolás nélkül és olcsón lehetne bekötni ebbe a rendszerbe? Már régóta szemezem a Xiaomi Mi Temperature and Humidity Monitor 2 hőmérséklet-, és páratartalom mérővel, amihez most 2000 Ft-ért lehet hozzájutni az mStore akciója keretében.

Xiaomi Mi Temperature and Humidity Monitor 2

Ez pontosan a LYWSD03MMC modell, mely egy precíz Sensirion szenzorral, 1,5”-os LCD kijelzővel rendelkezik, és Bluetooth 4.2 BLE vezeték nélküli kapcsolaton keresztül kommunikál. Egy CR2032 elemmel működik, ezt külön szerezzük be, mert nem része a csomagnak. Ezzel akár fél-egy évig képes üzemelni. Természetesen Bluetooth-on tud kapcsolódni mobiltelefonhoz, illetve Bluetooth Gateway-hez, azonban én direktbe, egy Linuxos számítógéppel, jelen esetben egy Raspberry PI-vel szeretnék hozzá kapcsolódni, és kinyerni belőle az adatokat. (Nincs szükség egyedi firmware telepítésére.)

Ezt utána csak be kell kötni a Prometheusba, mely időközönként lekérdezi és eltárolja az adatokat, majd egy Grafana dashboardot létrehozni, mely megjeleníti azokat.

Grafana dashboard

Közben természetesen szerettem volna megismerni a kapcsolódó technológiákat is.

JTechLog Technology Radar

Emlékszem régebben mennyire nagy esemény volt mindig, mikor kijött a Thoughtworks Technology Radar legfrissebb kiadása. A Thoughtworksről annyit kell tudni, hogy 1999-ben csatlakozott a céghez Martin Fowler, és több munkatársával együtt jelentős és meghatározó könyveket írtak, melyeket én is javaslok elolvasni. Ezek például:

  • Martin Fowler - Refactoring
  • Martin Fowler - Patterns of Enterprise Application Architecture
  • Jez Humble, David Farley - Continuous Delivery
  • Sam Newman - Building Microservices

A cég mindig nagyon haladó gondolkodású volt, és bizonyos időközönként közreadta a Technology Radart, melyben grafikusan ábrázolták, hogy mi is a véleményük az épp aktuális technológiákról, eszközökről, módszertanokról.