So sánh: kiến trúc stateful vs stateless

So sánh: kiến trúc stateful vs stateless
Photo by Aaron Burden from Unsplash

Kiến trúc stateful hay stateless có thể có nhiều cách diễn giải khác nhau, tùy thuộc vào bối cảnh cụ thể. Trong bài viết này, tôi chỉ muốn nói kiến trúc cho các ứng dụng web nói riêng và các ứng dụng chạy trên cloud nói chung. Kiến trúc stateful hay stateless có thể ảnh hưởng trực tiếp đến quá trình phát triển phần mềm. Hiện nay, kiến trúc stateless đã trở thành lựa chọn mặc định.

Stateful, stateless là gì?

Stateful nghĩa là với mỗi thao tác của người dùng, ứng dụng phải lưu trữ “state” (trạng thái) của ứng dụng và sử dụng thông tin này để xử lý các thao tác tiếp theo. Còn stateless thì ngược lại, ứng dụng không lưu trữ “state”, các thao tác của người dùng được xử lý hoàn toàn độc lập.

Dù stateful hay stateless, thì ứng dụng cũng phải cung cấp một trải nghiệm liền mạch, liên tục cho người dùng. Ví dụ, người dùng chỉ cần đăng nhập 1 lần và tiếp tục sử dụng tài khoản đã đăng nhập để thao tác, chứ không phải mỗi thao tác lại phải đăng nhập để xác thực.

“State” là một khái niệm trừu tượng và trong các bối cảnh khác nhau sẽ có ý nghĩa khác nhau. Với các ứng dụng web, state thường là các thông tin liên quan đến session (phiên làm việc của người dùng), trong đó quan trọng nhất là thông tin đăng nhập. Còn thao tác của người dùng tương ứng với mỗi truy vấn từ client lên server.

Bối cảnh lịch sử

Trong lịch sử, có sự chênh lệch rất lớn giữa cấu hình phần cứng cũng như phần mềm giữa máy tính cá nhân và máy chủ. Máy tính cá nhân thường có cấu hình thấp và không thể thực hiện nhiều tính toán phức tạp. Do đó, mạng máy tính thường triển khai kiến trúc client-server. Trong đó, hầu hết những công việc tính toán, xử lý sẽ được đẩy lên server có cấu hình mạnh hơn.

Giao thức HTTP lại được thiết kế là một giao thức “stateless”. Stateless trong trường hợp này có nghĩa là các truy vấn HTTP hoàn toàn độc lập với nhau. Truy vấn sau không có thông tin gì và cũng không sử dụng thông tin gì của truy vấn trước.

Mặc dù vậy, HTTP được bổ sung những tính năng như cookie để có thể giúp xác định các truy vấn đến từ cùng một client. Thực sự tôi không thể tưởng tượng được nếu không có cookie thì các ứng dụng web sẽ thế nào.

Điều này khiến cho việc xây dựng các ứng dụng web rất khó khăn khi phải thực hiện các giao dịch kéo dài, những giao dịch cần thao tác qua nhiều trang màn hình. Ví dụ, với các ứng dụng mua sắm online, thao tác thường phải trải qua nhiều bước như sau:

  1. đăng nhập
  2. thêm sản phẩm vào giỏ hàng
  3. điền thông tin giao hàng
  4. tiến hành thanh toán

Để hoàn tất giao dịch, mỗi bước ở trên đều phải được lưu lại “trạng thái”, dựa vào thông tin đó để tiến hành bước tiếp theo. Các bước 2, 3, 4 có thể dùng cơ sở dữ liệu để lưu và trích xuất thông tin mỗi khi có truy vấn từ client. Nhưng riêng trạng thái “đã đăng nhập” của bước 1 cần phải được duy trì trong suốt phiên giao dịch.

Vì HTTP là một giao thức stateless, việc lưu trữ trạng thái đăng nhập này cũng không đơn giản. Có 2 cách để giải quyết vấn đề này:

  • Stateful: lưu thông tin cần thiết trên server, đó sẽ là những thông tin về trạng thái hiện tại như trạng thái đã đăng nhập, tài khoản đăng nhập, v.v… Với các truy vấn tiếp theo từ cùng một client, những thông tin này sẽ được trích xuất để tiến hành các bước tiếp theo.
  • Stateless: lưu thông tin cần thiết ở phía client. Với mỗi truy vấn sẽ gửi kèm thông tin đó để “nhắc” server về trạng thái hiện tại của giao dịch. Thông thường, những thông tin này được lưu ở cookie hoặc localStorage.

