Bagikan melalui


Menggunakan Metode Asinkron di ASP.NET MVC 4

oleh Rick Anderson

Tutorial ini akan mengajari Anda dasar-dasar membangun aplikasi Web MVC ASP.NET asinkron menggunakan Visual Studio Express 2012 untuk Web, yang merupakan versi gratis dari Microsoft Visual Studio. Anda juga dapat menggunakan Visual Studio 2012.

Sampel lengkap disediakan untuk tutorial ini di github https://github.com/RickAndMSFT/Async-ASP.NET/

Kelas Pengontrol MVC 4 ASP.NET dalam kombinasi .NET 4.5 memungkinkan Anda menulis metode tindakan asinkron yang mengembalikan objek jenis Task<ActionResult>. .NET Framework 4 memperkenalkan konsep pemrograman asinkron yang disebut sebagai Tugas dan ASP.NET MVC 4 mendukung Tugas. Tugas diwakili oleh jenis Tugas dan jenis terkait di namespace System.Threading.Tasks . .NET Framework 4.5 dibangun pada dukungan asinkron ini dengan kata kunci tunggu dan asinkron yang membuat bekerja dengan objek Tugas jauh lebih kompleks daripada pendekatan asinkron sebelumnya. Kata kunci yang ditunggu adalah singkatan sintaksis untuk menunjukkan bahwa sepotong kode harus secara asinkron menunggu pada beberapa bagian kode lainnya. Kata kunci asinkron mewakili petunjuk yang dapat Anda gunakan untuk menandai metode sebagai metode asinkron berbasis tugas. Kombinasi menunggu, asinkron, dan objek Tugas membuatnya jauh lebih mudah bagi Anda untuk menulis kode asinkron di .NET 4.5. Model baru untuk metode asinkron disebut Pola Asinkron Berbasis Tugas (TAP). Tutorial ini mengasumsikan Anda memiliki beberapa keakraban dengan pemrograman asinkron menggunakan kata kunci tunggu dan asinkron dan namespace layanan Tugas .

Untuk informasi selengkapnya tentang menggunakan kata kunci tunggu dan asinkron dan namespace layanan Tugas , lihat referensi berikut ini.

Bagaimana Permintaan Diproses oleh Kumpulan Utas

Di server web, .NET Framework mempertahankan kumpulan utas yang digunakan untuk melayani permintaan ASP.NET. Ketika permintaan tiba, utas dari kumpulan dikirim untuk memproses permintaan tersebut. Jika permintaan diproses secara sinkron, utas yang memproses permintaan sibuk saat permintaan sedang diproses, dan utas tersebut tidak dapat melayani permintaan lain.

Ini mungkin tidak menjadi masalah, karena kumpulan utas dapat dibuat cukup besar untuk mengakomodasi banyak utas yang sibuk. Namun, jumlah utas di kumpulan utas dibatasi (maksimum default untuk .NET 4.5 adalah 5.000). Dalam aplikasi besar dengan konkurensi permintaan jangka panjang yang tinggi, semua utas yang tersedia mungkin sibuk. Kondisi ini dikenal sebagai kelaparan utas. Ketika kondisi ini tercapai, server web mengantre permintaan. Jika antrean permintaan menjadi penuh, server web menolak permintaan dengan status HTTP 503 (Server Terlalu Sibuk). Kumpulan utas CLR memiliki batasan pada injeksi utas baru. Jika konkurensi bersifat bursty (artinya, situs web Anda bisa tiba-tiba mendapatkan sejumlah besar permintaan) dan semua utas permintaan yang tersedia sibuk karena panggilan backend dengan latensi tinggi, tingkat injeksi utas terbatas dapat membuat aplikasi Anda merespons dengan sangat buruk. Selain itu, setiap utas baru yang ditambahkan ke kumpulan utas memiliki overhead (seperti memori tumpukan 1 MB). Aplikasi web yang menggunakan metode sinkron untuk melayani panggilan latensi tinggi di mana kumpulan utas tumbuh ke maksimum default .NET 4.5 sebesar 5.000 utas akan mengonsumsi sekitar 5 GB lebih banyak memori daripada aplikasi yang dapat melayani permintaan yang sama menggunakan metode asinkron dan hanya 50 utas. Saat Anda melakukan pekerjaan asinkron, Anda tidak selalu menggunakan utas. Misalnya, ketika Anda membuat permintaan layanan web asinkron, ASP.NET tidak akan menggunakan utas apa pun antara panggilan metode asinkron dan menunggu. Menggunakan kumpulan utas untuk melayani permintaan dengan latensi tinggi dapat menyebabkan jejak memori yang besar dan pemanfaatan perangkat keras server yang buruk.

