Blog
Shuttle: Der schnellste Weg zur Bereitstellung eines benutzerdefinierten HTTP-basierten Anwendungsservers?

Ist Shuttle möglicherweise der schnellste Weg, einen benutzerdefinierten HTTP-basierten Anwendungsserver bereitzustellen?

tl;dr
| Wer: | Konzentrieren Sie sich auf das einfache Prototyping Ihres POC oder die Skalierung von MVP-Backends in Rust |
| Was: | Bieten Sie die schnellstmögliche Backend-Entwicklung |
| Wann: | Alpha März 2022, Beta Juni 2023, ... |
| Wo: | Serverloses Backend-Framework in deren Cloud, Ihrer Cloud oder "in Ihrer Garage" |
| Warum? | Das Einrichten und Verkabeln der gesamten Infrastruktur ist ein zeitaufwändiger und fehleranfälliger Prozess. |
| Wie: | Wenn das etwas für Sie sein könnte, lesen Sie weiter für Details ... |
Lassen Sie es uns herausfinden.
Voraussetzungen
- Rust installieren rustup akzeptieren Sie die Standardeinstellungen
- Einfach installieren
cargo install just - Setup Editor astronvim helix vscode rustrover andere
Beginnen Sie mit einem neuen Verzeichnis, das Sie nach Ihrem Wunsch für das Projekt benennen. Als Erstes fügen Sie die Datei rust-toolchain.toml hinzu, damit beim ersten Aufruf eines beliebigen 'cargo'-Befehls automatisch die spezifische Version installiert wird, die wir für dieses Projekt verwenden möchten. Sie werden feststellen, dass ich ein bestimmtes Datum für die nächtliche Toolchain gewählt habe. Normalerweise mache ich das, um sicherzustellen, dass das Projekt auch Monate oder sogar Jahre nach seiner Einrichtung noch lauffähig ist. Außerdem verwende ich eine nächtliche Toolchain, um von einigen Funktionen zu profitieren, die es noch nicht in die stabile Version geschafft haben.
Kopieren Sie den folgenden Text in die Datei 'rust-toolchain.toml' im Stammverzeichnis des Projekts:
[toolchain]
channel = "nightly-2024-02-24"
components = [
"rust-analyzer",
"rust-docs",
"rust-src",
"rustfmt",
"clippy",
"llvm-tools",
]
Das zweite Element ist eine 'justfile', die von dem Tool verwendet wird, das wir in den Vorbedingungen installiert haben. Ähnlich wie make erlaubt das 'just'-Tool eine sehr kurze Syntax für die Einrichtung von Build-Aufgaben mit wenig mehr als der Sammlung der Befehlszeilenaufrufe und ein paar Dateiformatkonventionen.
Kopieren Sie die unten stehende 'justfile' in das Stammverzeichnis Ihres Projekts:
set dotenv-load
# list available recipes
list:
just --list
# watch for file changes then restart the recipe
watch recipe:
cargo watch -c -s 'just {{recipe}}'
# run shuttle project locally
run:
cargo shuttle run
# create and deploy a new shuttle project
start:
cargo shuttle project start && RUST_LOG=cargo_shuttle cargo shuttle deploy --allow-dirty
# update your existing shuttle project
deploy:
RUST_LOG=cargo_shuttle cargo shuttle deploy --allow-dirty
# restart (clean) your existing shuttle project then deploy
restart:
cargo shuttle project restart && RUST_LOG=cargo_shuttle cargo shuttle deploy --allow-dirty
# install or update tools
tools:
cargo install --locked cargo-shuttle
cargo install --locked just
Jetzt, wo wir diese beiden Dateien haben, können wir beginnen. In der 'justfile' sind alle Aufgaben enthalten, die wir benötigen, um dieses Shuttle-Projekt einzurichten, sowie ein paar praktische "Rezepte". Das erste, das wir ausführen werden, lädt alle zusätzlich benötigten Werkzeuge.
Rufen Sie den folgenden Befehl im Stammverzeichnis Ihres Projekts auf.
just tools
Nachdem Sie nun gesehen haben, wie alle Tools installiert werden, was wahrscheinlich einige Minuten gedauert hat, es sei denn, Sie hatten sie bereits, können wir mit der Codierung beginnen. Erstellen Sie das Verzeichnis 'src' im Stammverzeichnis des Projekts. Der gesamte Quellcode wird hier abgelegt. Außerdem erstellen wir ein 'dist'-Verzeichnis, in dem wir die Dateien ablegen, die mit unserem HTTP-Server verteilt werden sollen, wenn er bereitgestellt wird. Für dieses Projekt werden wir nur einen einfachen Hallo-Welt-REST-Aufruf und eine statische 'index.html'-Seite einfügen.
Bei einer typischen binären ausführbaren Datei nennen wir die Quelldatei 'main.rs'. Beginnen wir mit den Grundlagen für eine Shuttle-Anwendung. Am Ende werden wir uns für eines der drei wichtigsten http-Server-Frameworks entscheiden müssen. Shuttle scheint "alle zu unterstützen", d.h. unabhängig davon, wofür Sie sich entscheiden, haben sie die Integrationskiste und ein funktionierendes Beispiel für Sie bereit.
Erstellen wir unsere 'Cargo.toml' und fügen wir die Abhängigkeits-Kisten (Bibliotheken) für das erste Framework, 'axum', hinzu.
Fügen Sie das Folgende in Ihre Datei 'Cargo.toml' im Stammverzeichnis des Projekts ein:
[package]
name = "launch"
version = "0.1.0"
edition = "2021"
rust-version = "1.75"
publish = false
[workspace]
[dependencies]
shuttle-runtime = "0.39"
tokio = "1.36"
tracing = "0.1"
axum = { version = "0.7" }
tower-http = { version = "0.5", features = ["fs"] }
shuttle-axum = { version = "0.39" }
Nachdem Sie Ihre 'Cargo.toml' geändert haben, fügen Sie den folgenden Code zu Ihrer 'src/main.rs' hinzu:
use axum::{routing::get, Router};
use tower_http::services::ServeDir;
#[shuttle_runtime::main]
async fn main() -> shuttle_axum::ShuttleAxum {
let router = Router::new()
.route("/hi-axum", get(hello_world))
.nest_service("/", ServeDir::new("./dist"));
Ok(router.into())
}
Hierfür benötigen wir eine weitere Konfigurationsdatei. Die Datei 'Shuttle.toml', die sich ebenfalls im Stammverzeichnis des Projekts befindet, enthält spezifische Direktiven für den Befehl 'cargo shuttle'. Sie müssen den "Namen" so ändern, dass er global für alle Shuttle-Clients eindeutig ist. Wenn Sie versuchen, das Projekt so zu installieren, wie es ist, wird Shuttle Sie darüber informieren, dass der Projektname bereits vergeben ist.
Fügen Sie den folgenden Text in Ihre Datei "Shuttle.toml" ein und ändern Sie den Namen:
name = "xebia-launch"
assets = ["dist/*"]
Erstellen Sie dann eine Datei 'index.html' im Verzeichnis 'dist' und kopieren Sie den folgenden Code hinein (oder einen HTML-Code Ihrer Wahl für Ihre Homepage):
<html><body>
<img src="https://pages.xebia.com/hs-fs/hubfs/Xebia-AWS-Banner.jpg?width=600&name=Xebia-AWS-Banner.jpg" alt="Xebia logo"/>
</body></html>
Probieren wir es schließlich lokal aus, indem wir den Befehl cargo shuttle run verwenden und ihn mit just run aufrufen.
Rufen Sie in einem beliebigen Webbrowser auf demselben Rechner die URL "http://localhost:8000/" auf.
Der Inhalt von 'index.html' sollte serviert werden.
Fügen wir nun einen Endpunkt hinzu, der die Antwort generiert, anstatt nur eine statische Datei auszugeben. Fügen Sie den folgenden Code in Ihre 'main.rs' ein:
use axum::{routing::get, Router};
use tower_http::services::ServeDir;
#[shuttle_runtime::main]
async fn main() -> shuttle_axum::ShuttleAxum {
let router = Router::new()
.route("/hi-axum", get(hello_world))
.nest_service("/", ServeDir::new("./dist"));
Ok(router.into())
}
async fn hello_world() -> &'static str {
"Hello, World!"
}
Versuchen Sie den Endpunkt, und bemerken Sie, dass.... nichts enthält. Wir müssen just run mit Strg-C beenden und neu starten, ODER wir verwenden just watch run, um den Neustart automatisch durchzuführen, wenn wir Datei-Änderungen speichern.
Versuchen Sie den Endpunkt erneut unter "http://localhost:8000/hi-axum ". Er sollte jetzt funktionieren.
Ok, es ist Zeit für das Feature-Gate. Ändern Sie den Inhalt Ihrer Dateien so, dass er mit den unten stehenden Beispielen übereinstimmt:
'Fracht.toml'
[package]
name = "launch"
version = "0.1.0"
edition = "2021"
rust-version = "1.75"
publish = false
[workspace]
[dependencies]
shuttle-runtime = "0.39"
tokio = "1.36"
tracing = "0.1"
actix-web = { version = "4.5", optional = true }
actix-files = { version = "0.6", optional = true }
shuttle-actix-web = { version = "0.39", optional = true }
axum = { version = "0.7", optional = true }
tower-http = { version = "0.5", features = ["fs"], optional = true }
shuttle-axum = { version = "0.39", optional = true }
rocket = { version = "0.5", optional = true }
shuttle-rocket = { version = "0.39", optional = true }
[features]
default = []
actix = ["dep:actix-web", "dep:actix-files", "dep:shuttle-actix-web"]
axum = ["dep:axum", "dep:tower-http", "dep:shuttle-axum"]
rocket = ["dep:rocket", "dep:shuttle-rocket"]
'main.rs'
///////////////// actix /////////////////
#[cfg(feature = "actix")]
use actix_files::Files;
#[cfg(feature = "actix")]
use actix_web::{get, web::ServiceConfig};
#[cfg(feature = "actix")]
use shuttle_actix_web::ShuttleActixWeb;
#[cfg(feature = "actix")]
#[shuttle_runtime::main]
async fn actix_web() -> ShuttleActixWeb<impl FnOnce(&mut ServiceConfig) + Send + Clone + 'static> {
let config = move |cfg: &mut ServiceConfig| {
cfg.service(hello_world).service(
Files::new("/", "./dist")
.index_file("index.html")
.prefer_utf8(true),
);
};
Ok(config.into())
}
///////////////// axum /////////////////
#[cfg(feature = "axum")]
use axum::{routing::get, Router};
#[cfg(feature = "axum")]
use tower_http::services::ServeDir;
#[cfg(feature = "axum")]
#[shuttle_runtime::main]
async fn main() -> shuttle_axum::ShuttleAxum {
let router = Router::new()
.route("/hi-axum", get(hello_world))
.nest_service("/", ServeDir::new("./dist"));
Ok(router.into())
}
///////////////// rocket /////////////////
#[cfg(feature = "rocket")]
use rocket::{
fs::{relative, NamedFile},
get, routes,
};
#[cfg(feature = "rocket")]
use std::path::{Path, PathBuf};
#[cfg(feature = "rocket")]
#[get("/<path..>")]
pub async fn serve(mut path: PathBuf) -> Option<NamedFile> {
path.set_extension("html");
let mut path = Path::new(relative!("./dist")).join(path);
if path.is_dir() {
path.push("index.html");
}
NamedFile::open(path).await.ok()
}
#[cfg(feature = "rocket")]
#[shuttle_runtime::main]
async fn rocket() -> shuttle_rocket::ShuttleRocket {
let rocket = rocket::build().mount("/", routes![hello_world, launch, serve]);
Ok(rocket.into())
}
///////////////// all of them /////////////////
#[cfg_attr(feature = "actix", get("/hi-actix"))]
#[cfg_attr(feature = "rocket", get("/hi-rocket"))]
async fn hello_world() -> &'static str {
"Hello, World!"
}
#[cfg(feature = "rocket")]
#[cfg_attr(feature = "rocket", get("/launch"))]
async fn launch() -> &'static str {
"3... 2... 1... Liftoff!"
}
Danach haben Sie drei verschiedene http-Server-Frameworks zur Auswahl, die alle in derselben 'main.rs' enthalten sind. Um anzugeben, welches Framework Sie verwenden möchten, geben Sie es einfach in der Funktion "default" an. Versuchen Sie, nur die Standardfunktion zu ändern und zu speichern, um eine der Optionen "axum", "actix" oder "rocket" zu erhalten. Jede Änderung sollte dieses Framework neu starten und bedienen. Beachten Sie, dass das Shuttle-Team versucht hat, den Code zwischen den Frameworks so ähnlich wie möglich zu gestalten. Die Unterschiede in der Funktionsweise der Frameworks machen jedoch die verschiedenen Rückgabewert-Signaturen erforderlich. Beachten Sie auch, dass die IDE Ihrer Wahl wahrscheinlich den Code "verdunkelt", der nicht durch die Standardfunktion aktiviert ist.
Wir sind nun bereit, dies auf Shuttle zu übertragen. Erstellen Sie ein Konto bei"shuttle.rs". In der Web-Konsole finden Sie unter Ihrer benannten Profilseite den "API-Schlüssel", den Sie einmal in der Befehlszeile eingeben müssen.
Führen Sie just start an Ihrer Eingabeaufforderung aus, wiederum vom Stammverzeichnis des Projekts aus. Das neue Projekt wird auf den Shuttle-Servern erstellt, Ihr Projektcode wird gezippt und an POST gesendet, wo er in einen Container extrahiert, gebaut und ausgeführt wird.
Probieren Sie es aus unter der Adresse " https://your-project-name.shuttleapp.rs ".
Um Änderungen zu verteilen, einschließlich des Löschens aller vorherigen Zustände, die möglicherweise noch vorhanden sind, verwenden Sie just restart. Und wenn Sie möchten, dass dies automatisch nach jedem Speichern von Codeänderungen geschieht, verwenden Sie just watch restart. Probieren Sie es aus.
Da haben Sie es. Sie haben erfolgreich einen Rust-basierten Webanwendungsserver erstellt und eingesetzt, der nun für jedermann verfügbar ist!
Verfasst von
Paul LaCrosse
Unsere Ideen
Weitere Blogs
Contact



