Tìm hiểu về HTTP Long-Polling
This post hasn't been updated for 3 years
- Ban đầu, các ứng dụng web được phát triển xung quanh mô hình clinet/server, nơi mà web client luôn phải là bên bắt đầu cho các transactions. Do đó không có cơ chế cho máy chủ gửi một các dữ liệu hay sự kiện đến client mà không cần client bắt đầu với 1 request. Để khắc phục việc này, người ta phát triển các phương thức giao tiếp HTTP khác như webSocket, long-polling, JSONP polling, Piggyback polling, comet streaming, comet long-polling....
1. HTTP 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 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.
2. HTTP long-polling
- Như đã nói về một nhược điểm của polling thì long-poling được sinh ra để giải quyết nhược điểm này bằng cách giảm thiểu việc yêu cầu liên tiếp trong khi không có dữ liệu hữu ích trả về.
Ý tưởng: Client gửi 1 request và server sẽ giữ lại request đó và sẽ hồi đáp nó khi có một sự kiện tương ứng diễn ra ( nhưng sẽ có 1 trường hợp là request từ client đã đến timeout nhưng vẫn chưa có sự kiện mong đợi nào, khi đó, server sẽ buộc phải trả về 1 response nhưng có thể là không kèm theo bất kỳ dữ liệu có ích nào.)
- Client gửi 1 yêu cầu.
- Server tiếp nhận yêu cầu và xửa lý yêu cầu trên dữ liệu mong đợi liên tục trong 1 khoản thời gian nhất định.
- Khi có sự kiện diễn ra(ví dụ: thay đổi trên dữ liệu mong đợi) server sẽ hồi đáp lại client thông qua phương thức http truyền thống.
- Client sau khi nhận được response tử server, sẽ xử lý và bắt đầu tiếp tục lắng nghe server bằng việc bắt đầu lại bước 1 với 1 http request. Ưu điểm:
- Đơn giản, dễ hiểu, dễ tiếp cận.
- Làm việc với mọi trình duyệt.
- Không yêu cầu đặc biết phía server.
- Giảm thiểu tiêu thụ tài nguyên. 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.
2. Demo
- Đó là lý thuyết, bây giờ chúng ta sẽ ứng dụng long-polling để nhận định lại về các vấn đề mà tôi đã nêu trên.
- Ta sẽ thử demo về 1 ứng dụng chat. Nói đến ứng dụng chat thì nhiều người có lẽ sẽ nghĩ đến websocket, nhưn long-polling cũng là 1 giải pháp khá hay, và theo tôi nghỉ nó sẽ tiết kiệm tài nguyên hơn là websocket với việc gửi quá nhiều tin broadcast.
Ý tưởng
* Chúng ta sẽ xây dựng demo của chúng ta bao gồm các phương thức chính:
* client:
1 . Show tất cả tin nhắn.
2 . Gửi 1 tin nhắn.
3. Cập nhật tin nhắn mới
* server:
1 . Show tất cả tin nhắn.
2 . Tạo 1 tin nhắn.
3. trả về các tin nhắn mới.
Triển khai
- Khởi tạo dự án:
rails new chat_demo rails g model Message name:string content:string rails g controller messages rails db:create db:migrate
Server: model message.rb
scope :after_messages, ->message_id do
where("id > ?", message_id) if Message.find_by(id: message_id) || message_id == 0
end
messages_controller.rb Ta xây dựng phương thức tìm các tin nhắn mới mà client chưa được cập nhật đồng thời giữ lại request từ client và trả về khi có tn mới. tùy theo setting timeout của request mà ta quyết định thời gian lưu lại request cho phù hợp.
private
def check_new_messages
10.times do
@new_messages = Message.after_messages params[:last_id].to_i
return @new_messages if @new_messages.any?
sleep 5
end
nil
end
index trả về tất cả các tin nhắn khi request html và chỉ trả về
def index
@message = Message.new
respond_to do |format|
format.html do
@messages = Message.all
end
format.js do
@messages = check_new_messages
end
end
@last_id = @messages&.last&.id || 0
end
client:
- Phương thức thêm vào tin nhắn mới:
function appendMessages(messages, last_id){
$(".message-content").append(messages);
$("input[name='last_id']").val(last_id);
scrollToNewestMessage();
}
- Đưa thanh cuộn đến vị trí dưới cùng
function scrollToNewestMessage(){
$(".message-content").animate({
scrollTop: $(".message-content")[0].scrollHeight
}, 1000);
}
- Phương thức request thêm tin nhắn từ server. chúng ta sẽ lặp lich để tiếp tục gửi lại request lên server mỗi khi nhận được respones từ server. Mình cho lặp lại cả khi có lỗi xuất hiện.
function requestNewestMessages(){
setTimeout(function(){
$.ajax({
type: "GET",
url: $("#message-form").attr("action"),
data: "last_id=" + $("input[name='last_id']").val(),
dataType: "script",
success: function(data){
requestNewestMessages();
},
error: function(error_message) {
requestNewestMessages();
}
});
}, 3000);
}
Một ứng dụng chat tất nhiên cần nhiều thao tác và trau chuốt hơn nữa, nhưng như vậy là cơ bản chúng ta đã hình thành công việc chính. Demo: https://github.com/phantien133/chat_demo
All Rights Reserved