Làm thế nào để kiến ​​trúc một ứng dụng web dựa trên websockets nặng thời gian thực?


17

Trong quá trình phát triển Ứng dụng một trang thời gian thực, tôi đã dần dần chấp nhận các websockets để trao quyền cho người dùng của mình với dữ liệu cập nhật. Trong giai đoạn này, tôi rất buồn khi nhận thấy rằng tôi đã phá hủy quá nhiều cấu trúc ứng dụng của mình và tôi đã không tìm được giải pháp cho hiện tượng này.

Trước khi đi vào chi tiết cụ thể, chỉ cần một chút bối cảnh:

  • Các ứng dụng web là một SPA thời gian thực;
  • Phần cuối là trong Ruby on Rails. Các sự kiện trong thời gian thực được Ruby đẩy tới một phím Redis, sau đó một máy chủ nút vi rút lại và đẩy nó vào Socket.Io;
  • Frontend nằm trong AngularJS và kết nối trực tiếp với máy chủ socket.io trong Node.

Về phía máy chủ, trước thời gian thực, tôi đã phân tách rõ ràng các tài nguyên dựa trên bộ điều khiển / mô hình, với việc xử lý được đính kèm với mỗi tài nguyên. Thiết kế MVC cổ điển này đã được cắt nhỏ hoàn toàn, hoặc ít nhất là bỏ qua, ngay khi tôi bắt đầu đẩy công cụ qua websockets cho người dùng của mình. Bây giờ tôi có một đường ống duy nhất trong đó tất cả các ứng dụng của tôi chảy xuống dữ liệu có cấu trúc ít nhiều . Và tôi thấy nó căng thẳng.

Về mặt trước, mối quan tâm chính là sự trùng lặp của logic kinh doanh. Khi người dùng tải trang, tôi phải tải các mô hình cuộc gọi AJAX cổ điển của mình. Nhưng tôi cũng phải xử lý dữ liệu thời gian thực tràn vào và tôi thấy mình sao chép nhiều logic kinh doanh phía khách hàng để duy trì tính nhất quán của các mô hình phía khách hàng.

Sau một số nghiên cứu, tôi không thể tìm thấy bất kỳ bài viết, bài viết, sách hay bất cứ điều gì có thể đưa ra lời khuyên về cách người ta có thể và nên thiết kế kiến ​​trúc của một ứng dụng web hiện đại với một vài chủ đề cụ thể:

  • Làm cách nào để cấu trúc dữ liệu được gửi từ máy chủ đến người dùng?
    • Tôi có nên chỉ gửi các sự kiện như "tài nguyên này đã được cập nhật và bạn nên tải lại thông qua cuộc gọi AJAX" hoặc đẩy dữ liệu cập nhật và thay thế dữ liệu trước đó được tải qua các cuộc gọi AJAX ban đầu?
    • Làm thế nào để xác định một bộ xương mạch lạc và có thể mở rộng để dữ liệu được gửi? Đây có phải là thông báo cập nhật mô hình hay thông báo "có lỗi với blahblahblah"
  • Làm thế nào để không gửi dữ liệu về mọi thứ từ bất cứ đâu trong phụ trợ?
  • Làm thế nào để giảm trùng lặp logic nghiệp vụ cả trên máy chủ và phía máy khách?

Bạn có chắc chắn Rails là lựa chọn tốt nhất cho SPA của bạn? Rails rất tuyệt, nhưng nó nhắm vào ứng dụng nguyên khối ... bạn có thể muốn một phụ trợ mô-đun với sự tách biệt mối quan tâm ... Tùy thuộc vào nhu cầu của bạn, tôi sẽ xem xét các khung Ruby thay thế cho một SPA thời gian thực.
Bí ẩn

1
Tôi không chắc Rails là lựa chọn tốt nhất, nhưng tôi rất hài lòng với stack ở vị trí ít nhất là trên phần phụ trợ (có lẽ vì tôi tốt với khung cụ thể này). Mối quan tâm của tôi ở đây là nhiều hơn về cách thiết kế SPA trên quan điểm bất khả tri công nghệ. Và tôi cũng không muốn nhân số lượng ngôn ngữ, khung và thư viện trong một dự án.
Philippe Durix

Điểm chuẩn được liên kết có thể có vấn đề, nhưng nó cho thấy điểm yếu trong việc triển khai ActiveRecord hiện tại. Tôi có thể bị thiên vị về plezi.io , nhưng như đã chỉ ra trong các kết quả sau này của điểm chuẩn , nó cung cấp các cải tiến hiệu suất đáng kể, ngay cả trước khi phân cụm và Redis (chưa được thử nghiệm). Tôi nghĩ rằng nó hoạt động tốt hơn node.js ... Cho đến khi mọi thứ thay đổi, tôi sẽ sử dụng plezi.io.
Bí ẩn

