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.
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?
sumber
Jawaban:
Mengikuti dengan @DavidSchwartz dan @Matt ini terlihat seperti utas, mengunci masalah pengelolaan.
Saya menyarankan:
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.
Gunakan kolam utas jika tidak menggunakannya.
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.
sumber