Reverse Ajax, Phần 1: Giới thiệu về Comet

Ajax (Asynchronous JavaScript và XML), một kỹ thuật dành cho trình duyệt dựa trên JavaScript, cho phép sử dụng một đoạn mã lệnh để đáp ứng các yêu cầu HTTP cho từng thành phần mà không cần phải refresh lại toàn bộ trang web. Ajax đã được ứng dụng hơn 10 năm nay. Mặc dù tên của nó có kèm theo XML, nhưng bạn có thể truyền tải bất cứ thứ gì trong m ột yêu cầu Ajax. Dữ liệu được sử dụng phổ biến nhất là JSON, nó có cú pháp gần với cú pháp của JavaScript và tiêu thụ ít băng thông hơn. Liệt kê 1 là ví dụ về một yêu cầu Ajax để lấy ra tên của một vùng địa phương từ mã bưu chính của nó.

pdf11 trang | Chia sẻ: lylyngoc | Lượt xem: 1800 | Lượt tải: 1download
Bạn đang xem nội dung tài liệu Reverse Ajax, Phần 1: Giới thiệu về Comet, để tải tài liệu về máy bạn click vào nút DOWNLOAD ở trên
Reverse Ajax, Phần 1: Giới thiệu về Comet Ajax, Reverse Ajax và WebSockets Ajax (Asynchronous JavaScript và XML), một kỹ thuật dành cho trình duyệt dựa trên JavaScript, cho phép sử dụng một đoạn mã lệnh để đáp ứng các yêu cầu HTTP cho từng thành phần mà không cần phải refresh lại toàn bộ trang web. Ajax đã được ứng dụng hơn 10 năm nay. Mặc dù tên của nó có kèm theo XML, nhưng bạn có thể truyền tải bất cứ thứ gì trong một yêu cầu Ajax. Dữ liệu được sử dụng phổ biến nhất là JSON, nó có cú pháp gần với cú pháp của JavaScript và tiêu thụ ít băng thông hơn. Liệt kê 1 là ví dụ về một yêu cầu Ajax để lấy ra tên của một vùng địa phương từ mã bưu chính của nó. Liệt kê 1. Ví dụ về yêu cầu Ajax var url = ' + $('#postalCode').val() + '&country=' + $('#country').val() + '&callback=?'; $.getJSON(url, function(data) { $('#placeName').val(data.postalcodes[0].placeName); }); Bạn có thể xem cách hoạt động của ví dụ trên trong file listing1.html có ở đây. Về cơ bản Reverse Ajax là một khái niệm: có thể gửi dữ liệu từ máy chủ đến máy khách. Trong một yêu cầu Ajax của HTTP tiêu chuẩn, dữ liệu được gửi đến máy chủ. Reverse Ajax có thể được mô phỏng để tạo ra một yêu cầu Ajax, theo những cách cụ thể được nêu ra trong bài viết này, do đó, máy chủ có thể gửi các sự kiện đến máy khách càng nhanh càng tốt (giao tiếp với độ trễ thấp). WebSockets, có kèm theo HTML5, là một kỹ thuật mới hơn. Nhiều trình duyệt đã hỗ trợ nó (Firefox, Google Chrome, Safari và những trình duyệt khác). WebSockets tạo nên các kênh truyền thông song song, hai chiều. Kết nối này được mở ra thông qua một yêu cầu HTTP được gọi là WebSockets handshake với một số header đặc biệt. Kết nối này được duy trì và bạn có thể viết và nhận dữ liệu bằng JavaScript, như thể bạn đang sử dụng một TCP socket nguyên bản. WebSockets sẽ được trình bày trong phần 2 của loạt bài này. Về đầu trang Các kỹ thuật Reverse Ajax Mục tiêu của kỹ thuật Reverse Ajax là giúp các máy chủ đẩy thông tin đến máy khách. Theo mặc định các yêu cầu Ajax là không chính thống và chỉ có thể được bắt đầu từ máy khách đến máy chủ. Bạn có thể vượt qua hạn chế này bằng cách sử dụng các kỹ thuật để mô phỏng truyền thông đáp ứng giữa máy chủ và máy khách. HTTP polling và JSONP polling Polling bao gồm việc gửi một thông điệp từ phía máy khách đến máy chủ để yêu cầu một thông tin dữ liệu nào đó. Thực ra đây chỉ là một yêu cầu HTTP của Ajax. Để có được các sự kiện từ máy chủ càng sớm thì khoảng thời gian polling (thời gian giữa các yêu cầu) phải càng ngắn càng tốt. Có một nhược điểm là: nếu khoảng thời gian này càng ngắn, trình duyệt của máy khách sẽ đưa ra nhiều yêu cầu hơn, trong đó có những yêu cầu sẽ không trả về bất kỳ dữ liệu có ích nào khiến cho băng thông bị hao tốn và xử lý tài nguyên vô ích. Bảng thời gian trong Hình 1 cho thấy cách mà máy khách gửi các yêu cầu polling nhưng chẳng có thông tin nào được trả về cả. Máy khách phải chờ đến lần polling tiếp theo để có được hai sự kiện do máy chủ thu nhận được. Hình 1. Reverse Ajax với việc polling của HTTP Về bản chất, polling của JSONP giống như polling của HTTP. Tuy nhiên, có sự khác biệt ở chỗ với JSONP bạn có thể đưa ra yêu cầu giữa các domain (các yêu cầu không có trong domain của bạn). JSONP được dùng trong Liệt kê 1 để nhận về một tên địa phương từ một mã bưu chính. Thường thì có thể nhận ra một yêu cầu JSONP bằng cách xem tham số gọi lại và nội dung trả về của nó, đó chính là mã JavaScript có thể chạy được. Để thực hiện polling trong JavaScript, bạn có thể sử dụng hàm setInterval để định thời gian gửi các yêu cầu Ajax, như trong Liệt kê 2: Liệt kê 2. Polling trong JavaScript setInterval(function() { $.getJSON('events', function(events) { console.log(events); }); }, 2000); Đoạn mã demo kỹ thuật polling ở đây cho thấy phương pháp này tiêu thụ băng thông như thế nào. Khoảng thời gian cho các lần polling ngắn nhưng có thể bạn sẽ nhận các kết quả trả về mà chẳng có sự kiện (event) mới nào. Liệt kê 3 hiển thị kết quả của việc polling ở ví dụ mẫu. Liệt kê 3. Kết quả của đoạn demo polling trong ví dụ mẫu [client] checking for events... [client] no event [client] checking for events... [client] 2 events [event] At Sun Jun 05 15:17:14 EDT 2011 [event] At Sun Jun 05 15:17:14 EDT 2011 [client] checking for events... [client] 1 events [event] At Sun Jun 05 15:17:16 EDT 2011 Polling trong JavaScript có các ưu và nhược điểm.  Ưu điểm: Nó thực sự dễ thực hiện và không đòi hỏi bất kỳ tính năng đặc biệt nào ở phía máy chủ. Nó cũng làm việc trong tất cả các trình duyệt.  Nhược điểm: Phương pháp này hiếm khi được sử dụng vì nó không hề linh động. Hãy tưởng tượng, giả sử có 100 máy khách, trong đó mỗi máy gửi các yêu cầu polling trong 2 giây thì số lượng băng thông và tài nguyên bị hao tốn như thế nào, ở đây 30% yêu cầu được trả về không hề có chút dữ liệu. Piggyback Piggyback polling là một phương pháp thông minh hơn nhiều so với polling đơn thuần vì nó có xu hướng loại bỏ tất cả các yêu cầu không cần thiết (các yêu cầu không trả về dữ liệu nào). Không cần định sẵn một khoảng thời gian interval nào cả; yêu cầu sẽ được gửi đi khi máy khách cần gửi một yêu cầu đến máy chủ. Sự khác biệt nằm ở cách phản hồi được chia thành hai phần: phản hồi với dữ liệu được yêu cầu và các sự kiện từ máy chủ. Hình 2 chính là ví dụ. Hình 2. Reverse Ajax với Piggyback polling Khi thực hiện kỹ thuật Piggyback, thông thường tất cả các yêu cầu Ajax gửi đến máy chủ có thể được trả về một phản hồi hỗn hợp. Ví dụ mẫu về cách thực hiện kỹ thuật này có ở đây và trong Liệt kê 4 dưới đây. Liệt kê 4. Ví dụ mẫu về mã piggyback $('#submit').click(function() { $.post('ajax', function(data) { var valid = data.formValid; // process validation results // then process the other part of the response (events) processEvents(data.events); }); }); Liệt kê 5 hiển thị kết quả khi sử dụng phương pháp piggyback. Liệt kê 5. Kết quả piggyback [client] checking for events... [server] form valid ? true [client] 4 events [event] At Sun Jun 05 16:08:32 EDT 2011 [event] At Sun Jun 05 16:08:34 EDT 2011 [event] At Sun Jun 05 16:08:34 EDT 2011 [event] At Sun Jun 05 16:08:37 EDT 2011 Bạn có thể thấy kết quả của việc kiểm tra hợp lệ một khuôn mẫu (form validation) và các sự kiện được thêm vào phản hồi. Một lần nữa, phương pháp này có những ưu và nhược điểm.  Ưu điểm: Không có các yêu cầu nào trả về mà không có dữ liệu, vì máy khách kiểm soát khi nó gửi các yêu cầu, nên bạn tiêu thụ ít tài nguyên hơn. Nó cũng làm việc trong tất cả các trình duyệt và không yêu cầu các tính năng đặc biệt ở phía máy chủ.  Nhược điểm: Bạn sẽ không biết khi nào mà các sự kiện ở phía máy chủ được gửi tới máy khách vì nó đòi hỏi phải có một hành động từ phía máy khách để yêu cầu chúng. Về đầu trang Comet Reverse Ajax với kỹ thuật polling hay piggyback cũng còn rất hạn chế: vì nó không co giãn và không cung cấp khả năng giao tiếp với độ trễ thấp (tức là các sự kiện phải được gửi đến trình duyệt ngay khi chúng đến máy chủ). Comet là một mô hình ứng dụng web, ở đây một yêu cầu được gửi đến máy chủ và vẫn tiếp tục trong một thời gian dài cho đến khi hết giờ hoặc xuất hiện một sự kiện từ máy chủ. Khi yêu cầu này được hoàn thành thì sẽ có một yêu cầu Ajax khác được gửi đi để chờ các sự kiện khác từ máy chủ. Với Comet, các máy chủ web có thể gửi dữ liệu cho máy khách mà cần phải có một yêu cầu cụ thể nào. Ưu điểm lớn của Comet ở chỗ mỗi máy khách luôn có một liên kết giao tiếp đến máy chủ. Máy chủ có thể đẩy các sự kiện vào các máy khách bằng cách thực hiện commit (hay hoàn thành) ngay lập tức các phản hồi ngay khi chúng đến hoặc thậm chí nó có thể tích lũy và gửi một lần. Vì một yêu cầu được mở trong một thời gian dài, nên cần có các tính năng đặc biệt ở phía máy chủ để xử lý tất cả những long-lived này (long-lived requets - các yêu cầu có thời gian sống lâu). Hình 3 là ví dụ. (Phần 2 của loạt bài này sẽ giải thích các ràng buộc máy chủ chi tiết hơn). Hình 3. Reverse Ajax với Comet Các cách thực hiện của Comet có thể được chia thành hai loại: một loại sử dụng chế độ streaming và một loại khác sử dụng long polling. Về đầu trang Comet sử dụng kỹ thuật HTTP Streaming Trong chế độ streaming, một kết nối được mở liên tục. Sẽ chỉ có một yêu cầu long-lived (#1 trong Hình 3) do mỗi sự kiện đến phía máy chủ được gửi đi thông qua cùng một kết nối. Do đó, nó đòi hỏi ở phía máy khách phải có cơ chế phân chia các phản hồi đến từ cùng một nguồn kết nối đó. Về mặt kỹ thuật, hai kỹ thuật phổ biến về streaming là Forever Iframes (Các IFrame ẩn) và tính năng đa-phần (multi-part) của đối tượng XMLHttpRequest được sử dụng để tạo ra các yêu cầu Ajax trong JavaScript. Forever Iframes Kỹ thuật Forever Iframes sử dụng một thẻ Iframe ẩn đặt trong trang với thuộc tính src trỏ đến đường dẫn servlet nhằm trả về các sự kiện máy chủ. Mỗi khi nhận được một sự kiện, servlet sẽ viết và đổ vào một thẻ script với mã JavaScript bên trong. Nội dung của iframe sẽ được thêm vào thẻ script này và được thực thi.  Ưu điểm: Dễ thực hiện và nó hoạt động trong tất cả các trình duyệt hỗ trợ các iframe.  Nhược điểm: Không có cách nào để thực hiện xử lý lỗi hoặc theo dõi trạng thái của kết nối, bởi vì tất cả các kết nối và dữ liệu đều được trình duyệt xử lý thông qua các thẻ HTML. Do đó bạn không biết khi nào thì kết nối bị ngắt ở cả hai phía. Tính năng multi-part của XMLHttpRequest Kỹ thuật thứ hai đáng tin cậy hơn là sử dụng cờ multi-part được hỗ trợ bởi một số trình duyệt (như Firefox) trên đối tượng XMLHttpRequest. Một yêu cầu Ajax được gửi và được mở ở phía máy chủ. Mỗi lần một sự kiện đến, một phản hồi multi-part được viết thông qua cùng một kết nối. Liệt kê 6 mô tả ví dụ này. Liệt kê 6. Mẫu JavaScript để thiết lập một yêu cầu streaming multi-part var xhr = $.ajaxSettings.xhr(); xhr.multipart = true; xhr.open('GET', 'ajax', true); xhr.onreadystatechange = function() { if (xhr.readyState == 4) { processEvents($.parseJSON(xhr.responseText)); } }; xhr.send(null); Về phía máy chủ, mọi thứ phức tạp hơn một chút. Trước tiên, bạn phải thiết lập yêu cầu multi- part, rồi treo (suspend) kết nối. Liệt kê 7 cho thấy cách treo một yêu cầu HTTP streaming. (Phần 3 của loạt bài này sẽ trình bày chi tiết hơn về các API). Liệt kê 7. Ngắt một HTTP treaming trong một servlet khi sử dụng API Servlet 3 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // start the suspension of the request AsyncContext asyncContext = req.startAsync(); asyncContext.setTimeout(0); // send the multipart separator back to the client resp.setContentType("multipart/x-mixed-replace;boundary=\"" + boundary + "\""); resp.setHeader("Connection", "keep-alive"); resp.getOutputStream().print("--" + boundary); resp.flushBuffer(); // put the async context in a list for future usage asyncContexts.offer(asyncContext); } Giờ đây, mỗi khi một sự kiện xảy ra, bạn có thể lặp qua tất cả các kết nối đang bị treo và viết dữ liệu tới chúng, như thể hiện trong Liệt kê 8: Liệt kê 8. Gửi các sự kiện tới một yêu cầu multi-part đang bị treo bằng cách sử dụng API Servlet 3 for (AsyncContext asyncContext : asyncContexts) { HttpServletResponse peer = (HttpServletResponse) asyncContext.getResponse(); peer.getOutputStream().println("Content-Type: application/json"); peer.getOutputStream().println(); peer.getOutputStream().println(new JSONArray() .put("At " + new Date()).toString()); peer.getOutputStream().println("--" + boundary); peer.flushBuffer(); } Các file mà bạn tải về theo bài viết này, trong thư mục Comet-streaming, trình bày về HTTP streaming . Khi bạn chạy ví dụ mẫu và mở trang chủ ra, bạn sẽ thấy các sự kiện lập tức xuất hiện không đồng bộ ngay khi đến máy chủ. Ngoài ra, nếu bạn sử dụng addon Firebug, bạn có thể thấy rằng chỉ có một yêu cầu Ajax được mở. Nếu bạn xem kỹ hơn, bạn sẽ thấy các phản hồi JSON được hiển thị ở tab Response, như thể hiện trong Hình 4: Hình 4. Cửa sổ Firebug của một yêu cầu HTTP streaming Như thường lệ, kỹ thuật này cũng có những ưu và nhược điểm sau.  Ưu điểm: Chỉ có một kết nối liên tục được mở. Đây là kỹ thuật Comet, tiết kiệm việc sử dụng băng thông nhất.  Nhược điểm: Không phải tất cả các trình duyệt đều hỗ trợ cờ multi-part. Một số thư viện được sử dụng rộng rãi, chẳng hạn như CometD trong Java, đã cho thấy nhiều vấn đề về bộ nhớ đệm (buffer). Ví dụ, các khối dữ liệu (multi-part) có thể được lưu giữ trong một buffer và chỉ được gửi đi chỉ khi kết nối hoàn thành hoặc khi buffer đầy, điều này có thể tạo ra độ trễ cao hơn dự kiến. Về đầu trang Comet sử dụng kỹ thuật HTTP long-polling Chế độ long-polling liên quan đến các kỹ thuật để mở một kết nối. Kết nối được mở bởi máy chủ và ngay khi sự kiện xảy ra thì phản hồi được commit và kết nối được đóng lại. Sau đó, một kết nối long-polling mới lại ngay lập tức được máy khách mở để đón các sự kiện gửi đến. Bạn có thể thực hiện HTTP long-polling bằng cách sử dụng các thẻ script hoặc đơn giản chỉ thông qua một đối tượng XMLHttpRequest. Các thẻ script Với các iframe, mục tiêu là nối thêm một thẻ script vào trang của bạn để chạy kịch bản lệnh này. Máy chủ sẽ: treo kết nối cho đến khi một sự kiện xảy ra, gửi nội dung kịch bản lệnh lại cho trình duyệt và sau đó mở lại một thẻ script để nhận những sự kiện tiếp theo.  Ưu điểm: Vì dựa trên các thẻ HTML, nên kỹ thuật này rất dễ thực hiện và làm việc trên các domain (theo mặc định, XMLHttpRequest không cho phép thực hiện yêu cầu trên domain hoặc các domain con khác).  Nhược điểm: Tương tự như kỹ thuật iframe, thiếu xử lý lỗi và bạn không thể biết trạng thái hay khả năng để ngắt một kết nối. Về đầu trang Long-polling của XMLHttpRequest Phương pháp Comet thứ hai được khuyên dùng là mở một yêu cầu Ajax đến máy chủ và chờ phản hồi. Đòi hỏi máy chủ phải có tính năng đặc thù cho phép treo các yêu cầu. Ngay khi sự kiện xảy ra, máy chủ sẽ gửi lại phản hồi theo yêu cầu bị treo và đóng nó lại, giống như khi bạn đóng luồng dữ liệu kết quả của một phản hồi servlet. Sau đó máy khách sẽ sử dụng phản hồi đó và mở một yêu cầu Ajax long-lived mới đến máy chủ, như trong Liệt kê 9: Liệt kê 9. Mã JavaScript mẫu để thiết lập các yêu cầu long-polling function long_polling() { $.getJSON('ajax', function(events) { processEvents(events); long_polling(); }); } long_polling(); Ở tầng sau, mã này cũng sử dụng API Servlet 3 để treo yêu cầu, cũng như HTTP streaming, nhưng bạn không cần tất cả mã xử lý multi-part. Liệt kê 10 thể hiện ví dụ này. Liệt kê 10. Treo một yêu cầu Ajax long-polling protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { AsyncContext asyncContext = req.startAsync(); asyncContext.setTimeout(0); asyncContexts.offer(asyncContext); } Khi nhận một sự kiện, chỉ cần thực hiện tất cả các yêu cầu bị treo và hoàn thành chúng, như trong Liệt kê 11: Liệt kê 11. Hoàn thành một yêu cầu Ajax long-polling khi một sự kiện xảy ra while (!asyncContexts.isEmpty()) { AsyncContext asyncContext = asyncContexts.poll(); HttpServletResponse peer = (HttpServletResponse) asyncContext.getResponse(); peer.getWriter().write( new JSONArray().put("At " + new Date()).toString()); peer.setStatus(HttpServletResponse.SC_OK); peer.setContentType("application/json"); asyncContext.complete(); } Trong các file mã nguồn đi kèm, thư mục comet-long-polling có một ứng dụng web mẫu về long-polling mà bạn có thể chạy bằng cách sử dụng lệnh mvn jetty:run.  Ưu điểm: Dễ thực hiện bên phía máy khách với một hệ thống xử lý lỗi và quản lý thời gian chờ tốt. Kỹ thuật này cũng cho phép khứ hồi giữa các kết nối ở phía máy chủ, do kết nối không liên tục (đây là một tin mừng khi bạn có rất nhiều máy khách trên ứng dụng của mình). Nó cũng làm việc trên tất cả các trình duyệt; bạn chỉ cần sử dụng đối tượng XMLHttpRequest bằng cách gửi một yêu cầu Ajax đơn giản.  Nhược điểm: Không có nhược điểm đáng kể nào so với các kỹ thuật khác. Tuy nhiên, giống như tất cả các kỹ thuật mà chúng ta đã thảo luận, vẫn có một nhược điểm là dựa vào một kết nối HTTP không trạng thái, mà nó yêu cầu các tính năng đặc biệt bên phía máy chủ để có thể treo nó tạm thời. Về đầu trang Khuyến cáo Vì tất cả các trình duyệt hiện đại đều hỗ trợ đặc tả CORS (Cross-Origin Resource Sharing - Chia sẻ giữa các nguồn tài nguyên gốc), cho phép XHR thực hiện yêu cầu qua các domain, nên nhu cầu về các kỹ thuật dựa trên kịch bản lệnh và dựa trên khung nội tuyến không được ủng hộ. Cách tốt nhất để thực hiện và sử dụng Comet với Reverse Ajax là thông qua đối tượng XMLHttpRequest, cung cấp một xử lý kết nối thực và xử lý lỗi. Nếu cho rằng không phải tất cả các trình duyệt đều hỗ trợ cờ multi-part và multi-part streaming có thể là một vấn đề về buffer thì điều quan trọng là bạn sử dụng Comet thông qua kỹ thuật HTTP long-polling với đối tượng XMLHttpRequest (một yêu cầu Ajax đơn giản bị treo bên phía máy chủ). Tất cả các trình duyệt hỗ trợ Ajax cũng sẽ hỗ trợ phương pháp này. Về đầu trang Kết luận Bài viết này đã giới thiệu về các kỹ thuật Reverse Ajax. Đã chỉ ra các cách khác nhau để thực hiện giao tiếp Reverse Ajax và cũng giải thích những ưu và nhược điểm của mỗi cách. Tùy vào tình huống cụ thể và các yêu cầu trong ứng dụng của bạn sẽ dẫn đến việc lựa chọn phương pháp nào là tốt nhất. Dù vậy, phương pháp Comet với kỹ thuật Ajax long-polling là cách hay có thể lựa chọn nếu như bạn muốn các tính năng: giao tiếp với độ trễ thấp; phát hiện lỗi và thời gian chờ; tính đơn giản; và hỗ trợ tốt từ tất cả các trình duyệt và nền tảng. Tiếp theo Phần 2 của loạt bài này, bạn sẽ khám phá một kỹ thuật Reverse Ajax thứ ba: đó là WebSockets. Mặc dù không phải tất cả các trình duyệt đều hỗ trợ nó nhưng chắc chắn WebSockets sẽ là một phương tiện truyền thông rất tốt cho Reverse Ajax. WebSockets loại bỏ tất cả các ràng buộc liên quan đến đặc tính không trạng thái (stateless) của một kết nối HTTP. Phần 2 cũng sẽ trình bày các ràng buộc phía máy chủ do các kỹ thuật Comet và WebSocket gây ra.
Tài liệu liên quan