Bài hôm trước ta đã tìm hiểu về Pub/Sub và các key concepts của nó, cũng như vấn đề mà nó giải quyết. Bài hôm nay chúng ta sẽ đi tìm hiểu về PubSub Message, Publisher and Subscriber pattern.
Pub/Sub Message Lifecycle
Sau đây sẽ là luồng đi của 1 Pub/Sub message:
- Publisher tạo và gửi message đến Pub/Sub Topic
- Message được lưu trữ trong Message Storage
- Xảy ra đồng thời với bước 2, Pub/Sub gửi message đến tất cả các Subscription đã đăng ký với Pub/Sub Topic
- Các Subscription sẽ gửi message đến tất cả các Subscriber đã đăng ký với Subscription
- Sau khi Subscriber xử lý thành công, nó sẽ gửi 1 acknowledment đến Subscription, sau đó Pub/Sub sẽ xóa message này ra khỏi Message Storage
Outstanding Message là message đã được delivered đến subscriber nhưng chưa được acknowledged (chưa ACK). Có nghĩa là nó đã được gửi đến Subscriber và Subscriber đang xử lý nó thì được gọi là Outstanding Message. Trong Subscriber có 1 AckDeadline time (được config khi tạo Subscriber), nếu vẫn chưa vượt quá AckDeadline time này thì message vẫn được coi là Outstading. Khi vượt quá AckDeadline thì message này không còn là Outstanding nữa và sẵn sàng được Pub/Sub gửi vào Subscriber để xử lý lại.
Status of a message in Pub/Sub
Pub/Sub message có 3 trạng thái:
- Acknowledged messages (acked): là khi Subscriber đã xử lý xong message và gửi acknowledgement đến Pub/Sub. Ví dụ Pub/Sub topic này có 1 Subscription, và Subscription này có 2 Subscribers (message chỉ được gửi đến 1 trong 2 Subscriber này, đây là cơ chế LoadBalancing của Pub/Sub chúng ta sẽ bàn nó sau) , thì khi mà Subscriber xử lý xong và gửi acknowledment đến Pub/Sub thì message này được xem là acked, đồng thời Pub/Sub sẽ xóa nó ra khỏi Message Storage.
- Unacknowledged messages (unacked): là khi vượt quá ackDeadline, Pub/Sub vẫn chưa nhận được acknowledment từ Subscriber. Khi đó message sẽ được Pub/Sub gửi lại vào Subscriber. Đây là cơ chế retry của Pub/Sub nó sẽ được retry cho đến khi Subscriber acknowledment đến Pub/Sub mà không vượt quá ackDeadline, hoặc khi message hết thời gian lưu trú trong Message Storage. Pub/Sub cũng có sẵn cơ chế backoff để tránh việc retry liên tục message lỗi, khiến cho Subscriber bị quá tải. Trong trường hợp Subscriber acknowledment trễ sau ackDeadline hoặc acknowledment bị mất vì transient network issue sẽ gây ra vấn đề Duplicate Message. Duplicate là trade-off cho reliability.
- Negatively acknowledged messages (nacked): là khi Subscriber nacked message, ngay lập tức nó sẽ được Pub/Sub gửi lại vào Subscriber. Chúng ta có thể set modifyAckDeadline với giá trị = 0 để NACK ngay, hoặc > 0 để delay, thì Pub/Sub sẽ đợi đến hết thời gian này mới bắt đầu gửi lại message. Thông thường thì sẽ hiếm khi sử dụng nacked, bởi vì khi có lỗi xảy ra mà cứ retry ngay lập tức mà chưa biết nó là retryable hay non-retryable error thì sẽ khiến Subscriber quá tải và bị crash. Theo kinh nghiệm của mình thì sẽ setup thêm 1 DeadLetterQueue (DLQ), để khi retry message đâu đó khoản 5 lần mà vẫn không thể xử lý được thì sẽ gửi nó vào DLQ để lưu trữ, phục vụ cho việc investigate error cũng như reply lại message đến Subscriber sau khi Subscriber có khả năng xử lý được lỗi, đảm bảo lỗi được fix cũng như không bị mất dữ liệu.
Để tránh việc duplicate message:
- Chúng ta cần phải đảm bảo set AckDeadline một khoản hợp lý, không thấp hơn, hoặc quá gần với Subscriber Latency.
- Improve Subscriber service để giảm Latency.
Choose a Pub/Sub publish and subscribe pattern
Pub/Sub publish and subscribe pattern cũng có 3 loại:
- Fan in (many-to-one): nhiều Publishers và chỉ có 1 Subscriber
- Load balanced (many-to-many): Nhiều Publisher và nhiều Subscribers. Trong patern này Subscription có nhiều Subscribers, một message chỉ được process bởi 1 Subscriber, nên nó mới được gọi là LoadBalanced, Subscription lúc này sẽ đóng vai trò như 1 Load Balancer phân phối message đến các Subscriber. Trong cùng 1 Subscription, messages được distributed giữa các subscribers theo round-robin hoặc based on processing capacity. Ví dụ như Subscriber là 1 K8S Service có nhiều pods, lúc này Subscriptions sẽ phân phối message đến tất cả các pods.
- Fan out (one-to-many): Một Publisher nhưng có nhiều Subscriptions, mỗi Subscriptions có thể có nhiều Subscribers hoặc 1 Subscriber.
Bạn còn nhớ ứng dụng đặt hàng chúng ta đã đề cập ở bài trước:
Kiến trúc mà chúng ta sử dụng cho ứng dụng này là Fan out (one-to-many), cụ thể 1 event đi vào sẽ được xử lý và gửi đến service tiếp theo và Notification service.
Hãy xem qua Publisher and Subscriber pattern của Order service, ta có 1 Order topic dùng để nhận message từ Order service (Publisher). Trong Order topic này, ta tạo 2 Subscriptions là packaging và notification, 2 subscriptions này sẽ gửi message đến Packaging service và Notification service. Tương tự cho các service khác.
Global Expansion
Cloud Pub/Sub is Global service, vì thế chúng ta chỉ cần tập trung vào development services còn scale global thì để cho Pub/Sub xử lý. Có nghĩa là bạn không cần phải quan tâm đến Publishers và Subscribers service có cùng region hay không. Ví dụ như bạn có 3 Publishers khác region, nhưng vẫn gửi được message đến cùng 1 Topic. Subscription publish message đến 3 Subscribers khác region.
Tổng kết
Chúng ta đã vừa tìm hiểu xong về Lifecycle của Pub/Sub message, các status of message và Publisher and Subscriber pattern. Bài tiếp theo chúng ta sẽ tìm hiểu về Pull and Push Subscriptions.