Tuy nhiên, cả 2 cách tiếp cận trên vẫn còn tồn tại nhiều vấn đề cần giải quyết.

Với phương pháp stateful, phải tìm cách đảm bảo rằng server xử lý truy vấn của client phải được duy trì cho toàn bộ truy vấn cho đến khi hoàn tất. Nghĩa là với những ứng dụng có nhiều server để giảm tải, phải tìm cách làm sao mọi truy vấn từ cùng người dùng phải đến được cùng một server.

Còn với phương pháp stateless, phải có cách mã hóa và xác thực dữ liệu để đảm bảo những thông tin nhận được từ client là đầy đủ và chính xác (client không thể chỉnh sửa hay làm giả trạng thái đăng nhập). Đồng thời phải bảo vệ những dữ liệu đó trong quá trình gửi các gói tin qua lại.

Trước kia, hầu hết các ứng dụng trên Internet đều sử dụng phương pháp stateful. Có nhiều lý do cho việc này:

  • Thời đó, lượng truy cập các ứng dụng không quá lớn, do đó không có yêu cầu về việc scale. Vì vậy, một ứng dụng web có thể hoạt động chỉ với 1 server.
  • Việc lưu trữ và xử lý dữ liệu được thực hiện ngay trên server, không phụ thuộc vào dịch vụ bên ngoài, hiệu suất được đảm bảo.
  • Client thường có cấu hình thấp không thể đảm bảo việc lưu trữ và tính toán dữ liệu.
  • Các ứng dụng còn đơn giản, thông tin state còn ít nên việc duy trì stateful không quá khó.

Tuy nhiên, thế giới đã thay đổi, công nghệ ngày càng phát triển. Các ứng dụng stateful không cần trực tiếp lưu trữ thông tin session. Thay vào đó, những thông tin này sẽ được lưu trữ ở tầng cache. Dần dần, các ứng dụng chuyển từ stateful sang stateless.

Ví dụ với cách tiếp cận stateful ở trên, có thể chuyển sang stateless đơn giản bằng cách lưu session trên một server chuyên dụng. Khi đó, ứng dụng không phụ thuộc vào state và có thể xử lý các truy vấn độc lập (miễn là gửi lên session ID để trích xuất được dữ liệu). Ranh giới giữa stateful và stateless trong trường hợp này tương đối mong manh, nhưng ứng dụng như vậy có thể coi là stateless.

Hiện nay, client ngày càng có cấu hình mạnh hơn, có thể thực hiện nhiều tính toán và xử lý phức tạp hơn. Các ứng dụng web cũng ngày càng nhiều tính năng hơn, cho phép tương tác mạnh hơn. Đồng thời, kiến trúc hạ tầng của các ứng dụng cũng ngày càng phức tạp hơn. Đó là lúc stateless thể hiện ưu thế tuyệt đối của mình trước stateful.

So sánh: stateful vs stateless

SOAP, REST & GraphQL

Việc chuyển đổi từ stateful sang stateless liên quan đến việc triển khai ứng dụng web theo những mô hình khác nhau.

Đầu những năm 2000, SOAP (Simple Object Access Protocol) là giao thức thống trị thị trường. Mặc dù các ứng dụng dựa trên SOAP có thể là stateless, nhưng hầu hết các ứng dụng đều là stateful và việc lưu trữ dữ liệu session được thực hiện ngay trên server.

REST (REpresentational State Transfer) có thể coi là một design pattern hơn là giao thức. Một ứng dụng RESTful có thể được triển khai theo nhiều hướng khác nhau. Nhưng đặc điểm chung là các ứng dụng RESTful đều là stateless. Dần dần, REST trở nên phổ biến hơn và thay thế SOAP.

GraphQL là một phương thức mới nổi hiện nay khi mà frontend ngày càng có vai trò quan trọng hơn. Tương tự như RESTful, các ứng dụng dựa trên GraphQL đều là stateless. GraphQL cho phép client được tùy chỉnh truy vấn để lấy dữ liệu thích hợp từ server.

Ưu điểm của stateful

