Kebijakan Percobaan Ulang di Library Klien C++

Halaman ini menjelaskan model percobaan ulang yang digunakan oleh library klien C++.

Library klien mengeluarkan RPC (Remote Procedure Calls) atas nama Anda. RPC ini bisa gagal karena error sementara. Server memulai ulang, load balancer menutup koneksi yang kelebihan beban atau koneksi nonaktif, dan batas kapasitas dapat berlaku, dan ini hanyalah beberapa contoh kegagalan sementara.

Library tersebut dapat menampilkan error ini ke aplikasi. Namun, banyak error ini mudah ditangani di library, sehingga kode aplikasi menjadi lebih sederhana.

Error yang Dapat Dicoba Lagi dan Operasi yang Dapat Dicoba Lagi

Hanya error sementara yang dapat dicoba lagi. Misalnya, kUnavailable menunjukkan bahwa klien tidak dapat terhubung, atau kehilangan koneksi ke layanan saat permintaan sedang berlangsung. Kondisi ini hampir selalu bersifat sementara, meskipun proses pemulihannya mungkin memerlukan waktu lama. Error ini selalu dapat dicoba lagi (dengan asumsi operasi itu sendiri aman untuk dicoba lagi). Dalam kontrak, error kPermissionDenied memerlukan intervensi tambahan (biasanya oleh manusia) untuk diselesaikan. Error tersebut tidak dianggap "sementara", atau setidaknya tidak sementara dalam skala waktu yang dipertimbangkan oleh loop percobaan ulang di library klien.

Demikian pula, beberapa operasi tidak aman untuk dicoba lagi, apa pun sifat error-nya. Hal ini termasuk operasi apa pun yang membuat perubahan inkremental. Misalnya, tidak aman untuk mencoba kembali operasi untuk menghapus "versi terbaru X" jika mungkin ada beberapa versi resource bernama "X". Hal ini karena pemanggil mungkin bermaksud menghapus satu versi, dan mencoba lagi permintaan tersebut dapat mengakibatkan penghapusan semua versi.

Mengonfigurasi loop percobaan ulang

Library klien menerima tiga parameter konfigurasi berbeda untuk mengontrol loop percobaan ulang:

  • *IdempotencyPolicy menentukan apakah permintaan tertentu bersifat idempoten. Hanya permintaan tersebut yang akan dicoba lagi.
  • *RetryPolicy menentukan (a) apakah error harus dianggap sebagai kegagalan sementara, dan (b) berapa lama (atau berapa kali) library klien mencoba ulang permintaan.
  • *BackoffPolicy menentukan berapa lama library klien menunggu sebelum mengirimkan ulang permintaan.

Kebijakan Idempotensi Default

Secara umum, operasi akan dianggap idempoten jika berhasil memanggil fungsi beberapa kali meninggalkan sistem dalam status yang sama seperti berhasil memanggil fungsi satu kali. Hanya operasi idempoten yang aman untuk dicoba lagi. Contoh operasi idempoten mencakup, tetapi tidak terbatas pada, semua operasi hanya baca, dan operasi yang hanya dapat berhasil satu kali.

Secara default, library klien hanya memperlakukan RPC yang diimplementasikan melalui kata kerja GET atau PUT sebagai idempoten. Hal ini mungkin terlalu konservatif, di beberapa layanan, bahkan beberapa permintaan POST bersifat idempoten. Anda dapat mengganti kebijakan idempotensi default kapan saja agar lebih sesuai dengan kebutuhan Anda.

Beberapa operasi hanya idempoten jika menyertakan prakondisi. Misalnya, "hapus versi terbaru jika versi terbaru adalah Y" bersifat idempoten, karena hanya dapat berhasil satu kali.

Dari waktu ke waktu, library klien menerima peningkatan untuk memperlakukan lebih banyak operasi sebagai idempoten. Kami mempertimbangkan peningkatan ini sebagai perbaikan bug, sehingga tidak menyebabkan gangguan meskipun perubahan tersebut mengubah perilaku library klien.

Perlu diperhatikan bahwa meskipun mungkin aman untuk mencoba ulang operasi, bukan berarti operasi tersebut memberikan hasil yang sama pada upaya kedua vs. upaya pertama yang berhasil. Misalnya, membuat resource yang diidentifikasi secara unik mungkin aman untuk dicoba lagi, karena upaya kedua dan berturut-turut gagal dan meninggalkan sistem dalam status yang sama. Namun, klien mungkin menerima error "sudah ada" saat mencoba lagi.

Kebijakan Percobaan Ulang Default

Sesuai dengan panduan yang diuraikan dalam aip/194, sebagian besar library klien C++ hanya mencoba ulang UNAVAILABLE gRPC-error. Nilai ini dipetakan ke StatusCode::kUnavailable. Kebijakan defaultnya adalah permintaan percobaan ulang selama 30 menit.

Perlu diperhatikan bahwa error kUnavailable tidak menunjukkan bahwa server gagal menerima permintaan. Kode error ini digunakan jika permintaan tidak dapat dikirim, tetapi juga digunakan jika permintaan berhasil dikirim, diterima oleh layanan, dan koneksi terputus sebelum respons diterima oleh klien. Selain itu, jika dapat menentukan apakah permintaan berhasil diterima, Anda dapat menyelesaikan masalah Dua Umum, yaitu masalah umum yang menyebabkan sistem terdistribusi.

Oleh karena itu, tidak aman untuk mencoba kembali semua operasi yang gagal dengan kUnavailable. Idempotensi operasi juga penting.

Kebijakan Backoff Default

Secara default, sebagian besar library menggunakan strategi backoff eksponensial terpotong, dengan jitter. Backoff awal adalah 1 detik, backoff maksimum adalah 5 menit, dan backoff berlipat ganda setelah setiap percobaan ulang.

Mengubah kebijakan percobaan ulang dan backoff default

Setiap library menentukan struct *Option untuk mengonfigurasi kebijakan ini. Anda dapat memberikan opsi ini saat membuat class *Client, atau bahkan pada setiap permintaan.

Misalnya, contoh berikut menunjukkan cara mengubah kebijakan percobaan ulang dan backoff untuk klien Cloud Pub/Sub:

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";
}

Lihat dokumentasi setiap library untuk menemukan nama dan contoh spesifik untuk library tersebut.

Langkah Berikutnya