Wiederholungsrichtlinien in den C++-Clientbibliotheken

Auf dieser Seite wird das Wiederholungsmodell beschrieben, das von den C++-Clientbibliotheken verwendet wird.

Die Clientbibliotheken senden in Ihrem Namen RPCs (Remote Procedure Calls). Diese RPCs können aufgrund vorübergehender Fehler fehlschlagen. Server werden neu gestartet, Load-Balancer schließen überlastete oder inaktive Verbindungen und Ratenlimits können in Kraft treten. Dies sind nur einige Beispiele für vorübergehende Fehler.

Die Bibliotheken können diese Fehler an die Anwendung zurückgeben. Viele dieser Fehler lassen sich jedoch einfach in der Bibliothek beheben, was den Anwendungscode vereinfacht.

Wiederholbare Fehler und wiederholbare Vorgänge

Nur vorübergehende Fehler können wiederholt werden. kUnavailable gibt beispielsweise an, dass der Client keine Verbindung herstellen konnte oder die Verbindung zu einem Dienst verloren hat, während eine Anfrage ausgeführt wurde. Dies ist fast immer ein vorübergehender Zustand, die Wiederherstellung kann jedoch lange dauern. Diese Fehler können immer wiederholt werden (vorausgesetzt, der Vorgang selbst kann sicher wiederholt werden). Im Gegensatz dazu erfordern kPermissionDenied-Fehler zusätzliche Maßnahmen (in der Regel durch einen Menschen), um behoben zu werden. Solche Fehler werden nicht als „vorübergehend“ betrachtet oder zumindest nicht in den Zeiträumen, die von den Wiederholungsschleifen in der Clientbibliothek berücksichtigt werden.

Ebenso ist es bei einigen Vorgängen nicht sicher, sie zu wiederholen, unabhängig von der Art des Fehlers. Dazu gehören alle Vorgänge, die inkrementelle Änderungen vornehmen. Es ist beispielsweise nicht sicher, einen Vorgang zum Entfernen der „neuesten Version von X“ zu wiederholen, wenn es mehrere Versionen einer Ressource mit dem Namen „X“ gibt. Das liegt daran, dass der Aufrufer wahrscheinlich nur eine einzelne Version entfernen wollte. Wenn eine solche Anfrage wiederholt wird, können alle Versionen entfernt werden.

Wiederholungsschleifen konfigurieren

Die Clientbibliotheken akzeptieren drei verschiedene Konfigurationsparameter, um die Wiederholungsschleifen zu steuern:

  • Die *IdempotencyPolicy bestimmt, ob eine bestimmte Anfrage idempotent ist. Nur solche Anfragen werden wiederholt.
  • Die *RetryPolicy bestimmt (a), ob ein Fehler als vorübergehender Fehler betrachtet werden soll, und (b), wie lange (oder wie oft) die Clientbibliothek eine Anfrage wiederholt.
  • Die *BackoffPolicy bestimmt, wie lange die Clientbibliothek wartet, bevor sie die Anfrage noch einmal sendet.

Standardmäßige Idempotenzrichtlinie

Im Allgemeinen ist ein Vorgang idempotent, wenn das System nach mehreren erfolgreichen Aufrufen der Funktion denselben Zustand hat wie nach einem erfolgreichen Aufruf der Funktion. Nur idempotente Vorgänge können sicher wiederholt werden. Beispiele für idempotente Vorgänge sind alle schreibgeschützten Vorgänge und Vorgänge, die nur einmal erfolgreich ausgeführt werden können.

Standardmäßig behandelt die Clientbibliothek nur RPCs, die über die Verben GET oder PUT implementiert werden, als idempotent. Das ist möglicherweise zu konservativ. Bei einigen Diensten sind sogar einige POST-Anfragen idempotent. Sie können die Standardrichtlinie für die Idempotenz jederzeit überschreiben, um sie besser an Ihre Anforderungen anzupassen.

Einige Vorgänge sind nur idempotent, wenn sie Vorbedingungen enthalten. Beispielsweise ist „Entferne die neueste Version, wenn die neueste Version Y ist“ idempotent, da sie nur einmal erfolgreich ausgeführt werden kann.

Gelegentlich werden die Clientbibliotheken verbessert, um mehr Vorgänge als idempotent zu behandeln. Wir betrachten diese Verbesserungen als Fehlerkorrekturen und daher als nicht abwärtskompatibel, auch wenn sie das Verhalten der Clientbibliothek ändern.

