Salah satu alasan keberhasilan ASP.NET adalah menurunkan hambatan masuk bagi pengembang Web. Anda tidak harus memiliki gelar PhD di bidang ilmu komputer untuk menulis kode ASP.NET. Banyak pengembang ASP.NET yang saya temui di tempat kerja belajar secara otodidak dan menulis spreadsheet Microsoft® Excel® sebelum mereka menulis C# atau Visual Basic®. Sekarang, mereka sedang menulis aplikasi Web, dan secara keseluruhan, mereka pantas mendapatkan pujian atas pekerjaan yang mereka lakukan.
Namun dengan kekuatan datanglah tanggung jawab, dan bahkan pengembang ASP.NET yang berpengalaman pun bisa membuat kesalahan. Selama bertahun-tahun berkonsultasi pada proyek ASP.NET, saya menemukan bahwa kesalahan tertentu sangat mungkin menyebabkan kerusakan berulang. Beberapa kesalahan ini dapat memengaruhi kinerja. Kesalahan lain dapat menghambat skalabilitas. Beberapa bug juga dapat menghabiskan waktu berharga bagi tim pengembangan untuk melacak bug dan perilaku yang tidak terduga.
Berikut 10 kendala yang dapat menyebabkan masalah selama rilis aplikasi produksi ASP.NET dan cara menghindarinya. Semua contoh berasal dari pengalaman saya sendiri membangun aplikasi Web nyata di perusahaan nyata, dan dalam beberapa kasus saya memberikan konteks dengan menjelaskan beberapa masalah yang dihadapi tim pengembangan ASP.NET selama proses pengembangan.
LoadControl dan Output Caching Ada sangat sedikit aplikasi ASP.NET yang tidak menggunakan kontrol pengguna. Sebelum halaman master, pengembang menggunakan kontrol pengguna untuk mengekstrak konten umum, seperti header dan footer. Bahkan di ASP.NET 2.0, kontrol pengguna menyediakan cara yang efisien untuk merangkum konten dan perilaku dan membagi halaman menjadi beberapa wilayah yang kemampuan cache-nya dapat dikontrol secara independen dari halaman secara keseluruhan (suatu proses yang disebut segmen). ).
Kontrol pengguna dapat dimuat secara deklaratif atau paksa. Pemuatan paksa bergantung pada Page.LoadControl, yang membuat instance kontrol pengguna dan mengembalikan referensi kontrol. Jika kontrol pengguna berisi anggota tipe khusus (misalnya, properti publik), Anda dapat memberikan referensi dan mengakses anggota khusus dari kode Anda. Kontrol pengguna pada Gambar 1 mengimplementasikan properti bernama BackColor. Kode berikut memuat kontrol pengguna dan memberikan nilai ke BackColor:
protected void Page_Load(pengirim objek, EventArgs e){// Memuat kontrol pengguna dan menambahkannya ke halaman Control control = LoadControl("~/MyUserControl.ascx") ;PlaceHolder1 .Controls.Add(control);//Atur warna latar belakangnya ((MyUserControl)control).BackColor = Color.Yellow;}
Kode di atas sebenarnya sangat sederhana, tetapi ini adalah jebakan yang menunggu pengembang yang tidak waspada untuk jatuh ke dalamnya. Bisakah Anda menemukan kekurangannya?
Jika Anda menebak bahwa masalahnya terkait dengan caching keluaran, Anda benar. Seperti yang Anda lihat, contoh kode di atas dikompilasi dan berjalan dengan baik, tetapi jika Anda mencoba menambahkan pernyataan berikut (yang sah) ke MyUserControl.ascx:
<%@ OutputCache Duration="5" VaryByParam="None" %>
Kemudian saat berikutnya Anda menjalankan halaman tersebut, Anda akan melihat InvalidCastException (oh senangnya!) dan pesan kesalahan berikut:
"Tidak dapat mentransmisikan objek bertipe 'System.Web.UI.PartialCachingControl' untuk mengetik 'MyUserControl'."
Oleh karena itu, kode ini berjalan dengan baik tanpa direktif OutputCache, tetapi gagal jika direktif OutputCache ditambahkan. ASP.NET tidak seharusnya berperilaku seperti ini. Halaman (dan kontrol) harus agnostik terhadap caching keluaran. Jadi, apa artinya ini?
Masalahnya adalah ketika caching output diaktifkan untuk kontrol pengguna, LoadControl tidak lagi mengembalikan referensi ke instance kontrol, melainkan mengembalikan referensi ke instance PartialCachingControl, yang mungkin atau mungkin tidak membungkus instance kontrol; keluaran kontrol adalah cache. Oleh karena itu, jika pengembang memanggil LoadControl untuk memuat kontrol pengguna secara dinamis dan mengonversi referensi kontrol untuk mengakses metode dan properti khusus kontrol, mereka harus memperhatikan cara mereka melakukan hal ini sehingga kode akan berjalan terlepas dari apakah ada arahan OutputCache.
Gambar 2 mengilustrasikan cara yang benar untuk memuat kontrol pengguna secara dinamis dan mengonversi referensi kontrol yang dikembalikan. Berikut ringkasan cara kerjanya:
• Jika file ASCX tidak memiliki direktif OutputCache, LoadControl akan mengembalikan referensi MyUserControl. Page_Load mengonversi referensi ke MyUserControl dan menyetel properti BackColor kontrol.
• Jika file ASCX menyertakan direktif OutputCache dan output kontrol tidak di-cache, LoadControl mengembalikan referensi ke PartialCachingControl yang properti CachedControl-nya berisi referensi ke MyUserControl yang mendasarinya. Page_Load mengonversi PartialCachingControl.CachedControl menjadi MyUserControl dan menyetel properti BackColor kontrol.
• Jika file ASCX menyertakan direktif OutputCache dan output kontrol di-cache, LoadControl mengembalikan referensi ke PartialCachingControl yang properti CachedControl-nya kosong. Perhatikan bahwa Page_Load tidak lagi dilanjutkan. Properti BackColor kontrol tidak dapat disetel karena output kontrol berasal dari cache output. Dengan kata lain, tidak ada MyUserControl untuk menyetel properti sama sekali.
Kode pada Gambar 2 akan berjalan terlepas dari apakah ada direktif OutputCache di file .ascx. Meski terlihat sedikit lebih rumit, namun akan terhindar dari kesalahan yang mengganggu. Sederhana tidak selalu berarti mudah dirawat.
Kembali ke Atas Sesi dan Caching Output Berbicara tentang caching output, baik ASP.NET 1.1 dan ASP.NET 2.0 memiliki potensi masalah yang mempengaruhi halaman cache output di server yang menjalankan Windows Server™ 2003 dan IIS 6.0. Saya pribadi telah melihat masalah ini terjadi dua kali di server produksi ASP.NET, dan kedua kali masalah ini diselesaikan dengan mematikan buffering keluaran. Belakangan saya mengetahui bahwa ada solusi yang lebih baik daripada menonaktifkan caching keluaran. Inilah tampilannya ketika saya pertama kali mengalami masalah ini.
Apa yang terjadi adalah sebuah situs web (sebut saja Contoso.com di sini, yang menjalankan aplikasi e-commerce publik di ranah web ASP.NET kecil) menghubungi tim saya dan mengeluh bahwa mereka mengalami kesalahan "cross-threading". Pelanggan yang menggunakan situs web Contoso.com sering kali tiba-tiba kehilangan data yang dimasukkan, namun malah melihat data terkait pengguna lain. Setelah sedikit analisis, kami menemukan bahwa deskripsi cross-threading tidak akurat; kesalahan "cross-session" lebih tepat. Tampaknya Contoso.com menyimpan data dalam status sesi, dan karena alasan tertentu pengguna sesekali dan secara acak terhubung ke sesi pengguna lain.
Salah satu anggota tim saya menulis alat diagnostik yang mencatat elemen kunci dari setiap permintaan dan respons HTTP, termasuk header Cookie. Dia kemudian menginstal alat tersebut di server Web Contoso.com dan membiarkannya berjalan selama beberapa hari. Hasilnya sangat jelas. Sekitar sekali dalam setiap 100.000 permintaan, ASP.NET dengan benar menetapkan ID sesi ke sesi yang benar-benar baru dan mengembalikan ID sesi di header Set-Cookie. Kemudian mengembalikan ID sesi yang sama (yaitu, header Set-Cookie yang sama) pada permintaan berikutnya yang berdekatan, meskipun permintaan tersebut telah dikaitkan dengan sesi yang valid dan ID sesi dalam cookie telah dikirimkan dengan benar. Akibatnya, ASP.NET secara acak mengalihkan pengguna dari sesi mereka sendiri dan menghubungkan mereka ke sesi lain.
Kami terkejut dan berusaha mencari tahu alasannya. Kami pertama kali memeriksa kode sumber Contoso.com dan, yang membuat kami lega, masalahnya tidak ada di sana. Selanjutnya, untuk memastikan bahwa masalahnya tidak terkait dengan host aplikasi di ranah Web, kami hanya membiarkan satu server berjalan dan mematikan semua server lainnya. Masalahnya tetap ada, yang tidak mengejutkan karena log kami menunjukkan bahwa header Set-Cookie yang cocok tidak pernah berasal dari dua server berbeda. ASP.NET secara tidak sengaja menghasilkan ID sesi duplikat, yang luar biasa karena menggunakan kelas .NET Framework RNGCryptoServiceProvider untuk menghasilkan ID ini, dan ID sesi cukup panjang untuk memastikan bahwa ID yang sama tidak pernah dibuat dua kali (setidaknya pada saat berikutnya). tidak akan dihasilkan dua kali dalam triliunan tahun). Selain itu, meskipun RNGCryptoServiceProvider secara keliru menghasilkan nomor acak berulang, hal ini tidak menjelaskan mengapa ASP.NET secara misterius mengganti ID sesi yang valid dengan yang baru (yang tidak unik).
Berdasarkan firasat, kami memutuskan untuk melihat cache keluaran. Ketika OutputCacheModule menyimpan respons HTTP dalam cache, ia harus berhati-hati untuk tidak menyimpan header Set-Cookie dalam cache; jika tidak, respons cache yang berisi ID sesi baru akan menghubungkan semua penerima respons cache (dan pengguna yang permintaannya menghasilkan respons cache) ke sesi yang sama. Kami memeriksa kode sumber; Contoso.com mengaktifkan cache keluaran di kedua halaman. Kami mematikan cache keluaran. Hasilnya, aplikasi berjalan selama beberapa hari tanpa satu pun masalah lintas sesi. Setelah itu, berjalan tanpa kesalahan selama lebih dari dua tahun. Di perusahaan lain dengan aplikasi berbeda dan kumpulan server web berbeda, kami melihat masalah yang sama hilang. Sama seperti di Contoso.com, menghilangkan cache keluaran akan menyelesaikan masalah.
Microsoft kemudian mengonfirmasi bahwa perilaku ini berasal dari masalah di OutputCacheModule. (Pembaruan mungkin telah dirilis pada saat Anda membaca artikel ini.) Ketika ASP.NET digunakan dengan IIS 6.0 dan caching mode kernel diaktifkan, OutputCacheModule terkadang gagal menghapus header Set-Cookie dari respons cache yang diteruskannya ke Http.sys. Berikut ini adalah urutan spesifik kejadian yang menyebabkan kesalahan:
• Pengguna yang belum mengunjungi situs tersebut baru-baru ini (sehingga tidak memiliki sesi terkait) meminta halaman yang memiliki caching keluaran yang diaktifkan, namun keluarannya saat ini tidak tersedia di cache.
• Permintaan mengeksekusi kode yang mengakses sesi pengguna yang paling baru dibuat, menyebabkan cookie ID sesi dikembalikan dalam header Set-Cookie dari respons.
• OutputCacheModule memberikan output ke Http.sys, namun tidak dapat menghapus header Set-Cookie dari respons.
• Http.sys mengembalikan respons yang di-cache pada permintaan berikutnya, sehingga secara keliru menghubungkan pengguna lain ke sesi tersebut.
Pesan moral dari cerita ini? Status sesi dan cache keluaran mode kernel tidak dapat digabungkan. Jika Anda menggunakan status sesi di halaman dengan caching keluaran diaktifkan, dan aplikasi berjalan pada IIS 6.0, Anda perlu mematikan caching keluaran mode kernel. Anda masih akan mendapat manfaat dari caching keluaran, tetapi karena caching keluaran mode kernel jauh lebih cepat daripada caching keluaran normal, caching tidak akan seefisien itu. Untuk informasi selengkapnya tentang masalah ini, lihat support.microsoft.com/kb/917072.
Anda dapat menonaktifkan caching output mode kernel untuk halaman individual dengan menyertakan atribut VaryByParam="*" di direktif OutputCache halaman, meskipun hal ini dapat mengakibatkan peningkatan kebutuhan memori secara tiba-tiba. Pendekatan lain yang lebih aman adalah mematikan caching mode kernel untuk seluruh aplikasi dengan menyertakan elemen berikut di web.config:
<httpRuntime aktifkanKernelOutputCache="false" />
Anda juga dapat menggunakan pengaturan registri untuk menonaktifkan caching keluaran mode kernel secara global, yaitu menonaktifkan caching keluaran mode kernel untuk semua server. Lihat support.microsoft.com/kb/820129 untuk detailnya.
Setiap kali saya mendengar pelanggan melaporkan masalah sesi yang membingungkan, saya bertanya kepada mereka apakah mereka menggunakan cache keluaran pada halaman mana pun. Jika mereka menggunakan caching keluaran, dan OS hostnya adalah Windows Server 2003, saya akan merekomendasikan agar mereka menonaktifkan caching keluaran mode kernel. Masalahnya biasanya terpecahkan. Jika masalah tidak teratasi, berarti ada bug pada kode. Waspada!
Kembali ke atas
Formulir Otentikasi Tiket Seumur Hidup Bisakah Anda mengidentifikasi masalah dengan kode berikut?
FormsAuthentication.RedirectFromLoginPage(nama pengguna, benar);
Kode ini mungkin tampak baik-baik saja, namun tidak boleh digunakan dalam aplikasi ASP.NET 1.x kecuali kode di tempat lain dalam aplikasi mengimbangi efek negatif dari pernyataan ini. Jika Anda tidak yakin alasannya, teruslah membaca.
FormsAuthentication.RedirectFromLoginPage melakukan dua tugas. Pertama, ketika FormsAuthenticationModule mengarahkan pengguna ke halaman login, FormsAuthentication.RedirectFromLoginPage mengarahkan pengguna ke halaman yang awalnya mereka minta. Kedua, ia mengeluarkan tiket otentikasi (biasanya dibawa dalam cookie, dan selalu dibawa dalam cookie di ASP.NET 1.x) yang memungkinkan pengguna untuk tetap diautentikasi untuk jangka waktu yang telah ditentukan.
Masalahnya terletak pada periode ini. Di ASP.NET 1.x, meneruskan parameter lain yang salah ke RedirectFromLoginPage akan mengeluarkan tiket autentikasi sementara yang kedaluwarsa setelah 30 menit secara default. (Anda dapat mengubah periode batas waktu menggunakan atribut Timeout di elemen web.config.) Namun, meneruskan parameter true lainnya akan mengeluarkan tiket otentikasi permanen yang berlaku selama 50 tahun! Ini menimbulkan masalah karena Jika seseorang mencuri otentikasi itu tiket, mereka dapat menggunakan identitas korban untuk mengakses situs web selama durasi tiket. Ada banyak cara untuk mencuri tiket otentikasi — menyelidiki lalu lintas yang tidak terenkripsi pada titik akses nirkabel publik, membuat skrip di seluruh situs web, mendapatkan akses fisik ke komputer korban, dll. — jadi meneruskan true ke RedirectFromLoginPage lebih aman daripada menonaktifkan situs web Anda. Tidak jauh lebih baik. Untungnya, masalah ini telah teratasi di ASP.NET 2.0. RedirectFromLoginPage sekarang menerima batas waktu yang ditentukan di web.config untuk tiket otentikasi sementara dan permanen dengan cara yang sama.
Salah satu solusinya adalah dengan tidak pernah memberikan nilai true pada parameter kedua RedirectFromLoginPage di aplikasi ASP.NET 1.x. Namun hal ini tidak praktis karena halaman login sering kali menampilkan kotak "Biarkan saya tetap login" yang dapat dicentang oleh pengguna untuk menerima cookie autentikasi permanen, bukan cookie autentikasi sementara. Solusi lain adalah dengan menggunakan cuplikan kode di Global.asax (atau modul HTTP jika Anda mau), yang memodifikasi cookie yang berisi tiket otentikasi permanen sebelum dikembalikan ke browser.
Gambar 3 berisi salah satu cuplikan kode tersebut. Jika cuplikan kode ini berada di Global.asax, cuplikan ini akan mengubah properti Expires dari cookie autentikasi Formulir permanen keluar sehingga cookie akan kedaluwarsa setelah 24 jam. Anda dapat mengatur batas waktu ke tanggal mana pun yang Anda suka dengan mengubah baris yang diberi komentar "Tanggal kedaluwarsa baru".
Anda mungkin merasa aneh bahwa metode Application_EndRequest memanggil metode Helper lokal (GetCookieFromResponse) untuk memeriksa cookie otentikasi untuk respons keluar. Metode Helper adalah solusi untuk bug lain di ASP.NET 1.1 yang menyebabkan cookie palsu ditambahkan ke respons jika Anda menggunakan generator indeks string HttpCookieCollection untuk memeriksa cookie yang tidak ada. Menggunakan generator indeks bilangan bulat sebagai GetCookieFromResponse memecahkan masalah.
Kembali ke Atas Status Tampilan: Pembunuh Performa Senyap Dalam arti tertentu, status tampilan adalah hal terhebat yang pernah ada. Lagi pula, status tampilan memungkinkan halaman dan kontrol mempertahankan status di antara postback. Oleh karena itu, Anda tidak perlu menulis kode untuk mencegah teks dalam kotak teks menghilang ketika tombol diklik, atau untuk meminta ulang database dan mengikat ulang DataGrid setelah postback, seperti yang Anda lakukan di ASP tradisional.
Namun kondisi tampilan memiliki sisi negatifnya: Jika ukurannya terlalu besar, kondisi tampilan akan menjadi pembunuh kinerja secara diam-diam. Beberapa kontrol, seperti kotak teks, membuat keputusan berdasarkan status tampilan. Kontrol lain (terutama DataGrid dan GridView) menentukan status tampilannya berdasarkan jumlah informasi yang ditampilkan. Saya akan gentar jika GridView menampilkan 200 atau 300 baris data. Meskipun status tampilan ASP.NET 2.0 kira-kira setengah dari ukuran status tampilan ASP.NET 1.x, GridView yang buruk dapat dengan mudah mengurangi bandwidth efektif koneksi antara browser dan server Web sebesar 50% atau lebih.
Anda dapat menonaktifkan status tampilan untuk masing-masing kontrol dengan menyetel EnableViewState ke false, namun beberapa kontrol (terutama DataGrid) kehilangan beberapa fungsi saat tidak dapat menggunakan status tampilan. Solusi yang lebih baik untuk mengontrol status tampilan adalah dengan menyimpannya di server. Di ASP.NET 1.x, Anda dapat mengganti metode LoadPageStateFromPersistenceMedium dan SavePageStateToPersistenceMedium halaman dan menangani status tampilan sesuai keinginan Anda. Kode pada Gambar 4 menunjukkan override yang mencegah status tampilan dipertahankan di bidang tersembunyi dan malah mempertahankannya dalam status sesi. Menyimpan status tampilan dalam status sesi sangat efektif bila digunakan dengan model proses status sesi default (yaitu, saat status sesi disimpan dalam proses pekerja ASP.NET di memori). Sebaliknya, jika status sesi disimpan dalam database, hanya pengujian yang dapat menunjukkan apakah mempertahankan status tampilan dalam status sesi akan meningkatkan atau menurunkan performa.
Pendekatan yang sama digunakan di ASP.NET 2.0, namun ASP.NET 2.0 menyediakan cara yang lebih mudah untuk mempertahankan status tampilan dalam status sesi. Pertama, tentukan adaptor halaman kustom yang metode GetStatePersisternya mengembalikan instance kelas .NET Framework SessionPageStatePersister:
public class SessionPageStateAdapter :System.Web.UI.Adapters.PageAdapter{public override PageStatePersister GetStatePersister () {return new SessionPageStatePersister(this.Page ) ;
Kemudian, daftarkan adaptor halaman kustom sebagai adaptor halaman default dengan menempatkan file App.browsers ke dalam folder App_Browsers aplikasi Anda sebagai berikut:
<browsers><browser refID="Default"><controlAdapters><adapter controlType=" System.Web. UI.Page"adapterType="SessionPageStateAdapter" /></controlAdapters></browser></browsers>
(Anda dapat memberi nama file apa pun yang Anda suka, asalkan memiliki ekstensi .browsers.) Setelah itu, ASP.NET memuat adaptor halaman dan menggunakan SessionPageStatePersister yang dikembalikan untuk mempertahankan semua status halaman, termasuk status tampilan.
Salah satu kelemahan menggunakan adaptor halaman khusus adalah adaptor ini berlaku secara global untuk setiap halaman dalam aplikasi. Jika Anda lebih memilih untuk mempertahankan status tampilan beberapa halaman dalam status sesi tetapi tidak pada halaman lainnya, gunakan metode yang ditunjukkan pada Gambar 4. Selain itu, Anda mungkin mengalami masalah dalam menggunakan metode ini jika pengguna membuat beberapa jendela browser dalam sesi yang sama.
Kembali ke atas
Status sesi SQL Server: Pembunuh kinerja lainnya
ASP.NET memudahkan untuk menyimpan status sesi dalam database: cukup alihkan tombol di web.config dan status sesi dengan mudah dipindahkan ke database backend. Ini adalah fitur penting untuk aplikasi yang berjalan di ranah Web karena memungkinkan setiap server di ranah berbagi repositori umum status sesi. Aktivitas database yang ditambahkan mengurangi performa permintaan individual, namun peningkatan skalabilitas menggantikan hilangnya performa.
Kedengarannya bagus, tapi segalanya berubah ketika Anda mempertimbangkan beberapa hal:
• Bahkan dalam aplikasi yang menggunakan status sesi, sebagian besar halaman tidak menggunakan status sesi.
• Secara default, manajer status sesi ASP.NET melakukan dua akses (satu akses baca dan satu akses tulis) ke penyimpanan data sesi di setiap permintaan, terlepas dari apakah halaman yang diminta menggunakan status sesi.
Dengan kata lain, saat Anda menggunakan opsi status sesi SQL Server™, Anda membayar harga (dua akses database) pada setiap permintaan—bahkan pada permintaan untuk halaman yang tidak ada hubungannya dengan status sesi. Hal ini berdampak negatif langsung pada throughput seluruh situs web.
Gambar 5 Hilangkan akses database status sesi yang tidak perlu
Jadi apa yang harus Anda lakukan? Sederhana saja: nonaktifkan status sesi di halaman yang tidak menggunakan status sesi. Ini selalu merupakan ide bagus, tapi ini sangat penting ketika status sesi disimpan dalam database. Gambar 5 menunjukkan cara menonaktifkan status sesi. Jika halaman tidak menggunakan status sesi sama sekali, sertakan EnableSessionState="false" di direktif Halamannya, seperti ini:
<%@ Page EnableSessionState="false" ... %>
Arahan ini mencegah manajer status sesi membaca dan menulis ke database status sesi pada setiap permintaan. Jika halaman membaca data dari status sesi tetapi tidak menulis data (yaitu, tidak mengubah konten sesi pengguna), setel EnableSessionState ke ReadOnly sebagai berikut:
<%@ Page EnableSessionState="ReadOnly" ... %>
Terakhir, jika halaman memerlukan akses baca/tulis ke status sesi, hilangkan properti EnableSessionState atau setel ke true:
<%@ Page EnableSessionState="true" ... %>
Dengan mengendalikan status sesi dengan cara ini, Anda memastikan bahwa ASP.NET hanya mengakses database status sesi ketika benar-benar diperlukan. Menghilangkan akses database yang tidak diperlukan adalah langkah pertama dalam membangun aplikasi berkinerja tinggi.
Omong-omong, properti EnableSessionState bersifat publik. Properti ini telah didokumentasikan sejak ASP.NET 1.0, namun saya masih jarang melihat pengembang memanfaatkannya. Mungkin karena itu tidak terlalu penting untuk model status sesi default di memori. Tapi ini penting untuk model SQL Server.
Kembali ke Atas Peran yang Tidak Di-cache Pernyataan berikut sering muncul di file web.config aplikasi ASP.NET 2.0 dan dalam contoh yang memperkenalkan manajer peran ASP.NET 2.0:
<roleManager diaktifkan="benar" />
Namun seperti yang ditunjukkan di atas, pernyataan ini memang memiliki dampak negatif yang signifikan terhadap kinerja. Tahukah kamu alasannya?
Secara default, manajer peran ASP.NET 2.0 tidak menyimpan data peran dalam cache. Sebaliknya, ia berkonsultasi dengan penyimpanan data peran setiap kali diperlukan untuk menentukan peran mana, jika ada, yang dimiliki pengguna. Artinya, setelah pengguna diautentikasi, halaman apa pun yang memanfaatkan data peran (misalnya, halaman yang menggunakan peta situs dengan pengaturan kliping keamanan diaktifkan, dan halaman yang memiliki akses terbatas menggunakan arahan URL berbasis peran di web.config) akan menyebabkan peran tersebut manajer untuk menanyakan peran penyimpanan data. Jika peran disimpan dalam database, Anda dapat dengan mudah menghilangkan akses beberapa database untuk setiap permintaan. Solusinya adalah dengan mengonfigurasi manajer peran untuk menyimpan data peran dalam cookie:
<roleManager diaktifkan="benar" cacheRolesInCookie="benar" />
Anda dapat menggunakan atribut <roleManager> lainnya untuk mengontrol karakteristik cookie peran—misalnya, berapa lama cookie akan tetap valid (dan seberapa sering manajer peran kembali ke database peran). Cookie peran ditandatangani dan dienkripsi secara default, sehingga risiko keamanan, meskipun tidak nol, dapat dimitigasi.
Kembali ke serialisasi properti file topConfiguration
Layanan Profil ASP.NET 2.0 menyediakan solusi siap pakai untuk masalah pemeliharaan status per pengguna, seperti preferensi personalisasi dan preferensi bahasa. Untuk menggunakan layanan profil, Anda menentukan profil XML yang berisi atribut yang ingin Anda pertahankan atas nama pengguna individual. ASP.NET kemudian mengkompilasi kelas yang berisi properti yang sama dan menyediakan akses yang diketik dengan kuat ke instance kelas melalui properti file konfigurasi yang ditambahkan ke halaman.
Fleksibilitas profil sangat bagus sehingga memungkinkan tipe data khusus digunakan sebagai properti profil. Namun, ada masalah yang saya lihat secara pribadi menyebabkan pengembang melakukan kesalahan. Gambar 6 berisi kelas sederhana bernama Posts dan definisi profil yang menggunakan Posts sebagai atribut profil. Namun, kelas ini dan file konfigurasi ini menghasilkan perilaku yang tidak diharapkan saat runtime. Bisakah Anda mengetahui alasannya?
Masalahnya adalah Posts berisi bidang pribadi yang disebut _count, yang harus diserialisasi dan dideserialisasi agar dapat sepenuhnya membekukan dan membekukan ulang instance kelas. Namun, _count tidak diserialkan dan dideserialisasi karena bersifat pribadi dan Manajer Profil ASP.NET menggunakan serialisasi XML secara default untuk membuat serialisasi dan deserialisasi tipe kustom. Serializer XML mengabaikan anggota non-publik. Oleh karena itu, instance Postingan diserialisasi dan dideserialisasi, tetapi setiap kali instance kelas dideserialisasi, _count disetel ulang ke 0.
Salah satu solusinya adalah menjadikan _count sebagai bidang publik, bukan bidang pribadi. Solusi lain adalah dengan merangkum _count dengan properti baca/tulis publik. Solusi terbaik adalah menandai Posting sebagai serializable (menggunakan SerializableAttribute) dan mengonfigurasi manajer profil untuk menggunakan serializer biner .NET Framework untuk membuat serialisasi dan deserialisasi instance kelas. Solusi ini mempertahankan desain kelas itu sendiri. Tidak seperti serializer XML, serializer biner membuat serialisasi bidang terlepas dari apakah bidang tersebut dapat diakses. Gambar 7 menunjukkan versi tetap dari kelas Posting dan menyoroti perubahan definisi profil yang menyertainya.
Satu hal yang harus Anda ingat adalah jika Anda menggunakan tipe data khusus sebagai properti profil dan tipe data tersebut memiliki anggota data non-publik yang harus dibuat serial agar dapat membuat serialisasi sepenuhnya pada instance tipe tersebut, gunakan serializeAs=" Biner" di properti deklarasi properti dan pastikan bahwa tipe itu sendiri dapat diserialkan. Jika tidak, serialisasi penuh tidak akan terjadi, dan Anda akan membuang waktu untuk mencari tahu mengapa profil tersebut tidak berfungsi.
Kembali ke Atas Kumpulan Thread Saturasi Saya sering terkejut dengan jumlah sebenarnya halaman ASP.NET yang saya lihat ketika menjalankan query database dan menunggu 15 detik atau lebih agar hasil query dikembalikan. (Saya juga menunggu 15 menit sebelum melihat hasil kueri saya!) Terkadang penundaan merupakan konsekuensi yang tidak dapat dihindari dari banyaknya data yang dikembalikan; di lain waktu penundaan tersebut disebabkan oleh desain database yang buruk. Namun apa pun alasannya, kueri database yang panjang atau jenis operasi I/O yang panjang akan menyebabkan throughput menurun dalam aplikasi ASP.NET.
Saya telah menjelaskan masalah ini secara mendetail sebelumnya, jadi saya tidak akan membahasnya terlalu detail di sini. Cukuplah untuk mengatakan bahwa ASP.NET bergantung pada kumpulan thread terbatas untuk menangani permintaan. Jika semua thread sibuk menunggu permintaan database, panggilan layanan Web, atau operasi I/O lainnya selesai, thread tersebut akan dilepaskan saat operasi selesai. selesai. Sebelum thread dikeluarkan, permintaan lain harus diantri dan menunggu. Saat permintaan dimasukkan dalam antrean, kinerja turun drastis. Jika antrean penuh, ASP.NET menyebabkan permintaan berikutnya gagal dengan kesalahan HTTP 503. Ini bukan situasi yang ingin kami lihat pada aplikasi produksi di server Web produksi.
Solusinya adalah halaman asinkron, salah satu fitur ASP.NET 2.0 yang terbaik namun kurang dikenal. Permintaan untuk halaman asinkron dimulai pada thread, namun ketika memulai operasi I/O, permintaan tersebut kembali ke thread tersebut dan antarmuka IAsyncResult ASP.NET. Ketika operasi selesai, permintaan memberitahukan ASP.NET melalui IAsyncResult, dan ASP.NET menarik thread lain dari kumpulan dan menyelesaikan pemrosesan permintaan. Perlu dicatat bahwa ketika operasi I/O terjadi, tidak ada thread kumpulan thread yang ditempati. Hal ini dapat meningkatkan throughput secara signifikan dengan mencegah permintaan untuk halaman lain (halaman yang tidak melakukan operasi I/O yang panjang) menunggu dalam antrean.
Anda dapat membaca semua tentang halaman asinkron di Majalah MSDN® edisi Oktober 2005. Halaman apa pun yang terikat I/O, bukan terikat mesin, dan membutuhkan waktu lama untuk dieksekusi, mempunyai peluang bagus untuk menjadi halaman asinkron.
Ketika saya memberi tahu pengembang tentang halaman asinkron, mereka sering merespons dengan "Itu bagus, tapi saya tidak memerlukannya di aplikasi saya." Dan saya membalasnya dengan "Apakah ada halaman Anda yang perlu menanyakan database?" layanan web? Sudahkah Anda memeriksa penghitung kinerja ASP.NET untuk statistik permintaan antrian dan waktu tunggu rata-rata? Meskipun aplikasi Anda berjalan dengan baik sejauh ini, seiring bertambahnya ukuran klien Anda, "
Faktanya, sebagian besar aplikasi ASP.NET dunia nyata memerlukan halaman asinkron. Harap ingat ini!
Kembali ke Atas Peniruan Identitas dan Otorisasi ACL Berikut ini adalah arahan konfigurasi sederhana, namun membuat mata saya berbinar setiap kali melihatnya di web.config:
<identity impersonate="true" />
Arahan ini memungkinkan peniruan identitas sisi klien dalam aplikasi ASP.NET. Ini melampirkan token akses yang mewakili klien ke thread yang menangani permintaan sehingga pemeriksaan keamanan yang dilakukan oleh sistem operasi bertentangan dengan identitas klien dan bukan identitas proses pekerja. Aplikasi ASP.NET jarang memerlukan mocking; pengalaman saya memberi tahu saya bahwa pengembang sering kali mengaktifkan mocking karena alasan yang salah. Inilah alasannya.
Pengembang sering kali mengaktifkan peniruan identitas dalam aplikasi ASP.NET sehingga izin sistem file dapat digunakan untuk membatasi akses ke halaman. Jika Bob tidak memiliki izin untuk melihat Salaries.aspx, pengembang akan mengaktifkan peniruan identitas sehingga Bob dapat dicegah untuk melihat Salaries.aspx dengan mengatur daftar kontrol akses (ACL) untuk menolak izin membaca Bob. Namun ada bahaya tersembunyi berikut ini: peniruan identitas tidak diperlukan untuk otorisasi ACL. Saat Anda mengaktifkan Otentikasi Windows di aplikasi ASP.NET, ASP.NET secara otomatis memeriksa ACL untuk setiap halaman .aspx yang diminta dan menolak permintaan dari penelepon yang tidak memiliki izin untuk membaca file. Ini masih berperilaku seperti ini meskipun simulasi dinonaktifkan.
Terkadang perlu untuk membenarkan simulasi tersebut. Namun biasanya Anda bisa menghindarinya dengan desain yang bagus. Misalnya, asumsikan Salaries.aspx menanyakan database untuk informasi gaji yang hanya diketahui oleh manajer. Dengan peniruan identitas, Anda dapat menggunakan izin database untuk menolak kemampuan personel non-manajerial untuk menanyakan data penggajian. Atau Anda dapat mengabaikan peniruan identitas dan membatasi akses ke data penggajian dengan mengatur ACL untuk Salaries.aspx sehingga non-administrator tidak memiliki akses baca. Pendekatan terakhir memberikan kinerja yang lebih baik karena menghindari ejekan sepenuhnya. Ini juga menghilangkan akses database yang tidak perlu. Mengapa permintaan database ditolak hanya karena alasan keamanan?
Ngomong-ngomong, saya pernah membantu memecahkan masalah aplikasi ASP lama yang memulai ulang secara berkala karena jejak memori yang tidak dibatasi. Pengembang yang tidak berpengalaman mengubah pernyataan target SELECT menjadi SELECT * tanpa mempertimbangkan bahwa tabel yang ditanyakan berisi gambar yang besar dan banyak. Masalah ini diperburuk oleh kebocoran memori yang tidak terdeteksi. (Area kode terkelola saya!) Sebuah aplikasi yang telah berfungsi dengan baik selama bertahun-tahun tiba-tiba berhenti berfungsi karena pernyataan SELECT yang biasanya mengembalikan satu atau dua kilobyte data kini mengembalikan beberapa megabyte. Ditambah lagi dengan masalah kontrol versi yang tidak memadai, dan kehidupan tim pengembangan harus menjadi “hiperaktif” — dan dengan “hiperaktif,” ini seperti harus menonton anak-anak Anda memainkan permainan yang menjengkelkan saat Anda pergi ke sana. tidur di malam hari. Pertandingan sepak bola yang membosankan.
Secara teori, kebocoran memori tradisional tidak dapat terjadi pada aplikasi ASP.NET yang seluruhnya terdiri dari kode yang dikelola. Namun penggunaan memori yang tidak mencukupi dapat memengaruhi kinerja dengan memaksa pengumpulan sampah lebih sering terjadi. Bahkan dalam aplikasi ASP.NET, berhati-hatilah terhadap SELECT *!
Kembali ke atas Jangan bergantung sepenuhnya pada itu — siapkan file konfigurasi database!
Sebagai konsultan, saya sering ditanya mengapa aplikasi tidak berjalan sesuai harapan. Baru-baru ini, seseorang bertanya kepada tim saya mengapa aplikasi ASP.NET hanya menyelesaikan sekitar 1/100 throughput (permintaan per detik) yang diperlukan untuk meminta dokumen. Masalah yang kami temukan sebelumnya adalah masalah unik yang pernah kami lihat di aplikasi Web yang tidak berfungsi dengan baik—dan merupakan pelajaran yang harus kita tanggapi dengan serius.
Kami menjalankan SQL Server Profiler dan memantau interaksi antara aplikasi ini dan database back-end. Dalam kasus yang lebih ekstrim, hanya satu klik tombol yang menyebabkan lebih dari 1.500 kesalahan terjadi di database. Anda tidak dapat membangun aplikasi berkinerja tinggi seperti itu. Arsitektur yang baik selalu dimulai dengan desain basis data yang baik. Tidak peduli seberapa efisien kode Anda, itu tidak akan berhasil jika terbebani oleh database yang ditulis dengan buruk.
Arsitektur akses data yang buruk biasanya dihasilkan dari satu atau lebih dari yang berikut:
• Desain basis data yang buruk (biasanya dirancang oleh pengembang, bukan administrator basis data).
• Penggunaan kumpulan data dan dataAdapters - terutama DataAdapter.update, yang berfungsi dengan baik untuk aplikasi Windows Forms dan klien kaya lainnya, tetapi umumnya tidak ideal untuk aplikasi web.
• Lapisan akses data yang dirancang dengan buruk (DAL) yang memiliki perhitungan yang kurang diprogram dan mengkonsumsi banyak siklus CPU untuk melakukan operasi yang relatif sederhana.
Masalahnya harus diidentifikasi sebelum dapat diobati. Cara mengidentifikasi masalah akses data adalah dengan menjalankan SQL Server Profiler atau alat yang setara untuk melihat apa yang terjadi di balik layar. Tuning kinerja selesai setelah memeriksa komunikasi antara aplikasi dan database. Cobalah - Anda mungkin terkejut dengan apa yang Anda temukan.
Kembali ke kesimpulan teratas sekarang Anda tahu beberapa masalah dan solusi mereka yang mungkin Anda temui saat membangun aplikasi produksi ASP.NET. Langkah selanjutnya adalah melihat lebih dekat pada kode Anda sendiri dan mencoba menghindari beberapa masalah yang saya uraikan di sini. ASP.NET mungkin telah menurunkan penghalang untuk masuk untuk pengembang web, tetapi aplikasi Anda memiliki alasan untuk menjadi fleksibel, stabil, dan efisien. Harap pertimbangkan ini dengan cermat untuk menghindari kesalahan pemula.
Gambar 8 memberikan daftar periksa pendek yang dapat Anda gunakan untuk menghindari jebakan yang dijelaskan dalam artikel ini. Anda dapat membuat daftar periksa cacat keamanan yang serupa. Misalnya:
• Sudahkah Anda mengenkripsi bagian konfigurasi yang berisi data sensitif?
• Apakah Anda memeriksa dan memvalidasi input yang digunakan dalam operasi basis data dan apakah Anda menggunakan input yang dikodekan HTML sebagai output?
• Apakah direktori virtual Anda berisi file dengan ekstensi yang tidak dilindungi?
Pertanyaan -pertanyaan ini penting jika Anda menghargai integritas situs web Anda, server yang menampungnya, dan sumber daya backend yang mereka andalkan.
Jeff Prosise adalah editor yang berkontribusi untuk majalah MSDN dan penulis beberapa buku, termasuk pemrograman Microsoft .NET (Microsoft Press, 2002). Dia juga salah satu pendiri Wintellect, sebuah perusahaan konsultan perangkat lunak dan pendidikan.
Dari Majalah MSDN edisi Juli 2006.