Memproses Permintaan Asinkron

Dalam aplikasi web yang melihat sejumlah besar permintaan bersamaan saat start-up atau memiliki beban bursty (di mana konkurensi meningkat tiba-tiba), membuat panggilan layanan web tidak sinkron meningkatkan responsivitas aplikasi. Permintaan asinkron membutuhkan waktu yang sama untuk diproses sebagai permintaan sinkron. Jika permintaan melakukan panggilan layanan web yang memerlukan dua detik untuk diselesaikan, permintaan membutuhkan waktu dua detik baik dilakukan secara sinkron atau asinkron. Namun selama panggilan asinkron, utas tidak diblokir untuk merespons permintaan lain saat menunggu permintaan pertama selesai. Oleh karena itu, permintaan asinkron mencegah antrean permintaan dan pertumbuhan kumpulan utas ketika ada banyak permintaan bersamaan yang memanggil operasi yang berjalan lama.

Memilih Metode Tindakan Sinkron atau Asinkron

Bagian ini mencantumkan panduan kapan harus menggunakan metode tindakan sinkron atau asinkron. Ini hanyalah panduan; periksa setiap aplikasi satu per satu untuk menentukan apakah metode asinkron membantu performa.

Secara umum, gunakan metode sinkron untuk kondisi berikut:

  • Operasinya sederhana atau berjalan singkat.
  • Kesederhanaan lebih penting daripada efisiensi.
  • Operasi ini terutama merupakan operasi CPU alih-alih operasi yang melibatkan disk ekstensif atau overhead jaringan. Menggunakan metode tindakan asinkron pada operasi terikat CPU tidak memberikan manfaat dan menghasilkan lebih banyak overhead.

Secara umum, gunakan metode asinkron untuk kondisi berikut:

  • Anda memanggil layanan yang dapat dikonsumsi melalui metode asinkron, dan Anda menggunakan .NET 4.5 atau yang lebih tinggi.
  • Operasi terikat jaringan atau terikat I/O alih-alih terikat CPU.
  • Paralelisme lebih penting daripada kesederhanaan kode.
  • Anda ingin menyediakan mekanisme yang memungkinkan pengguna membatalkan permintaan yang berjalan lama.
  • Ketika manfaat beralih utas melebihi biaya pengalihan konteks. Secara umum, Anda harus membuat metode asinkron jika metode sinkron menunggu utas permintaan ASP.NET saat tidak melakukan pekerjaan. Dengan melakukan panggilan asinkron, utas permintaan ASP.NET tidak terhenti tidak melakukan pekerjaan saat menunggu permintaan layanan web selesai.
  • Pengujian menunjukkan bahwa operasi pemblokiran adalah hambatan dalam performa situs dan bahwa IIS dapat melayani lebih banyak permintaan dengan menggunakan metode asinkron untuk panggilan pemblokiran ini.

Sampel yang dapat diunduh menunjukkan cara menggunakan metode tindakan asinkron secara efektif. Sampel yang disediakan dirancang untuk memberikan demonstrasi sederhana pemrograman asinkron di ASP.NET MVC 4 menggunakan .NET 4.5. Sampel tidak dimaksudkan untuk menjadi arsitektur referensi untuk pemrograman asinkron di ASP.NET MVC. Program sampel memanggil ASP.NET metode Web API yang pada gilirannya memanggil Task.Delay untuk mensimulasikan panggilan layanan web yang berjalan lama. Sebagian besar aplikasi produksi tidak akan menunjukkan manfaat yang jelas untuk menggunakan metode tindakan asinkron.

Beberapa aplikasi mengharuskan semua metode tindakan menjadi asinkron. Seringkali, mengonversi beberapa metode tindakan sinkron ke metode asinkron memberikan peningkatan efisiensi terbaik untuk jumlah pekerjaan yang diperlukan.

Aplikasi Sampel

