Giới thiệu
Thông qua WebSockets và EventSource, HTML5 cho phép nhà phát triển xây dựng các ứng dụng web giao tiếp theo thời gian thực với máy chủ. Stream Congress (có trong Cửa hàng Chrome trực tuyến) cung cấp thông tin cập nhật trực tiếp về hoạt động của Quốc hội Hoa Kỳ. Kênh này phát trực tuyến thông tin cập nhật về hoạt động trên sàn của cả Hạ viện và Thượng viện, tin tức cập nhật có liên quan, tweet của các thành viên Quốc hội và thông tin cập nhật khác trên mạng xã hội. Ứng dụng này sẽ được để mở cả ngày vì ứng dụng ghi lại hoạt động kinh doanh của Quốc hội.
Bắt đầu với WebSocket
Thông số kỹ thuật WebSockets đã nhận được khá nhiều sự chú ý nhờ những gì nó mang lại: một ổ cắm TCP ổn định, hai chiều giữa trình duyệt và máy chủ. Không có định dạng dữ liệu nào được áp dụng cho ổ cắm TCP; nhà phát triển có thể tự do xác định giao thức nhắn tin. Trong thực tế, việc truyền các đối tượng JSON xung quanh dưới dạng chuỗi là thuận tiện nhất. Mã JavaScript phía máy khách để theo dõi các bản cập nhật trực tiếp rất rõ ràng và đơn giản:
var liveSocket = new WebSocket("ws://streamcongress.com:8080/live");
liveSocket.onmessage = function (payload) {
addToStream(JSON.parse(payload.data).reverse());
};
Mặc dù tính năng hỗ trợ trình duyệt cho WebSockets rất đơn giản, nhưng tính năng hỗ trợ phía máy chủ vẫn đang trong giai đoạn hình thành. Socket.IO trên Node.js cung cấp một trong những giải pháp phía máy chủ hoàn thiện và mạnh mẽ nhất. Máy chủ do sự kiện điều khiển như Node.js là phù hợp với WebSocket. Đối với các phương thức triển khai thay thế, nhà phát triển Python có thể sử dụng Twisted và Tornado, còn nhà phát triển Ruby có EventMachine.
Giới thiệu về Cramp
Cramp là một khung web Ruby không đồng bộ chạy trên EventMachine. Tác giả: Pratik Naik, một thành viên của nhóm cốt lõi Ruby on Rails. Cung cấp ngôn ngữ cụ thể theo miền (DSL) cho các ứng dụng web theo thời gian thực, Cramp là lựa chọn lý tưởng cho các nhà phát triển web Ruby. Những người quen thuộc với việc viết trình điều khiển trong Ruby on Rails sẽ nhận ra phong cách của Cramp:
require "rubygems"
require "bundler"
Bundler.require
require 'cramp'
require 'http_router'
require 'active_support/json'
require 'thin'
Cramp::Websocket.backend = :thin
class LiveSocket < Cramp::Websocket
periodic_timer :check_activities, :every => 15
def check_activities
@latest_activity ||= nil
new_activities = find_activities_since(@latest_activity)
@latest_activity = new_activities.first unless new_activities.empty?
render new_activities.to_json
end
end
routes = HttpRouter.new do
add('/live').to(LiveSocket)
end
run routes
Vì Cramp nằm trên EventMachine không chặn, nên bạn cần lưu ý một số điều sau:
- Bạn phải sử dụng trình điều khiển cơ sở dữ liệu không chặn, chẳng hạn như MySQLPlus và em-mongo.
- Phải sử dụng máy chủ web do sự kiện điều khiển. Hỗ trợ tích hợp sẵn cho Mỏng và Cầu vồng.
- Ứng dụng Cramp phải chạy riêng biệt với ứng dụng Rails chính cung cấp năng lượng cho Stream Congress, khởi động lại và theo dõi độc lập.
Các hạn chế hiện tại
WebSockets gặp phải một sự cố vào ngày 8 tháng 12 năm 2010 khi một lỗ hổng bảo mật được công bố. Cả Firefox và Opera đều đã xoá tính năng hỗ trợ trình duyệt cho WebSocket. Mặc dù không có đoạn mã polyfill JavaScript thuần tuý nào, nhưng có một tính năng dự phòng Flash được sử dụng rộng rãi. Tuy nhiên, việc dựa vào Flash là điều không lý tưởng. Mặc dù Chrome và Safari tiếp tục hỗ trợ WebSocket, nhưng rõ ràng là để hỗ trợ tất cả trình duyệt hiện đại mà không cần dựa vào Flash, WebSocket sẽ cần được thay thế.
Quay lại tính năng thăm dò ý kiến AJAX
Chúng tôi đã quyết định chuyển từ WebSocket sang phương thức thăm dò ý kiến AJAX "cũ". Mặc dù kém hiệu quả hơn nhiều từ góc độ I/O của ổ đĩa và mạng, nhưng tính năng thăm dò ý kiến AJAX đã đơn giản hoá việc triển khai kỹ thuật của tính năng Hội nghị phát trực tuyến. Đáng kể nhất là nhu cầu cần một ứng dụng Cramp riêng biệt đã bị loại bỏ. Thay vào đó, điểm cuối AJAX do ứng dụng Rails cung cấp. Mã phía máy khách đã được sửa đổi để hỗ trợ thăm dò AJAX của jQuery:
var fillStream = function(mostRecentActivity) {
$.getJSON(requestURL, function(data) {
addToStream(data.reverse());
setTimeout(function() {
fillStream(recentActivities.last());
}, 15000);
});
};
AJAX polling, though, is not without its downsides. Relying on the HTTP request/response cycle means that the server sees constant load even when there aren't any new updates. And of course, AJAX polling doesn't take advantage of what HTML5 has to offer.
## EventSource: The right tool for the job
Up to this point, a key factor was ignored about the nature of Stream Congress: the app only needs to stream updates one way, from server to client - downstream. It didn't need to be real-time, upstream client-to-server communication.
In this sense, WebSockets is overkill for Stream Congress. Server-to-client communication is so common that it's been given a general term: push. In fact, many existing solutions for WebSockets, from the hosted [PusherApp](http://pusherapp.com) to the Rails library [Socky](https://github.com/socky), optimize for push and don't support client-to-server communication at all.
Enter EventSource, also called Server-Sent Events. The specification compares favorably to WebSockets in the context to server to client push:
- A similar, simple JavaScript API on the browser side.
- The open connection is HTTP-based, not dropping to the low level of TCP.
- Automatic reconnection when the connection is closed.
### Going Back to Cramp
In recent months, Cramp has added support for EventSource. The code is very similar to the WebSockets implementation:
```ruby
class LiveEvents < Cramp::Action
self.transport = :sse
periodic_timer :latest, :every => 15
def latest
@latest_activity ||= nil
new_activities = find_activities_since(@latest_activity)
@latest_activity = new_activities.first unless new_activities.empty?
render new_activities.to_json
end
end
routes = HttpRouter.new do
add('/').to(LiveEvents)
end
run routes
Một vấn đề quan trọng cần lưu ý với EventSource là kết nối nhiều miền không được cho phép. Điều này có nghĩa là ứng dụng Cramp phải được phân phát từ cùng một miền streamcongress.com với ứng dụng Rails chính. Bạn có thể thực hiện việc này bằng cách sử dụng proxy tại máy chủ web. Giả sử ứng dụng Cramp được cung cấp bởi Thin và chạy trên cổng 8000, cấu hình Apache sẽ có dạng như sau:
LoadModule proxy_module /usr/lib/apache2/modules/mod_proxy.so
LoadModule proxy_http_module /usr/lib/apache2/modules/mod_proxy_http.so
LoadModule proxy_balancer_module /usr/lib/apache2/modules/mod_proxy_balancer.so
<VirtualHost *:80>
ServerName streamcongress.com
DocumentRoot /projects/streamcongress/www/current/public
RailsEnv production
RackEnv production
<Directory /projects/streamcongress/www/current/public>
Order allow,deny
Allow from all
Options -MultiViews
</Directory>
<Proxy balancer://thin>
BalancerMember http://localhost:8000
</Proxy>
ProxyPass /live balancer://thin/
ProxyPassReverse /live balancer://thin/
ProxyPreserveHost on
</VirtualHost>
Cấu hình này đặt điểm cuối EventSource tại streamcongress.com/live
.
Polyfill ổn định
Một trong những lợi thế đáng kể nhất của EventSource so với WebSocket là phương thức dự phòng hoàn toàn dựa trên JavaScript, không phụ thuộc vào Flash. Polyfill của Remy Sharp thực hiện việc này bằng cách triển khai tính năng thăm dò ý kiến dài hạn trong các trình duyệt không hỗ trợ EventSource. Do đó, EventSource hiện hoạt động trên tất cả trình duyệt hiện đại có bật JavaScript.
Kết luận
HTML5 mở ra nhiều khả năng mới và thú vị cho việc phát triển web. Với WebSockets và EventSource, các nhà phát triển web hiện có các tiêu chuẩn rõ ràng, rõ ràng để hỗ trợ các ứng dụng web theo thời gian thực. Tuy nhiên, không phải người dùng nào cũng sử dụng trình duyệt hiện đại. xuống cấp nhẹ phải được xem xét khi chọn triển khai các công nghệ này. Ngoài ra, công cụ phía máy chủ cho WebSockets và EventSource vẫn đang ở giai đoạn đầu. Bạn cần lưu ý những yếu tố này khi phát triển ứng dụng HTML5 theo thời gian thực.