Mit lang andauernden Vorgängen arbeiten

Manchmal stellt eine API eine Methode bereit, deren Ausführung viel Zeit in Anspruch nimmt. Anstatt zu blockieren, während die Aufgabe ausgeführt wird, können Sie ein Promise zurückgeben und den Nutzer den Status prüfen lassen.

Die Google Cloud Clientbibliotheken für Rust bieten Hilfsfunktionen für die Arbeit mit diesen Vorgängen mit langer Ausführungszeit. In dieser Anleitung erfahren Sie, wie Sie Vorgänge mit langer Ausführungszeit starten und auf ihren Abschluss warten.

Vorbereitung

In dieser Anleitung wird der Cloud Storage-Dienst verwendet, um die Code-Snippets konkret zu halten. Diese Konzepte gelten für alle anderen Dienste, die Vorgänge mit langer Ausführungszeit verwenden.

Bevor Sie dieser Anleitung folgen, sollten Sie Folgendes tun:

Eine vollständige Installationsanleitung für die Rust-Bibliotheken finden Sie unter Entwicklungsumgebung einrichten.

Abhängigkeiten

Deklarieren Sie Google Cloud Abhängigkeiten in Ihrer Cargo.toml Datei:

cargo add google-cloud-storage google-cloud-lro google-cloud-longrunning

Außerdem benötigen Sie mehrere tokio-Funktionen:

cargo add tokio --features full,macros

Vorgang mit langer Ausführungszeit starten

In diesem Beispiel wird „Ordner umbenennen“ verwendet. Dieser Vorgang kann bei großen Ordnern lange dauern, ist aber bei kleineren Ordnern relativ schnell.

Um einen Vorgang mit langer Ausführungszeit zu starten, müssen Sie einen Client initialisieren und den RPC ausführen.

Fügen Sie zuerst use-Deklarationen hinzu, um lange Paketnamen zu vermeiden:

use anyhow::anyhow;
use google_cloud_longrunning as longrunning;
use google_cloud_storage::client::StorageControl;

Erstellen Sie als Nächstes den Client:

let client = StorageControl::builder().build().await?;

In den Rust-Clientbibliotheken wird jede Anfrage durch eine Methode dargestellt, die einen Anforderungs-Builder zurückgibt. Rufen Sie die Methode auf dem Client auf, um den Anforderungs-Builder zu erstellen:

let operation = client
    .rename_folder()
    .set_name(format!("projects/_/buckets/{bucket}/folders/{folder}"))
    .set_destination_folder_id(dest)

Die Beispielfunktionen akzeptieren die Bucket- und Ordnernamen als Argumente:

pub async fn manual(bucket: &str, folder: &str, dest: &str) -> anyhow::Result<()> {

Stellen Sie die Anfrage und warten Sie, bis ein Vorgang zurückgegeben wird. Dieser Operation-Vorgang dient als Promise für das Ergebnis der Anfrage mit langer Ausführungszeit:

    let operation =
        // ...
        .send()
        .await?;

Diese Anfrage startet den Vorgang im Hintergrund. Warten Sie, bis der Vorgang abgeschlossen ist, um festzustellen, ob er erfolgreich war.

Vorgang mit langer Ausführungszeit automatisch abfragen

Wenn Sie die automatische Abfrage konfigurieren möchten, starten Sie einen Vorgang mit langer Ausführungszeit mit einem Poller anstelle von .send().wait:

.poller()
.until_done()
.await?;

Führen Sie zuerst das Poller-Trait mit einer use-Deklaration ein:

use google_cloud_lro::Poller;

Initialisieren Sie dann den Client und bereiten Sie die Anfrage wie zuvor vor:

let response = client
    .rename_folder()
    .set_name(format!("projects/_/buckets/{bucket}/folders/{folder}"))
    .set_destination_folder_id(dest)

Fragen Sie ab, bis der Vorgang abgeschlossen ist, und geben Sie das Ergebnis aus:

    .poller()
    .until_done()
    .await?;

println!("LRO completed, response={response:?}");

Vorgang mit langer Ausführungszeit mit Zwischenergebnissen abfragen

Die Methode .until_done() ist praktisch, lässt aber teilweise Fortschrittsberichte von Vorgängen mit langer Ausführungszeit aus. Wenn Ihre Anwendung diese Informationen benötigt, verwenden Sie den Poller direkt:

    let mut poller = client
        .rename_folder()
        /* more stuff */
        .poller();

Verwenden Sie den Poller dann in einer Schleife:

while let Some(p) = poller.poll().await {
    match p {
        PollingResult::Completed(r) => {
            println!("LRO completed, response={r:?}");
        }
        PollingResult::InProgress(m) => {
            println!("LRO in progress, metadata={m:?}");
        }
        PollingResult::PollingError(e) => {
            println!("Transient error polling the LRO: {e}");
        }
    }
    tokio::time::sleep(std::time::Duration::from_millis(500)).await;
}

Diese Schleife wartet explizit, bevor sie noch einmal abfragt. Der Abfragezeitraum hängt vom jeweiligen Vorgang und seiner Nutzlast ab. Weitere Informationen finden Sie in der Dienstdokumentation oder experimentieren Sie mit Ihren Daten, um einen geeigneten Wert zu ermitteln.

Vorgang mit langer Ausführungszeit manuell abfragen

Wir empfehlen zwar automatisierte Abfrageansätze, aber es ist auch möglich, einen Vorgang mit langer Ausführungszeit manuell abzufragen. Weitere Informationen finden Sie in der Referenzdokumentation zur Operation-Nachricht.

Starten Sie den Vorgang mit langer Ausführungszeit mit dem Client:

    let mut operation = client
        .rename_folder()
        /* more stuff */
        .send()
        .await?;

Starten Sie eine Abfrageschleife und prüfen Sie mit dem Feld done, ob der Vorgang abgeschlossen ist:

let response: anyhow::Result<Folder> = loop {
    if operation.done {

Wenn der Vorgang abgeschlossen ist, enthält er in der Regel ein Ergebnis. Das Ergebnis-Feld ist optional, da der Dienst done als „true“ ohne Ergebnis zurückgeben kann. Ein erfolgreicher Löschvorgang hat beispielsweise keinen Rückgabewert. In diesem Beispiel gibt der Cloud Storage-Dienst immer einen Wert zurück:

match &operation.result {
    None => {
        break Err(anyhow!("missing result for finished operation"));
    }

Ein gestarteter Vorgang wird möglicherweise nicht erfolgreich abgeschlossen. Das Ergebnis kann ein Fehler oder eine gültige Antwort sein. Prüfen Sie zuerst auf Fehler:

Some(r) => {
    break match r {
        longrunning::model::operation::Result::Error(s) => {
            Err(anyhow!("operation completed with error {s:?}"))
        }

Der Fehlertyp ist ein Status Nachrichtentyp. Dieser implementiert NICHT das Standard-Trait Error. Konvertieren Sie ihn manuell mit Error::service in einen gültigen Fehler.

Wenn das Ergebnis erfolgreich ist, extrahieren Sie den Antworttyp. Diesen Typ finden Sie in der Dokumentation zur LRO-Methode oder in der API-Dokumentation des Dienstes:

longrunning::model::operation::Result::Response(any) => {
    let response = any.to_msg::<Folder>()?;
    Ok(response)
}

Beachten Sie, dass die Extraktion des Werts fehlschlagen kann, wenn der Typ nicht mit dem übereinstimmt, was der Dienst gesendet hat.

Google Cloud Typen können in Zukunft Felder und Zweige hinzufügen. Die Google Cloud Clientbibliotheken für Rust kennzeichnen alle Structs und Enums als #[non_exhaustive]. Behandeln Sie diesen Fall:

_ => Err(anyhow!("unexpected result branch {r:?}")),

Wenn der Vorgang noch nicht abgeschlossen ist, kann er Metadaten enthalten. Einige Dienste enthalten erste Informationen zur Anfrage, während andere Dienste teilweise Fortschrittsberichte enthalten. Sie können diese Metadaten extrahieren und melden:

if let Some(any) = &operation.metadata {
    let metadata = any.to_msg::<RenameFolderMetadata>()?;
    println!("LRO in progress, metadata={metadata:?}");
}

Warten Sie, bevor Sie noch einmal abfragen. Sie können den Abfragezeitraum mit abgeschnittenem exponentiellem Backoff anpassen. In diesem Beispiel wird alle 500 ms abgefragt:

tokio::time::sleep(std::time::Duration::from_millis(500)).await;

Fragen Sie den Vorgangsstatus ab:

if let Ok(attempt) = client
    .get_operation()
    .set_name(&operation.name)
    .send()
    .await
{
    operation = attempt;
}

Der Einfachheit halber werden in diesem Beispiel alle Fehler ignoriert. In Ihrer Anwendung können Sie eine Teilmenge von Fehlern als nicht wiederherstellbar behandeln und die Anzahl der Abfrageversuche begrenzen.

Nächste Schritte

  • Sehen Sie sich den Quellcode für die Beispiele auf GitHub an.