Luo CRUD-sovellusliittymä Rust-sovelluksen avulla

Alkuperäisen Solmu / Rust REST -vertailuni jälkeen olen halunnut seurata kattavaa opasta yksinkertaisten CRUD-toimintojen saamiseksi käyttöön ja ajamiseen Rustissa.

Vaikka Rustilla on monimutkaisuuttaan, joka tulee Node.js: stä, yksi asia, joka on pitänyt minut kielenkiinnostavana, on sen yksinkertaisuus. Se on tyylikäs ja löytää hyvän tasapainon turvallisuuden ja kognitiivisen intuition välillä.

Joka tapauksessa toivottavasti tämä opas auttaa niitä, jotka ovat uusia Rustille, ja rohkaisee aidalla olevia.

Vieritä alas, jos haluat nähdä kuinka Rust vertaa Java: ta ja Node.js: ta

Tärkeimmät käytetyt puitteet

  • Rocket - web-kehys nopeiden verkkosovellusten kirjoittamiseen
  • Serde - kehys Rust-tietorakenteiden serialisoimiseksi ja ansaitsemiseksi
  • Dieseli - turvallinen, laajennettava ORM ja kyselyjen valmistaja

Luo sovelluksemme Rust-sovellukseen

Ensin luomme uuden Rust-projektin alla olevien komentojen avulla.

[sean @ lappy486: ~] $ rahti uusi sankari-api --bin && cd sankari-api
    Luotu binaarinen (sovellus) `sankari-api` -projekti

Ennen kuin siirrymme eteenpäin, Rocket vaatii meitä käyttämään öistä ruosterakennetta. Onneksi on olemassa nopea komento, jota voimme käyttää kanavien vaihtamiseen.

$ rustup oletuksena iltaisin
$ rustup -päivitys ja & lastin päivitys

Saat täältä kytkimen tuloksia, mutta voit vahvistaa sen onnistuneen lähettämällä --version lipun rahtiin ja rustc

$ lastin --version && rustc - muunto
rahti 1.26.0-yö (5f83bb404 2018-03-09)
rustc 1.26.0-yö (55c984ee5 2018-03-16)

Ensimmäisen Rust-riippuvuutemme lisääminen (raketti)

Hyvä on, hero-api-hakemistostamme löydät Cargo.toml- ja src-kansiot. Cargo.toml toimii samalla tavalla kuin Noden paketti.json, jota käsittelemme myöhemmin - ja uskon, että src on tarpeeksi kuvaava :)

Meidän on lisättävä Rocket riippuvuutena muokkaamalla Cargo.toml ja lisäämällä seuraavat kaksi riippuvuutta. On varmasti enemmän lisättävää myöhemmin, mutta tämä saa ainakin meidät aloittamaan.

[Riippuvuudet]
raketti = "0.3.6"
rocket_codegen = "0.3.6"

Nyt voimme muokata src / main.rs - lainaamme vain Rocket.rsin meille lähettämän aloituskoodin varmistaaksemme, että kaikki toimii tässä vaiheessa.

Se asia, joka mielestäni houkuttelevinta Rust (ja raketti) on, kuinka itse kuvaava suurin osa koodista on.

  • # [Get ( "/ / ")]

