+9

HTML5 Server-Sent Event

Giới thiệu

Chúng ta đã nghe nhiều về ứng dụng web real-time (như ứng dụng chat, game online,...), một trong những cách thức phổ biến được dùng đến đó là Web Socket. Bên cạnh đó các bạn cũng có thể đã nghe qua về các công nghệ khác như Ajax polling, Server-Sent events,...

Trong bài viết này, mình xin giới thiệu về HTML5 Server-Sent events.

HTML5 Server-Sent Events là gì?

HTML5 Server-Sent Events (SSE) hay còn được biết đến với tên gọi HTML5 Event Source là một Web Api cho phép kết nối thời gian thực giữa server và client (browser). Tuy nhiên, khác với WebSockets, SSE chỉ có 1 chiều dữ liệu, đó là từ server đến client (chỉ có server gửi dữ liệu đến client).

Tại sao dùng SSE khi đã có Web Sockets

Ưu điểm của SSE đó là API đơn giản, dễ thực hiện, không yêu cầu việc setup và implement phức tạp như Web sockets. SSE hoạt động với giao thức HTTP thông thường, không yêu cầu một Web socket server mới hay các giao thức đặc biệt khác.

Trong thực tế, không phải ứng dụng web real time nào cũng cần sự tương tác 2 chiều giữa client và server, trong trường hợp này việc sử dụng SSE là dễ dàng hơn rất nhiều. Có thể kế đến một số trường hợp như:

  • Ứng dụng dashboard, đồ thị chart
  • News feeds
  • Ứng dụng theo dõi hoạt động của server
  • Ứng dụng có dữ liệu thay đổi nhanh chóng như giá cả, thị trường, tình trạng hàng hóa, thông tin các trận đấu thể thao,...

Một ưu điểm nữa của SSE đó là, client có thể tự động kết nối lại server nếu bị mất kết nối, thời gian timeout trước khi client kết nối lại có thể tùy chỉnh được từ server. Khi thực hiện kết nối lại, client sẽ tự động gửi ID của event cuối cùng nó nhận được, nhờ đó server có thể biết được client đã bị bỏ qua bao nhiêu message để gửi lại những message đó.

SSE hoạt động với hầu hết các trình duyệt, ngoại trừ IE. Không biết trong tương lai IE có support hay nó có còn tồn tại hay không 😄, tuy nhiên, chúng ta vẫn có thể sử dụng các polyfills để support đầy đủ tất cả các trình duyệt.

Cách thức hoạt động

Mô hình kết nối

image

p=. _^http://stackoverflow.com/questions/11077857/what-are-long-polling-websockets-server-sent-events-sse-and-comet^_
SSE gồm 2 thành phần chính
  • Server side gửi dữ liệu dạng text UTF-8 đến client theo định dạng "Event stream" (MIME: text/event-stream).

    • Định dạng event stream

      Dạng đơn giản, response chỉ trả về dữ liệu (phần phía sau data:), kết thúc bằng 2 ký tự \n. Client sẽ nhận event mặc định là message, client lấy dữ liệu thông qua event.data

      data: a message\n\n
      

      Dữ liệu dạng JSON

      data: {"message": "JSON"}\n\n
      

      Kèm theo message id, client lấy giá trị này thông qua event.lastEventId

      id: 1111\n
      data: a message\n\n
      

      Kèm theo event name, eg: custom-event, client lắng nghe sự kiện custom-event

      data: Data of message event\n\n
      event: custom-event\n
      data: Data of custom-event\n\n
      

      Các dòng bắt đầu với kí tự : được xem như comment

      : This is a comment
      data: a message\n\n
      

      Kiểm soát thời gian timeout trước khi client thực hiện kết nối lại, ví dụ 10s

      retry: 10000\n
      data: a message\n\n
      
    • Server example

      Ví dụ đơn giản sử dụng PHP, lấy thông tin CPU usage của server gửi dữ liệu cho client mỗi 2s

      <?php
      // Set necessary headers
      header('Content-Type: text/event-stream');
      header('Cache-Control: no-cache');
      
      require __DIR__ . '/vendor/autoload.php';
      
      $linfo = new \Linfo\Linfo;
      $parser = $linfo->getParser();
      
      // Using while to keep server connection open, so we have only one request.
      // If connection is closed browser will reconnect and will send last event Id.
      while (true) {
          $parser->determineCPUPercentage();
          sendMessage($parser->getCPUUsage(), 'cpu', time());
      
          sleep(2);
      }
      
      function sendMessage($value, $event = null, $id = null, $retry = null)
      {
          if ($retry) {
              echo "retry: $retry\n";
          }
      
          if ($id) {
              echo "id: $id\n";
          }
      
          if ($event) {
              echo "event: $event\n";
          }
      
          echo "data: $value\n\n";
      
          ob_flush();
          flush();
      }
      

      Hoặc sử dụng libSSE-php

      <?php
      require __DIR__ . '/vendor/autoload.php';
      
      use Sse\Events\TimedEvent;
      use Sse\SSE;
      use Linfo\Linfo;
      
      class SysEvent extends TimedEvent
      {
          /**
           * the interval in seconds to get new data
           */
          public $period = 1;
      
          private $linfo;
      
          public function __construct()
          {
              $this->linfo = new Linfo;
          }
      
          /**
           * Get updated data
           */
          public function update()
          {
              $parser = $this->linfo->getParser();
              $parser->determineCPUPercentage();
      
              return $parser->getCPUUsage();
          }
      }
      
      $sse = new SSE();
      $sse->addEventListener('cpu', new SysEvent());
      $sse->start();
      
  • Client side sử dụng EventSource interface để nhận dữ liệu từ server như các DOM events (ví dụ như onclick, onhover,...) thông thường khác.

    var evtSource = new EventSource("/path/to/server-resource");
    
    // Event khi clien connect thành công đến server
    evtSource.addEventListener('open', function(event) {
        console.log('Connection was opened');
    }, false);
    
    // Khi có lỗi xảy ra (VD: connection closed)
    // Client tự động connect lại sau (mặc định) 3 giây
    evtSource.addEventListener('error', function(event) {
        if (event.eventPhase == EventSource.CLOSED) {
            console.log('> Connection was closed');
        }
    }, false);
    
    // Event khi server gửi dữ liệu, tên event mặc định là message
    evtSource.addEventListener('message', function(event) {
        var data = event.data; // or JSON.parse(event.data) if json
        var dataId = event.lastEventId; // Id được gửi server. Khi kết nối lại,
                                        // client sẽ tự dộng gửi kèm trong header Last-Event-Id
        if (dataId == 'CLOSED') {
            // Ngăn client không kết nối lại server
            evtSource.close();
        }
    }, false);
    
    // Custom event, server trả về sự kiện với tên: custom-event
    evtSource.addEventListener('custom-event', function(event) {
        var data = event.data;
        // ...
    }, false);
    

Demo

SSE Demo

References


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí