Bài viết về Server-sent event (SSE), là một kỹ thuật trong HTML5 tạo ra kết nối chuyển dữ liệu một chiều từ server đến client.
Mục lục
Tại sao dùng Server-sent Events (SSE)
SSE tạo ra kết nối một chiều từ server đến client sử dụng giao thức HTTP truyền thống với định nghĩa dữ liệu đơn giản cho phía server (event stream format) và API gọn nhẹ phía client.
So với phương pháp polling từ client lên server để kiểm tra dữ liệu thì SSE hiệu quả hơn rất nhiều do client chỉ cần tạo kết nối HTTP lên server một lần, và server giữ kết nối đó để liên tục gửi data cho client.
WebSockets thì phức tạp và hoành tráng hơn hẳn SSE. Tuy nhiên WebSockets là kết nối hai chiều mà đôi khi ta lại không cần chiều từ client lên server. Ngoài ra WebSockets là một giao thức hoàn toàn khác và đòi hỏi server phải hỗ trợ (Điểm này thì Node.js là số 1, PHP thì cần thêm thư viện ví dụ Ratchet PHP WebSockets).
Định dạng dữ liệu của Server-sent Events
Phía server phải tuân thủ in dữ liệu theo format của text/event-stream
như sau:
Prefix | Kiểu | Mô tả |
---|---|---|
data: | string | Dữ liệu là một chuỗi ký tự kết thúc bằng hai ký tự xuống dòng \n\n .Ví dụ: data: This is a message\n\n data luôn là chuỗi cuối cùng được gửi từ server.Nếu muốn chuỗi dữ liệu có nhiều dòng thì ta có thể dùng nhiều dòng data: nhưng kết thúc chỉ với một ký tự \n , trừ dòng cuối cùng vẫn phải là hai ký tự \n .Ví dụ: data: First message\n Phía client sẽ nhận được chuỗi First message\nSecond message\nLast message .SSE không có cấu trúc object nhưng bạn có thể thay bằng chuỗi JSON và phía client dùng JSON.parse() để chuyển thành object. |
id: | string | Thông báo cho phía client EventSource biết được sự kiện cuối cùng trong trường hợp mất kết nối. Khi EventSource thiết lập lại kết nối sẽ gửi lên server một HTTP header là Last-Event-ID để server biết mà trả lại dữ liệu phù hợp.Ví dụ: id: some_id\n Phía client EventSource cũng có thể lấy giá trị id này qua event.lastEventId . |
event: | string | Chỉ định tên sự kiện sẽ được EventSource kích hoạt bằng EventSource.addEventListener(eventName, callback) .Ví dụ: event: bigdata\n Ta đón sự kiện bigdata như sau:source.addEventListener("bigdata", function(e) { Chú ý dữ liệu của data: khi event: diễn ra sẽ không bắt được trong EventSource.onmessage . |
retry: | integer | Khi bị ngắt kết nối do timeout (không có dữ liệu mới sau một thời gian) hoặc máy chủ trả về HTTP code 200, EventSource sẽ đợi một khoảng thời gian retry (milliseconds) trước khi kết nối lại từ đầu.Nếu không chỉ định retry , mặc định trình duyệt sẽ kết nối lại sau 3 giây. |
Mã nguồn tạo kết nối và nhận dữ liệu Server-sent Events phía client (JavaScript)
Bộ khung khai báo cơ bản phía client browser bằng JavaScript như sau:
if ( !!window.EventSource ) { // Pass the URL of the event stream to subscribe, // it might be relative or absolute var source = new EventSource( "sse.php" ); source.onopen = function(e) { console.log("SSE connection was established."); }; source.onerror = function(e) { console.error("SSE error:", e); } // handle incoming data source.onmessage = function(e) { console.log("SSE data received:", e.id, e.data); }; // custom events source.addEventListener("bigdata", function(e) { // TODO some specific processing goes here console.log("SSE event received:", e.data); }, false); } else { console.error( "Your web browser does not support SSE" ); }
Lưu ý khi dùng Server-sent Events
CORS
EvenSource cũng như các HTTP request thông thường từ client đều nhạy cảm với CORS. Server phải cùng domain với script hoặc gửi header Access-Control-Allow-Origin
để cho phép domain chứa script.
Server phải giữ kết nối
Phía server nếu không giữ kết nối (ví dụ bằng event loop) mà chỉ đơn thuần trả ít dữ liệu rồi exit (Code 200) thì Server-sent Events sẽ tìm cách kết nối lại liên tục và hành xử chẳng khác gì polling – hoàn toàn không đúng với tinh thần thiết kế của SSE.
Đóng kết nối Server-sent Events từ phía client
Để đóng kết nối từ phía JavaScript client, chúng ta sử dụng hàm source.close()
.
Nếu bạn không thực hiện đóng kết nối EventSource, trình duyệt khi không thấy có dữ liệu mới sau một thời gian sẽ tự hiểu là bị connection timeout và kết nối lại với server từ đầu.
Yêu cầu đóng kết nối Server-sent Events từ phía server
Phía server có thể chủ động yêu cầu ngừng kết nối này bằng cách gửi một dấu hiệu, ví dụ dùng id:
hoặc tạo một sự kiện tùy chỉnh với event:
để client biết mà gọi source.close()
.
Ví dụ server gửi chuỗi:
id: close\n data: \n\n
Đoạn mã phía client:
source.onmessage = function(e) { if (e.lastEventId === 'close') source.close(); else console.log(e.data); };
Một cách ứng dụng SSE đó là thực thi xử lý một phức tạp trên server trong vẫn khi trả về thông tin real-time về client, tham khảo tại ĐÂY.