Dalam kondisi apa (jika ada) adalah praktik yang baik untuk menanyakan dua server dan hanya mengonsumsi respons tercepat?


12

Saya telah bertanya apa yang sekarang menjadi pertanyaan Komunitas-dihapus pada SO tentang mengapa seseorang menggunakan javascript Promise.race, dan pengguna rep tinggi berkomentar:

Jika Anda memiliki dua layanan yang menghitung beberapa nilai, Anda dapat meminta keduanya secara paralel dan menggunakan nilai mana yang pernah dikembalikan terlebih dahulu, daripada meminta satu, menunggu kegagalan, dan kemudian meminta yang kedua.

Saya sudah googled tentang redundansi dan use case ini secara umum tetapi saya tidak dapat menemukan apa pun dan, dari POV saya, tidak pernah merupakan ide yang baik untuk hanya menambahkan beban kerja ke server / layanan jika Anda tidak akan menggunakan respons.


Contoh mainan: daripada selalu menggunakan quicksort, Anda menyalin data, mengirimkannya ke quicksort, dan mergesort, dan heapsort, ... dll. Anda tidak perlu memeriksa input untuk melihat apakah ini merupakan kasus yang patologis. untuk semua dari mereka, karena itu tidak akan menjadi kasus patologis untuk mereka semua
Caleth

Makalah Dekan dan Barroso The Tail at Scale menyebut variasi pada pendekatan ini "Permintaan lindung nilai". Ini juga membahas pro dan kontra dari beberapa pendekatan terkait untuk mengendalikan variabilitas ekor panjang dalam tingkat kesalahan dan latensi.
Daniel Pryden

"Permintaan server" kedua bisa jadi palsu. Itu hanya ingin selama 5 detik dan kemudian mengembalikan respons placeholder. Itu membuat Anda kehabisan waktu atas permintaan sebenarnya.
user253751

Pesan Lyft, lalu pesan Uber. Ambil yang mana yang lebih dulu.
user2023861

@ user2023861 dalam analogi ini, sementara satu pengemudi tanpa tujuan mengemudi menuju lokasi Anda, ia bisa saja mengambil permintaan lain
Adelin

Jawaban:


11

Saya berpendapat bahwa ini lebih merupakan pertanyaan ekonomi. Namun, itu adalah panggilan penilaian yang harus mampu dilakukan oleh insinyur. Karenanya, saya menjawab.

Saya membagi jawaban saya menjadi empat bagian:

  • Manajemen risiko
  • Strategi
  • Biaya
  • Intuisi

Manajemen risiko

Jadi, terkadang klien Anda gagal mendapatkan respons dari server. Saya akan menganggap ini bukan karena kesalahan program (jika tidak solusinya adalah memperbaikinya, jadi lakukanlah itu). Sebaliknya, itu pasti karena situasi kebetulan di luar kendali Anda ...

Namun tidak di luar pengetahuan Anda. Kamu harus tahu:

  • Seberapa sering hal itu terjadi.
  • Apa dampaknya.

Misalnya, jika gagal dan coba ulang hanya terjadi sekitar 2% dari waktu, mungkin tidak layak untuk mengatasinya. Jika itu terjadi sekitar 80% dari waktu, yah ... tergantung ...

Berapa lama klien harus menunggu? Dan bagaimana hal itu diterjemahkan ke dalam biaya ... Anda tahu, Anda memiliki sedikit keterlambatan dalam aplikasi reguler, mungkin itu bukan masalah besar. Jika ini penting, dan Anda memiliki aplikasi waktu nyata atau permainan video online, ini akan membuat pengguna menjauh, dan Anda mungkin lebih baik berinvestasi di server yang lebih banyak atau lebih baik. Jika tidak, Anda mungkin dapat menaruh pesan "memuat" atau "menunggu server". Kecuali, penundaannya sangat besar (dalam urutan puluhan detik), maka itu bisa terlalu banyak bahkan untuk aplikasi reguler.


Strategi

Seperti yang saya katakan di atas, ada lebih dari satu cara untuk mengatasi masalah ini. Saya akan menganggap Anda sudah memiliki penerapan coba-gagal-coba loop. Jadi, mari kita lihat ...

  • Letakkan pesan pemuatan. Itu murah, membantu retensi pengguna.
  • Kueri secara paralel. Bisa lebih cepat, masih bisa gagal. Akan memerlukan server yang berlebihan (bisa mahal), akan membuang waktu server dan lalu lintas jaringan.
  • Permintaan secara paralel untuk membuat server yang lebih cepat dan menggunakannya sejak saat itu. Bisa lebih cepat, masih bisa gagal. Akan membutuhkan server yang berlebihan (bisa mahal), tidak akan menyia-nyiakan waktu server dan lalu lintas jaringan.

Sekarang, perhatikan saya katakan ini masih bisa gagal. Jika kami menganggap bahwa permintaan ke server memiliki peluang kegagalan 80%, maka permintaan paralel ke dua server memiliki peluang kegagalan 64%. Jadi, Anda mungkin masih harus mencoba lagi.

Keuntungan bonus memilih server yang lebih cepat dan terus menggunakannya, adalah bahwa server yang lebih cepat juga lebih kecil kemungkinannya gagal karena masalah jaringan.

Yang mengingatkan saya, jika Anda dapat mengetahui mengapa permintaan gagal, lakukanlah. Ini dapat membantu Anda mengelola situasi dengan lebih baik, bahkan jika Anda tidak dapat mencegah kegagalan tersebut. Misalnya, apakah Anda memerlukan lebih banyak kecepatan transfer di sisi server?

Lebih lagi:

  • Menyebarkan beberapa server di seluruh dunia, dan memilih server berdasarkan geolokasi.
  • Lakukan load balancing di sisi server (mesin khusus akan mengambil semua permintaan, dan mengaitkannya ke server Anda, Anda dapat memiliki paralelisme Anda di sana, atau strategi keseimbangan yang lebih baik).

Dan siapa bilang Anda hanya perlu melakukan satu ini? Anda dapat menempatkan pesan pemuatan, permintaan beberapa server yang tersebar di seluruh dunia untuk memilih yang lebih cepat dan hanya menggunakannya dari sana, pada kegagalan coba lagi pada satu lingkaran, dan minta masing-masing server tersebut menjadi sekelompok mesin dengan penyeimbangan beban . Kenapa tidak? Ya, biaya ...


Biaya

Ada empat biaya:

  • Biaya pengembangan (biasanya sangat murah)
  • Biaya penempatan (biasanya tinggi)
  • Runtime biaya (tergantung pada jenis aplikasi dan model bisnis)
  • Biaya kegagalan (mungkin rendah, tetapi tidak perlu)

Anda harus menyeimbangkannya.

Misalnya, katakanlah Anda menghasilkan sekitar satu dolar per pengguna yang puas. Anda memiliki 3000 pengguna per hari. Bahwa permintaan gagal sekitar 50% dari waktu. Dan 2% dari pengguna tersebut pergi tanpa membayar saat permintaan gagal. Ini berarti Anda kehilangan (3000 * 50% * 2%) 30 dolar per hari. Sekarang, katakanlah bahwa mengembangkan fitur baru akan dikenakan biaya 100 dolar AS dan menyebarkan server akan dikenakan biaya 800 dolar AS - dan mengabaikan biaya runtime - ini berarti Anda akan mendapatkan pengembalian investasi dalam ((100 + 800) / 30 ) 30 hari. Sekarang, Anda dapat memeriksa anggaran Anda dan memutuskan.

Jangan menganggap nilai-nilai ini mewakili kenyataan, saya memilihnya untuk kenyamanan matematika.

Adendum:

  • Ingatlah bahwa saya juga mengabaikan detailnya. Misalnya, Anda mungkin memiliki sedikit biaya penggunaan tetapi harus membayar waktu CPU dan Anda perlu mempertimbangkan itu.
  • Beberapa klien mungkin menghargai jika Anda tidak membuang paket data mereka dalam permintaan yang berlebihan.
  • Meningkatkan produk Anda dapat membantu menghadirkan iklan alami.
  • Jangan lupa biaya peluang. Haruskah Anda mengembangkan sesuatu yang lain?

Masalahnya adalah, jika Anda mempertimbangkan masalah dalam hal biaya balacing, Anda dapat membuat estimasi biaya untuk strategi yang Anda pertimbangkan, dan menggunakan analisis ini untuk memutuskan.


Intuisi

Intuisi jika ditumbuhkan oleh pengalaman. Saya tidak menyarankan untuk melakukan analisis semacam ini setiap waktu. Beberapa orang melakukannya, dan itu tidak masalah. Saya menyarankan Anda untuk memiliki pemahaman tentang hal ini, dan mengembangkan intuisi untuk itu.

Selanjutnya, dalam bidang teknik, selain dari pengetahuan yang kita dapatkan dari sains yang sebenarnya, kita juga belajar dalam praktik dan menyusun pedoman tentang apa yang berhasil dan yang tidak. Oleh karena itu, sering kali bijaksana untuk melihat keadaan seni ... meskipun, kadang-kadang Anda perlu melihat di luar area Anda.

Dalam hal ini, saya akan melihat video game online. Mereka memiliki layar muat, mereka memiliki beberapa server, mereka akan memilih server berdasarkan latensi, dan mereka bahkan dapat memungkinkan pengguna untuk berpindah server. Kami tahu itu berhasil.

Saya menyarankan untuk melakukan itu daripada memboroskan lalu lintas jaringan dan waktu server pada setiap permintaan, juga menyadari bahwa bahkan dengan server berlebihan, kegagalan dapat terjadi.


2
Saya rasa saya tidak perlu mengatakannya tetapi ini adalah jawaban yang bagus :) Saya tahu saya akan menerimanya di 10 baris pertama, tetapi saya memberi Anda kesempatan untuk tetap gagal dan membacanya sampai akhir. Anda tidak
Adelin

9

Ini dapat diterima, jika waktu klien lebih berharga daripada waktu di server.

Jika klien harus cepat dan akurat. Anda dapat membenarkan permintaan beberapa server. Dan bagus untuk membatalkan permintaan jika jawaban yang valid diterima.

Dan tentu saja selalu bijaksana untuk berkonsultasi dengan pemilik / pengelola server.


Mengapa Anda harus membatalkan permintaan? Tentunya itu subyektif.
JᴀʏMᴇᴇ

@ JᴀʏMᴇᴇ, Itu dibangun dalam paranoia. Saya pernah bekerja dengan sistem yang tidak menghapus antriannya dan macet ketika antrian penuh (Ya itu perangkat lunak profesional).
Toon Krijthe

4

Teknik ini dapat mengurangi latensi. Waktu respons server tidak deterministik. Pada skala kemungkinan akan ada setidaknya satu server yang menunjukkan waktu respons yang buruk. Oleh karena itu, apa pun yang menggunakan server itu juga akan memiliki waktu respons yang buruk. Dengan mengirimkan ke beberapa server, seseorang mengurangi risiko berbicara dengan server yang berkinerja buruk.

Biaya termasuk lalu lintas jaringan tambahan, pemrosesan server yang terbuang dan kompleksitas aplikasi (pikir ini bisa disembunyikan di perpustakaan). Biaya ini dapat dikurangi dengan membatalkan permintaan yang tidak digunakan, atau menunggu sebentar sebelum mengirim permintaan kedua.

Ini satu kertas , dan satu lagi . Saya ingat pernah membaca makalah Google implementasi mereka juga.


2

Saya sebagian besar setuju dengan jawaban lain, tetapi saya pikir ini harus sangat jarang dalam praktek. Saya ingin membagikan contoh yang jauh lebih umum dan masuk akal untuk kapan Anda akan menggunakan Promise.race(), sesuatu yang kebetulan saya gunakan untuk beberapa minggu yang lalu (baik, setara python).

