Dukungan pemrosesan asinkron di Servlet 3.0

Bahkan sebagai API tingkat menengah yang berlindung dalam kerangka kerja Web berbasis komponen UI modern dan teknologi layanan Web, spesifikasi Servlet 3.0 yang masuk (JSR 315) akan berdampak besar pada pengembangan aplikasi Web Java. Penulis Xinyu Liu menjelaskan secara rinci mengapa pemrosesan asynchronous adalah dasar untuk aplikasi kolaboratif multi-pengguna yang mendefinisikan Web 2.0. Dia juga merangkum peningkatan lain dari Servlet 3.0 seperti kemudahan konfigurasi dan kelengkapan. Level: Menengah

Spesifikasi Java Servlet adalah denominator umum untuk sebagian besar teknologi Web Java sisi server, termasuk JavaServer Pages (JSP), JavaServer Faces (JSF), berbagai kerangka kerja Web, SOAP dan API layanan Web RESTful, dan umpan berita. Servlet yang berjalan di bawah teknologi ini membuatnya portabel di semua server Web Java (wadah servlet). Setiap perubahan yang diusulkan pada API yang diterima secara luas ini untuk menangani komunikasi HTTP akan berpotensi memengaruhi semua teknologi Web sisi server yang berafiliasi.

Spesifikasi Servlet 3.0 yang akan datang, yang lolos tinjauan publik pada Januari 2009, merupakan rilis utama dengan fitur-fitur baru yang penting yang akan mengubah kehidupan pengembang Web Java menjadi lebih baik. Berikut daftar apa yang dapat Anda harapkan di Servlet 3.0:

  • Dukungan asynchronous
  • Kemudahan konfigurasi
  • Pluggabilitas
  • Peningkatan pada API yang ada

Dukungan asinkron adalah peningkatan paling signifikan dari Servlet 3.0, yang dimaksudkan untuk membuat pemrosesan sisi server aplikasi Ajax jauh lebih efisien. Dalam artikel ini saya akan fokus pada dukungan asinkron di Servlet 3.0, dimulai dengan menjelaskan masalah koneksi dan konsumsi thread yang mendasari perlunya dukungan asinkron. Saya kemudian akan menjelaskan bagaimana aplikasi dunia nyata saat ini menggunakan pemrosesan asinkron dalam implementasi server push seperti Comet, atau membalikkan Ajax. Terakhir, saya akan menyentuh peningkatan lain dari Servlet 3.0 seperti kemampuan memasukkan dan kemudahan konfigurasi, meninggalkan Anda dengan kesan yang baik tentang Servlet 3.0 dan dampaknya pada pengembangan Web Java.

Dukungan asynchronous: Konsep latar belakang

Teknologi Web 2.0 secara drastis mengubah profil lalu lintas antara klien Web (seperti browser) dan server Web. Dukungan asinkron yang diperkenalkan di Servlet 3.0 dirancang untuk menanggapi tantangan baru ini. Untuk memahami pentingnya pemrosesan asinkron, pertama-tama mari kita pertimbangkan evolusi komunikasi HTTP.

HTTP 1.0 hingga HTTP 1.1

Peningkatan besar dalam standar HTTP 1.1 adalah koneksi persisten . Di HTTP 1.0, koneksi antara klien Web dan server ditutup setelah satu siklus permintaan / respons. Di HTTP 1.1, koneksi tetap hidup dan digunakan kembali untuk beberapa permintaan. Koneksi persisten mengurangi kelambatan komunikasi dengan jelas, karena klien tidak perlu menegosiasikan ulang koneksi TCP setelah setiap permintaan.

Benang per koneksi

Mencari tahu cara membuat server Web lebih skalabel merupakan tantangan berkelanjutan bagi vendor. Utas per koneksi HTTP, yang didasarkan pada koneksi persisten HTTP 1.1, adalah solusi yang umum diadopsi vendor. Di bawah strategi ini, setiap koneksi HTTP antara klien dan server dikaitkan dengan satu utas di sisi server. Utas dialokasikan dari kumpulan utas yang dikelola server. Setelah koneksi ditutup, utas khusus didaur ulang kembali ke kumpulan dan siap untuk melayani tugas lain. Bergantung pada konfigurasi perangkat keras, pendekatan ini dapat menskalakan ke sejumlah besar koneksi bersamaan. Eksperimen dengan server Web profil tinggi telah menghasilkan hasil numerik yang mengungkapkan bahwa konsumsi memori meningkat hampir berbanding lurus dengan jumlah koneksi HTTP. Alasannya adalah utas relatif mahal dalam hal penggunaan memori. Server yang dikonfigurasi dengan jumlah utas tetap dapat menderitamasalah kelaparan thread , di mana permintaan dari klien baru ditolak setelah semua thread di pool diambil.

