I am currently using Rails to prepare backed for reacting and react-native applications. The token is used for authentication.
I've got two features in my application, which require WebSockets:
- Chat system(one chat open at a time)
- Notification system
I see two ways to implement WebSockets:
Two separate channels
First channel: NotificationChannel
to which I subscribe when client opens application and then I broadcast sth if it is required
Second channel: ChatChannel
to which I subscribe when I open a chat, the code would look as follows:
class ChatChannel < ApplicationCable::Channel
def subscribed
authorize_chat_room_id(params[:chat_room_id]) #
stream_from "chat_channel_#{params[:chat_room_id]}"
end
# here methods for receiving data from client
# maybe some other methods for tracking if sb is typing and so on
# private authorize_chat_room_id method
end
In this scenario user is subscribed maximally to 2 channels and authorization is performed only once when subscribing to chat.
In this approach, I authorize only once the user, when he subscribes to channel, however, I don't know how having 2 channels opened instead of 1, affects performance on the server.
One channel
The single channel would be called: PersonalChannel
, and would take care of all information flow, the code would look sth like this:
def subscribed
stream_from "personal_channel_#{current_user.id}"
end
def send_message(data)
if authorize_chat_room_id(data['chat_room_id'])
#create message
end
end
def start_typing(data)
if authorize_chat_room_id(data['chat_room_id'])
# some broadcast
end
end
And when I would broadcast, then I would send in the payload for example type(ex. 'MESSAGE_SEND'), and based on that, react client would print the message if chat would be open.
The advantage of this approach is that when sending the message I can broadcast only to one user and I won't get back message from the channel. But in this case, I have to authorize each time performed an action.
My question is which solution would be better in case of performance and what is generally accepted approach(to make Channels very general or to make each Channel take care of single task)?
On this issue, I would suggest that you consider prioritizing code maintenance concerns over performance.
The concept of
channels
allows us to decouple publishers from consumers. This simplifies a lot of code and helps with separation of concerns.For example, a user posting in a chat room wouldn't need to know (nor manage) how many people are subscribed to that chat room, nor who they might be. These concerns are now handled by the pub/sub system rather than the user.
This separation of concerns makes adding or removing chat members much easier (you don't need to updated every user, you only need to update the pub/sub system).
This separation also prevents multiple copies of the data from being stored (the pub/sub system holds the only valid copy).
In the long run, by consolidating all the data into a single
PersonalChannel
, you'll be reintroducing the coupling between publishers and consumers - this will increase complexity and make maintenance mode difficult over time.In the chatroom example, each user will need to grab a copy of all the room's members and send each message to all users. This increases the number of copies (of the member's list) as well as introduces synchronization concerns.
This advantage is actually something you can easily achieve using the first approach by subscribing each user to a personal channel as well as the other channels, subscribing to three channels instead of two.