Katakanlah Anda memiliki daftar tugas yang panjang, beberapa yang dapat dijalankan secara paralel, dan beberapa yang harus dijalankan sebelum yang lain. Anda dapat memulai semua tugas tanpa ketergantungan, lalu tunggu daftar itu dengan Promise.race(). Segera setelah tugas pertama selesai, Anda dapat memulai tugas apa pun yang bergantung pada tugas pertama itu, dan Promise.race()sekali lagi pada daftar baru dikombinasikan dengan tugas-tugas yang belum selesai dari daftar asli. Terus ulangi sampai semua tugas selesai.

Catatan API Javascript tidak dirancang secara ideal untuk ini. Ini adalah jumlah minimum yang bisa digunakan, dan Anda harus menambahkan sedikit kode lem. Namun, maksud saya adalah bahwa fungsi seperti race()jarang digunakan untuk redundansi. Mereka ada terutama ketika Anda benar-benar menginginkan hasil dari semua janji, tetapi tidak ingin menunggu semuanya selesai sebelum mengambil tindakan selanjutnya.


Masalahnya adalah, setidaknya dengan Javascript's Promise.race, Anda benar-benar memulai tugas setiap kali Anda menjalankan metode balapan. Ini tidak akan berada pada tugas yang belum selesai, itu akan menjadi serangkaian tugas baru, tanpa memperhatikan apa yang dijalankan sebelumnya (kecuali Anda menerapkan logika itu di tingkat tugas). Daftar asli dilupakan sebaliknya, dan hanya nilai pengembalian tugas pertama yang tersisa
Adelin

1
Janji dalam Javascript dimulai dengan penuh semangat, ketika new Promisedipanggil, dan tidak dimulai kembali ketika Promise.race()dipanggil. Beberapa implementasi janji malas, tetapi bersemangat jauh lebih umum. Anda dapat menguji dengan membuat janji di konsol yang masuk ke konsol. Anda akan segera melihatnya log. Kemudian sampaikan janji itu kepada Promise.race(). Anda akan melihatnya tidak masuk lagi.
Karl Bielefeldt

Ah itu benar. Tetapi afaik nilai pengembalian sisa janji kecuali yang pertama dilupakan, dengan janji.race
Adelin

Itu sebabnya saya mengatakan API tidak dirancang secara ideal. Anda harus menyimpan set tugas asli dalam suatu variabel di suatu tempat.
Karl Bielefeldt

1

Selain pertimbangan teknis, Anda mungkin ingin menggunakan pendekatan ini ketika itu merupakan bagian dari model bisnis Anda yang sebenarnya.

Variasi pada pendekatan ini relatif umum dalam penawaran waktu nyata pada iklan. Dalam model ini, penerbit (penyedia ruang iklan) akan meminta pengiklan (penyedia iklan) untuk menawar tayangan oleh pengguna tertentu. Jadi untuk setiap tayangan tersebut, Anda akan meminta setiap pengiklan berlangganan, mengirimkan pertanyaan dengan detail tayangan ke titik akhir yang disediakan oleh masing-masing pengiklan (atau sebagai alternatif, skrip yang disediakan oleh pengiklan yang berjalan sebagai titik akhir di server Anda sendiri), balapan semua permintaan ini hingga batas waktu (mis. 100 ms) dan kemudian mengambil tawaran tertinggi, mengabaikan yang lain.

Variasi khusus dari ini yang membantu mengurangi waktu tunggu klien adalah membuat penerbit mengizinkan nilai sasaran minimum untuk tawaran tersebut, sehingga tawaran pengiklan pertama yang melampaui nilai itu akan segera diterima (atau, jika tidak ada tawaran yang melampaui nilai, yang max akan diambil). Jadi, dalam variasi ini, kueri pertama yang tiba bisa menang dan yang lainnya dibuang, meskipun mereka sama bagus atau bahkan lebih baik.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.