Câu trả lời:


10

Làm cách nào để cấu trúc dữ liệu được gửi từ máy chủ đến người dùng?

Sử dụng mẫu tin nhắn . Chà, bạn đã sử dụng một giao thức nhắn tin, nhưng ý tôi là cấu trúc các thay đổi dưới dạng tin nhắn ... cụ thể là các sự kiện. Khi phía máy chủ thay đổi, điều đó dẫn đến các sự kiện kinh doanh. Trong kịch bản của bạn, quan điểm khách hàng của bạn quan tâm đến những sự kiện này. Các sự kiện nên chứa tất cả dữ liệu liên quan đến thay đổi đó (không nhất thiết là tất cả dữ liệu xem). Trang khách sau đó sẽ cập nhật các phần của chế độ xem mà nó đang duy trì với dữ liệu sự kiện.

Ví dụ: nếu bạn đang cập nhật mã chứng khoán và AAPL đã thay đổi, bạn sẽ không muốn đẩy tất cả giá cổ phiếu xuống hoặc thậm chí tất cả dữ liệu về AAPL (tên, mô tả, v.v.). Bạn sẽ chỉ đẩy AAPL, đồng bằng và giá mới. Trên máy khách, sau đó bạn sẽ chỉ cập nhật giá cổ phiếu đó trên chế độ xem.

Tôi có nên chỉ gửi các sự kiện như "tài nguyên này đã được cập nhật và bạn nên tải lại thông qua cuộc gọi AJAX" hoặc đẩy dữ liệu cập nhật và thay thế dữ liệu trước đó được tải qua các cuộc gọi AJAX ban đầu?

Tôi cũng không nói. Nếu bạn đang gửi sự kiện, hãy tiếp tục và gửi dữ liệu có liên quan với nó (không phải toàn bộ dữ liệu của đối tượng). Đặt tên cho loại sự kiện này. (Việc đặt tên và dữ liệu nào có liên quan đến sự kiện đó nằm ngoài phạm vi hoạt động cơ học của hệ thống. Điều này có liên quan nhiều hơn đến cách logic kinh doanh được mô hình hóa.) Trình cập nhật chế độ xem của bạn cần biết cách dịch từng sự kiện cụ thể vào thay đổi chế độ xem chính xác (nghĩa là chỉ cập nhật những gì đã thay đổi).

Làm thế nào để xác định một bộ xương mạch lạc và có thể mở rộng để dữ liệu được gửi? Đây có phải là thông báo cập nhật mô hình hay thông báo "có lỗi với blahblahblah"

Tôi muốn nói rằng đây là một câu hỏi lớn, kết thúc mở nên được chia thành nhiều câu hỏi khác và được đăng riêng.

Nhìn chung, hệ thống back end của bạn sẽ tạo và gửi các sự kiện cho những sự kiện quan trọng đến doanh nghiệp của bạn. Những người có thể đến từ nguồn cấp dữ liệu bên ngoài hoặc từ hoạt động trong chính back-end.

Làm thế nào để không gửi dữ liệu về mọi thứ từ bất cứ đâu trong phụ trợ?

Sử dụng xuất bản / đăng ký . Khi SPA của bạn tải một trang mới quan tâm đến việc nhận các cập nhật theo thời gian thực, trang sẽ chỉ đăng ký những sự kiện mà nó có thể sử dụng và gọi logic cập nhật chế độ xem khi các sự kiện đó đến. Bạn có thể sẽ cần logic pub / sub máy chủ để giảm tải mạng. Các thư viện tồn tại cho quán rượu / phụ Websocket, nhưng tôi không chắc những thứ đó trong hệ sinh thái Rails.

Làm thế nào để giảm trùng lặp logic nghiệp vụ cả trên máy chủ và phía máy khách?

Có vẻ như bạn phải cập nhật dữ liệu xem trên cả máy khách và máy chủ. Tôi đoán là bạn cần dữ liệu xem phía máy chủ để bạn có ảnh chụp nhanh để bắt đầu ứng dụng khách thời gian thực. Do có hai ngôn ngữ / nền tảng liên quan (Ruby và Javascript), logic cập nhật khung nhìn sẽ phải được viết bằng cả hai. Ngoài việc dịch mã (có vấn đề riêng), tôi không thấy cách nào khác.

Điểm kỹ thuật: Thao tác dữ liệu (xem cập nhật) không phải là logic nghiệp vụ. Nếu bạn có nghĩa là xác thực trường hợp sử dụng, thì điều đó dường như là không thể tránh khỏi vì việc xác thực của khách hàng là cần thiết cho trải nghiệm người dùng tốt, nhưng cuối cùng không thể được máy chủ tin cậy.


Đây là cách tôi thấy một điều như vậy có cấu trúc tốt.

Lượt xem của khách hàng:

  • Yêu cầu ảnh chụp nhanh và số sự kiện nhìn thấy lần cuối của chế độ xem
    • Điều này sẽ chuẩn bị trước khung nhìn để khách hàng không phải xây dựng từ đầu.
    • Có thể vượt qua HTTP GET để đơn giản
  • Tạo kết nối websocket và đăng ký các sự kiện cụ thể, bắt đầu từ số sự kiện cuối cùng của chế độ xem.
  • Nhận các sự kiện qua websocket và cập nhật chế độ xem dựa trên loại / dữ liệu sự kiện.

Các lệnh của khách hàng:

  • Yêu cầu thay đổi dữ liệu (HTTP PUT / POST / DELETE)
    • Phản hồi chỉ là thành công hay thất bại + lỗi
    • (Các sự kiện được tạo bởi thay đổi sẽ xuất hiện trên websocket và kích hoạt cập nhật chế độ xem.)

Phía máy chủ thực sự có thể được chia thành nhiều thành phần với trách nhiệm hạn chế. Một trong đó chỉ xử lý các yêu cầu đến và tạo sự kiện. Một người khác có thể quản lý đăng ký của khách hàng, lắng nghe các sự kiện (nói trong quá trình) và chuyển tiếp các sự kiện phù hợp cho người đăng ký. Bạn có thể có một phần ba lắng nghe các sự kiện và cập nhật các chế độ xem phía máy chủ - có thể điều này thậm chí xảy ra trước khi người đăng ký nhận được các sự kiện.

Những gì tôi đã mô tả là một hình thức nhắn tin CQRS + và một chiến lược điển hình để giải quyết các loại vấn đề bạn đang gặp phải.

Tôi đã không đưa Nguồn sự kiện vào mô tả này vì tôi không chắc đó là thứ gì đó bạn muốn đảm nhận hay nếu bạn cần nó. Nhưng nó là một mô hình liên quan.


Tôi đã tiến bộ rất nhiều về chủ đề này, và những gợi ý bạn đưa ra rất hữu ích. Tôi chấp nhận câu trả lời vì tôi đã sử dụng nhiều lời khuyên, ngay cả khi tôi không sử dụng tất cả. Tôi sẽ mô tả con đường tôi đi theo trong một câu trả lời khác.
Philippe Durix

4

Sau một vài tháng làm việc chủ yếu là phụ trợ, tôi đã có thể sử dụng một số lời khuyên ở đây để giải quyết các vấn đề mà nền tảng đang gặp phải.

Mục tiêu chính khi suy nghĩ lại về phần phụ trợ là gắn bó hết mức có thể với CRUD. Tất cả các hành động, tin nhắn và yêu cầu nằm rải rác xung quanh nhiều tuyến đường được tập hợp lại thành các tài nguyên được tạo, cập nhật, đọc hoặc xóa . Bây giờ nghe có vẻ rõ ràng, nhưng đây là một cách suy nghĩ rất khó để áp dụng cẩn thận.

Sau khi mọi thứ đã được sắp xếp thành tài nguyên, tôi đã có thể đính kèm tin nhắn thời gian thực vào các mô hình.

  • Sáng tạo kích hoạt một thông điệp với lỗ tài nguyên mới;
  • Cập nhật kích hoạt một thông báo chỉ có các thuộc tính được cập nhật (cộng với UUID);
  • Xóa sẽ kích hoạt một thông báo xóa.

Trên API còn lại, tất cả các phương thức tạo, cập nhật, xóa đều tạo ra phản hồi chỉ đầu, mã HTTP thông báo về thành công hay thất bại và dữ liệu thực tế được đẩy qua websockets.

Ở mặt trước, mỗi tài nguyên được xử lý bởi một thành phần cụ thể tải HTTP qua máng khởi tạo, sau đó đăng ký cập nhật và duy trì trạng thái của chúng theo thời gian. Các khung nhìn sau đó liên kết với các thành phần này để hiển thị các tài nguyên và thực hiện các hành động trên các tài nguyên đó qua các thành phần tương tự.


Tôi thấy Nguồn tin nhắn và sự kiện CQRS + đọc rất thú vị, nhưng cảm thấy nó hơi phức tạp cho vấn đề của tôi và có lẽ thích nghi hơn với các ứng dụng chuyên sâu trong đó việc đưa dữ liệu vào cơ sở dữ liệu tập trung là một thứ xa xỉ đắt tiền. Nhưng tôi chắc chắn sẽ ghi nhớ phương pháp này.

Trong trường hợp này, ứng dụng sẽ có một vài khách hàng đồng thời và tôi đã tham gia vào nhóm phụ thuộc rất nhiều vào cơ sở dữ liệu. Các mô hình thay đổi nhất được lưu trữ vào Redis mà tôi tin tưởng để xử lý vài trăm cập nhật mỗi giây.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.