Anda dapat mengunduh aplikasi sampel dari https://github.com/RickAndMSFT/Async-ASP.NET/ situs GitHub . Repositori terdiri dari tiga proyek:

  • Mvc4Async: Proyek ASP.NET MVC 4 yang berisi kode yang digunakan dalam tutorial ini. Ini membuat panggilan Web API ke layanan WebAPIpgw .
  • WebAPIpgw: Proyek API Web ASP.NET MVC 4 yang mengimplementasikan Products, Gizmos and Widgets pengontrol. Ini menyediakan data untuk proyek WebAppAsync dan proyek Mvc4Async .
  • WebAppAsync: Proyek ASP.NET Web Forms yang digunakan dalam tutorial lain.

Metode Tindakan Sinkron Gizmos

Kode berikut menunjukkan Gizmos metode tindakan sinkron yang digunakan untuk menampilkan daftar gizmos. (Untuk artikel ini, gizmo adalah perangkat mekanis fiktif.)

public ActionResult Gizmos()
{
    ViewBag.SyncOrAsync = "Synchronous";
    var gizmoService = new GizmoService();
    return View("Gizmos", gizmoService.GetGizmos());
}

Kode berikut menunjukkan GetGizmos metode layanan gizmo.

public class GizmoService
{
    public async Task<List<Gizmo>> GetGizmosAsync(
        // Implementation removed.
       
    public List<Gizmo> GetGizmos()
    {
        var uri = Util.getServiceUri("Gizmos");
        using (WebClient webClient = new WebClient())
        {
            return JsonConvert.DeserializeObject<List<Gizmo>>(
                webClient.DownloadString(uri)
            );
        }
    }
}

Metode ini GizmoService GetGizmos meneruskan URI ke layanan HTTP ASP.NET Web API yang mengembalikan daftar data gizmos. Proyek WebAPIpgw berisi implementasi API gizmos, widget Web dan product pengontrol.
Gambar berikut menunjukkan tampilan gizmos dari proyek sampel.

Gizmos

Membuat Metode Tindakan Gizmos Asinkron

Sampel menggunakan asinkron baru dan menunggu kata kunci (tersedia di .NET 4.5 dan Visual Studio 2012) untuk memungkinkan pengompilasi bertanggung jawab untuk mempertahankan transformasi rumit yang diperlukan untuk pemrograman asinkron. Pengompilasi memungkinkan Anda menulis kode menggunakan konstruksi alur kontrol sinkron C#dan pengompilasi secara otomatis menerapkan transformasi yang diperlukan untuk menggunakan panggilan balik untuk menghindari pemblokiran utas.

Kode berikut menunjukkan Gizmos metode sinkron dan GizmosAsync metode asinkron. Jika browser Anda mendukung elemen HTML 5<mark>, Anda akan melihat perubahan dalam GizmosAsync sorotan kuning.

public ActionResult Gizmos()
{
    ViewBag.SyncOrAsync = "Synchronous";
    var gizmoService = new GizmoService();
    return View("Gizmos", gizmoService.GetGizmos());
}
public async Task<ActionResult> GizmosAsync()
{
    ViewBag.SyncOrAsync = "Asynchronous";
    var gizmoService = new GizmoService();
    return View("Gizmos", await gizmoService.GetGizmosAsync());
}

Perubahan berikut diterapkan untuk memungkinkan GizmosAsync menjadi asinkron.

