Bagaimana cara kerja servlets? Instansiasi, sesi, variabel bersama, dan multithreading

1144

Misalkan, saya memiliki server web yang menampung banyak servlet. Untuk informasi yang lewat di antara servlet tersebut, saya mengatur variabel sesi dan instance.

Sekarang, jika 2 atau lebih pengguna mengirim permintaan ke server ini, lalu apa yang terjadi pada variabel sesi?
Akankah mereka semua menjadi umum untuk semua pengguna atau mereka akan berbeda untuk setiap pengguna?
Jika mereka berbeda, lalu bagaimana server dapat membedakan antara pengguna yang berbeda?

Satu lagi pertanyaan yang serupa, jika ada npengguna yang mengakses servlet tertentu, maka servlet ini akan dipakai hanya saat pertama kali pengguna pertama mengaksesnya atau apakah ia akan dipakai untuk semua pengguna secara terpisah?
Dengan kata lain, apa yang terjadi pada variabel instan?

Ku Jon
sumber

Jawaban:

1821

ServletContext

Ketika wadah servlet (seperti Apache Tomcat ) mulai, itu akan menyebarkan dan memuat semua aplikasi webnya. Ketika aplikasi web dimuat, wadah servlet membuat ServletContextsekali dan menyimpannya di memori server. Aplikasi web web.xmldan semua web-fragment.xmlfile yang disertakan diuraikan, dan masing-masing <servlet>, <filter>dan <listener>ditemukan (atau masing-masing kelas dijelaskan dengan @WebServlet, @WebFilterdan @WebListenermasing - masing) dipakai satu kali dan disimpan dalam memori server juga. Untuk setiap filter instantiated, init()metodenya dipanggil dengan yang baru FilterConfig.

Ketika Servletmemiliki <servlet><load-on-startup>atau @WebServlet(loadOnStartup)nilai lebih besar dari 0, maka init()metode ini juga dipanggil saat startup dengan yang baru ServletConfig. Servlet tersebut diinisialisasi dalam urutan yang sama yang ditentukan oleh nilai tersebut ( 1adalah 1, 22, dll). Jika nilai yang sama ditetapkan untuk lebih dari satu servlet, maka masing-masing servlets dimuat dalam urutan yang sama seperti yang muncul dalam web.xml, web-fragment.xmlatau @WebServletclassloading. Jika nilai "load-on-startup" tidak ada, init()metode akan dipanggil kapan saja permintaan HTTP mengenai servlet untuk pertama kalinya.

Ketika wadah servlet selesai dengan semua langkah inisialisasi yang dijelaskan di atas, maka ServletContextListener#contextInitialized()akan dipanggil.

Ketika servlet kontainer menutup, itu membongkar semua aplikasi web, memanggil destroy()metode semua servlets yang diinisialisasi dan filter, dan semua ServletContext, Servlet, Filterdan Listenercontoh yang menghancurkan. Akhirnya ServletContextListener#contextDestroyed()akan dipanggil.

HttpServletRequest dan HttpServletResponse

Wadah servlet dilampirkan ke server web yang mendengarkan permintaan HTTP pada nomor port tertentu (port 8080 biasanya digunakan selama pengembangan dan port 80 dalam produksi). Ketika klien (misalnya pengguna dengan browser web, atau menggunakan programURLConnection ) mengirim permintaan HTTP, wadah servlet membuat objek HttpServletRequestdan baru HttpServletResponsedan melewati mereka melalui apa pun yang ditentukan Filterdalam rantai dan, akhirnya, Servletinstance.

Dalam kasus filter , doFilter()metode dipanggil. Ketika kode penampung servlet memanggil chain.doFilter(request, response), permintaan dan tanggapan berlanjut ke filter berikutnya, atau tekan servlet jika tidak ada filter yang tersisa.

Dalam kasus servlets , service()metode dipanggil. Secara default, metode ini menentukan salah satu doXxx()metode yang akan digunakan berdasarkan request.getMethod(). Jika metode yang ditentukan tidak ada dari servlet, maka kesalahan HTTP 405 dikembalikan dalam respons.

Objek permintaan menyediakan akses ke semua informasi tentang permintaan HTTP, seperti URL, header, string kueri, dan badan. Objek respons memberikan kemampuan untuk mengontrol dan mengirim respons HTTP seperti yang Anda inginkan, misalnya, memungkinkan Anda untuk mengatur header dan isi (biasanya dengan konten HTML yang dihasilkan dari file JSP). Ketika respons HTTP dilakukan dan diselesaikan, objek permintaan dan respons didaur ulang dan tersedia untuk digunakan kembali.

