Mengapa kita mendapatkan lonjakan tiba-tiba di waktu respons?

12

Kami memiliki API yang diimplementasikan menggunakan ServiceStack yang di-host di IIS. Saat melakukan pengujian beban API, kami menemukan bahwa waktu responsnya bagus tetapi memburuk dengan cepat segera setelah kami mencapai sekitar 3.500 pengguna bersamaan per server. Kami memiliki dua server dan ketika memukul mereka dengan 7.000 pengguna, waktu respons rata-rata berada di bawah 500 ms untuk semua titik akhir. Kotak-kotak berada di belakang load balancer sehingga kami mendapatkan 3.500 persetujuan per server. Namun begitu kami meningkatkan jumlah pengguna secara bersamaan, kami melihat peningkatan yang signifikan dalam waktu tanggapan. Meningkatkan pengguna secara bersamaan menjadi 5.000 per server memberi kami waktu respons rata-rata per titik akhir sekitar 7 detik.

Memori dan CPU di server cukup rendah, baik saat waktu respons baik dan ketika setelah memburuk. Pada puncaknya dengan 10.000 pengguna secara bersamaan, rata-rata CPU di bawah 50% dan RAM berada di antara 3-4 GB dari 16. Ini membuat kami berpikir bahwa kami mencapai semacam batasan di suatu tempat. Tangkapan layar di bawah ini menunjukkan beberapa penghitung kunci dalam perfmon selama tes beban dengan total 10.000 pengguna secara bersamaan. Penghitung yang disorot adalah permintaan / detik. Di sebelah kanan tangkapan layar Anda dapat melihat grafik permintaan per detik menjadi sangat tidak menentu. Ini adalah indikator utama untuk waktu respons yang lambat. Segera setelah kami melihat pola ini, kami melihat waktu respons lambat dalam uji beban.

screenshot perfmon dengan permintaan per detik disorot

Bagaimana kita mengatasi masalah kinerja ini? Kami mencoba mengidentifikasi apakah ini masalah pengkodean atau masalah konfigurasi. Apakah ada pengaturan di web.config atau IIS yang dapat menjelaskan perilaku ini? Kumpulan aplikasi menjalankan .NET v4.0 dan versi IIS 7.5. Satu-satunya perubahan yang kami lakukan dari pengaturan default adalah memperbarui kumpulan panjang nilai antrian aplikasi dari 1.000 menjadi 5.000. Kami juga telah menambahkan pengaturan konfigurasi berikut ke file Aspnet.config:

<system.web>
    <applicationPool 
        maxConcurrentRequestsPerCPU="5000"
        maxConcurrentThreadsPerCPU="0" 
        requestQueueLimit="5000" />
</system.web>

Keterangan lebih lanjut:

Tujuan API adalah untuk menggabungkan data dari berbagai sumber eksternal dan kembali sebagai JSON. Saat ini menggunakan implementasi cache InMemory untuk cache panggilan eksternal individu di lapisan data. Permintaan pertama ke sumber daya akan mengambil semua data yang diperlukan dan permintaan berikutnya untuk sumber daya yang sama akan mendapatkan hasil dari cache. Kami memiliki 'pelari cache' yang diimplementasikan sebagai proses latar belakang yang memperbarui informasi dalam cache pada interval waktu tertentu. Kami telah menambahkan penguncian di sekitar kode yang mengambil data dari sumber daya eksternal. Kami juga telah mengimplementasikan layanan untuk mengambil data dari sumber eksternal secara asinkron sehingga titik akhir seharusnya hanya selambat panggilan eksternal paling lambat (kecuali kami memiliki data dalam cache tentu saja). Ini dilakukan dengan menggunakan kelas System.Threading.Tasks.Task.Mungkinkah kita mencapai batasan dalam hal jumlah utas yang tersedia untuk proses?