Stateful có những ưu điểm của riêng nó, và sẽ phát huy hiệu quả trong những tình huống cụ thể. Một số điểm mạnh của nó:

  • Hiệu suất: ứng dụng stateful lưu session trên server, cho phép xử lý nhanh các giao dịch. Vì không phụ thuộc vào yếu tố bên ngoài, các ứng dụng như vậy cung cấp trải nghiệm liên tục với hiệu suất rất cao.
  • Giảm tải database: ứng dụng stateful được cho là sẽ cần ít truy vấn đến database hơn. Bởi vì những thông tin như session, thông tin về truy vấn trước đó được lưu trữ sẵn trên server, hầu hết các thông tin cần thiết đều đã có sẵn. Điều đó cũng giúp ứng dụng có khả năng tính toán nhanh với hiệu suất cao và ít áp lực lên database.

Ưu điểm của stateless

Một ứng dụng stateless có nhiều ưu điểm nổi bật hơn hẳn so với stateful.

Trước hết, server của ứng dụng stateless sẽ dễ dàng triển khai và cấu hình hạ tầng hơn. Với các ứng dụng stateful, yêu cầu cần phải gắn chặt client với đúng server tương ứng, ít nhất là khi giao dịch chưa hoàn tất. Việc này không phải là không thể làm được, nhưng sẽ yêu cầu thêm nhiều bước cấu hình phức tạp. Những thứ đó sẽ hoàn toàn không cần thiết với một ứng dụng stateless.

Các ứng dụng stateless cũng có khả năng chống chịu lỗi cao hơn. Các truy vấn là độc lập, có thể xử lý bởi bất kỳ server nào. Vì vậy, nếu như một server quá tải hoặc gặp sự cố, truy vấn sẽ được chuyển cho server khác xử lý. Những lỗi như vậy không ảnh hưởng gì đến người dùng.

Một ưu điểm khác của stateless là ứng dụng có thể scale theo chiều ngang (tăng số lượng server) rất dễ dàng. Với các ứng dụng stateful, cùng một giao dịch phải được xử lý bởi cùng một server, vì vậy việc scale sẽ không mang lại nhiều hiệu quả.

Stateless phù hợp với các ứng dụng hiện đại

Việc client và server ít phụ thuộc lẫn nhau giúp các ứng dụng có thể được triển khai theo nhiều kiến trúc đơn giản hơn, đa dạng hơn. Ngày nay, các ứng dụng thường được module hóa, và các công nghệ cũng đang được phát triển theo hướng đó.

Kiến trúc stateless đã thúc đẩy việc chia nhỏ ứng dụng thành nhiều module chức năng nhỏ hơn, đơn giản hơn thay vì cả ứng dụng là một khối duy nhất trong quá khứ. Một số kiến trúc hiện đại như microservice cũng là xu thế trong thời gian gần đây. Chia nhỏ chức năng thành những module riêng biệt rất dễ dàng vì chương trình không có “state”.

Stateless cũng đặc biệt phù hợp với các ứng dụng cloud. Các ứng dụng stateless trên cloud có thể dễ dàng cầu hình hạ tầng, thiết lập scale tự động để đáp ứng yêu cầu khi lượng truy cập tăng lên. Những ứng dụng đang chạy trên máy chủ on premise cũng có thể chuyển từng phần lên cloud mà không ảnh hưởng đến hoạt động chung.

Vì scale theo chiều ngang dễ dàng hơn, chi phí vận hành của các ứng dụng stateless trên cloud cũng dễ dàng được tối ưu hơn. Các ứng dụng có thể thêm máy chủ vào khi lượng truy cập tăng và giảm bớt vào các giờ thấp điểm.

Với những ứng dụng stateless và module hóa, việc áp dụng những phương pháp phát triển phần mềm mới như agile cũng đơn giản hơn. Vì ít có sự phụ thuộc lẫn nhau, từng phần của ứng dụng cho thể được cập nhật, bổ sung tính năng liên tục mà không phải lo lắng về sự ảnh hưởng của nó.

Nói chung, kiến trúc stateless sẽ là lựa chọn hợp lý cho các ứng dụng web nói riêng và các ứng dụng online nói chung.

Ví dụ thực tế

Quay trở lại ví dụ về ứng dụng mua sắm online trong phần trước. Chúng ta sẽ xem xét kỹ hơn về stateful và stateless trong ứng dụng đó.