HttpSession

Ketika klien mengunjungi webapp untuk pertama kalinya dan / atau HttpSessiondiperoleh untuk pertama kalinya melalui request.getSession(), wadah servlet membuat HttpSessionobjek baru , menghasilkan ID panjang dan unik (yang bisa Anda dapatkan session.getId()), dan menyimpannya di server. Penyimpanan. Wadah servlet juga menetapkan a Cookiedi Set-Cookieheader respons HTTP dengan JSESSIONIDsebagai namanya dan ID sesi unik sebagai nilainya.

Sesuai spesifikasi cookie HTTP (kontrak yang harus dipatuhi oleh setiap browser web dan server web yang layak), klien (browser web) diharuskan untuk mengirim cookie ini kembali dalam permintaan berikutnya di Cookieheader selama cookie tersebut valid (cookie tersebut valid) yaitu ID unik harus merujuk ke sesi yang tidak kedaluwarsa dan domain dan jalurnya benar). Menggunakan monitor lalu lintas HTTP bawaan browser Anda, Anda dapat memverifikasi bahwa cookie tersebut valid (tekan F12 di Chrome / Firefox 23+ / IE9 +, dan periksa tab Net / Network ). Wadah servlet akan memeriksa Cookietajuk setiap permintaan HTTP yang masuk untuk mengetahui keberadaan cookie dengan nama JSESSIONIDdan menggunakan nilainya (ID sesi) untuk mendapatkan yang terkait HttpSessiondari memori server.

The HttpSessiontetap hidup sampai telah diam (yaitu tidak digunakan dalam permintaan) untuk lebih dari nilai batas waktu yang ditentukan dalam <session-timeout>, pengaturan di web.xml. Nilai batas waktu default ke 30 menit. Jadi, ketika klien tidak mengunjungi aplikasi web lebih lama dari waktu yang ditentukan, wadah servlet merusak sesi. Setiap permintaan berikutnya, bahkan dengan cookie yang ditentukan, tidak akan memiliki akses ke sesi yang sama lagi; wadah servlet akan membuat sesi baru.

Di sisi klien, cookie sesi tetap hidup selama instance browser berjalan. Jadi, jika klien menutup instance browser (semua tab / windows), maka sesi tersebut dibuang ke sisi klien. Dalam instance browser baru, cookie yang terkait dengan sesi tidak akan ada, sehingga cookie tidak akan lagi dikirim. Hal ini menyebabkan pembuatan yang sama sekali baru HttpSession, dengan cookie sesi yang sama sekali baru digunakan.

Pendeknya

  • The ServletContexthidup selama kehidupan aplikasi web. Itu dibagikan di antara semua permintaan di semua sesi.
  • The HttpSessionhidup selama klien berinteraksi dengan aplikasi web dengan contoh browser yang sama, dan sesi belum habis waktunya di sisi server. Itu dibagikan di antara semua permintaan dalam sesi yang sama .
  • Itu HttpServletRequestdan HttpServletResponsehidup dari saat servlet menerima permintaan HTTP dari klien, sampai respons lengkap (halaman web) telah tiba. Itu tidak dibagikan di tempat lain.
  • Semua Servlet, Filterdan Listenercontoh hidup selama aplikasi web hidup. Mereka dibagikan di antara semua permintaan di semua sesi.
  • Apa pun attributeyang didefinisikan ServletContext, HttpServletRequestdan HttpSessionakan hidup selama objek yang dipermasalahkan hidup. Objek itu sendiri mewakili "ruang lingkup" dalam kerangka kerja manajemen kacang seperti JSF, CDI, Spring, dll. Kerangka kerja tersebut menyimpan kacang scoping mereka sebagai attributelingkup pencocokan terdekat.

Keamanan Thread

Yang mengatakan, perhatian utama Anda mungkin keamanan benang . Anda sekarang harus tahu bahwa servlet dan filter dibagikan di antara semua permintaan. Itu hal yang menyenangkan tentang Java, itu multithreaded dan utas berbeda (baca: permintaan HTTP) dapat menggunakan contoh yang sama. Kalau tidak akan terlalu mahal untuk dibuat ulang, init()dan destroy()mereka untuk setiap permintaan tunggal.

Anda juga harus menyadari bahwa Anda tidak boleh menetapkan permintaan atau data cakupan sesi sebagai variabel instan dari servlet atau filter. Ini akan dibagikan di antara semua permintaan lain di sesi lain. Itu bukan thread-safe! Contoh di bawah menggambarkan ini:

public class ExampleServlet extends HttpServlet {