  • Metode ditandai dengan kata kunci asinkron , yang memberi tahu pengkompilasi untuk menghasilkan panggilan balik untuk bagian isi dan untuk secara otomatis membuat Task<ActionResult> yang dikembalikan.
  • "Asinkron" ditambahkan ke nama metode. Menambahkan "Asinkron" tidak diperlukan tetapi merupakan konvensi saat menulis metode asinkron.
  • Jenis pengembalian diubah dari ActionResult ke Task<ActionResult>. Jenis Task<ActionResult> pengembalian mewakili pekerjaan yang sedang berlangsung dan menyediakan penelepon metode dengan handel untuk menunggu penyelesaian operasi asinkron. Dalam hal ini, pemanggil adalah layanan web. Task<ActionResult> mewakili pekerjaan yang sedang berlangsung dengan hasil ActionResult.
  • Kata kunci tunggu diterapkan ke panggilan layanan web.
  • API layanan web asinkron disebut (GetGizmosAsync).

Di dalam isi GetGizmosAsync metode metode metode asinkron lain, GetGizmosAsync dipanggil. GetGizmosAsync segera mengembalikan Task<List<Gizmo>> yang akhirnya akan selesai ketika data tersedia. Karena Anda tidak ingin melakukan hal lain sampai Anda memiliki data gizmo, kode menunggu tugas (menggunakan kata kunci tunggu ). Anda dapat menggunakan kata kunci tunggu hanya dalam metode yang dianotasi dengan kata kunci asinkron .

Kata kunci tunggu tidak memblokir utas hingga tugas selesai. Ini mendaftar sisa metode sebagai panggilan balik pada tugas, dan segera kembali. Ketika tugas yang ditunggu akhirnya selesai, itu akan memanggil panggilan balik tersebut dan dengan demikian melanjutkan eksekusi metode tepat di tempat terakhirnya. Untuk informasi selengkapnya tentang menggunakan kata kunci tunggu dan asinkron dan namespace layanan Tugas , lihat referensi asinkron.

Kode berikut menunjukkan metode GetGizmos dan GetGizmosAsync.

public List<Gizmo> GetGizmos()
{
    var uri = Util.getServiceUri("Gizmos");
    using (WebClient webClient = new WebClient())
    {
        return JsonConvert.DeserializeObject<List<Gizmo>>(
            webClient.DownloadString(uri)
        );
    }
}
public async Task<List<Gizmo>> GetGizmosAsync()
{
    var uri = Util.getServiceUri("Gizmos");
    using (HttpClient httpClient = new HttpClient())
    {
        var response = await httpClient.GetAsync(uri);
        return (await response.Content.ReadAsAsync<List<Gizmo>>());
    }
}

Perubahan asinkron mirip dengan yang dibuat pada GizmosAsync di atas.

  • Tanda tangan metode diannotasi dengan kata kunci asinkron , jenis pengembalian diubah menjadi Task<List<Gizmo>>, dan Asinkron ditambahkan ke nama metode.
  • Kelas HttpClient asinkron digunakan alih-alih kelas WebClient .
  • Kata kunci tunggu diterapkan ke metode asinkron HttpClient .

Gambar berikut menunjukkan tampilan gizmo asinkron.

asinkron

Presentasi browser data gizmos identik dengan tampilan yang dibuat oleh panggilan sinkron. Satu-satunya perbedaan adalah versi asinkron mungkin lebih berkinerja di bawah beban berat.

Melakukan Beberapa Operasi secara Paralel

Metode tindakan asinkron memiliki keuntungan signifikan dibandingkan metode sinkron ketika tindakan harus melakukan beberapa operasi independen. Dalam sampel yang disediakan, metode PWGsinkron (untuk Produk, Widget, dan Gizmos) menampilkan hasil tiga panggilan layanan web untuk mendapatkan daftar produk, widget, dan gizmos. Proyek ASP.NET Web API yang menyediakan layanan ini menggunakan Task.Delay untuk mensimulasikan latensi atau panggilan jaringan lambat. Ketika penundaan diatur ke 500 milidetik, metode asinkron PWGasync membutuhkan sedikit lebih dari 500 milidetik untuk diselesaikan sementara versi sinkron PWG membutuhkan lebih dari 1.500 milidetik. Metode sinkron PWG ditampilkan dalam kode berikut.

public ActionResult PWG()
{
    ViewBag.SyncType = "Synchronous";
    var widgetService = new WidgetService();
    var prodService = new ProductService();
    var gizmoService = new GizmoService();

    var pwgVM = new ProdGizWidgetVM(
        widgetService.GetWidgets(),
        prodService.GetProducts(),
        gizmoService.GetGizmos()
       );

    return View("PWG", pwgVM);
}

Metode asinkron PWGasync ditunjukkan dalam kode berikut.

public async Task<ActionResult> PWGasync()
{
    ViewBag.SyncType = "Asynchronous";
    var widgetService = new WidgetService();
    var prodService = new ProductService();
    var gizmoService = new GizmoService();

    var widgetTask = widgetService.GetWidgetsAsync();
    var prodTask = prodService.GetProductsAsync();
    var gizmoTask = gizmoService.GetGizmosAsync();

    await Task.WhenAll(widgetTask, prodTask, gizmoTask);

    var pwgVM = new ProdGizWidgetVM(
       widgetTask.Result,
       prodTask.Result,
       gizmoTask.Result
       );

    return View("PWG", pwgVM);
}

Gambar berikut menunjukkan tampilan yang dikembalikan dari metode PWGasync .

pwgAsync

Menggunakan Token Pembatalan

Metode tindakan asinkron yang dikembalikan Task<ActionResult>dapat dibatalkan, yaitu mereka mengambil parameter CancellationToken saat disediakan dengan atribut AsyncTimeout . Kode berikut menunjukkan GizmosCancelAsync metode dengan batas waktu 150 milidetik.

[AsyncTimeout(150)]
[HandleError(ExceptionType = typeof(TimeoutException),
                                    View = "TimeoutError")]
public async Task<ActionResult> GizmosCancelAsync(
                       CancellationToken cancellationToken )
{
    ViewBag.SyncOrAsync = "Asynchronous";
    var gizmoService = new GizmoService();
    return View("Gizmos",
        await gizmoService.GetGizmosAsync(cancellationToken));
}

Kode berikut menunjukkan kelebihan beban GetGizmosAsync, yang mengambil parameter CancellationToken .

public async Task<List<Gizmo>> GetGizmosAsync(string uri,
    CancellationToken cancelToken = default(CancellationToken))
{
    using (HttpClient httpClient = new HttpClient())
    {
        var response = await httpClient.GetAsync(uri, cancelToken);
        return (await response.Content.ReadAsAsync<List<Gizmo>>());
    }
}

Dalam aplikasi sampel yang disediakan, memilih tautan Demo Token Pembatalan memanggil GizmosCancelAsync metode dan menunjukkan pembatalan panggilan asinkron.

Konfigurasi Server untuk Panggilan Layanan Web Konkurensi Tinggi/Latensi Tinggi

Untuk mewujudkan manfaat aplikasi web asinkron, Anda mungkin perlu membuat beberapa perubahan pada konfigurasi server default. Ingatlah hal-hal berikut saat mengonfigurasi dan menekankan pengujian aplikasi web asinkron Anda.