Tässä paljastamme yhden päätepisteen, joka hyödyntää Rocketin merkintää ja joka ottaa kaksi parametria ja . Tämän jälkeen kartoitamme nämä parametrit reittinkäsittelijällemme ja määrittelemme niiden tyypit - jos tyypit eivät ole sovitut, reittinkäsittelyyn ei vedota ja 404 palautetaan.

  • fn hello (nimi: jouset, ikä: u8) -> jouset {

Reittinkäsittelijämme palauttaa muotoillun raidan Rust-muodossa! makro, mukana toimitetulla nimellä ja iällä. Huomaa, että paluulausekkeille ei ole jäljellä puolipistettä

  • muoto! ("Hei, {} vuoden ikäinen nimeltä {}!", ikä, nimi)

Päämenetelmä, samoin kuin muut kielet, nimitetään osuvasti pääksi. Tämä on ensimmäinen toiminto, joka käynnistetään, kun sovelluksemme toimii. Täällä kehotamme Rocketia käynnistymään ja liittämään reitin kiinnittimen käyttämällä juuria kontekstina / hello. Tämä antaa meille URL-osoitteen, kuten: / hei / / .

fn main () {
  raketti :: Ignite ()
    .mount ("/ hei", reitit! [hei])
    .tuoda markkinoille();
}

Kaksi tiedostoasi tulisi näyttää samanlaiselta kuin alla oleva kuva. Testataan, että kaikki toimii, käynnistämällä palvelimemme käyttämällä thecargo run -komentoa. Tämän pitäisi käynnistää palvelin localhostissa: 8000, johon voi osua selaimen / curl-osoitteen kautta.

Rauhallisten päätepisteiden luominen JSON: n (Serde) avulla

Joten yksi päätepiste saa meidät toistaiseksi vain. API: lle ajatellen haluamme yksinkertaisia ​​CRUD-toimintoja sankaritietojen hallintaan; joten heti yläosasta, haluamme:

  • Luo: POST / sankari
  • Lue: GET / sankareita
  • Päivitys: PUT / sankari /: id
  • Poista: POISTA / sankari /: id

Nyt kun meillä on tämä näkökulmasta - käytämme JSON: ta ensisijaisena keinona tietojen vaihtoon, joten seuraa seuraava riippuvuutemme: Serde.

Tämä edellyttää seuraavia lisäyksiä Cargo.toml: iin:

Haluamme myös miettiä, mitä ominaisuuksia tietyllä sankarilla pitäisi olla. Alla luomme src / hero.rs: iin yksinkertaisen sankarimallin - lisäämällä Serden merkinnät Serialize ja Deserialize, jotta mallimme voidaan purkaa ja muuntaa JSON: ksi.

tunnus on valinnainen, koska CREATE-päätepisteeseemme osuvilla kuluttajilla ei ole vielä tunnusta lähetettäväksi. Kun noutamme tietoja ja toimitamme ne vastauksena, kaikilla DB: stämme on tunnus määritetty.

Mennään eteenpäin ja luodaan CRUD-päätepisteemme nyt käyttämällä näennäistietoja toistaiseksi:

Yllä oleviin sisällytetään uudet riippuvuutemme ja viitataan sankarityyppiin, vedämme myös Json ja Value rocket_contribista; tämä helpottaa JSON-pyyntöjen / vastausten käsittelyä.

# [makro_käyttö] ulkoinen laatikko rocket_contrib;
# [makro_käyttö] ulkoinen laatikko serde_derive;
käytä rocket_contrib :: {Json, Value};
mod sankari;
käytä sankaria :: {sankari};

Lisäämme myös muut toiminnot (POST, PUT, DELETE). Reittimerkinnässä oleva dataattribuutti kertoo vain, että Rocket odottaa kehotietoja - kartoittaa sitten kehon parametriin. Tässä sanotaan, että odotettavan ruumiin tulisi olla sankarin muotoinen, mutta kääritty JSON: iin.

# [viesti ("/", data = "")]
fn luoda (sankari: Json ) -> Json  {

Meidän pitäisi nyt pystyä osumaan mihin tahansa määritettyihin päätepisteihimme käyttämällä tavallista lepoasiakasta / curl-ohjelmaa jne.

Pysyvät tietomme kautta ORM (Diesel)

Mielestäni voimme kaikki olla yhtä mieltä siitä, että muutaman URL-osoitteen saaminen on hienoa, mutta se ei ole kovin hyödyllinen, jos lähettämämme tiedot eivät ole pysyviä. Käytämme tätä varten Dieseliä, koska se on tällä hetkellä yksi kypsimmistä Rust ORM -kehyksistä.

Tunnustan, että tämän tekeminen ensimmäistä kertaa on varmasti mukana prosessissa, mutta kun alkuperäinen käynnistyslokero on poissa käytöstä, se toimii melko hyvin. Olen kokeillut tätä blackbeam raw mysql -ohjaimen kanssa, ja vaikka se toimii, kooditietokanta pilaantuu todella nopeasti ..

Itse asiassa Sean Griffin (Dieselin kirjoittaja) kirjoitti hienon artikkelin, joka kuvaa juuri tätä asiaa.

Aluksi asennamme Diesel CLI:

$ lastin asennus diesel_cli

Sitten kerromme Dieselille, missä tietokantamme pitäisi elää ja suorittaa asennus:

$ export DATABASE_URL = mysql: // käyttäjä: pass @ localhost / sankarit
$ dieselasetukset
    Tietokannan luominen: sankareita

Seuraavaksi luomme siirtymisstrategian - tämä periaatteessa antaa meille mahdollisuuden pitää tarkistuksia tietokantamme kehittyessä ajan myötä.

Dieselin muuttoliike tuottaa sankareita
    Siirtojen luominen / 2018-03-17-180012_heroes / up.sql
    Siirtojen luominen / 2018-03-17-180012_heroes / down.sql

Meidän on muokattava näitä kahta tiedostoa sisällyttääksesi sankarit-skeemamme SQL-tiedostoon

Nyt voimme suorittaa siirron - joka suorittaa up.sql: n DB: tämme vastaan

[sean @ lappy486: ~ / hero-api] $ dieselin siirto ajon aikana
    Käynnissä siirtyminen 2018-03-17-180012_heroes

Jos onnea, se on viimeinen SQL, jota meidän tulisi koskea tähän projektiin. Mennään eteenpäin ja lisätään Diesel riippuvuuteen Cargo.toml -sivustollamme. Aiomme myös lisätä r2d2-dieselin, jonka avulla voimme hallita db-yhteyden yhdistämistä.

[Riippuvuudet]
diesel = {versio = "1.0.0", ominaisuudet = ["mysql"]}
diesel_codegen = {version = "*", features = ["mysql"]}
r2d2 = "*"
r2d2-diesel = "*"

Luomme src / db.rs tietokantayhteyden luomiseksi ja uima-altaan hallinnoimiseksi - onneksi suurin osa tästä annettiin Rocket Connection Guard -koodin aloituskoodilla

Meidän on myös tehtävä seuraavat lisäykset sitoaksesi sankarimallimme uuteen taulukkotietoihimme, jonka olemme luoneet:

Tämän avulla voimme luoda automaattisesti luodun kaavion, joka johdetaan rakenteestamme

$ diesel print-schema> src / schema.rs

Aiomme kuitenkin tehdä pienen muutoksen, jotta voimme hyödyntää samaa mallia kysely- ja kiinnitysobjekteille. Dieselin kirjoittaja esitti oikeudenmukaisen päättelyn esineemme jakamiseksi kahteen malliin, mutta pidän parempana mukavuutta ja vähemmän koodin pilaantumista. Joten sitä varten muokkaamme src / schema.rs

Ilman yllä olevaa muutosta vaadimme kahta mallia: yhden lisättävään (ilman tunnusta) ja toisen noudettavaan (tunnuksella).

Nyt voimme tuoda nämä uudet riippuvuudet lisäämällä seuraavat intosrc / main.rs:

# [makro_käyttö] ulkokuoriöljy;
ulkoinen laatikko r2d2;
ulkokuori r2d2_diesel;
mod db;
mod-skeema;

Seuraavaksi sanomme, että Rocket hallitsee db-yhteysvarantamme lisäämällä seuraava rakettiimme :: ignite () -ketjuun:

.manage (db :: connect ())

Älä usko, nyt olemme valmiita saamaan tämän asian aikaan! Tässä paljastamme muutamia menetelmiä luomalla toteutuksen Hero-sovellukselle src / hero.rs - tallenna hienostuneempaan virheiden käsittelyyn.

Nyt voimme hyödyntää vastikään luotuja menetelmiä reittinkäsittelylaitteidemme sisällä

Huomaa, että jokainen reittinkäsittelylaitteidemme ja sankarin toteutuksen menetelmä hyväksyy nyt käytännössä samat / samanlaiset argumentit: Jotkut tunnuksen, sankariobjektin ja tietokantayhteyden yhdistelmät.

Lisätietoja tietojen kyselystä on Dieselin aloittamisen oppaissa.

Viimeinkin voimme nyt luoda sankarin ja nähdä todellisia tuloksia REST-asiakasohjelmassa. Sankarien noutaminen, päivittäminen ja poistaminen toimivat myös odotetusti.

Sovellusliittymämme testaaminen (wrk)

Nyt kun meillä on jonkin verran täydellinen sovellusliittymä, on todennäköisesti syytä vertailla tätä asiaa, koska yksi ruosteen suosituimmista puolista on sen suorittaminen muilla kielillä.

Mutta ennen kuin kokeilin sitä, halusin saada kuvan siitä, kuinka muut suositut kielet ja kehykset pinottuvat.

Tätä varten käytin yksinkertaisesti wrk: tä paikallisesti MacBook Prossa, eikä muita käyttäjän sovelluksia tai palveluita ole käynnissä.

MacBook Pro (15-tuumainen, 2016)
Suoritin: 2,9 GHz: n Intel Intel Core i7
Muisti: 16 Gt 2133 MHz LPDDR3
Varastointi: 1 TB Flash-muisti

Java 1.8 (Spring Boot, Hibernate)

Vedin yhteen nopean esimerkin, joka paljasti saman GET Heroes -menetelmän, jonka loimme yllä olevaan Rust-sovellusliittymäämme seuraamalla Rajeev Kumar Singhin hienoa opetusohjelmaa (löytyy täältä)

Käytetty esimerkkikoodi löytyy täältä:

  • https://github.com/sean3z/spring-boot-hero-api-example

Tulokset eivät olleet huonoja @ 4584 pyyntöä sekunnissa

Solmu 9.8 (Restify, Sequelize)

Solmutiimi on tehnyt merkittäviä parannuksia solmuun 9. Nyt V8: n sytytys- ja turbofan-vetovoimat hyödyntävät - Solmun suorituskyky on lähes kolminkertaistunut.

Käytetty esimerkkikoodi löytyy täältä:

  • https://github.com/sean3z/nodejs-hero-api-example

Tulokset eivät olleet melkein mitä toivoin @ 2 506 pyyntöä sekunnissa. On huomionarvoista, että raa'an mysql-paketin käyttö lähes kaksinkertaisti käsitellyt pyynnöt sekunnissa.

Ruoste 1,26 (raketti, diesel)

Viimeiseksi, testataksesi Rust-palvelintamme, haluamme varmistaa, että ROCKET_ENV on asetettu prod, muuten se toimii kehitystilassa.

$ lastin rakennus - vapauta &&d-kohde / julkaisu /
$ sudo ROCKET_ENV = tuote / hero-api
     Rocket on käynnistänyt osoitteesta http://0.0.0.0:80

Käytetty esimerkkikoodi löytyy täältä:

  • https://github.com/sean3z/rocket-diesel-rest-api-example

Ruoste ylitti vakavasti molemmat muut puitteet

johtopäätös

Solmu tuotti pienimmän koodijalanjäljen, mutta selvästi ilman tyyppiturvallisuutta ja suorituskykyasteikon alaosassa - vaikkakin 2k / s / sek ei ole mikään syytä huijata. Voimme parantaa Solmun suorituskykyä nastier-koodin ylläpidon kustannuksella.

Javalla oli selvästi suurin koodijalanjälki. Käynnistys kestää myös pisin keskimäärin ~ 4 sekuntia. Ei oikeastaan ​​muuta ilmoittamista täällä, sitä ei vain ollut .. hauskaa kehittää?

Ruoste on uskomattoman hauska kieli, ja se on uskomattoman nopea. On sanomattakin selvää, että nopeus ei ole ainoa tekijä harkittaessa työlle sopivaa työkalua, mutta se on varmasti tärkeä.

Lisätietoja Rustista saat tutustumalla “Rust-ohjelmointikieleen”