Giới thiệu về Spring Boot: Server-Sent Events

Version mới nhất của Spring, Spring 5 hỗ trợ khá tốt để làm việc với bất đồng bộ và Reactive web app. Hôm nay mình sẽ giới thiệu cho mọi người implement một tool trong đống tính năng mới này là Server-Sent Event trong web app của mình. OK Let's begin 😃

Server-Sent Events (SSE)

Đầu tiên mình tìm hiểu xem SSE là gì. Theo wiki: SSE là một công nghệ nơi mà browser sẽ nhận update một cách tự động từ server thông qua HTTP. SSE EventSource API được tiêu chuẩn hoá như là một phần của HTML5 bởi chính W3C. Theo springio doc: Client đăng ký với các nguồn sự kiện trên một máy server. Nếu server có dữ liệu mới, nó sẽ gửi một phản ứng đến tất cả các client đã đăng ký. Server có thể đóng kết nối và dừng gửi sự kiện cho client, đồng thời client cũng có thể tự ngừng lắng nghe các sự kiện từ server. SSE là một giao thức một chiều tức là chỉ có thể gửi dữ liệu từ server đến client. SSE được gửi qua HTTP truyền thống, do đó chúng không yêu cầu bất kỳ một implementation đặc biệt nào trên server cả.

Stock Market Example

Để hiểu rõ hơn về SSE, ta làm một ví dụ nhỏ về thị trường chứng khoán như sau:

  • Khởi tạo stock market bằng list stock với giá của nó
  • Sau mỗi giây, update giá của stock và tạo stock transaction
  • Client sẽ nhận được thông tin stock sau mỗi giây từ server

Stock Class sẽ như thế này 😃

class Stock {
    String name;
    float price;
}

Còn Stock Transaction sẽ như sau

class StockTransaction {
    String user;
    Stock stock;
    Date when;
}

Reactive SSE

Chúng ta sẽ tạo ra một service return một Flux các StockTransactions

@Service
class StockTransactionService {
    Flux<StockTransaction> getStockTransactions() {
        Flux<Long> interval = Flux.interval(Duration.ofSeconds(1));
        interval.subscribe((i) -> stockList.forEach(stock -> stock.setPrice(changePrice(stock.getPrice()))));
        Flux<StockTransaction> stockTransactionFlux = Flux.fromStream(Stream.generate(() -> new StockTransaction(getRandomUser(), getRandomStock(), new Date())));
        return Flux.zip(interval, stockTransactionFlux).map(Tuple2::getT2);
    }
}

Chúng ta cần một Flux generate ra mỗi số long mỗi một giây. Sau đó chúng ta update giá cho từng stock (tất nhiên là random ^^). Chúng ta cần một Flux khác để tạo ra StockTransactions. Flux.zip chính là để combine 2 Flux lại với nhau. Kết quả cuối cùng là ta có Flux

REST API

Chúng ta sẽ tạo ra 1 endpoint GET để liên tục lấy data mới nhất về stock.

@GetMapping(produces = MediaType.APPLICATION_STREAM_JSON_VALUE)
    public Flux<StockTransaction> stockTransactionEvents(){
        return stockTransactionService.getStockTransactions();
    }

MediaType.APPLICATION_STREAM_JSON_VALUE là để chỉ ra rằng server sẽ send SSE.

Kết quả khi gọi API

{"user":"TamNT","stock":{"name":"BIDV","price":33.51},"when":1506153731190}
{"user":"TamCT","stock":{"name":"Vietcombank","price":17.49},"when":1506153732194}
{"user":"TamNT","stock":{"name":"PVCombank","price":73.67},"when":1506153733192}
{"user":"ThanhD","stock":{"name":"BIDV","price":24.63},"when":1506153734194}
{"user":"TamNT","stock":{"name":"BIDV","price":24.57},"when":1506153735193}
{"user":"HieuTM","stock":{"name":"PVCombank","price":59.85},"when":1506153736194}
{"user":"HieuPV","stock":{"name":"Vietcombank","price":17.26},"when":1506153737194}
{"user":"ThanhD","stock":{"name":"Vietcombank","price":17.21},"when":1506153738195}
{"user":"TamNT","stock":{"name":"BIDV","price":19.9},"when":1506153739195}
{"user":"TamCT","stock":{"name":"BIDV","price":21.93},"when":1506153740191}

Trên đây là một ví dụ rất đơn giản về cách sử dụng Spring Boot với Server-Sent Events. Source code mọi người có thể tham khảo ở đây.


All Rights Reserved