Di sisi lain, untuk banyak situs Web, pengguna meminta halaman dari server hanya secara sporadis. Ini dikenal sebagai model halaman demi halaman . Untaian koneksi sebagian besar tidak aktif, yang merupakan pemborosan sumber daya.

Benang per permintaan

Berkat kapabilitas I / O non-pemblokiran yang diperkenalkan di paket New I / O API untuk Java Platform (NIO) Java 4, koneksi HTTP persisten tidak mengharuskan utas terus-menerus dilampirkan padanya. Untaian dapat dialokasikan ke koneksi hanya ketika permintaan sedang diproses. Ketika koneksi tidak aktif antara permintaan, utas dapat didaur ulang, dan koneksi ditempatkan dalam set pilihan NIO terpusat untuk mendeteksi permintaan baru tanpa menggunakan utas terpisah. Model ini, disebut utas per permintaan, berpotensi memungkinkan server Web menangani semakin banyak koneksi pengguna dengan jumlah utas yang tetap. Dengan konfigurasi perangkat keras yang sama, server Web yang berjalan dalam mode ini berskala jauh lebih baik daripada mode thread-per-koneksi. Saat ini, server Web populer - termasuk Tomcat, Jetty, GlassFish (Grizzly), WebLogic, dan WebSphere - semua menggunakan utas per permintaan melalui Java NIO. Untuk pengembang aplikasi, kabar baiknya adalah bahwa server Web mengimplementasikan I / O non-pemblokiran secara tersembunyi, tanpa eksposur apa pun ke aplikasi melalui API servlet.

Memenuhi tantangan Ajax

Untuk menawarkan pengalaman pengguna yang lebih kaya dengan antarmuka yang lebih responsif, semakin banyak aplikasi Web menggunakan Ajax. Pengguna aplikasi Ajax berinteraksi dengan server Web lebih sering daripada model halaman demi halaman. Tidak seperti permintaan pengguna biasa, permintaan Ajax dapat sering dikirim oleh satu klien ke server. Selain itu, klien dan skrip yang berjalan di sisi klien dapat memeriksa server Web secara teratur untuk pembaruan. Permintaan yang lebih serentak menyebabkan lebih banyak utas dikonsumsi, yang membatalkan manfaat pendekatan utas per permintaan ke tingkat yang lebih tinggi.

Berjalan lambat, sumber daya terbatas

Some slow-running back-end routines worsen the situation. For example, a request could be blocked by a depleted JDBC connection pool, or a low-throughput Web service endpoint. Until the resource becomes available, the thread could be stuck with the pending request for a long time. It would be better to place the request in a centralized queue waiting for available resources and recycle that thread. This effectively throttles the number of request threads to match the capacity of the slow-running back-end routines. It also suggests that at a certain point during request processing (when the request is stored in the queue), no threads are consumed for the request at all. Asynchronous support in Servlet 3.0 is designed to achieve this scenario through a universal and portable approach, whether Ajax is used or not. Listing 1 shows you how it works.

Listing 1. Throttling access to resources