Với một ứng dụng stateful, các bước thực hiện sẽ như sau:

  • Người dùng truy cập ứng dụng và sẽ được hiển thị màn hình đăng nhập. Một số thông tin để tracking sẽ được ghi vào cookie của trình duyệt. Dựa vào thông tin này, server hoặc bộ cân bằng tải có thể xác định các truy vấn tiếp theo là đến từ cùng trình duyệt này.
  • Người dùng đăng nhập, và server sẽ ghi nhận trạng thái “đã đăng nhập” cùng một số thông tin về người dùng như user ID.
  • Người dùng thực hiện đặt hàng, server sẽ lấy thông tin trạng thái “đã đăng nhập” của người dùng và tiến hành đặt hàng, lúc này trạng thái của giao dịch sẽ là “chờ thanh toán”.
  • Người dùng thực hiện thanh toán, server sẽ thực hiện thanh toán và hiển thị thông báo giao dịch thành công.

Mọi bước ở trên đều là một giao dịch và nó phải được xử lý bởi cùng một server. Giả sử có bất kỳ vấn đề gì xảy ra, ví dụ server bị quá tải, toàn bộ giao dịch sẽ không thực hiện được. Các truy vấn tiếp theo sẽ đến server khác không có dữ liệu state và người dùng phải thao tác lại từ đầu.

Với cùng một ứng dụng, nếu triển khai theo hướng stateless, mọi chuyện sẽ diễn ra như sau:

  • Người dùng truy cập ứng dụng và được hiển thị trang đăng nhập. Không cần tracking vì bất kỳ server nào cũng có thể xử lý truy vấn.
  • Người dùng đăng nhập thành công, server sẽ gửi một token cho trình duyệt của người dùng.
  • Người dùng tiến hành đặt hàng, trình duyệt sẽ gửi token đã nhận được lên server. Server xác nhận token và tiến hành đặt hàng, hiển thị trang thanh toán cho người dùng.
  • Người dùng thanh toán, tiếp tục sử dụng token đã nhận được. Server cũng xác nhận token và tiến hành thanh toán, hiển thị giao dịch thành công với người dùng.

Về cơ bản, trải nghiệm của người dùng với ứng dụng stateful hay stateless không khác gì nhau. Tuy nhiên, ứng dụng stateless thoạt nghe có vẻ phức tạp hơn (mỗi truy vấn của người dùng sẽ gửi token và server sẽ xác nhận lại) lại có nhiều ưu điểm hơn hẳn.

Bằng cách sử dụng token, không có sự phụ thuộc vào một server cụ thể nào để xử lý truy vấn của người dùng. Bất kỳ một server nào cũng có thể xử lý truy vấn. Do đó, mỗi bước trong giao dịch ở trên có thể được xử lý bởi một server khác nhau. Để tối ưu hóa hiệu suất, mỗi bước có thể được tiến hành bởi một server với cấu hình và khả năng tính toán phù hợp.

Đặc biệt, nếu server gặp sự cố vào giao dịch bị tạm dừng ở bước nào đó, người dùng có thể dễ dàng gửi một truy vấn khác, và nó sẽ được xử lý bởi một server khác và giao dịch sẽ tiếp tục tiến hành. Điều này mang lại trải nghiệm tốt hơn so với ứng dụng stateful.

Kết luận

Kiến trúc stateful rất quan trọng trong quá khứ, khi mọi tính toán tập trung vào server và client rất hạn chế về cấu hình. Và khi đó, các ứng dụng vẫn còn đơn giản, việc client và server phụ thuộc vào nhau vẫn chưa phải là vấn đề.

Giờ đây, client ngày càng mạnh mẽ hơn và các dịch vụ web thường được yêu cầu mở rộng quy mô tới hàng triệu hoặc thậm chí hàng tỷ người dùng. Kiến trúc stateless sẽ phù hợp hơn với những ứng dụng như vậy.

Tôi xin lỗi nếu bài viết có bất kỳ typo nào. Nếu bạn nhận thấy điều gì bất thường, xin hãy cho tôi biết.

Nếu có bất điều gì muốn nói, bạn có thể liên hệ với tôi qua các mạng xã hội, tạo discussion hoặc report issue trên Github.

Welcome

manhhomienbienthuy

Đây là thế giới của manhhomienbienthuy (naa). Chào mừng đến với thế giới của tôi!

Bài viết liên quan

Bài viết mới

Chuyên mục

Lưu trữ theo năm

Thông tin liên hệ

Cảm ơn bạn đã quan tâm blog của tôi. Nếu có bất điều gì muốn nói, bạn có thể liên hệ với tôi qua các mạng xã hội, tạo discussion hoặc report issue trên Github.