SSE qua proxy và cách kiểm độ trễ từng sự kiện
SSE qua proxy cần giữ luồng sự kiện được chuyển đều thay vì chờ gom thành một response lớn. Status 200 chưa chứng minh người nhận thấy dữ liệu đúng lúc. Bài này thiết kế server phát timestamp, đo độ trễ nhận và phân biệt buffering với timeout của từng chặng. Cùng ProxyVN kiểm tra đầu vào, đối chiếu kết quả và nhận biết giới hạn trước khi áp dụng.
SSE qua proxy là một response kéo dài
Server-Sent Events dùng luồng HTTP để máy chủ gửi sự kiện đến client. Một kết nối có thể mở lâu và phát nhiều bản tin theo thời gian. Khác với tải HTML thông thường, thời điểm client nhận từng bản tin là một phần của chất lượng kết quả.
Nếu proxy đợi body hoàn tất mới trả, nội dung vẫn có thể đủ nhưng ứng dụng mất tính cập nhật. Bảng giám sát sẽ đứng yên rồi nhảy nhiều dòng một lúc. Chỉ kiểm số dòng cuối cùng sẽ bỏ sót lỗi này vì dữ liệu không nhất thiết bị mất.
Tài liệu Using server-sent events của MDN mô tả định dạng sự kiện và cách dùng EventSource. Bài này xây phép đo riêng trên server lab. Không dùng kết quả của HTTP stream đơn giản để xác nhận mọi hành vi của trình duyệt hoặc mọi reverse proxy.
Một dòng data chưa phải một sự kiện hoàn chỉnh
Một sự kiện cần được kết thúc theo định dạng luồng để client biết khi nào có thể phát callback. Nếu server viết data nhưng chưa ghi dòng trống phân cách, phía nhận có thể chờ dù byte đã đi qua mạng. Trường hợp này khác với proxy giữ buffer.
Luồng nên được phát dưới kiểu text/event-stream và byte văn bản phù hợp với giao thức. Sự kiện có thể gồm nhiều dòng data, có id hoặc tên event theo nhu cầu ứng dụng. Lab đầu nên giữ một dòng dữ liệu ngắn và một dấu kết thúc rõ ràng để giảm biến số.
Một parser chỉ in mọi dòng nhận được chưa tương đương EventSource. Nó có thể in một nửa bản tin trước khi sự kiện hợp lệ hoàn tất. Khi đo độ trễ, ghi thời điểm nhận đủ sự kiện, không chỉ thời điểm nhận byte đầu hoặc dòng data đầu.
Tách SSE khỏi WebSocket
SSE truyền sự kiện từ server trong một response HTTP. WebSocket có quy trình bắt tay và cơ chế frame riêng, phục vụ giao tiếp hai chiều theo mô hình khác. Cấu hình Upgrade dành cho WebSocket không phải câu trả lời tự động cho một luồng SSE bị gom dữ liệu.
Nếu ứng dụng chỉ cần cập nhật tiến trình công việc từ server, SSE có thể phù hợp mà không thêm giao thức frame. Nếu client cần gửi dữ liệu liên tục trên cùng kết nối, cần đánh giá mô hình ứng dụng riêng. Không chọn công nghệ chỉ theo việc cả hai đều được gọi là realtime.
SSE qua proxy trong bộ đo timestamp
Lab nên có ba điểm rõ ràng: origin tự tạo, chặng trung gian kiểm soát được và client ghi thời điểm nhận. Dùng cùng một máy cho phép lấy đồng hồ nhất quán ở giai đoạn kiểm cơ chế. Sau đó mới chuyển sang nhiều máy nếu muốn đánh giá độ trễ mạng thực.