@WebServlet(name="myServlet", urlPatterns={"/slowprocess"}, asyncSupported=true) public class MyServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) { AsyncContext aCtx = request.startAsync(request, response); ServletContext appScope = request.getServletContext(); ((Queue)appScope.getAttribute("slowWebServiceJobQueue")).add(aCtx); } } @WebServletContextListener public class SlowWebService implements ServletContextListener { public void contextInitialized(ServletContextEvent sce) { Queue jobQueue = new ConcurrentLinkedQueue(); sce.getServletContext().setAttribute("slowWebServiceJobQueue", jobQueue); // pool size matching Web services capacity Executor executor = Executors.newFixedThreadPool(10); while(true) { if(!jobQueue.isEmpty()) { final AsyncContext aCtx = jobQueue.poll(); executor.execute(new Runnable(){ public void run() { ServletRequest request = aCtx.getRequest(); // get parameteres // invoke a Web service endpoint // set results aCtx.forward("/result.jsp"); } }); } } } public void contextDestroyed(ServletContextEvent sce) { } }

When the asyncSupported attribute is set to true, the response object is not committed on method exit. Calling startAsync() returns an AsyncContext object that caches the request/response object pair. The AsyncContext object is then stored in an application-scoped queue. Without any delay, the doGet() method returns, and the original request thread is recycled. In the ServletContextListener object, separate threads initiated during application launch monitor the queue and resume request processing whenever the resources become available. After a request is processed, you have the option of calling ServletResponse.getWriter().print(...), and then complete() to commit the response, or calling forward() to direct the flow to a JSP page to be displayed as the result. Note that JSP pages are servlets with an asyncSupported attribute that defaults to false.

In addition, the AsyncEvent and AsynListener classes in Servlet 3.0 give developers elaborate control of asynchronous lifecycle events. You can register an AsynListener through the ServletRequest.addAsyncListener() method. After the startAsync() method is called on the request, an AsyncEvent is sent to the registered AsyncListener as soon as the asynchronous operation has completed or timed out. The AsyncEvent also contains the same request and response objects as in the AsyncContext object.

Server push

A more interesting and vital use case for the Servlet 3.0 asynchronous feature is server push. GTalk, a widget that lets GMail users chat online, is an example of server push. GTalk doesn't poll the server frequently to check if a new message is available to display. Instead it waits for the server to push back new messages. This approach has two obvious advantages: low-lag communication without requests being sent, and no waste of server resources and network bandwidth.

Ajax allows a user to interact with a page even if other requests from the same user are being processed at the same time. A common use case is to have a browser regularly poll the server for updates of state changes without interrupting the user. However, high polling frequencies waste server resources and network bandwidth. If the server could actively push data to browsers -- in other words, deliver asynchronous messages to clients on events (state changes) -- Ajax applications would perform better and save precious server and network resources.

The HTTP protocol is a request/response protocol. A client sends a request message to a server, and the server replies with a response message. The server can't initiate a connection with a client or send an unexpected message to the client. This aspect of the HTTP protocol seemingly makes server push impossible. But several ingenious techniques have been devised to circumvent this constraint:

  • Service streaming (streaming) allows a server to send a message to a client when an event occurs, without an explicit request from the client. In real-world implementations, the client initiates a connection to the server through a request, and the response returns bits and pieces each time a server-side event occurs; the response lasts (theoretically) forever. Those bits and pieces can be interpreted by client-side JavaScript and displayed through the browser's incremental rendering ability.
  • Long polling, also known as asynchronous polling, is a hybrid of pure server push and client pull. It is based on the Bayeux protocol, which uses a topic-based publish-subscribe scheme. As in streaming, a client subscribes to a connection channel on the server by sending a request. The server holds the request and waits for an event to happen. Once the event occurs (or after a predefined timeout), a complete response message is sent to the client. Upon receiving the response, the client immediately sends a new request. The server, then, almost always has an outstanding request that it can use to deliver data in response to a server-side event. Long polling is relatively easier to implement on the browser side than streaming.
  • Passive piggyback: When the server has an update to send, it waits for the next time the browser makes a request and then sends its update along with the response that the browser was expecting.

Service streaming and long polling, implemented with Ajax, are known as Comet, or reverse Ajax. (Some developers call all interactive techniques reverse Ajax, including regular polling, Comet, and piggyback.)

Ajax improves single-user responsiveness. Server-push technologies like Comet improve application responsiveness for collaborative, multi-user applications without the overhead of regular polling.

Aspek klien dari teknik server push - seperti hidden iframes, XMLHttpRequeststreaming, dan beberapa library Dojo dan jQuery yang memfasilitasi komunikasi asynchronous - berada di luar cakupan artikel ini. Sebaliknya, minat kami adalah pada sisi server, khususnya bagaimana spesifikasi Servlet 3.0 membantu mengimplementasikan aplikasi interaktif dengan server push.