Loạt bài này cho bạn thấy cách phát triển các ứng dụng web theo hướng sự kiện bằng cách sử
dụng các kỹ thuật Reverse Ajax. Phần 1 đã giới thiệu Reverse Ajax, polling, streaming, Comet
và long-polling. Phần 2 đã giải thích cách thực hiện Reverse Ajax bằng cách sử dụng
WebSockets và đã thảo luận về những hạn chế của các máy chủ web khi sử dụng Comet và
WebSockets. Phần 3 đã cho thấy rằng việc triển khai thực hiện hệ thống truyền thông
WebSockets hoặc Comet riêng của bạn có thể khó khăn nếu bạn cần hỗ trợ một số máy chủ hoặc
cung cấp một ứng dụng web độc lập cho người dùng để triển khai trên máy chủ của riêng họ.
Thậm chí nếu mã JavaScript trên máy khách đơn giản, thì bạn vẫn cần một số cách xử lý trường
hợp ngoại lệ, các tính năng kết nối lại và tin báo. Về phía máy chủ, việc thiếu một API chung
(global API) và nhiều API máy chủ web dẫn đến nhu cầu cần có các framework để mang lại một
sự trừu tượng hóa. Phần 3 cũng đã thảo luận Socket.IO.
10 trang |
Chia sẻ: lylyngoc | Lượt xem: 1621 | Lượt tải: 1
Bạn đang xem nội dung tài liệu Reverse Ajax, Phần 4: Atmosphere và CometD, để tải tài liệu về máy bạn click vào nút DOWNLOAD ở trên
Reverse Ajax, Phần 4: Atmosphere và CometD
Giới thiệu
Loạt bài này cho bạn thấy cách phát triển các ứng dụng web theo hướng sự kiện bằng cách sử
dụng các kỹ thuật Reverse Ajax. Phần 1 đã giới thiệu Reverse Ajax, polling, streaming, Comet
và long-polling. Phần 2 đã giải thích cách thực hiện Reverse Ajax bằng cách sử dụng
WebSockets và đã thảo luận về những hạn chế của các máy chủ web khi sử dụng Comet và
WebSockets. Phần 3 đã cho thấy rằng việc triển khai thực hiện hệ thống truyền thông
WebSockets hoặc Comet riêng của bạn có thể khó khăn nếu bạn cần hỗ trợ một số máy chủ hoặc
cung cấp một ứng dụng web độc lập cho người dùng để triển khai trên máy chủ của riêng họ.
Thậm chí nếu mã JavaScript trên máy khách đơn giản, thì bạn vẫn cần một số cách xử lý trường
hợp ngoại lệ, các tính năng kết nối lại và tin báo. Về phía máy chủ, việc thiếu một API chung
(global API) và nhiều API máy chủ web dẫn đến nhu cầu cần có các framework để mang lại một
sự trừu tượng hóa. Phần 3 cũng đã thảo luận Socket.IO.
Trong bài này, hãy tìm hiểu về Atmosphere và CometD. Chúng là các thư viện Reverse Ajax nổi
tiếng cho các máy chủ Java.
Bạn có thể tải về mã nguồn được sử dụng trong bài này.
Điều kiện tiên quyết
Để nắm vững kiến thức trong bài này, tốt nhất bạn nên biết trước JavaScript và Java. Để chạy ví
dụ mẫu trong bài này, bạn cũng cần có phiên bản Maven và JDK mới nhất (xem phần Tài
nguyên).
Về đầu trang
Framework Atmosphere
Atmosphere là một framework Java cung cấp API chung để sử dụng các tính năng Comet và
WebSocket của nhiều máy chủ web, gồm có Tomcat, Jetty, GlassFish, WebLogic, Grizzly,
JBossWeb, JBoss và Resin. Bất kỳ máy chủ web nào đang hỗ trợ Đặc tả Servlet 3.0 thì cũng sẽ
hỗ trợ. Trong số các framework được trình bày trong loạt bài này, Atmosphere hỗ trợ nhiều máy
chủ nhất.
Atmosphere có thể phát hiện ra các API máy chủ nguyên gốc (cho Comet và WebSockets) và
chuyển đổi ngược lại Servlet 3.0, nếu có, cho Comet. Hoặc, cũng với Comet, nó sẽ quay lại một
chế độ không đồng bộ "có quản lý" (nhưng không có khả năng co giãn như Jetty Continuations).
Atmosphere đã được dùng hơn hai năm và vẫn tiếp tục được phát triển. Nó được sử dụng trong
các ứng dụng web lớn như JIRA, một trong những trình dò tìm vấn đề (issue tracker) nổi tiếng
nhất. Hình 1 cho thấy kiến trúc của Atmosphere.
Hình 1. Kiến trúc của Atmosphere
Framework Atmosphere bao gồm Atmosphere runtime, cung cấp một API chung cho tất cả các
giải pháp và tiêu chuẩn máy chủ web khác nhau. Trên hết, máy khách có thể truy cập vào các
tính năng API và Reverse Ajax thông qua Google Web Toolkit (GWT – Bộ công cụ Web của
Google) bằng cách thiết lập chỉ một servlet. Hoặc, bạn cũng có thể sử dụng Jersey, một
framework thực hiện JSR-311 (đặc tả JAX-RS). Vì thế, Atmosphere có thể được sử dụng trong
các dịch vụ tiện lợi có cung cấp các chú thích bổ sung. Sau khi cấu hình mô đun mà bạn chọn,
bạn có thể truy cập Atmosphere runtime bằng cách thực thi một số lớp (được thảo luận sau trong
bài này). Bạn cũng có thể tùy chọn sử dụng một số plugin được cung cấp để tăng thêm hỗ trợ cho
việc phân cụm (clustering), tạo thông báo, tích hợp phụ thuộc và nhiều hơn nữa. Nếu bạn đang
sử dụng một web framework (Wicket, Struts, Spring MVC), bạn có thể thêm vào sự hỗ trợ
Reverse Ajax bằng cách sử dụng MeteorServlet của Atmosphere. Servlet này trưng ra một đối
tượng Meteor, có thể được lấy ra trong các trình điều khiển và các dịch vụ của bạn để treo
(suspend) hoặc tiếp tục (resume) lại các yêu cầu.
Sức mạnh của Atmosphere vẫn còn ở phía máy chủ: nó cung cấp một API chuẩn hóa bao gồm tất
cả các giải pháp khác nhau và các cách để truyền thông với WebSockets hoặc Comet.
Atmosphere không sử dụng một giao thức giữa máy khách và máy chủ, như Socket.IO và
CometD. Cả hai thư viện đó đều cung cấp một JavaScript phía máy khách và một servlet phía
máy chủ sử dụng cách giao tiếp thông qua giao thức đặc trưng (handshaking, tạo thông báo, tin
báo và nhịp hoạt động). Mục tiêu của Atmosphere là cung cấp một kênh truyền dẫn chung bên
phía máy chủ. Nếu bạn cần phải sử dụng một giao thức đặc trưng, chẳng hạn như Bayeux (một
giao thức được CometD sử dụng), bạn phải phát triển "trình xử lý" riêng của mình theo
Atmosphere. Plugin CometD chỉ làm một việc là tận dụng API của Atmosphere để suspend và
resume lại các yêu cầu và nó ủy quyền cho các lớp CometD để quản lý truyền thông CometD khi
sử dụng giao thức Bayeux.
Atmosphere đi kèm với một thư viện máy khách jQuery để giúp cho việc thiết lập kết nối dễ
dàng, nó có thể tự động phát hiện ra kiểu truyền tải tốt nhất hiện có (WebSockets hoặc CometD).
Việc sử dụng plugin jQuery của Atmosphere cũng tương tự như API WebSockets của HTML5.
Đầu tiên, bạn kết nối đến máy chủ, đăng ký một cuộc phản hồi để nhận các thông báo và sau đó
bạn có thể đẩy ra dữ liệu nào đó.
Mã nguồn kèm theo bài này có chứa một ví dụ mẫu Atmosphere trực tiếp sử dụng một trình xử
lý với servlet của Atmosphere. Mã máy khách luôn giống nhau; đó là mã tương tự như mã được
sử dụng trong các Phần 1, 2 và 3 của loạt bài này (trong các ví dụ mẫu chat khi sử dụng Comet
long-polling). Có thể bạn đang sử dụng plugin jQuery của Atmosphere, nhưng chưa cần thiết vì
Atmosphere không thi hành bất kỳ giao thức truyền thông nào. Ngoài ra bạn có thể xem xét các
ví dụ khác trong dự án của Atmosphere, nhất là những ví dụ sử dụng các chú thích JSR-311
(Jersey). Chúng thực sự đơn giản hóa việc viết các trình xử lý.
Liệt kê 1 cho thấy giao diện của trình xử lý Atmosphere.
Liệt kê 1. Giao diện AtmosphereHandler
public interface AtmosphereHandler {
void onRequest(AtmosphereResource resource)
throws IOException;
void onStateChange(AtmosphereResourceEvent event)
throws IOException;
void destroy();
}
Phương thức onRequest nhận tất cả các yêu cầu từ máy khách và quyết định xem có suspend
hoặc resume lại chúng (hoặc không làm gì) hay không. Mỗi lần suspend hoặc resume lại yêu cầu,
một thông báo được phát đi hoặc diễn ra một thời gian chờ timeout, một sự kiện được gửi và
nhận bằng phương thức onStateChange.
Cách thực hiện phương thức onRequest để chat trong Comet được hiển thị như Liệt kê 2.
Liệt kê 2. Giao diện AtmosphereHandler - onRequest
Broadcaster broadcaster =
BroadcasterFactory.getDefault().lookup(
DefaultBroadcaster.class, ChatHandler.class.getName(), true);
broadcaster.setScope(Broadcaster.SCOPE.APPLICATION);
resource.setBroadcaster(broadcaster);
HttpServletRequest req = resource.getRequest();
String user = (String) req.getSession().getAttribute("user");
if (user != null) {
if ("GET".equals(req.getMethod())) {
resource.suspend(-1, false);
} else if ("POST".equals(req.getMethod())) {
String cmd = req.getParameter("cmd");
String message = req.getParameter("message");
if ("disconnect".equals(cmd)) {
close(resource);
} else if (message != null && message.trim().length() > 0) {
broadcaster.broadcast("[" + user + "] " + message);
}
}
}
Một quy ước điển hình là treo các yêu cầu GET và gửi các thông báo bằng phương thức POST.
Khi nhận được một thông báo, nó sẽ được chuyến tiếp (broadcast) đến tất cả các nơi đã được
đăng ký trong trình phát quảng bá (broadcaster). Lưu ý rằng ví dụ này không bao giờ gửi bất cứ
điều gì tới luồng dữ liệu kết quả HttpServlet. Hành động broadcast và suspend chỉ là gửi các
sự kiện đã nhận được khi thực thi các phương thức khác, như thể hiện trong Liệt kê 3:
Liệt kê 3. Giao diện AtmosphereHandler - onStateChange
Broadcaster broadcaster =
BroadcasterFactory.getDefault().lookup(
DefaultBroadcaster.class, ChatHandler.class.getName(), true);
// Client closed the connection.
if (event.isCancelled()) {
close(event.getResource());
return;
}
try {
String message = (String) event.getMessage();
if (message != null) {
PrintWriter writer =
event.getResource().getResponse().getWriter();
writer.write(message);
writer.flush();
}
} finally {
if (!event.isResumedOnTimeout()) {
event.getResource().resume();
}
}
Bây giờ bạn đã có những gì cần thiết để Comet chat hoạt động. Tóm lại, các khái niệm
Atmosphere trọng tâm là: đối tượng tài nguyên đại diện cho kết nối và một broadcaster chịu
trách nhiệm khởi động các sự kiện đến các tài nguyên và quyết định khi nào suspend hoặc
resume lại một yêu cầu. Lưu ý rằng ví dụ này chỉ dùng cho Comet. Để có thể sử dụng
WebSockets và Comet, nên sử dụng một thư viện phía máy khách và sẽ cần một trình xử lý phức
tạp hơn.
Bảng 1 mô tả những lợi ích và hạn chế khi sử dụng framework Atmosphere.
Bảng 1. Những lợi ích và hạn chế của khung công tác Atmosphere
Lợi ích Hạn chế
Nếu bạn phải triển khai một ứng dụng
web trong một số máy chủ web mà bạn
không thể kiểm soát được. Bạn sẽ có cơ
hội tốt là các tính năng Reverse Ajax
trong ứng dụng của bạn sẽ hoạt động đúng
do số máy chủ web được Atmosphere hỗ
trợ.
Khi bạn cần một API phổ biến trên một
giao tiếp Reverse Ajax, mà không cần
định nghĩa bất kỳ giao thức nào, vì có thể
bạn muốn phát triển hay mở rộng nó.
Thiếu tài liệu hướng dẫn về kiến trúc, dự án, các khái
niệm và các API của Atmosphere, những thứ này sẽ
rất có ích nếu bạn muốn xem mã nguồn hoặc phân
tích một số ví dụ mẫu được cung cấp. API này có kỹ
thuật cao và đôi khi mơ hồ, so với các API đơn giản
của các framework khác, chẳng hạn như Socket.IO và
CometD. Ngay cả khi có sử dụng các chú thích trong
Atmosphere, thì một số tên và các thuộc tính của nó
vẫn quá mang tính kỹ thuật.
Mặc dù có khả năng trừu tượng hóa tốt ở phía máy
chủ, nhưng không có một thư viện tốt nào ở phía máy
khách. Không có giao thức nào cả, do đó, tất cả các
tính năng bổ sung đều được dành cho các nhà phát
triển. Nếu bạn yêu cầu một ứng dụng web lớn, có khả
năng co giãn và phát hiện timeout, tin báo, back-off,
liên miền, v.v... nhất là khi làm việc với các thiết bị di
động thì thư viện này là quá đơn giản. Trong trường
hợp này, CometD cho độ tin cậy hơn rất nhiều; nó sử
dụng một giao thức truyền thông để kích hoạt một số
luồng điều khiển và phát hiện lỗi, tất cả đều được
cung cấp trong CometD. Việc sử dụng máy khách
JavaScript của CometD với plugin CometD của
Atmosphere có thể là một lựa chọn thay thế tốt nếu
bạn cần các tính năng bổ sung.
Về đầu trang
Framework CometD
Framework CometD, một giải pháp truyền thông theo sự kiện dựa trên HTTP, đã được dùng
trong nhiều năm. Phiên bản 2 đã thêm sự hỗ trợ cho việc cấu hình chú thích và WebSockets.
Framework CometD cung cấp một phần máy chủ Java và một phần máy khách Java, cộng với
các thư viện máy khách JavaScript dựa trên jQuery và Dojo. CometD sử dụng một giao thức
truyền thông chuẩn hóa được gọi là Bayeux, cho phép bạn kích hoạt một số phần mở rộng cho
tin báo, kiểm soát luồng, đồng bộ hóa, phân cụm và v.v.
Cách tiếp cận theo sự kiện của CometD hoàn toàn phù hợp với các khái niệm mới về phát triển
web theo sự kiện. Như với các giao diện người dùng máy tính để bàn truyền thống, tất cả các
thành phần đều truyền thông qua một bus để gửi các khai báo và nhận các sự kiện. Do đó tất cả
cách truyền thông đều là không đồng bộ.
Framework CometD:
Có đủ tài liệu cần thiết.
Cung cấp các ví dụ mẫu và các nguyên mẫu Maven giúp dễ dàng khởi động một dự án.
API được thiết kế tốt cho phép phát triển mở rộng.
Cung cấp một mô đun phân cụm (clustering), được gọi là Oort, cung cấp cho bạn khả
năng chạy nhiều máy chủ web CometD như các nút trong một cụm phía sau một trình cân
bằng tải để điều chỉnh co giãn theo một số lượng lớn các kết nối HTTP.
Hỗ trợ các chính sách bảo mật để cho phép thiết lập xem ai có quyền gửi thông báo qua
kênh nào.
Tích hợp khá tốt với Spring và Google Guice (các dependency injection framework).
Giao thức Bayeux
Giao thức truyền thông Bayeux chủ yếu thông qua HTTP. Nó cung cấp không đồng bộ một cách
truyền thông đáp ứng hai chiều giữa máy khách và máy chủ. Giao thức Bayeux dựa trên các
kênh mà ở đó các thông báo được định tuyến và phân phối từ máy khách đến máy chủ, máy chủ
đến máy khách hoặc máy khách đến máy khách (nhưng thông qua máy chủ). Bayeux là một kiểu
giao thức đăng ký-xuất bản. CometD thực hiện giao thức Bayeux và do đó cung cấp một tầng
trừu tượng hóa dựa trên các kiểu truyền tải Comet và WebSocket để định tuyến các yêu cầu qua
Bayeux.
Máy chủ và đặc tính của nó
CometD được đóng gói với ba kiểu truyền tải: JSON, JSONP và WebSocket. Chúng phụ thuộc
vào API Continuations và WebSocket của Jetty. Theo mặc định, có thể sử dụng CometD với
Jetty 6, 7, 8, cũng như bất kỳ các máy chủ khác nào hỗ trợ Đặc tả Servlet 3.0. Có thể bổ sung và
phát triển các kiểu truyền tải theo cách giống như các phần mở rộng. Bạn có thể viết các kiểu
truyền tải hỗ trợ API WebSocket của Grizzly và các API khác, rồi thêm chúng vào bước cấu
hình máy chủ CometD. Hình 2 cho thấy một tổng quan về các khối CometD chính.
Hình 2. Kiến trúc của CometD
Tầng bảo mật để truy cập các kênh thông báo không được hiển thị trong Hình 2.
Mã nguồn được cung cấp cùng với bài viết này gồm có một ứng dụng web sử dụng CometD.
Trình mô tả ứng dụng web có chứa định nghĩa được mô tả trong ví dụ chat ở Liệt kê 4.
Liệt kê 4. web.xml
cometd
org.cometd.java.annotation.AnnotationCometdServlet
true
[...]
services
ChatService
transports
com.ovea.cometd.websocket.jetty8.Jetty8WebSocketTransport
Servlet CometD hỗ trợ một số tùy chọn kiểm soát các thiết lập chung, chẳng hạn như khả năng
thiết lập các kiểu truyền tải và các dịch vụ. Trong ví dụ này, giả sử bạn muốn Jetty 8 hỗ trợ
WebSocket, lớp dịch vụ ChatService của CometD bên phía máy chủ sẽ kiểm soát phòng chat,
nơi mọi người có thể trò chuyện, như thể hiện trong Liệt kê 5:
Liệt kê 5. Lớp ChatService của CometD
@Service
public final class ChatService {
@Inject
BayeuxServer server;
@PostConstruct
void init() {
server.addListener(new BayeuxServer.SessionListener() {
@Override
public void sessionAdded(ServerSession session) {
[...]
}
@Override
public void sessionRemoved(ServerSession session, boolean
timedout) {
[...]
}
});
}
@Configure("/**")
void any(ConfigurableServerChannel channel) {
channel.addAuthorizer(GrantAuthorizer.GRANT_NONE);
}
@Configure("/chatroom")
void configure(ConfigurableServerChannel channel) {
channel.addAuthorizer(new Authorizer() {
@Override
public Result authorize(
[..] // check that the user is in session
}
});
}
@Listener("/chatroom")
void appendUser(ServerSession remote,
ServerMessage.Mutable message) {
[...]
}
}
Liệt kê 5 trình bày một số tính năng quan trọng của CometD, bao gồm:
Tích hợp phụ thuộc (Dependency injection)
Quản lý vòng đời
Cấu hình kênh chung
Quản lý bảo mật
Chuyển đổi thông báo (để thêm tên người dùng trước tất cả các thông báo)
Quản lý phiên làm việc
Về phía máy khách, ví dụ này không kích hoạt bất kỳ phần mở rộng nào -- chỉ cần mã CometD
thô, như trong Liệt kê 6:
Liệt kê 6. Mã máy khách của CometD
// First create t cometd object and configure it
var cometd = new $.Cometd('CometD chat client');
cometd.configure({
url: document.location + 'cometd',
logLevel: 'debug'
});
cometd.websocketEnabled = 'WebSocket' in window;
// Then we register some listeners. Meta channels (those with
// the form /meta/ are specific reserved channels)
cometd.addListener('/meta/disconnect', function(message) {
[...]
});
cometd.addListener('/meta/connect', function(message) {
[...]
});
// Then, starting a connexion can be done using:
cometd.handshake();
// And subscriptions with:
cometd.subscribe('/chatroom', function(event) {
[...] // event.data holds the message
});
// We finally send data to the chatroom like this:
cometd.publish('/chatroom', msg);
API phía máy khách của CometD dễ dùng và dễ hiểu, trong khi vẫn mạnh mẽ và có thể mở rộng
được. Bài này chỉ trình bày các phần chính của ứng dụng web, vì vậy hãy xem ứng dụng ví dụ để
có ý tưởng tốt hơn về sức mạnh của CometD.
Bảng 2 vạch ra những lợi ích và hạn chế khi sử dụng khung công tác CometD.
Bảng 2. Những lợi ích và hạn chế của CometD
Lợi ích Hạn chế
CometD đưa ra một giải pháp hoàn chỉnh, từ phía máy
khách và phía máy chủ, cũng như từ một máy khách Java
độc lập đến một máy chủ. Framework này có đủ tài liệu
cần thiết, có một API tốt và dễ sử dụng. Trên hết, nó có
cách tiếp cận theo hướng sự kiện. CometD và Bayeux là
một phần của nhiều ứng dụng web theo sự kiện. Các
khung công tác Reverse Ajax khác không cung cấp bất kỳ
cơ chế theo sự kiện nào, buộc người dùng cuối phải phát
triển giải pháp riêng của mình.
CometD hỗ trợ nhiều tính năng cần thiết, chẳng hạn như
kết nối lại, phát hiện thời gian chờ tin cậy, back-off, xử lý
theo bó, tin báo và nhiều hơn nữa mà bạn sẽ không tìm
thấy trong các framework Reverse Ajax khác. CometD
cho phép bạn đạt được cách truyền thông có độ trễ thấp,
tin cậy nhất có thể.
CometD hiện tại không hỗ trợ bất kỳ
container Servlet 2.5 nào ngoài Jetty
cho Comet (Tomcat) và nó cũng
không hỗ trợ WebSocket của
Glassfish/Grizzly.
Về đầu trang
Kết luận
Cả Atmosphere và CometD đều là các giải pháp Reverse Ajax nguồn mở, chắc chắn. Với Ovea,
sự lựa chọn của chúng tôi là CometD vì chúng tôi phát triển các ứng dụng web theo sự kiện có
khả năng co giãn với các thiết bị di động trong một môi trường có phân cụm và chúng tôi có toàn
quyền kiểm soát cơ sở hạ tầng (Jetty). Tuy nhiên, do không được tiếp tục phát triển, CometD
không thể là sự lựa chọn tốt nhất nếu bạn đang bán các ứng dụng web và muốn các tính năng
Reverse Ajax của mình làm việc trên càng nhiều máy chủ càng tốt. Nhưng giờ đây, càng ngày
càng có nhiều Web container hỗ trợ Đặc tả Servlet 3.0, nên đã giảm bớt sự hạn chế của CometD.
Khi nói về tầng truyền tải, sự khác biệt chính còn lại hiện nay phụ thuộc vào sự hỗ trợ của
WebSocket.