12/08/2018, 15:08

Tìm hiểu về HTTP Long-Polling

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 ...

  • 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.)
    1. Client gửi 1 yêu cầu.
    2. 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.
    3. 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.
    4. 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

0