Kadang-kadang saya harus mencoba lagi operasi beberapa kali sebelum menyerah. Kode saya seperti:
int retries = 3;
while(true) {
try {
DoSomething();
break; // success!
} catch {
if(--retries == 0) throw;
else Thread.Sleep(1000);
}
}
Saya ingin menulis ulang ini dalam fungsi coba lagi umum seperti:
TryThreeTimes(DoSomething);
Apakah mungkin dalam C #? Apa yang akan menjadi kode untuk TryThreeTimes()
metode ini?
Jawaban:
Pernyataan blanket catch yang hanya mencoba ulang panggilan yang sama bisa berbahaya jika digunakan sebagai mekanisme penanganan pengecualian umum. Karena itu, inilah pembungkus coba ulang berbasis lambda yang dapat Anda gunakan dengan metode apa pun. Saya memilih untuk memperhitungkan jumlah percobaan dan batas waktu coba ulang sebagai parameter untuk fleksibilitas yang lebih banyak:
Anda sekarang dapat menggunakan metode utilitas ini untuk melakukan coba lagi logika:
atau:
atau:
Atau Anda bahkan bisa membuat
async
kelebihan.sumber
Policy.Handle<DivideByZeroException>().WaitAndRetry(new[] { TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(3) });
Anda harus mencoba Polly . Ini adalah .NET library yang ditulis oleh saya yang memungkinkan pengembang untuk mengekspresikan kebijakan penanganan pengecualian sementara seperti Retry, Retry Forever, Wait and Retry atau Circuit Breaker dengan lancar.
Contoh
sumber
Ini mungkin ide yang buruk. Pertama, simbol dari pepatah "definisi kegilaan adalah melakukan hal yang sama dua kali dan mengharapkan hasil yang berbeda setiap kali". Kedua, pola pengkodean ini tidak bisa dikomposisi dengan baik. Sebagai contoh:
Misalkan lapisan perangkat keras jaringan Anda mengirim ulang sebuah paket tiga kali kegagalan, menunggu, katakanlah, satu detik di antara kegagalan.
Sekarang anggaplah lapisan perangkat lunak mengirim ulang pemberitahuan tentang kegagalan tiga kali pada kegagalan paket.
Sekarang anggaplah lapisan pemberitahuan mengaktifkan kembali pemberitahuan tiga kali pada kegagalan pengiriman pemberitahuan.
Sekarang anggaplah lapisan pelaporan kesalahan mengaktifkan kembali lapisan pemberitahuan tiga kali pada kegagalan pemberitahuan.
Dan sekarang misalkan server web mengaktifkan kembali pelaporan kesalahan tiga kali pada kegagalan kesalahan.
Dan sekarang anggap klien web mengirim ulang permintaan tiga kali setelah mendapatkan kesalahan dari server.
Sekarang anggaplah garis pada switch jaringan yang seharusnya untuk merutekan notifikasi ke administrator dicabut. Kapan pengguna klien web akhirnya mendapatkan pesan kesalahan mereka? Saya membuatnya sekitar dua belas menit kemudian.
Jangan sampai Anda menganggap ini hanya contoh konyol: kami telah melihat bug ini dalam kode pelanggan, meskipun jauh, jauh lebih buruk daripada yang saya jelaskan di sini. Dalam kode pelanggan tertentu, kesenjangan antara kondisi kesalahan terjadi dan akhirnya dilaporkan kepada pengguna adalah beberapa minggu karena begitu banyak lapisan secara otomatis mencoba kembali dengan menunggu. Bayangkan saja apa yang akan terjadi jika ada sepuluh percobaan bukan tiga .
Biasanya hal yang benar untuk dilakukan dengan kondisi kesalahan adalah melaporkannya segera dan membiarkan pengguna memutuskan apa yang harus dilakukan. Jika pengguna ingin membuat kebijakan coba ulang otomatis, biarkan mereka membuat kebijakan itu pada tingkat yang sesuai dalam abstraksi perangkat lunak.
sumber
Maka Anda akan menelepon:
... atau sebagai alternatif ...
Opsi yang lebih fleksibel:
Untuk digunakan sebagai:
Versi yang lebih modern dengan dukungan untuk async / menunggu:
Untuk digunakan sebagai:
sumber
--retryCount <= 0
karena ini akan berlangsung selamanya jika Anda ingin menonaktifkanretryCount
coba lagi dengan menetapkannya ke 0. Secara teknis istilah ini bukan nama yang benar-benar baik, karena itu tidak akan coba lagi jika Anda menetapkannya ke 1. baik ganti nama ketryCount
atau meletakkan - di belakang.Thread.Sleep
. Alternatifnya adalah menggunakan timer, atau lebih mungkin saat iniasync
untuk mencoba lagi, denganTask.Delay
.returns true
?Func<bool>
The Transient Kesalahan Penanganan Aplikasi Blokir menyediakan koleksi extensible strategi coba lagi termasuk:
Ini juga mencakup kumpulan strategi deteksi kesalahan untuk layanan berbasis cloud.
Untuk informasi lebih lanjut lihat bab Panduan Pengembang ini.
Tersedia melalui NuGet (mencari ' topaz ').
sumber
Mengizinkan fungsi dan coba lagi pesan
sumber
max retries
?Anda mungkin juga mempertimbangkan untuk menambahkan tipe pengecualian yang ingin Anda coba kembali. Misalnya apakah ini pengecualian waktu tunggu yang ingin Anda coba lagi? Pengecualian basis data?
Anda mungkin juga mencatat bahwa semua contoh lain memiliki masalah yang sama dengan pengujian untuk coba lagi == 0 dan coba lagi tak terhingga atau gagal untuk meningkatkan pengecualian ketika diberi nilai negatif. Juga Sleep (-1000) akan gagal di blok tangkap di atas. Bergantung pada seberapa 'konyol' Anda mengharapkan orang, tetapi pemrograman defensif tidak pernah sakit.
sumber
Saya penggemar metode rekursi dan ekstensi, jadi inilah dua sen saya:
sumber
Membangun pada karya sebelumnya, saya berpikir tentang meningkatkan logika coba lagi dengan tiga cara:
Menjadikannya
Action
metode ekstensiMetode ini kemudian dapat dipanggil seperti itu (metode anonim tentu saja dapat digunakan):
sumber
Sederhanakan dengan C # 6.0
sumber
Gunakan Polly
https://github.com/App-vNext/Polly-Samples
Ini adalah retry-generic yang saya gunakan dengan Polly
Gunakan seperti ini
sumber
Menerapkan jawaban LBushkin dengan cara terbaru:
dan untuk menggunakannya:
sedangkan fungsinya
[TaskFunction]
dapat berupaTask<T>
atau hanyaTask
.sumber
Saya akan menerapkan ini:
Saya tidak akan menggunakan pengecualian seperti yang mereka gunakan dalam contoh lain. Sepertinya saya bahwa jika kita mengharapkan kemungkinan bahwa suatu metode tidak akan berhasil, kegagalannya tidak terkecuali. Jadi metode yang saya panggil harus mengembalikan true jika berhasil, dan false jika gagal.
Mengapa ini
Func<bool, bool>
dan bukan hanyaFunc<bool>
? Jadi kalau saya mau metode untuk dapat melemparkan pengecualian pada kegagalan, saya punya cara untuk memberitahukan bahwa ini adalah percobaan terakhir.Jadi saya mungkin menggunakannya dengan kode seperti:
atau
Jika melewati parameter yang metode ini tidak gunakan terbukti canggung, itu sepele untuk menerapkan kelebihan
Retry
yang hanya membutuhkan waktuFunc<bool>
juga.sumber
StopIteration
pengecualian), dan ada alasannya.TryDo
Pola metode adalah lereng licin. Sebelum Anda menyadarinya, seluruh tumpukan panggilan Anda akan terdiri dariTryDo
metode. Pengecualian diciptakan untuk menghindari kekacauan seperti itu.Backoff eksponensial adalah strategi coba lagi yang baik daripada hanya mencoba x beberapa kali. Anda dapat menggunakan perpustakaan seperti Polly untuk mengimplementasikannya.
sumber
Bagi mereka yang ingin memiliki kedua opsi untuk mencoba kembali pada pengecualian apa pun atau secara eksplisit mengatur jenis pengecualian, gunakan ini:
sumber
Saya membutuhkan metode yang mendukung pembatalan, sementara saya melakukannya, saya menambahkan dukungan untuk mengembalikan kegagalan perantara.
Anda dapat menggunakan
Retry
fungsi seperti ini, coba lagi 3 kali dengan jeda 10 detik tetapi tanpa pembatalan.Atau, coba lagi selamanya setiap lima detik, kecuali dibatalkan.
Seperti yang bisa Anda tebak, dalam kode sumber saya, saya telah membebani
Retry
fungsi untuk mendukung berbagai jenis delgate yang ingin saya gunakan.sumber
Metode ini memungkinkan percobaan ulang pada jenis pengecualian tertentu (langsung melempar orang lain).
Contoh penggunaan:
sumber
Pembaruan setelah 6 tahun: sekarang saya menganggap bahwa pendekatan di bawah ini sangat buruk. Untuk membuat logika coba lagi kita harus mempertimbangkan untuk menggunakan perpustakaan seperti Polly.
async
Implementasi saya dari metode coba lagi:Poin-poin penting: Saya menggunakan
.ConfigureAwait(false);
danFunc<dynamic>
sebagai gantinyaFunc<T>
sumber
Task.Delay
dipanggil tanpa alasan.Atau bagaimana melakukannya dengan sedikit lebih rapi ....
Saya percaya melempar pengecualian pada umumnya harus dihindari sebagai mekanisme kecuali Anda melewati mereka di antara batas (seperti membangun perpustakaan yang dapat digunakan orang lain). Mengapa tidak mengembalikan
DoSomething()
perintah sajatrue
jika berhasil danfalse
sebaliknya?EDIT: Dan ini dapat dienkapsulasi di dalam fungsi seperti yang disarankan orang lain juga. Satu-satunya masalah adalah jika Anda tidak menulis
DoSomething()
fungsinya sendirisumber
Saya harus melewati beberapa parameter ke metode saya untuk mencoba lagi, dan memiliki nilai hasil; jadi saya butuh ekspresi .. Saya membangun kelas ini yang berfungsi (terinspirasi oleh LBushkin) Anda dapat menggunakannya seperti ini:
ps. solusi LBushkin melakukan satu lagi coba lagi = D
sumber
Saya akan menambahkan kode berikut ke jawaban yang diterima
Pada dasarnya kode di atas adalah membuat
Retry
kelas generik sehingga Anda dapat melewati jenis pengecualian yang ingin Anda tangkap untuk mencoba lagi.Sekarang gunakan itu hampir dengan cara yang sama tetapi menentukan jenis pengecualian
sumber
action
tidak melempar makaDo
kembali dengan demikianbreak
menjauh darifor
loop.Thread.Sleep
dipanggil tanpa alasan.Saya tahu jawaban ini sudah sangat lama tetapi saya hanya ingin mengomentari ini karena saya telah mengalami masalah saat menggunakan ini, lakukan, pernyataan apa pun dengan penghitung.
Selama bertahun-tahun, saya telah memutuskan pendekatan yang lebih baik. Yaitu dengan menggunakan semacam agregasi acara seperti ekstensi reaktif "Subjek" atau sejenisnya. Ketika percobaan gagal, Anda cukup mempublikasikan sebuah acara yang mengatakan percobaan gagal, dan minta fungsi agregator menjadwalkan ulang acara tersebut. Ini memungkinkan Anda mengontrol lebih jauh atas coba ulang tanpa mencemari panggilan itu sendiri dengan sekelompok loop coba lagi dan apa yang tidak. Anda juga tidak mengikat satu utas dengan seikat benang tidur.
sumber
Lakukan secara sederhana dalam C #, Java atau bahasa lain:
dan menggunakannya dalam kode Anda sangat sederhana:
atau Anda dapat menggunakannya dalam metode rekursif:
sumber
sumber
Request.BadRequest
?Retry helper: implementasi java umum yang berisi retries tipe yang dapat dikembalikan dan tidak berlaku.
Pemakaian:
sumber
Berikut adalah
async
/await
versi yang mengagregasi pengecualian dan mendukung pembatalan.sumber
sumber
throw;
-satunya cara loop infinite diakhiri, metode ini sebenarnya menerapkan "coba sampai gagal N kali" dan bukan yang diinginkan "coba hinggaN
waktu sampai berhasil". Anda memerlukanbreak;
ataureturn;
setelah panggilan keThingToTryDelegate();
sebaliknya panggilan akan dipanggil terus menerus jika tidak pernah gagal. Juga, ini tidak akan dikompilasi karena parameter pertamaTryNTimes
tidak memiliki nama. -1.Saya telah menulis sebuah kelas kecil berdasarkan jawaban yang diposting di sini. Semoga ini akan membantu seseorang: https://github.com/natenho/resiliency
sumber
Saya telah mengimplementasikan versi async dari jawaban yang diterima seperti itu - dan tampaknya berfungsi dengan baik - ada komentar?
Dan, sebut saja seperti ini:
sumber
Thread.Sleep
? Memblokir utas meniadakan manfaat asinkron. Saya juga cukup yakin bahwaTask DoAsync()
versi tersebut harus menerima argumen tipeFunc<Task>
.