Mit lang andauernden Vorgängen arbeiten

Gelegentlich kann es vorkommen, dass eine API eine Methode bereitstellt, deren Ausführung viel Zeit in Anspruch nimmt. Anstatt die Ausführung 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 Client Libraries for Rust bieten Hilfsprogramme für die Arbeit mit diesen zeitaufwendigen Vorgängen (Long-Running Operations, LROs). In diesem Leitfaden erfahren Sie, wie Sie LROs starten und auf deren Abschluss warten.

Vorbereitung

In diesem Leitfaden wird der Cloud Storage-Dienst verwendet, um die Code-Snippets konkret zu halten. Diese Konzepte gelten für alle anderen Dienste, die LROs verwenden.

Bevor Sie diese Anleitung durcharbeiten, sollten Sie Folgendes tun:

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

Abhängigkeiten

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

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. Bei großen Ordnern kann dieser Vorgang lange dauern, bei kleineren Ordnern ist er 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 Request Builder zurückgibt. Rufen Sie die Methode für den Client auf, um den Request 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. Dieses Operation fungiert als Promise für das Ergebnis der Anfrage mit langer Ausführungszeit:

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

Mit dieser Anfrage wird der Vorgang im Hintergrund gestartet. Warten Sie, bis der Vorgang abgeschlossen ist, um festzustellen, ob er erfolgreich war.

Vorgang mit langer Ausführungszeit automatisch abfragen

Wenn Sie das automatische Abrufen konfigurieren möchten, starten Sie einen Vorgang mit langer Ausführungszeit mit 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 den Vorgang ab, bis er 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 zwar praktisch, lässt aber Teilfortschrittsberichte 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 dann den Poller 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;
}

In dieser Schleife wird explizit gewartet, bevor wieder abgefragt wird. Der Abfragezeitraum hängt vom jeweiligen Vorgang und seiner Nutzlast ab. In der Dienstdokumentation finden Sie weitere Informationen. Sie können auch mit Ihren Daten experimentieren, um einen geeigneten Wert zu ermitteln.

Vorgang mit langer Ausführungszeit manuell abfragen

Wir empfehlen zwar automatisierte Abfrageverfahren, es ist aber auch möglich, einen Vorgang mit langer Ausführungszeit manuell abzufragen. Weitere Informationen finden Sie in der Referenzdokumentation zu Operation-Nachrichten.

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

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

Starten Sie eine Polling-Schleife und prüfen Sie anhand des Felds 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 Ergebnisfeld ist optional, da der Dienst done als „true“ zurückgeben kann, ohne ein Ergebnis zu liefern. 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, ob Fehler vorliegen:

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

Der Fehlertyp ist eine Status-Nachricht. Das Standard-Trait Error wird NICHT implementiert. Konvertieren Sie sie manuell mit Error::service in einen gültigen Fehler.

Wenn das Ergebnis erfolgreich ist, extrahieren Sie den Antworttyp. Sie finden diesen Typ 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)
}

Das Extrahieren des Werts kann fehlschlagen, wenn der Typ nicht dem entspricht, was der Dienst gesendet hat.

Google Cloud -Typen können in Zukunft Felder und Zweige hinzufügen. In den Google Cloud-Clientbibliotheken für Rust werden alle Strukturen und Enums als #[non_exhaustive] gekennzeichnet. So gehen Sie in diesem Fall vor:

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

Wenn der Vorgang noch nicht abgeschlossen ist, enthält er möglicherweise Metadaten. Einige Dienste enthalten erste Informationen zur Anfrage, andere teilweise Fortschrittsberichte. Sie können diese Metadaten extrahieren und in Berichten verwenden:

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. Erwägen Sie, den Abfragezeitraum mit einem abgeschnittenen exponentiellen Backoff anzupassen. 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 behebbar behandeln und die Anzahl der Polling-Versuche begrenzen.

Nächste Schritte

  • Den Quellcode für die Beispiele finden Sie auf GitHub.