Rails ActionCable — The good and bad parts
Bài đăng này đã không được cập nhật trong 3 năm
Rails 5 mới đây đã hỗ trợ WebSocket thông qua ActionCable. Đây là một sự hỗ trợ realtime rất lớn đối với cộng động Rails, tuy nhiên tôi nghĩ rằng các developer cần phải hiểu những điểm mạnh và điểm yếu của nó trước khi sử dụng.
Bài viết này được viết bởi người đồng sáng lập của Ably, một dịch vụ nền tảng mở rộng tin nhắc thời gian thực. Khi xem xét ActionCable, họ đã đặc biệt quan tâm tới cách các Rails team giải quyết các vấn đề liên quan tới tin nhắn thời gian thực mà họ đã giải quyết qua 3 năm xây dựng Ably. Mục đích của họ không phải là nhắm vào ActionCable khi nó là một bước phát triển lớn cho nền tảng Rails và giúp loại bỏ rất nhiều công sức code cho developer.
ActionCable là một bước đi đáng kể cho nền tảng Rails. Nó cho phép developer thoát ra khỏi mô hình request / response cũ để đến một kết nối liên tục được duy trì từ client đến Rails server. Giữ kết nối WebSocket mở cho phép các dữ liệu lưu thông thẻo cả 2 hướng giữa server và client. Đây là loại kết nối trạng thái (stateful), không giống như các yêu cầu HTTP, và các chi phí, thời gian trễ liên quan tới việc đẩy (pushing) dữ liệu thông thời gian thực giảm đáng kể so với HTTP polling.
Sơ lược về ActionCable
Trước khi đi sâu vào những gì mà ActionCable cung cấp, điều quan trọng nhất là phải hiểu được cách làm việc của ActionCable ở mức độ cao.
- Pub /Sub stream được cung cấp bởi tính năng Pub /Sub của Redis. Điều này cho phép nhiều hơn một Rails server chia sẻ trạng thái (state), publising đến Redis sẽ dẫn đến thông điệp được gửi tới tất cả các server. Mỗi stream sẽ sử dụng một kênh Redis (Redis channel) riêng biêt.
- Các đơn vị phân phôi tin nhắn là một pub /sub stream, mỗi tin nhắn được public được thực hiên jthongo qua một stream broadcast trong ActionCable
- Các ActionCable server sẽ chạy như một Rack server riêng biệt, tuy nhiên nó có thể được gắn bên trong ứng dụng của bạn hoặc như một ứng dụng độc lập. Nếu bạn gắn nó như một ứng dụng độc lấp, sau đó bạn sẽ cần phải nghĩ cách xử lí xác thực cho nó trên một port hoặc một domain khác.
- Kết nối Client-side đến server được thực hiện qua WebSockets. Cân bằng tải của bạn phải hỗ trợ WebSockets và giữ cho kết nối mở liên tục giữa truy cập của bạn và ActionCable server
- Kiểm soát dữ liệu nào được phát sóng (broadcasted) cho client được thực hiện thông qua bộ lọc kênh (channel) của bạn, nó có thể gọi bất kì logic nào mà có thể tồn tại trong ứng dụng Rails của bạn
ActionCable: Những điểm mạnh
ActionCable giúp Rails bắt kịp với Node.js + Socket.io và gần đây hơn là Realtime Django. Nó đáp ứng được mục tiêu nhận được nội dung và dữ liệu web-based dựa trên thời gian thực rất tốt. API rất đơn giản, nó chỉ hoạt động và thấy nhưu một phần mở rộng tự nhiên của Rails. Sự đơn giản này chắn chắn sẽ khuyến khích các Rails developer để thêm các thành phần thời gian thực tới các trang web họ xây dựng mà không cần hiểu được những phần phức tạp phái dưới.
Khi tìm hiểu về các ActionCable làm việc, có một vài điều ấn tượng như sau:
1. Đối tượng kết nối cơ bản và xác thực chia sẻ (Simple connection objects and shared authentication)
Khi mỗi kết nối đơn giản chỉ là một đối tượng Ruby, và các đối tượng Ruby là một phần của ứng dụng Rails, chia sẻ logic nghiệp vụ của bạn bao gồm việc xác thực là vô cùng đơn giản. Ví dụ nếu một cookie auth được ban hành trong một yêu cầu HTTP, kết nối WebSockets tiếp theo sẽ gửi cookie như là một phần của yêu cầu của WebSocket, và như vậy có thể được xác nhận trong cùng một cách. Khi kết nối là liên tục, xác thực là chỉ cần một lần và tất cả các Channel objects tiếp cận với người dùng xác thực qua việc sử dụng các identified_by delegate trên mỗi Channel
Chú ý: Chia sẻ xác thực chỉ áp dụng cho các ActionCable server được gắn trên các thiết bị đầu cuối, nếu ActionCable server của bạn nằm trên một port hoặc domain khác nhau thì điều này là không thể.
2. WebSocket gắn trên các thiêt bị đầu cuối (WebSocket mounted on the same endpoint)
Có thể có thể gắn kết các thiết bị đầu cuối WebSockets trực tiếp bên trong ứng dụng của bạn là một chiến thắng lớn, nớ đơn giản hóa việc triển khai nhanh chóng và chong phép chia sẻ tệp tin cookie
3. Đối tượng kênh chô mỗi subscription (Channel object for each subscription)
Channel objects được tạo ra cho mỗi subscriptions mới được thực hiện trên mỗi kết nối. Khái niệm rất đơn giản để hiểu, và Rails sau khi tạo và hủy các Channel objects như đăng kí hoặc kết nối cơ bản đến và đi.
4. Tự động kết nối và phục hồi subscription(Automatic connection and subscription recovery)
Các thư viện client-side ActionCable là trách nhiệm duy trì một kết nối và gán lại chuỗi tất cả các subscription tới Channels. Ví dụ, kết nối của bạn đột ngột chấm dứt, thư viện client-side ActionCable sẽ tự động thiêt lập lại kết nối và đăng kí cho tất cả các subscriptions đã đăng kí trước đó
5. Sockets 2 chiều (Bi-directional Sockets)
ActionCable là đa chiều (Bi - directional) để client có thể nhận dữ liệu phát sóng (broadcast) bằng cách sủ dụng phương pháp nhận được (the received method), nhưng cũng có thể gửi dữ liệu đến các public methods trên các đổi tượng kênh (Channel objects) sử dụng phía client-side thực hiện các chức năng
6. Redis backed
Sử dụng Redis để cung cấp các kênh Pub /Sub là một lựa chọn tốt, đó là một mảng hiệu suất cao và vững chắc của công nghệ phần mềm.
7. Shared view logic
Bởi vì Channel objects là một phần của Rails stack, về măt lí thuyết nó nhỏ để render một phần thay vì chỉ là dữ liệu gửi về cho ActionCable client qua WebSockets. Điều này có thể đơn giản hóa front-end logic vì nó không cần phải làm thế nào để render data, thay vì chỉ đơn giản là thay đỏi một phần thử DOM với một số mã HTML. Đây là một khái niệm khá giống với TurboLinks 3 hỗ trợ, ngoại trừ nội dụng được pushed thay vì pulled.
Một vài trường hợp sử dụng phổ biến ActionCable
- Trang thông báo và cảnh báo - ví dụ như nảy lên một cảnh báo trong trang web cho tất cả các người dùng hoặc thông báo cho người dùng kích hoạt bởi một tiên trình nền (background progress).
- Ứng dụng trang duy nhất, cho đến nay đã thăm dò ý kiến bản cập nhật, nhưng bây giờ có thể nhận được thông tin cập nhật đến các mô hình pushed thời gian thực.
- Nội dung stream - thêm logic nghiệp vụ vào các mô hình và các dịch vụ để tự động phát sóng (broadcast) thay đổi nội dung truy cập của người dùng.
- Live chat cho trang của bạn giữa các user.
ActionCable: Những điểm yếu
Các công việc thực hiện trên ActionCable bởi cộng đồng Rails là rất ấn tượng, tuy nhiên bạn cần phải nhớ rằng ActionCable không được thiết kế để khả năng mở rộng mạnh mẽ nhưng thay vì cung cấp một phương tiện liên lạc full-duplex đơn giản giữa Rails server và Websockets được kích hoạt. Vì lí do này mà nhiều thiết sót được nêu dưới đây không đáng ngạc nhiên mục tiêu ActionCable nhất định, tuy nhiên điều quan trọng là các developer nhận thức được nó.
1. Trạng thái kết nối (Connection state)
Như ActionCable là một quá trình lâu dài, mỗi kết nối được thiết lập có thể có trạng thái về server-side miễn là kết nối vẫn nguyên vẹn. Trạng thái này thường được lưu trữ hoặc tham chiếu từ mỗi Channel object tạo ra trong phản ứng (response) tới mỗi client's subscription. Tuy nhiên, vấn đề là mỗi khi mất kết nối từ client tới server sẽ dẫn tới mất tất cả các trạng thái của Channel object cho mỗi subscription.
Nếu trạng thái kết nối bị mất, sau đó tất cả các tin nhắn được đăng trong khi client bị ngắt kết nối nối bị mất. ActionCable không cung cấp phương tiện để bắt kịp về hoặc phát lại những gì đã xảy ra trong khi bị ngắt kết nối. Nếu bạn không thích ý tưởng không chỉ mất đi các thông điệp, nhưng cũng không biết bạn đã bỏ qua các thông điệp trong khi bị ngắt kết nối. ActionCable có thể hỗ trợ bạn.
2. Điểm duy nhất của thất bại (Single point of failure)
ActionCable được hỗ trợ bởi Redis Pub /Sub. Redis là một phần đáng kinh ngạc của kĩ thuật được sử dụng rộng rãi trong các sở sở hạ tâng mà Ably. Tuy nhiên giống như tất cả các dịch vụ, bạn phải đợi bảo trì và một số thời gian chết bất ngờ. Khai Redis instance của bạn down, như vậy là ActionCable. Clustering dĩ nhiên là một lựa chọn, tuy nhiên hiện tại của có cụm Redis dịch vụ cơ sở dữ liệu như một dịch vụ thương mại.
3. Độ trễ (Latency)
ActionCable cung cấp tin nhắn 2 chiều trên một kết nối WebSocket, tuy nhiên mỗi tin nhắn được publised hay received được chuyển qua Rails server trong một trung tâm dữ liệu duy nhất. Nếu bạn xây dựng một trò chơi hay nền tảng dịch vụ tài chính và các vấn đề độ trễ xảy ra, sau đó bạn có một đề nếu truy cập của bạn không ở gần server. Ví dụ bạn có 2 người chơi một trò chơi thời gian thực tại Úc, nhưng Rails server của bạn nằm ở châu Âu, mỗi tin nhắn được published cần phải đi nửa vòng thế giới và ngược lại. Người ta nghĩ rằng bạn chỉ có thể xác định một vị trsi một số Rails server tại Úc là tốt, tuy nhiên nó sẽ không làm việc như ActionCable được thiết kế để được hỗ trợ bởi một Redis database duy nhất.
4. Thất bại đôt ngột có thể hủy trạng thái (Abrupt failures can corrupt state)
Trong khi tìm hiểu cách ActionCable làm việc với một ứng dụng Rails đơn giản, tôi nhận thấy rằng các trạng thái ứng dụng đã bắt đầu bị hỏng do server đột ngột bị chấm dứt. Sau đó nó chợt nhận ra rằng các lập trình viên khác có thể sử dụng Redis hoặc một số nguồn dữ liệu khác để theo dõi tình trạng hệ thống, nhưng không có cách nào có thể đảm bảo rằng các server sẽ đóng là làm lại trạng thái của nó.
5. Can I rely on this? No, there is no ACK/NACK support
Tôi kiểm tra các thông điệp WebSocket liệu khi gọi một phương thức kênh để xem làm thế nào ActiveCable xử lý xác nhận tin nhắn. Ví dụ, nếu bạn đang có một cuộc trò chuyện với các đồng nghiệp và gửi tin nhắn cho cả nhóm, bạn có muốn biết rằng các thông điệp đã được gửi đến hệ thống. Nếu ứng dụng của bạn được thông báo rằng nó không thành công, thì ít nhất bạn có lợi ích của việc cập nhật giao diện người dùng cho người sử dụng hoặc có lẽ reattempting để xuất bản các tin nhắn.
Khi tôi nhìn vào các thông điệp thô, tôi đã ngạc nhiên khi thấy rằng không có sự thừa nhận về sự thành công hay thất bại đối với việc xuất bản của bất kỳ tin nhắn từ một khách hàng đến máy chủ ActionCable của bạn. Xem các thông điệp liệu dưới đây:
/* Published message from client following a call to @perform, see https://goo.gl/s3Hb9G */
{"command":"message","identifier":"{\"channel\":\"EventsChannel\"}","data":"{\"action\":\"request_count\"}"}
/* Published message from client following a call to @perform, see https://goo.gl/s3Hb9G */
{count: "2", broadcastAt: 1458940410054.843}
/* No ACK/NACK for the published message, how do you know it arrived? */
Không chỉ vậy, khi tôi nhìn vào mã mà sẽ gửi tin nhắn, nó thậm chí không cung cấp một callback cho một HTTP hoặc kết nối thất bại, xem https://goo.gl/s3Hb9G.
Nó là khá rõ ràng rằng nếu không có những loại failsafes, tin nhắn ActionCable không phải là mạnh mẽ để cung cấp hoặc nhận tin nhắn.
6. Trình duyệt hiện đại và mạng mở - sử dụng WebSockets (Modern browsers & open networks only please — we use WebSockets)
Tôi tìm thấy nó khó để tin rằng những người đóng góp ActionCable đã không chọn để sử dụng một trong các thư viện tuyệt vời mà cung cấp hỗ trợ dự phòng tốt như Engine.io hoặc SockJS. Tuy nhiên khi tôi nghĩ về những gì mà có yêu cầu, tôi tin rằng nhận ra lý do tại sao.
Thông thường nếu WebSockets không được hỗ trợ, một kết nối XHR được thực hiện cho các máy chủ trên HTTP. Trong trường hợp của ActionCable, yêu cầu đó sẽ được gửi đến một máy chủ Rails của bạn như là quyết định bởi cân bằng tải. Nếu ActionCable sau đó thực hiện các thành phần phía máy chủ của SockJS / Engine.IO, sau đó nó có hiệu quả có thể điều trị mà yêu cầu HTTP như một kết nối WebSocket. Tuy nhiên, các kết nối HTTP thường được đóng lại sau một khoảng thời gian (và thường cần phải được trong trường hợp của sao chổi và JSONP), và các tin nhắn công bố sẽ phát hành một yêu cầu HTTP mới. Như vậy, mọi yêu cầu HTTP tiếp theo không được bảo đảm để đi đến cùng một máy chủ. Nằm trong đó vấn đề, trạng thái kết nối có thể được trên một máy chủ khác nhau cho một yêu cầu đến tại nghĩa đội ActionCable sẽ cần phải di chuyển ra khỏi Rails thiết kế không quốc tịch và cho phép các đối tượng Kênh & Kết nối qua quá trình giao tiếp với nhau.
7. Tin nhắn đặt hàng (Message Ordering)
Nếu thứ tự các thông điệp được truyền đi là quan trọng với bạn thì ActionCable không phù hợp. Mỗi thông điệp được published không có một số nối tiếp vì vậy nó là không thể đối với việc tiếp nhận két thúc để đảm bảo rằng nó xử lí thông điệp theo thứ tự. Thậm chí nếu có thể làm điều đó, hoặc bạn thực hiện một trung gian để làm điều đó, hệ thống thông báo ở đầu kia sẽ không tôn trọng điều đó hoặc như là mỗi pub /sub sự kiện từ Redis được xử lí realtime.
8. Nền tảng khả năng cộng tác và tách (Platform Interoperability & Decoupling)
Khi nhìn vào ActionCable, một trong những điều đầu tiền tôi thấy làm là một số thử nghiệm hiêu suất. Như vậy, tôi muốn giao tiếp với ActionCable sử dụng một nền tảng khác hơn là một trình duyệt. Tôi đã xem qua các NPM gói ES6-ActionCable, nhưng nhanh chóng phát hiện ra nó chỉ làm việc trong 1 trình duyệt. May mắn thay thêm hỗ trợ Node.js được dễ dàng, tuy nhiên sau đó nhận ra rằng ActionCable có rất nhiều công nghệ javascripts và Ruby, đến bây giờ tôi không thấy có bất kì thư việc client nào cho các nền tảng iOS, Andoird, Go, Python ...
Tổng hợp lại
Sau khi đã lấy một cái nhìn tại ActionCable, tôi thấy một số công việc tuyệt vời mà sẽ có ảnh hưởng tốt đến cộng đồng Rails. Đẩy dữ liệu thời gian thực và nội dung cho du khách là một bước tiến rất lớn và cung cấp này như một phần của Rails với một API đơn giản là tốt di chuyển.
Tuy nhiên tôi tin rằng tại một số điểm, khi các developer cảm thấy họ cần phải thực hiện bảo đảm hoặc chuyển giao dữ liệu, hoặc họ muốn giảm tải các tin nhắn từ các máy chủ Rails của họ, sau đó họ nên xem xét trao đổi ra ActionCable cho một nền tảng nhắn tin thời gian thực mục đích xây dựng mạnh mẽ hơn. Nếu develper chịu được điều này trong tâm trí ngay từ đầu khi xây dựng các ứng dụng của họ, vì họ cao hơn ActionCable, việc di chuyển đến một dịch vụ thời gian thực nên được đơn giản như các khái niệm cơ bản của kênh pub / sub sử dụng cho xuất bản vẫn giữ nguyên.
Tôi hy vọng rằng khi Rails developer xem xét các lựa chọn thay thế để ActionCable, họ sẽ xem xét Ably. Tại Ably, chúng ta hãy xem xét một thông báo bị mất hoặc bị chậm lại trong những dịch vụ không thể chấp nhận, và kết quả là, chúng tôi đã tạo ra phần mềm kết chặt chẽ với cơ sở hạ tầng của chúng tôi để sửa chữa những vấn đề thách thức. Chúng tôi có fallbacks và chiến lược phục hồi trong các thư viện khách hàng của chúng tôi, cơ sở hạ tầng dự phòng toàn cầu, cơ sở dữ liệu của chúng tôi có nhân rộng toàn cầu, và như vậy, chúng tôi tin rằng chúng tôi có thể làm việc xung quanh hầu hết mọi tình huống trong khi duy trì bảo lãnh thực hiện. Chính vì lý do này, chúng tôi cung cấp một thời gian hoạt động 100% độc đáo và đảm bảo QoS.
Nếu bạn là một developer, tôi mời bạn để cung cấp cho bé một thử bằng cách đăng ký một khóa API miễn phí. Chúng tôi có thư viện client trong tất cả các ngôn ngữ phổ biến trong đó có một mà tôi đã viết trong Ruby.
Bài viết này được dịch từ bài viết Rails ActionCable — The good and bad parts.
All rights reserved