Kebijakan Coba Lagi di Library Klien C++

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

Library klien mengeluarkan RPC (Remote Procedure Call) atas nama Anda. RPC ini dapat gagal karena error sementara. Server dimulai ulang, load balancer menutup koneksi yang kelebihan beban atau tidak aktif, dan batas kecepatan dapat berlaku, dan ini hanyalah beberapa contoh kegagalan sementara.

Library dapat menampilkan error ini ke aplikasi. Namun, banyak error ini mudah ditangani di library, yang membuat kode aplikasi lebih sederhana.

Error yang Dapat Dicoba Ulang dan Operasi yang Dapat Dicoba Ulang

Hanya error sementara yang dapat dicoba lagi. Misalnya, kUnavailable menunjukkan bahwa klien tidak dapat terhubung, atau kehilangan koneksinya ke layanan saat permintaan sedang berlangsung. Hal ini hampir selalu merupakan kondisi sementara, meskipun mungkin memerlukan waktu yang lama untuk pulih. Error ini selalu dapat dicoba lagi (dengan asumsi operasi itu sendiri aman untuk dicoba lagi). Sebaliknya, error kPermissionDenied memerlukan intervensi tambahan (biasanya oleh manusia) untuk diselesaikan. Error semacam ini 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, terlepas dari sifat kesalahannya. Hal ini mencakup operasi apa pun yang membuat perubahan inkremental. Misalnya, tidak aman untuk mencoba lagi operasi guna menghapus "versi terbaru X" jika 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 yang berbeda untuk mengontrol loop percobaan ulang:

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

Kebijakan Idempotensi Default

Secara umum, operasi bersifat idempoten jika berhasil memanggil fungsi beberapa kali membuat sistem berada dalam status yang sama seperti berhasil memanggil fungsi sekali. Hanya operasi idempoten yang aman untuk dicoba ulang. 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 diterapkan 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 idempoten default kapan saja agar lebih sesuai dengan kebutuhan Anda.

Beberapa operasi hanya bersifat idempoten jika menyertakan prasyarat. 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 menganggap peningkatan ini sebagai perbaikan bug, dan oleh karena itu tidak akan merusak meskipun mengubah perilaku library klien.

Perhatikan bahwa meskipun operasi dapat dicoba lagi dengan aman, hal ini tidak berarti operasi menghasilkan hasil yang sama pada percobaan kedua dibandingkan dengan percobaan pertama yang berhasil. Misalnya, membuat resource yang diidentifikasi secara unik mungkin aman untuk dicoba lagi, karena upaya kedua dan berikutnya gagal dan membuat sistem dalam status yang sama. Namun, klien mungkin menerima error "sudah ada" pada upaya percobaan ulang.

Kebijakan Coba Lagi Default

Dengan mengikuti pedoman yang diuraikan dalam aip/194, sebagian besar library klien C++ hanya mencoba lagi error gRPC UNAVAILABLE. Nilai ini dipetakan ke StatusCode::kUnavailable. Kebijakan defaultnya adalah mencoba ulang permintaan selama 30 menit.

Perhatikan bahwa error kUnavailable tidak menunjukkan bahwa server gagal menerima permintaan. Kode error ini digunakan saat 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 Anda dapat menentukan apakah permintaan berhasil diterima, Anda dapat menyelesaikan masalah Dua Jenderal, hasil ketidakmungkinan yang terkenal dalam 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 yang dipangkas, dengan jitter. Backoff awal adalah 1 detik, backoff maksimum adalah 5 menit, dan backoff akan berlipat ganda setelah setiap percobaan ulang.

Mengubah kebijakan percobaan ulang dan backoff default

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

Misalnya, contoh ini menunjukkan cara mengubah kebijakan coba lagi dan jeda 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