    private Object thisIsNOTThreadSafe;

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        Object thisIsThreadSafe;

        thisIsNOTThreadSafe = request.getParameter("foo"); // BAD!! Shared among all requests!
        thisIsThreadSafe = request.getParameter("foo"); // OK, this is thread safe.
    } 
}

Lihat juga:

BalusC
sumber
25
Jadi ketika saya entah bagaimana bisa mengetahui JSessionId yang akan dikirim ke klien, saya bisa mencuri sesi nya?
Toskan
54
@Toskan: itu benar. Ini dikenal sebagai hack sesi fixation . Harap dicatat bahwa ini tidak khusus untuk JSP / Servlet. Semua bahasa sisi server lain yang mengelola sesi oleh cookie juga sensitif, seperti PHP dengan PHPSESSIDcookie, ASP.NET dengan ASP.NET_SessionIDcookie, dan sebagainya. Itu juga alasan penulisan ulang URL;jsessionid=xxx karena beberapa kerangka kerja JSP / Servlet MVC secara otomatis disukai. Pastikan ID sesi tidak pernah dibuka di URL atau dengan cara lain di laman web sehingga pengguna yang tidak sadar tidak akan diserang.
BalusC
11
@Toskan: Pastikan juga bahwa aplikasi web Anda tidak sensitif terhadap serangan XSS. Yaitu tidak menampilkan kembali input yang dikontrol pengguna dalam bentuk yang tidak terhapus. XSS membuka pintu bagi cara untuk mengumpulkan ID sesi semua pengguna akhir. Lihat juga Apa konsep umum di balik XSS?
BalusC
2
@ BalusC, Maaf atas kebodohan saya. Ini berarti semua pengguna mengakses instance yang sama dari iniIsNOTThreadSafe bukan?
menaungi
4
@ TwoThumbSticks 404 dikembalikan ketika seluruh servlet itu sendiri tidak ada. 405 dikembalikan ketika servlet hadir tetapi metode doXxx () yang diinginkan tidak diterapkan.
BalusC
428

Sesi

masukkan deskripsi gambar di sini masukkan deskripsi gambar di sini

Singkatnya: server web mengeluarkan pengidentifikasi unik untuk setiap pengunjung pada kunjungan pertamanya . Pengunjung harus membawa kembali kartu identitas itu untuk dikenali di waktu berikutnya. Pengidentifikasi ini juga memungkinkan server untuk memisahkan dengan benar objek yang dimiliki oleh satu sesi terhadap sesi lainnya.

Instansiasi Servlet

Jika beban-on-startup adalah palsu :

masukkan deskripsi gambar di sini masukkan deskripsi gambar di sini

Jika beban-on-startup adalah benar :

masukkan deskripsi gambar di sini masukkan deskripsi gambar di sini

Setelah dia berada di mode layanan dan di alur, servlet yang sama akan bekerja pada permintaan dari semua klien lain.

masukkan deskripsi gambar di sini

Mengapa bukan ide yang baik untuk memiliki satu instance per klien? Pikirkan tentang ini: Apakah Anda akan mempekerjakan satu orang pizza untuk setiap pesanan yang datang? Lakukan itu dan Anda akan keluar dari bisnis dalam waktu singkat.

Muncul dengan risiko kecil. Ingat: pria lajang ini menyimpan semua informasi pesanan di sakunya: jadi jika Anda tidak berhati-hati tentang keamanan utas pada servlet , ia mungkin akhirnya memberikan pesanan yang salah kepada klien tertentu.

Jop
sumber
26
Gambar Anda sangat bagus untuk pemahaman saya. Saya punya satu pertanyaan, Apa yang akan dilakukan restoran pizza ini ketika terlalu banyak pesanan pizza datang, hanya menunggu satu pizza pria atau menyewa lebih banyak pizza pria? Terima kasih.
zh18
6
Dia akan mengembalikan pesan denganto many requests at this moment. try again later
Please_Dont_Bully_Me_SO_Lords
3
Servlet, tidak seperti pengantar Pizza, dapat melakukan lebih dari satu pengiriman sekaligus. Mereka hanya perlu berhati-hati di mana mereka menuliskan alamat klien, rasa pizza ...
bruno
42

Session in Java servlets sama dengan sesi dalam bahasa lain seperti PHP. Ini unik untuk pengguna. Server dapat melacaknya dengan cara yang berbeda seperti cookie, penulisan ulang url dll. Artikel Java doc ini menjelaskannya dalam konteks Java servlets dan menunjukkan bahwa bagaimana tepatnya sesi dikelola adalah detail implementasi yang diserahkan kepada perancang server. Spesifikasi hanya menetapkan bahwa itu harus dipertahankan sebagai unik bagi pengguna di beberapa koneksi ke server. Lihat artikel ini dari Oracle untuk informasi lebih lanjut tentang kedua pertanyaan Anda.