Christian Hagelid
sumber
5
Berapa core yang dimiliki CPU Anda? Mungkin Anda memaksimalkan satu inti. Ketika angka ajaib adalah 50%, 25% atau 12,5%, itu menunjukkan bahwa Anda telah memaksimalkan inti dan karena alasan tertentu tidak dapat menggunakan core lain yang sedang duduk diam. Periksa inti maksimal.
David Schwartz
1
Apakah Anda punya satu utas per permintaan? Jadi untuk 5000 permintaan, apakah Anda sudah mendapatkan 5.000 utas? Jika Anda melakukannya maka kemungkinan masalah Anda. Sebagai gantinya, Anda harus membuat kumpulan utas dan menggunakan kumpulan utas untuk memproses permintaan, mengantri permintaan saat masuk ke kumpulan utas. Ketika utas telah selesai dengan permintaan, ia dapat memproses permintaan dari antrian. Diskusi semacam ini adalah yang terbaik untuk stackoverflow. Terlalu banyak utas berarti terlalu banyak saklar konteks.
Matt
1
Hanya cek kewarasan di sini, sudahkah Anda mencoba mematikan semua proses latar belakang Anda dan melihat apa perilaku hanya untuk JSON mengembalikan data statis dari cache? Dengan kata lain, membuat JSON Anda meminta data statis dan menghapus "panggilan async eksternal" yang menyegarkan cache Anda sepenuhnya. Juga, tergantung pada jumlah data JSON yang dilayani pada setiap permintaan, pernahkah Anda memikirkan throughput jaringan Anda dan jika permintaan mulai mencadangkan karena server tidak dapat mendorong data dengan cukup cepat?
Robert
1
+1 untuk saran Davids di atas. Anda harus benar-benar mengulang tes dan melihat dengan hati-hati setiap pemanfaatan inti. Saya sarankan Anda segera melakukan ini untuk menghilangkannya jika tidak ada yang lain. Kedua, saya agak curiga dengan cache Anda. Pertikaian kunci dapat menunjukkan perilaku seperti ini - di beberapa titik kritis, kunci menyebabkan penundaan yang pada gilirannya menyebabkan kunci ditahan lebih lama dari biasanya, menyebabkan titik kritis di mana segala sesuatunya menurun dengan cepat. Bisakah Anda membagikan kode caching dan penguncian Anda?
steve cook
1
Apa pengaturan disk untuk server (dengan asumsi bahwa karena mereka memuat seimbang, pengaturan disk adalah sama)? Bisakah Anda memposting semua spesifikasi untuk drive / server di posting awal Anda? Sudahkah Anda melemparkan perfmon pada disk pada drive fisik yang ada di IIS DAN file log IIS? Sangat mungkin Anda mungkin mengalami masalah dengan disk karena 3,500 permintaan = 3,500+ IIS masuk. Jika mereka berada di disk / partisi yang sama Anda bisa memiliki masalah besar di sana.
Techie Joe

Jawaban:

2

Mengikuti dengan @DavidSchwartz dan @Matt ini terlihat seperti utas, mengunci masalah pengelolaan.

Saya menyarankan:

  1. Bekukan panggilan eksternal dan cache yang dihasilkan untuk mereka dan jalankan uji beban dengan informasi eksternal statis hanya untuk membuang masalah yang tidak terkait dengan sisi lingkungan server.

  2. Gunakan kolam utas jika tidak menggunakannya.

  3. Tentang panggilan eksternal, Anda berkata, "Kami juga telah mengimplementasikan layanan untuk mengambil data dari sumber eksternal dengan cara yang tidak sinkron sehingga titik akhir hanya akan selambat panggilan eksternal yang paling lambat (kecuali kami memiliki data dalam cache tentu saja). "

Pertanyaannya adalah: - Sudahkah Anda memeriksa apakah ada data cache dikunci selama panggilan eksternal atau hanya saat menulis hasil panggilan eksternal ke dalam cache? (terlalu jelas tetapi harus mengatakan). - Apakah Anda mengunci seluruh cache atau mengecilkannya? (terlalu jelas tetapi harus mengatakan). - Sekalipun asinkron, seberapa sering panggilan eksternal berjalan? Bahkan jika mereka tidak menjalankannya terlalu sering, mereka dapat diblokir oleh jumlah permintaan yang berlebihan ke cache dari panggilan pengguna saat cache terkunci. Skenario ini biasanya menunjukkan persentase tetap dari CPU yang digunakan karena banyak utas menunggu dalam interval tetap dan "penguncian" juga harus dikelola. - Sudahkah Anda memeriksa apakah tugas eksternal berarti waktu respons juga meningkat ketika skenario lambat tiba?

Jika masalah masih berlanjut, saya sarankan menghindari kelas Tugas dan melakukan panggilan eksternal melalui kumpulan utas yang sama yang mengelola permintaan pengguna. Ini untuk menghindari skenario sebelumnya.

SaintJob 2.0
sumber