Auch wenn es sicher sein kann, einen Vorgang zu wiederholen, bedeutet das nicht, dass der Vorgang beim zweiten Versuch dasselbe Ergebnis wie beim ersten erfolgreichen Versuch liefert. Beispielsweise kann es sicher sein, die Erstellung einer eindeutig identifizierten Ressource zu wiederholen, da der zweite und die nachfolgenden Versuche fehlschlagen und das System im selben Zustand bleibt. Der Client erhält jedoch möglicherweise bei den Wiederholungsversuchen einen Fehler vom Typ „Bereits vorhanden“.

Standardmäßige Wiederholungsrichtlinie

Gemäß den Richtlinien in aip/194 werden die meisten C++-Clientbibliotheken nur UNAVAILABLE gRPC-Fehler wiederholt. Diese werden StatusCode::kUnavailable zugeordnet. Die Standardrichtlinie sieht vor, Anfragen 30 Minuten lang zu wiederholen.

kUnavailable-Fehler geben nicht an, dass der Server die Anfrage nicht erhalten hat. Dieser Fehlercode wird verwendet, wenn die Anfrage nicht gesendet werden kann, aber auch, wenn die Anfrage erfolgreich gesendet und vom Dienst empfangen wurde und die Verbindung verloren geht, bevor die Antwort vom Client empfangen wird. Wenn Sie außerdem feststellen könnten, ob die Anfrage erfolgreich empfangen wurde, könnten Sie das Problem der zwei Generälelösen, ein bekanntes Unmöglichkeitsergebnis in verteilten Systemen.

Daher ist es nicht sicher, alle Vorgänge zu wiederholen, die mit kUnavailable fehlschlagen. Auch die Idempotenz des Vorgangs spielt eine Rolle.

Standardmäßige Backoff-Richtlinie

Standardmäßig verwenden die meisten Bibliotheken eine verkürzte exponentielle Backoff-Strategie mit Jitter. Der anfängliche Backoff beträgt 1 Sekunde, der maximale Backoff 5 Minuten und der Backoff verdoppelt sich nach jedem Wiederholungsversuch.

Standardmäßige Wiederholungs- und Backoff-Richtlinien ändern

Jede Bibliothek definiert eine *Option-Struktur, um diese Richtlinien zu konfigurieren. Sie können diese Optionen beim Erstellen der *Client-Klasse oder sogar bei jeder Anfrage angeben.

Hier sehen Sie ein Beispiel, wie Sie die Wiederholungs- und Backoff-Richtlinien für einen Cloud Pub/Sub-Client ändern:

namespace pubsub = ::google::cloud::pubsub;
using ::google::cloud::future;
using ::google::cloud::Options;
using ::google::cloud::StatusOr;
[](std::string project_id, std::string topic_id) {
  auto topic = pubsub::Topic(std::move(project_id), std::move(topic_id));
  // By default a publisher will retry for 60 seconds, with an initial backoff
  // of 100ms, a maximum backoff of 60 seconds, and the backoff will grow by
  // 30% after each attempt. This changes those defaults.
  auto publisher = pubsub::Publisher(pubsub::MakePublisherConnection(
      std::move(topic),
      Options{}
          .set<pubsub::RetryPolicyOption>(
              pubsub::LimitedTimeRetryPolicy(
                  /*maximum_duration=*/std::chrono::minutes(10))
                  .clone())
          .set<pubsub::BackoffPolicyOption>(
              pubsub::ExponentialBackoffPolicy(
                  /*initial_delay=*/std::chrono::milliseconds(200),
                  /*maximum_delay=*/std::chrono::seconds(45),
                  /*scaling=*/2.0)
                  .clone())));

  std::vector<future<bool>> done;
  for (char const* data : {"1", "2", "3", "go!"}) {
    done.push_back(
        publisher.Publish(pubsub::MessageBuilder().SetData(data).Build())
            .then([](future<StatusOr<std::string>> f) {
              return f.get().ok();
            }));
  }
  publisher.Flush();
  int count = 0;
  for (auto& f : done) {
    if (f.get()) ++count;
  }
  std::cout << count << " messages sent successfully\n";
}

In der Dokumentation der einzelnen Bibliotheken finden Sie die spezifischen Namen und Beispiele für diese Bibliothek.

Nächste Schritte