Origin phát sự kiện theo thời gian
Chọn đồng hồ theo mục đích
Timestamp giờ thực giúp đối chiếu với log vận hành nhưng có thể chịu điều chỉnh đồng hồ. Đồng hồ đơn điệu phù hợp để tính khoảng thời gian trong cùng tiến trình hoặc cùng hệ thống theo điều kiện sử dụng. Không trừ hai monotonic timestamp từ hai máy độc lập rồi gọi đó là độ trễ mạng.
Trong lab cùng máy, origin có thể ghi thời điểm phát và client ghi thời điểm nhận đủ bản tin. Với triển khai nhiều máy, cần đồng bộ giờ hoặc chọn cách đo khác có sai số được nêu. Một con số có nhiều chữ số thập phân không đồng nghĩa phép đo chính xác đến mức đó.
Mỗi event nên có số thứ tự tăng dần. Timestamp cho biết thời gian, còn số thứ tự giúp phát hiện mất hoặc lặp bản tin. Dùng cả hai dễ chẩn đoán hơn một chuỗi thông báo không có khóa định danh.
Dựng origin tối thiểu trước khi thêm proxy
Ví dụ sau phát ba bản tin rồi đóng kết nối theo chủ đích. Đây là server lab dùng Python chuẩn, không phải server production. Chạy trong terminal riêng và dùng một client đọc stream để kiểm đường trực tiếp trước.
from http.server import BaseHTTPRequestHandler, HTTPServer
import json, time
class Stream(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header('Content-Type', 'text/event-stream')
self.send_header('Cache-Control', 'no-cache')
self.end_headers()
for number in range(3):
data = json.dumps({'seq': number, 'sent': time.time()})
self.wfile.write(f'id: {number}\ndata: {data}\n\n'.encode())
self.wfile.flush()
time.sleep(0.2)
HTTPServer(('127.0.0.1', 8783), Stream).serve_forever()
Flush ở ứng dụng chỉ đẩy dữ liệu khỏi buffer ở ranh giới mà lệnh đó kiểm soát. Nó không bắt mọi tầng trung gian phát ngay. Vì vậy phải đo phía client; việc server đã gọi flush không đủ để kết luận đường truyền không có buffering.
Server lab xử lý một yêu cầu tại một thời điểm theo lớp sử dụng. Không lấy nó để tạo nhiều kết nối benchmark. Giới hạn này giúp ví dụ nhỏ và dễ đọc, đồng thời nhắc rằng kiến trúc phục vụ nhiều subscriber là bài toán triển khai khác.
SSE qua proxy và cách nhận diện buffering
Buffering thường thể hiện bằng các sự kiện đến theo cụm dù origin phát đều. Cần so khoảng phát và khoảng nhận của cùng số thứ tự. Một biểu đồ tổng thời gian tải không cho biết dữ liệu bị giữ ở đâu trong khoảng giữa.
Đọc theo dòng và đọc theo chunk
Client có thể tự tạo hiện tượng gom dữ liệu nếu API chờ đủ một chunk lớn. Vì vậy thử một cách đọc phù hợp stream và ghi cả thời điểm byte đến lẫn thời điểm event hoàn tất khi cần. Không chỉ thay proxy rồi giữ nguyên một client đang đọc toàn bộ body.
Một chunk TCP không nhất thiết tương ứng một sự kiện. Có thể nhận hai sự kiện trong một lần đọc hoặc một sự kiện qua nhiều lần đọc. Parser phải giữ phần còn dở và chỉ phát event sau dấu phân cách hợp lệ.
Trong bộ thử đầu, dữ liệu ngắn giúp việc ghi log dễ quan sát. Tiếp theo thêm một event dài hơn buffer dự kiến để kiểm parser không gắn số lần read với số bản tin. Nếu số event thay đổi theo kích thước chunk, lỗi có thể nằm ở client.
So đường trực tiếp với hai hành vi trung gian
Một relay chuyển ngay từng phần dữ liệu là đối chứng cho tuyến giữ stream. Một relay cố ý đọc hết body trước khi trả là đối chứng gom dữ liệu. Hai relay lab cho phép chứng minh bộ đo có phân biệt được lỗi, trước khi dùng nó cho gateway thật.
Với relay gom, client thường nhận các event gần nhau sau khi origin đóng luồng. Số lượng có thể vẫn đủ ba. Với relay chuyển dần, khoảng nhận cần phản ánh khoảng phát trong sai số của môi trường, nhưng không nên ép một ngưỡng cứng lấy từ máy khác.
Khi chuyển sang reverse proxy thật, đọc cấu hình buffering của đúng phần mềm và đúng vị trí route. Tài liệu proxy_buffering của NGINX là một ví dụ theo sản phẩm. Không giả định mọi gateway hiểu cùng tên directive hoặc cùng response header điều khiển.
SSE qua proxy với timeout và heartbeat
Luồng mở lâu vẫn có thể bị đóng vì một chặng xem nó là không hoạt động. Khoảng im lặng giữa các sự kiện khác với tổng thời gian kết nối. Cần biết timeout nào đang áp dụng trước khi tăng mọi giá trị lên rất lớn.

Buffer và timeout trên từng chặng
Hiện tượng |
Ca kiểm riêng |
Bằng chứng nên lưu |
|---|---|---|
Event đến theo cụm |
Relay đọc hết body |
sent và received theo seq |
Luồng đóng khi yên lặng |
Chèn khoảng nghỉ có chủ đích |
Thời điểm hoạt động cuối |
Client bỏ giữa chừng |
Hạ timeout phía client |
Ngoại lệ và số event đã nhận |
Kết nối lại bị lặp |
Gửi lại id cũ |
Sổ id và trạng thái xử lý |
Heartbeat duy trì hoạt động quan sát được
Một dòng comment có thể dùng làm heartbeat theo thiết kế luồng. Nó khác event nghiệp vụ và không nên làm thay đổi trạng thái dữ liệu cuối. Khi đo, ghi riêng heartbeat với thông báo để tránh tính chúng thành sản phẩm mới hoặc tiến độ mới.
Chu kỳ heartbeat phải được chọn theo các giới hạn đã biết của hệ thống. Không có một khoảng giây đúng cho mọi proxy, cân bằng tải và client. Ghi giá trị đã chọn cùng timeout nhỏ nhất có liên quan để người bảo trì hiểu lý do.
Nếu heartbeat bị nén hoặc gom cùng body, nó có thể không xuất hiện kịp ở chặng giữ kết nối. Test cần xác nhận byte heartbeat đi qua phía nhận theo lịch, không chỉ xác nhận origin đã tạo chuỗi comment. Đây là chỗ buffering và timeout có thể tương tác.
Timeout client không đồng nghĩa server trả lỗi HTTP
Client có thể tự dừng chờ mà không nhận response lỗi mới. Khi báo sự cố, phân biệt ngoại lệ timeout với status nhận từ server hoặc gateway. Việc gán mọi ngắt luồng thành 504 sẽ làm nhầm bên phát sinh quyết định.
Một luồng đã trả header 200 rồi đứt giữa chừng cũng không nhất thiết có status thứ hai. Kết quả nghiệp vụ có thể chưa hoàn tất dù log chỉ ghi 200. Lưu trạng thái hoàn tất ứng dụng và số event nhận được ngoài status HTTP ban đầu.
Trong test, tạo một khoảng nghỉ dài hơn giới hạn client đã đặt và so với ca có heartbeat. Giữ thời gian lab ngắn để dễ tái hiện, nhưng ghi rõ đó là ngưỡng tự chọn. Không lấy ngưỡng thử vài phần giây làm khuyến nghị vận hành.
SSE qua proxy khi kết nối lại và chống lặp
Kết nối dài có thể bị ngắt vì nhiều lý do bình thường. Ứng dụng cần thiết kế cách tiếp tục theo id, trong phạm vi server hỗ trợ. Kết nối lại mà chỉ nhận dữ liệu mới có thể bỏ sót phần đã phát lúc client mất mạng.
Phân biệt nhận được với xử lý xong
Một event được parse không có nghĩa công việc lưu dữ liệu đã commit. Nếu client ghi id cuối ngay khi nhận nhưng crash trước khi lưu payload, lần chạy sau có thể bỏ qua dữ liệu chưa xử lý. Checkpoint nên gắn với ranh giới hoàn tất nghiệp vụ.
Ngược lại, nếu payload đã được lưu nhưng checkpoint chưa cập nhật, event có thể xuất hiện lại. Thiết kế thao tác ghi idempotent hoặc ràng buộc khóa theo event giúp xử lý lặp có kiểm soát. Không yêu cầu đường mạng tự bảo đảm một lần xử lý duy nhất.
Id cần có ý nghĩa ổn định trong phạm vi luồng của ứng dụng. Một bộ đếm khởi động lại từ không sau mỗi lần deploy có thể trùng với dữ liệu trước. Nếu dùng epoch hoặc stream version, đưa nó vào khóa để phân biệt hai thế hệ sự kiện.
Kiểm khả năng phát lại của origin
Khả năng tiếp tục không xuất hiện chỉ vì client gửi Last-Event-ID. Server phải hiểu id và có dữ liệu phù hợp để phát lại. Bộ thử nên ngắt sau một event, kết nối lại theo id đã xử lý rồi so đầu ra cuối với danh sách mong đợi.
Nếu server chỉ giữ lịch sử trong một khoảng thời gian, ứng dụng cần một nhánh đồng bộ lại khi id quá cũ. Đừng dùng vòng kết nối lại vô hạn để che việc không còn lịch sử. Trạng thái cần tải snapshot mới nên được hiển thị và ghi log riêng.
Khi server thay thứ tự hoặc gộp event, cần một hợp đồng mới về checkpoint. Giữ dữ liệu mẫu trước và sau thay đổi để kiểm tính tương thích. Một test chỉ đếm tổng số callback không đủ xác nhận dữ liệu không bị bỏ qua.
SSE qua proxy trong quan sát và triển khai
Quan sát luồng cần ưu tiên chỉ số theo thời gian. Thời gian đến event đầu, độ trễ mỗi event, khoảng im lặng lớn nhất và số lần kết nối lại giúp hiểu trải nghiệm thực. Tổng byte hoặc số kết nối đang mở chỉ mô tả một phần hệ thống.

Log độ trễ theo số thứ tự
Thiết kế log không làm chậm stream
Mỗi event nên có id và mốc thời gian cần thiết, nhưng không cần log toàn bộ payload nhạy cảm. Ghi một hash hoặc kích thước có thể đủ cho ca đối soát nội dung. Chọn dữ liệu log dựa trên câu hỏi cần trả lời thay vì ghi mọi thông tin rồi lọc sau.
Lệnh ghi log đồng bộ chậm ở origin có thể làm lịch phát lệch. Vì vậy log sent nên nằm sát điểm phát thực tế và cách ghi cần được đánh giá khi đo hiệu suất. Không quy mọi độ trễ quan sát được cho proxy nếu chính vòng phát đang chờ ổ đĩa.
Theo dõi kết nối được đóng bởi client cũng giúp phân biệt sự cố với thao tác rời trang. Một lỗi BrokenPipe khi trình duyệt đóng tab có thể là tình huống dự kiến. Hệ thống cần dọn tài nguyên và dừng worker của subscriber đó theo thiết kế.
Kiểm nén và cache trên route luồng
Nén có thể thêm một lớp buffer và thay đổi thời điểm byte được phát. Không phải mọi codec hoặc cấu hình đều gây cùng hành vi. Dùng cùng chuỗi event để kiểm ca có nén và không nén, ghi codec thực nhận thay vì chỉ ghi cấu hình dự kiến.
Chính sách cache cũng phải phù hợp với luồng. Một cache không hiểu ngữ cảnh có thể phát bản cũ hoặc giữ response chưa hoàn tất theo cách ngoài ý muốn. Cấu hình route cần được đánh giá cùng header ứng dụng, không chỉ thêm một nhãn no-cache rồi bỏ qua kiểm thực tế.
Nếu cân nhắc mua proxy cho client nhận sự kiện, xác nhận hỗ trợ kết nối kéo dài và giới hạn theo chính sách dịch vụ. Không suy khả năng SSE từ việc một request HTML ngắn trả thành công. Bài lab không kiểm gateway thương mại của ProxyVN.
SSE qua proxy và bộ ca nghiệm thu trước bàn giao
Nghiệm thu cần chứng minh cả đường stream tốt và các tình huống đứt luồng. Kết quả phải gắn với client, route và phiên bản proxy đã thử. Một video hiển thị cập nhật nhanh không thay cho log có số thứ tự để đối soát.

Nghiệm thu ngắt và kết nối lại
Ma trận theo vòng đời sự kiện
Ca đầu phát ba event đều nhau và giữ đủ ba id theo thứ tự. Ca thứ hai cho relay gom toàn bộ để bộ đo phát hiện độ trễ tăng theo cụm. Ca thứ ba chia một event qua nhiều chunk để kiểm parser không phát callback sớm.
Ca tiếp theo ngắt sau một id đã commit rồi kết nối lại theo hợp đồng server. So kết quả cuối bằng tập id và payload dự kiến, đồng thời kiểm không bỏ sót hoặc ghi lặp nghiệp vụ. Nếu server không hỗ trợ phát lại, ghi rõ giới hạn thay vì cho ca này pass.
Thêm ca khoảng im lặng dài, có và không có heartbeat, để xác định chặng nào đóng kết nối. Lưu ngoại lệ ở client và log origin cùng thời điểm hoạt động cuối. Chỉ đổi một yếu tố mỗi lượt giúp định vị timeout dễ hơn.
Biên bản phạm vi kiểm của bài
Server và relay tự tạo chỉ kiểm cơ chế phát, chuyển dần và gom body trong môi trường loopback. Chúng không chứng nhận hành vi EventSource của mọi trình duyệt, HTTP/2, hệ thống cân bằng tải hay mạng di động. Những môi trường đó cần cùng ma trận nhưng công cụ quan sát phù hợp hơn.
Khi chuyển vào production, chạy thử trên route giới hạn và có cách tắt riêng. Theo dõi số kết nối mở cùng tài nguyên worker để tránh một stream bị bỏ quên chiếm tài nguyên lâu. Dọn subscriber sau khi đóng là một tiêu chí vận hành ngoài việc dữ liệu đến đúng lúc.
Các tài liệu EventSource và NGINX proxy_read_timeout giúp đối chiếu từng thành phần. Giá trị timeout và cách reconnect vẫn phải chọn theo ứng dụng đã thử, không lấy một ví dụ làm mặc định chung.
Tránh phép đo làm mất chính hiện tượng cần tìm
Một công cụ chỉ hiển thị body sau khi response kết thúc không phù hợp để kết luận stream bị proxy giữ. Trước khi dùng, thử trực tiếp với origin phát cách quãng. Nếu công cụ đã gom dữ liệu ở đường trực tiếp, thay cách quan sát rồi mới đánh giá relay.
Đầu ra terminal cũng có thể bị buffer bởi quá trình bao ngoài. Ghi timestamp ngay lúc parser hoàn tất sự kiện vào tệp hoặc cấu trúc dữ liệu, sau đó in báo cáo cuối. Không lấy thời điểm màn hình hiển thị một khối log làm thời điểm socket nhận từng event.
Khi test chỉ kéo dài vài phần giây, hệ điều hành và lịch chạy tiến trình có thể tạo dao động đáng kể so với khoảng đo. Dùng bộ đối chứng tạo chênh lệch đủ rõ và lặp có giới hạn khi cần. Báo sai số quan sát thay vì chọn ngưỡng quá sát một lượt chạy.
Giữ dữ liệu và quyền truy cập khi luồng mở lâu
Luồng có xác thực cần được đánh giá theo thời hạn phiên và chính sách ứng dụng. Không mặc định một kết nối đã mở sẽ tiếp tục có quyền nhận mọi sự kiện sau khi quyền người dùng đổi. Cách thu hồi và tái xác thực cần được thiết kế riêng, ngoài chức năng buffer của proxy.
Đừng đưa token vào id sự kiện hoặc URL được ghi log chỉ để đơn giản ví dụ. Id phục vụ thứ tự và phát lại; thông tin xác thực thuộc kênh được bảo vệ theo thiết kế dịch vụ. Với lab tự tạo, dùng payload không có bí mật để người nhận có thể chạy lại an toàn.
Nếu route chỉ phục vụ một nhóm người dùng, kiểm cách stream được định tuyến giữa các subscriber. Nhận đúng thời điểm nhưng nhận nhầm sự kiện của phiên khác vẫn là lỗi nghiêm trọng về dữ liệu. Ca này cần nhãn người nhận và origin có kiểm soát, không được suy ra từ test ba event công khai.
Phân biệt thứ tự phát với thứ tự hoàn tất tác vụ
Số thứ tự sự kiện chỉ phản ánh thứ tự mà server chọn phát. Nếu nhiều tác vụ chạy song song, kết quả nghiệp vụ có thể hoàn tất theo thứ tự khác. Đừng dùng một bộ đếm luồng để suy ra thời điểm bắt đầu hoặc mức ưu tiên của công việc nếu payload không mô tả điều đó.
Một thông báo tiến độ có thể là snapshot trạng thái mới nhất, trong khi một thông báo giao dịch có thể là thay đổi không được phép bỏ qua. Hai loại này cần chính sách phát lại khác nhau. Với snapshot, có thể đồng bộ bằng trạng thái hiện tại; với thay đổi, cần sổ sự kiện phù hợp với hợp đồng ứng dụng.
Khi client bị chậm, quyết định giữ mọi event hay chỉ giữ bản mới nhất cần được đưa ra trước. Một hàng đợi không giới hạn có thể tăng bộ nhớ; một hàng đợi bỏ bản tin có thể làm thiếu dữ liệu. Proxy chuyển stream đúng không quyết định thay ứng dụng về ý nghĩa của việc bỏ event.
Ghi nhận ca đóng luồng bình thường
Origin lab đóng sau ba bản tin theo kế hoạch. Client cần phân biệt trường hợp này với việc đứt sau bản tin thứ nhất. Có thể dùng dấu hoàn tất trong payload hoặc một hợp đồng số lượng cho bộ thử, nhưng không gán số lượng cố định cho luồng production vô hạn.
Nếu ứng dụng có event kết thúc công việc, checkpoint hoàn tất nên dựa trên event đó cùng kết quả lưu dữ liệu. Đóng socket chưa đủ chứng minh công việc đã thành công. Ngược lại, một kết nối được giữ mở sau khi công việc xong cần được dọn theo chính sách tài nguyên.
Nhờ có trạng thái hoàn tất riêng, báo cáo sẽ phân loại được tải xong, ngắt giữa chừng và vẫn đang chờ. Ba trạng thái này có cách xử lý khác nhau. Không gom chúng thành một cột HTTP 200 rồi đánh giá cả tuyến đã ổn định.
Kiểm lại sau thay đổi route
Một thay đổi nhỏ ở đường dẫn có thể đưa request sang vị trí cấu hình khác. Vì thế giữ URL đầy đủ trong biên bản, bao gồm tiền tố route và chuỗi chuyển hướng nếu có. Không kết luận cấu hình stream đã được áp dụng chỉ vì nhìn thấy directive trong một tệp cấu hình.
Kiểm trạng thái cấu hình đang có hiệu lực bằng công cụ chính thức của hệ thống và log route đã nhận yêu cầu. Nếu có nhiều instance, thêm mã instance vào bằng chứng lab. Một node đúng và một node còn cấu hình cũ có thể tạo hiện tượng lúc nhanh lúc chậm dù đầu vào giống nhau.
Sau deploy, chạy lại đường trực tiếp và đường qua trung gian với cùng mã phiên bản event. Kiểm số lượng, thứ tự, độ trễ và cách kết thúc luồng theo tiêu chí đã chọn. Giữ kết quả trước thay đổi để người vận hành có thể so, thay vì chỉ giữ lượt thành công cuối cùng.
Khi không đủ quyền quan sát một chặng, ghi ranh giới đó trong kết luận. Có thể xác nhận client nhận đủ và kịp theo bộ thử, nhưng chưa xác định từng chặng bên trong. Cách diễn đạt này giữ báo cáo hữu ích mà không gán bằng chứng vượt quá khả năng đo.
Với stream, dữ liệu đến đủ và dữ liệu đến đúng lúc là hai tiêu chí phải đo riêng.
Anh em hãy lưu dữ liệu mẫu cùng cấu hình và kết quả đối chiếu để lần cập nhật sau có cơ sở kiểm lại. Tham khảo thêm tại Proxy.vn - Nhà cung cấp dịch vụ proxy chất lượng hàng đầu Việt Nam. Bắt đầu từ phạm vi nhỏ và tiêu chí rõ ràng khi triển khai SSE qua proxy.