Sunting Ada tutorial yang sangat baik di sini tentang cara bekerja dengan sesi di dalam servlets. Dan di sini adalah bab dari Sun tentang Java Servlets, apa yang mereka dan bagaimana menggunakannya. Di antara dua artikel itu, Anda harus bisa menjawab semua pertanyaan Anda.

Chris Thompson
sumber
Ini memunculkan pertanyaan lain bagi saya, Karena hanya ada satu konteks servlet untuk seluruh aplikasi dan kami mendapatkan akses ke variabel sesi melalui servletcontext ini, jadi bagaimana variabel sesi bisa unik untuk setiap pengguna? Terima kasih ..
Ku Jon
1
bagaimana Anda mengakses sesi dari servletContext? Anda tidak merujuk ke servletContext.setAttribute (), bukan?
matt b
4
@ KuJon Setiap aplikasi web memiliki satu ServletContextobjek. Objek itu memiliki nol, satu, atau lebih objek sesi - kumpulan objek sesi. Setiap sesi diidentifikasi oleh semacam string pengidentifikasi, seperti yang terlihat dalam kartun pada jawaban lainnya. Pengidentifikasi itu dilacak pada klien dengan cookie atau penulisan ulang URL. Setiap objek sesi memiliki variabelnya sendiri.
Basil Bourque
33

Ketika wadah servlet (seperti Apache Tomcat) dijalankan, ia akan membaca dari file web.xml (hanya satu per aplikasi) jika ada yang salah atau muncul kesalahan di konsol sisi wadah, jika tidak, ia akan menyebarkan dan memuat semua web aplikasi dengan menggunakan web.xml (dinamakan demikian sebagai deployment descriptor).

Selama fase instantiation servlet, instance servlet siap tetapi tidak dapat melayani permintaan klien karena hilang dengan dua bagian informasi:
1: informasi konteks
2: informasi konfigurasi awal

Mesin servlet membuat objek antarmuka servletConfig merangkum informasi yang hilang di atas ke dalamnya panggilan mesin servlet init () dari servlet dengan menyediakan referensi objek servletConfig sebagai argumen. Setelah init () benar-benar dieksekusi, servlet siap untuk melayani permintaan klien.

T) Dalam masa hidup servlet berapa kali instantiasi dan inisialisasi terjadi ??

A) hanya sekali (untuk setiap permintaan klien dibuat utas baru) hanya satu contoh servlet yang melayani sejumlah permintaan klien yaitu, setelah melayani satu server permintaan klien tidak mati. Ia menunggu permintaan klien lain yaitu batasan CGI (untuk setiap permintaan klien dibuat proses baru) diatasi dengan servlet (mesin servlet internal membuat utas).

Q) Bagaimana konsep sesi bekerja?

A) kapan saja getSession () dipanggil pada objek HttpServletRequest

Langkah 1 : objek permintaan dievaluasi untuk ID sesi masuk.

Langkah 2 : jika ID tidak tersedia, objek HttpSession baru dibuat dan sesi ID yang sesuai dihasilkan (yaitu dari HashTable) sesi ID disimpan ke objek respons httpservlet dan referensi objek HttpSession dikembalikan ke servlet (doGet / doPost) .

Langkah 3 : jika ID objek sesi baru yang tersedia tidak dibuat, sesi ID diambil dari pencarian objek permintaan yang dibuat dalam kumpulan sesi dengan menggunakan ID sesi sebagai kuncinya.

Setelah pencarian berhasil, ID sesi disimpan ke dalam HttpServletResponse dan referensi objek sesi yang ada dikembalikan ke doGet () atau doPost () dari UserDefineservlet.

catatan:

1) ketika kontrol keluar dari kode servlet ke klien jangan lupa bahwa objek sesi dipegang oleh wadah servlet yaitu, mesin servlet

2) multithreading diserahkan kepada pengembang servlet orang untuk mengimplementasikan mis., Menangani beberapa permintaan klien, tidak ada masalah dengan kode multithread

Formulir inshort:

Sebuah servlet dibuat ketika aplikasi dijalankan (ditempatkan pada wadah servlet) atau ketika diakses pertama kali (tergantung pada pengaturan load-on-startup) ketika servlet dipakai, metode init () dari servlet disebut kemudian servlet (satu-satunya instance) menangani semua permintaan (layanannya () metode dipanggil oleh banyak utas). Itu sebabnya tidak disarankan untuk melakukan sinkronisasi di dalamnya, dan Anda harus menghindari variabel instan dari servlet ketika aplikasi tidak digunakan (wadah servlet berhenti), metode destroy () dipanggil.

Ajay Takur
sumber
20

Sesi - apa yang dikatakan Chris Thompson.

Instantiasi - servlet di-instantiated ketika kontainer menerima permintaan pertama yang dipetakan ke servlet (kecuali servlet dikonfigurasikan untuk memuat saat startup dengan <load-on-startup>elemen di dalamnya web.xml). Contoh yang sama digunakan untuk melayani permintaan berikutnya.

Lauri Lehtinen
sumber
3
Benar. Pemikiran tambahan: Setiap permintaan mendapat utas baru (atau daur ulang) untuk dijalankan pada instance Servlet tunggal itu. Setiap Servlet memiliki satu instance, dan mungkin banyak utas (jika banyak permintaan simultan).
Basil Bourque
13

Spesifikasi Servlet JSR-315 dengan jelas mendefinisikan perilaku wadah web dalam metode layanan (dan doGet, doPost, doPut dll.) (2.3.3.1 Masalah Multithreading, Halaman 9):

Wadah servlet dapat mengirim permintaan bersamaan melalui metode layanan servlet. Untuk menangani permintaan, Pengembang Servlet harus membuat ketentuan yang memadai untuk pemrosesan bersamaan dengan beberapa utas dalam metode layanan.

Meskipun tidak disarankan, alternatif untuk Pengembang adalah mengimplementasikan antarmuka SingleThreadModel yang membutuhkan wadah untuk menjamin bahwa hanya ada satu utas permintaan pada suatu waktu dalam metode layanan. Wadah servlet dapat memenuhi persyaratan ini dengan membuat serial permintaan di servlet, atau dengan memelihara kumpulan instance servlet. Jika servlet adalah bagian dari aplikasi Web yang telah ditandai sebagai dapat didistribusikan, kontainer dapat menyimpan kumpulan instance servlet di setiap JVM yang didistribusikan oleh aplikasi tersebut.

Untuk servlets yang tidak mengimplementasikan antarmuka SingleThreadModel, jika metode layanan (atau metode seperti doGet atau doPost yang dikirim ke metode layanan dari kelas abstrak HttpServlet) telah ditentukan dengan kata kunci yang disinkronkan, wadah servlet tidak dapat menggunakan pendekatan kumpulan contoh , tetapi harus membuat cerita bersambung permintaan. Sangat disarankan bahwa Pengembang tidak menyinkronkan metode layanan (atau metode yang dikirim kepadanya) dalam keadaan ini karena efek yang merugikan pada kinerja

tharindu_DG
sumber
2
FYI, spec Servlet saat ini (2015-01) adalah 3.1, didefinisikan oleh JSR 340 .
Basil Bourque
1
Jawaban yang sangat rapi! @tharindu_DG
Tom Taylor
0

Seperti yang jelas dari penjelasan di atas, dengan menerapkan SingleThreadModel , servlet dapat dijamin keamanannya oleh wadah servlet. Implementasi kontainer dapat dilakukan dengan 2 cara:

1) Serialisasi permintaan (antrian) ke satu contoh - ini mirip dengan servlet TIDAK menerapkan SingleThreadModel TETAPI menyinkronkan metode layanan / doXXX; ATAU

2) Membuat kumpulan instance - yang merupakan pilihan yang lebih baik dan trade-off antara upaya boot-up / inisialisasi / waktu servlet sebagai terhadap parameter pembatasan (waktu memori / CPU) dari lingkungan hosting servlet.

Mahesh Balasubramanian
sumber
-1

Tidak. Servlets tidak aman untuk Thread

Ini memungkinkan Anda mengakses lebih dari satu utas sekaligus

jika Anda ingin menjadikannya Servlet sebagai Thread aman., Anda dapat melakukannya

Implement SingleThreadInterface(i) yang merupakan Antarmuka kosong tidak ada

metode

atau kita bisa mencari metode sinkronisasi

kita dapat membuat seluruh metode layanan disinkronkan dengan menggunakan disinkronkan

kata kunci di depan metode

Contoh::

public Synchronized class service(ServletRequest request,ServletResponse response)throws ServletException,IOException

atau kita dapat menempatkan blok kode di blok Disinkronkan

Contoh::

Synchronized(Object)

{

----Instructions-----

}

Saya merasa bahwa blok Sinkronisasi lebih baik daripada membuat seluruh metode

Disinkronkan

Ved Prakash
sumber