Chào các bạn, bài viết trước mình đã giới thiệu về cơ chế Polling. Qua bài viết này, mình sẽ tận dụng khả năng của Golang để triển khai cơ chế Polling (Short Polling và Long Polling).
Đây là bài blog thuộc series System Design của mình, nếu bạn quan tâm đến các vấn đề liên quan đến thiết kế hệ thống phần mềm, hãy theo dõi series này để cập nhật những kiến thức mới nhất nhé!
Short polling
Trong cách tiếp cận này, phía downstream sẽ thực hiện gửi request đến upstream sau mỗi khoảng thời gian nhất định. Đây là cách tiếp cận đơn giản nhất.
package main
import (
"context"
"fmt"
"time"
"github.com/brianvoe/gofakeit/v7"
)
func upstream() string {
return gofakeit.RandomString([]string{"", gofakeit.Name()})
}
func downstream(ctx context.Context) {
// Create a timer with 1 second interval
interval := 1 * time.Second
timer := time.NewTimer(interval)
defer timer.Stop()
// Start polling until the context is done
for {
select {
case <-ctx.Done():
return
case <-timer.C:
result := upstream()
if result != "" {
fmt.Println("Got result:", result)
} else {
fmt.Println("Got empty result")
}
timer.Reset(interval)
}
}
}
func main() {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
defer cancel()
downstream(ctx)
}
Nào, chúng ta hay điểm qua các phần đáng chú ý trong đoạn code trên nhé:
- upstream(): Hàm này sẽ trả về một giá trị ngẫu nhiên, giả lập việc lấy dữ liệu từ upstream. Nếu giá trị trả về là rỗng, ta sẽ in ra thông báo “Got empty result”, ngược lại ta sẽ in ra giá trị đó.
- downstream(): Hàm này sẽ thực hiện Polling với interval là 1 giây. Hãy chú ý tới vòng lặp
for
, ở đây mình sẽ dùngselect
để chờ đến khitimer.C
hoặcctx.Done()
được gửi tín hiệu. Nếutimer.C
được gửi tín hiệu, ta sẽ gọi hàmupstream()
và in ra kết quả, sau đó reset lại timer để chờ đến lần polling tiếp theo. Nếuctx.Done()
được gửi tín hiệu, ta sẽ kết thúc vòng lặp.
Rất đơn giản phải không nào? Bạn có thể thay đổi giá trị của interval để thay đổi tần suất polling. Trong thực tế, triển khai Upstream có thể là một API hoặc một hệ thống khác phức tạp hơn, nhưng tựu chung lại thì bản chất cơ chế Polling vẫn là như vậy.
Long polling
Với cơ chế Long Polling, phía Downstream sẽ gửi request đến Upstream và chờ đợi kết quả. Nếu không có kết quả nào được trả về, Downstream sẽ chờ đến khi có kết quả hoặc hết thời gian timeout.
package main
import (
"context"
"fmt"
"time"
"github.com/brianvoe/gofakeit/v7"
)
func upstream() string {
// Try to get a random name 5 times
// Non-empty represents a new data, otherwise old data
for i := 0; i < 5; i++ {
result := gofakeit.RandomString([]string{"", gofakeit.Name()})
if result != "" {
return result
}
// Simulate a delay (waiting for new data)
time.Sleep(time.Second)
}
return ""
}
func downstream(ctx context.Context) {
for {
select {
case <-ctx.Done():
return
// Polling upstream data with long polling technique
default:
result := upstream()
if result != "" {
fmt.Println("Got result:", result)
} else {
fmt.Println("Got empty result")
}
}
}
}
func main() {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
defer cancel()
downstream(ctx)
}
Trong đoạn code trên, mình đã thay đổi cách thức polling của upstream và downstream với một số điểm đáng chú ý sau:
- upstream(): Hàm này sẽ thử lấy dữ liệu từ upstream 5 lần, nếu không có dữ liệu nào được trả về, ta sẽ trả về giá trị rỗng. Điều này
giả lập việc downstream sẽ chờ đợi kết quả từ Upstream trong một khoảng thời gian nhất định. Với Go, ta có thể tận dụng Go’s runtime với
hàm
time.Sleep()
để giả lập việc chờ đợi, điều này sẽ không tốn tài nguyên hệ thống như việc sử dụng vòng lặp ở cơ chế short polling. - downstream(): Hàm này sẽ thực hiện Polling với cơ chế Long Polling. Chú ý tới vòng lặp
for
, ở đây mình sử dụngselect
để chờ đến khictx.Done()
được gửi tín hiệu. Nếu không có tín hiệu nào được gửi, ta sẽ gọi hàmupstream()
và in ra kết quả.
Với cách tiếp cận này, downstream sẽ chờ đợi kết quả từ Upstream trong một khoảng thời gian nhất định, nhưng khoảng thời gian này sẽ được cấu hình ở phía Upstream, không phải ở downstream. Điều này giúp giảm tải cho hệ thống downstream, cũng như upstream.
Kết luận
Như vậy, chúng ta đã học cách thực hiện triển khai cơ chế Polling với Golang. Đây chỉ là những cách tiếp cận cơ bản mà thôi, nhưng chúng nêu lên được bản chất của cơ chế Polling, bao gồm Short Polling và Long Polling. Ngoài ra, chúng ta cũng có thể mở rộng các cách triển khai khác dựa trên bản chất của cơ chế Polling, qua đó các bạn sẽ hiểu vì sao mà Polling lại được sử dụng rộng rãi trong thực tế.
Hy vọng bài viết này sẽ giúp ích cho các bạn trong việc hiểu rõ hơn về cơ chế Polling. Nếu có bất kỳ thắc mắc hoặc góp ý nào, hãy để lại comment bên dưới nhé. Chào tạm biệt và hẹn gặp lại ở bài viết tiếp theo 🖐
Tham khảo
- Mình có chia sẻ code trong bài viết ở đây nhé: Source code