  • Windows 7, Windows Vista, dan semua sistem operasi klien Windows memiliki maksimal 10 permintaan bersamaan. Anda akan memerlukan sistem operasi Windows Server untuk melihat manfaat metode asinkron di bawah beban tinggi.

  • Daftarkan .NET 4.5 dengan IIS dari prompt perintah yang ditinggikan:
    %windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_regiis -i
    Lihat Alat Pendaftaran IIS ASP.NET (Aspnet_regiis.exe)

  • Anda mungkin perlu meningkatkan batas antrean HTTP.sys dari nilai default 1.000 menjadi 5.000. Jika pengaturan terlalu rendah, Anda mungkin melihat HTTP.sys menolak permintaan dengan status HTTP 503. Untuk mengubah batas antrean HTTP.sys:

    • Buka manajer IIS dan navigasikan ke panel Kumpulan Aplikasi.
    • Klik kanan pada kumpulan aplikasi target dan pilih Pengaturan Tingkat Lanjut.
      Lanjutan
    • Dalam kotak dialog Pengaturan Tingkat Lanjut , ubah Panjang Antrean dari 1.000 menjadi 5.000.
      Panjang antrean

    Perhatikan pada gambar di atas, kerangka kerja .NET terdaftar sebagai v4.0, meskipun kumpulan aplikasi menggunakan .NET 4.5. Untuk memahami perbedaan ini, lihat hal berikut:

  • Jika aplikasi Anda menggunakan layanan web atau System.NET untuk berkomunikasi dengan backend melalui HTTP, Anda mungkin perlu meningkatkan elemen connectionManagement/maxconnection . Untuk aplikasi ASP.NET, ini dibatasi oleh fitur autoConfig hingga 12 kali jumlah CPU. Itu berarti bahwa pada quad-proc, Anda dapat memiliki paling banyak 12 * 4 = 48 koneksi bersamaan ke titik akhir IP. Karena ini terkait dengan autoConfig, cara termampu untuk meningkatkan maxconnection aplikasi ASP.NET adalah dengan mengatur System.Net.ServicePointManager.DefaultConnectionLimit secara terprogram dalam metode dari Application_Start dalam file global.asax . Lihat contoh unduhan untuk contoh.

  • Dalam .NET 4.5, default 5000 untuk MaxConcurrentRequestsPerCPU harus baik-baik saja.