Lobby
Revision for “Lobby” created on 6 July 2016 @ 20:07:04
Title | Lobby |
---|---|
Content | <h2>OVERVIEW</h2>The lobby service provides a means for users to join a virtual group using a pre-agreed lobby name. It is the on-boarding mechanism used to group multiple layout contexts together for synchronised playback and is used as a filter for real-time communication between different groups of users.<h2 id="definitions">DEFINITIONS</h2><em>Client</em> - is an instance of the 2Immerse application. In general, there is one client running per device, but a web browser could be running multiple instance of the application in different tabs.<em>Layout context</em> - is a temporary collection of clients on the same LAN that are collaborating to present one multi-device broadcast.<em>User</em> - is a person who has signed into a client. A user can be signed into multiple clients simultaneously.<em>Host</em> - is a user who launches a programme and therefore owns the layout context.<em>Lobby</em> - is a set of layout contexts that are watching a synchronised programme together.<h2 id="lobby-members">LOBBY MEMBERS</h2>The lobby service displays a table of users grouped by layout context. <img class="wp-image-790 aligncenter" src="https://2immerse.eu/wp-content/uploads/2016/05/lobbyUI.png" alt="lobbyUI" width="599" height="425" /> The challenge is to ensure the table accurately reflects user presence at all times. A user is considered present if a device they are signed into is in regular contact* with the lobby service.<em>* Regular contact can be achieved using a heartbeat mechanism over a stateless connection (e.g. long poll) or inferred by monitoring a permanent socket connection. Socket.io is a node.js library that abstracts away the mechanism used.</em>Each device in the layout context must therefore be instructed to declare and update its presence with the lobby server and must be capable of displaying the lobby table (DMApp component) if instructed to by the layout context. These requirements demand that a lobby client component be instantiated on each device in the layout context.A lobby member is uniquely identified by a (layoutContextId, userId) pair. This is because a user could be signed into several clients with their credentials and those clients could be used in different layout contexts.<h2>JOINING A LOBBY</h2>Synchronisation between layout contexts is brokered by the lobby service but it requires one user from each layout context to instigate a ‘Join Lobby’ operation. This must pull other members of the layout context into the lobby automatically. There are two possible schemes:<ol>
<li>The 'Join Lobby' operation broadcasts a message to all other devices in the layout context asking them to connect to a specified lobby.</li>
<li>The layout context joins the lobby, submitting and updating user presence information on behalf of members of the layout context.</li>
</ol>In the first approach, the lobby service is duplicating some of the presence tracking done by the layout context. In the second approach, where the layout service is acting as a proxy, the layout service is taking on additional responsibilities that prevent a clear separation of concerns. An alternative to both approaches is to separate out user presence management into a separate service which the lobby and layout services can depend upon. This could also act as a proxy for the call service, keeping it informed of contactable video-chat enabled peers.<h2 id="functional-description">FUNCTIONAL DESCRIPTION</h2>The lobby service is multi-tenant, allowing many lobbies to be hosted by a single service instance. Lobby membership information is distributed between lobby server instances as opposed to being persisted to a database. Horizontal scaling of lobby service instances is triggered when memory or network connection limits exceed a pre-defined threshold or latencies increase to a level that delivers a poor user experience.A lobby doesn’t exist prior to the first user joining and ceases to exist after the last user has left. Users can join and leave the lobby at any time and all members are notified of these events.Each client establishes a single secure web socket with one of the lobby service instances, either directly or through a presence service. The connection is used to track the client and to notify them of lobby events. It allows the client to subscribe to user leave/join/message notifications from the server and allows the server to detect disconnections.Clients making a lobby service request directly or indirectly via a presence service must provide a valid session access token in order to use the lobby service API. The token can be sent to the server via a cookie for both REST and web socket communications. The lobby service validates the access token and requests the session’s userId from the session service. Users are internally identified to the lobby service by their unique userId (which is the same as the call service's caller Id).On joining a lobby, a client is issued with a list of existing lobby members. Clients can track changes to lobby membership by processing leave and join notifications from the server. An administrator can connect to the lobby service for the purpose of moderation without joining a lobby.The lobby service subscribes to the session events, such as session expiration, session invalidation and user sign-out. It uses these events to automatically evict users from the lobby and drop their connections.A client wishing to create a new lobby can request a unique lobbyId from the lobby service. The lobbyId is a memorable, human readable string that can be shared by individuals via conventional channels of communication such as over the phone or by embedding it in a URL. It is the key piece of information used to group participants for synchronised media playback between layout contexts. A commonly used scheme is a hyphen separated list of 3-4 of the most commonly used English language nouns.The lobby service can broadcast application-defined messages on behalf of a user to all other users in the lobby and their signed-in clients. This is useful for signalling changes in the state of a shared experience and to synchronise actions between peers without having to establish separate peer-to-peer connections. An example would be an application-defined message instructing each client to begin playing a media stream.<h2 id="responsibilities">RESPONSIBILITIES</h2><ul>
<li>Provide a framework for group activities involving a number of users.</li>
<li>Maintain lobby connections and manage user membership</li>
<li>Share membership information with clients</li>
<li>Generate lobby events on the micro service message bus for subscribers to listen to.</li>
<li>Notify clients of join and leave events</li>
<li>Generate unique lobbyIds</li>
<li>Provide administration and chair functions.</li>
</ul><h2 id="voice-video-text-chat">VOICE/VIDEO/TEXT CHAT</h2>Lobbies provide a means of establishing video and voice chat between groups and users by making their callerIds (userIds) available to other members of the lobby. The call service uses these callerIds to broker connections between peers so that they can execute the session initiation protocols required for real-time video chat. It is then the call service's responsibility to resolve which client device to forward the offer to.Text chat is different to video and voice chat because conversation history must be preserved and unlike video chat, it is unlikely to require a dedicate device. There are two use cases:<ol>
<li>Communal text chat - everyone in the lobby can see the conversation</li>
<li>One-on-one chat - a user wishes to have a private chat with one of the other lobby members.</li>
</ol>It is likely that a separate service will be used to manage the lobby's communal chat history and to broadcast text messages to lobby members. Any client can render a view of chat history and add new messages. This doesn't require an offer/answer signalling mechanism; all clients participate. One-on-one chat may still be handled by a separate chat service, but will use offer/answer semantics via the call service to establish communication.<h2 id="architecture">ARCHITECTURE</h2><h3 id="overview">OVERVIEW</h3>A lobby server is responsible for running the core business logic and for maintaining persistent web socket connections with clients or a separate presence server. The number of server instances can be scaled up and down to meet demand. Clients within the same layout context could therefore be connected to different lobby servers and their connections may be lost if servers are taken offline, requiring the client to reconnect.A load balancer will distribute web socket connections to different server instances to spread the load and then use the publish/subscribe machinery of RabbitMQ, Redis or ZeroMQ to route messages between the server instances. Each server will maintain a list of connected clients grouped by lobbyId and will use routing keys to keep the number of messages between servers to a minimum.<h3 id="consistency-state">CONSISTENCY & STATE</h3>Lobby membership and connectivity is shared between lobby server instances without the need for a database. Lobby server instances communicate with each other to keep themselves up-to-date using message queues and routing exchanges. A publisher/subscriber model is used in conjunction with routing keys and RPCs to send lobby events to the right server instances.Changes to lobby membership are published as messages and routed to other server instances. Messages destined for the members of a lobby can be filtered using routing keys. This causes them to be routed to only those server instances responsible for managing connections to other members of the lobby. Servers that manage client connections for a given lobby can subscribe to the message queue using the lobby's routing key. When combined with a limit on lobby occupancy, the number of server-to-server messages can be kept to a minimum, permitting good scalability.<h3 id="alternatives">ALTERNATIVES</h3>An alternative scheme is to use a database to manage lobby membership and allow new servers to be provisioned in the event of scaling or failover, but this also requires a garbage collection service to ensure old lobbies and users are removed from the database on client disconnect or server failure. The benefit of using websocket / persistent connections is that garbage collection is implicit and so the record of lobby membership cannot become out of date with respect to the list of server connections.<h2>Collaborators</h2><ul>
<li>Logging service</li>
<li>Session service</li>
<li>User Identity service</li>
<li>Call service</li>
<li>Layout service</li>
</ul><h2>Verbs</h2><strong>AllocLobbyId</strong>(ssoToken)<em>Params</em>:ssoToken: session access token<em>Returns</em>:Unique lobbyId<em>Description</em>:Generates a unique, human readable lobbyId. <strong>Join</strong>(ssoToken, lobbyId)<em>Params</em>:ssoToken: session access tokenlobbyId: identifier or name of the lobby to join<em>Returns</em>:members: a list of lobby members<em>Description</em>:Adds the user specified by the session access token to the specified lobby and broadcasts a ‘joined’ notification event to each connected client. This method has no effect if the user is already a member of the specified lobby. A list of lobby members is returned in the response. If the lobby is full, the user will not be added to the lobby and an error response is returned. <strong>Leave</strong>(ssoToken, lobbyId)<em>Params</em>:ssoToken: session access tokenlobbyId: identifier or name of the lobby to leave<em>Description</em>Removes the user identified by the session access token from the specified lobby and broadcasts a ‘left’ notification event to each connected client. This method has no effect if user has already left the lobby. <strong>BroadcastMessage</strong>(ssoToken, lobbyId, message)<em>Params</em>:ssoToken: session access tokenlobbyId: identifier or name of the lobby to broadcast tomessage: application defined message payload<em>Description</em>:Broadcast an application-defined message to all connected clients. This is intended to allow application specific functionality to be layered on top of the lobby whilst keeping the lobby service as simple as possible. <strong>Close</strong>(ssoToken, lobbyId)<em>Params</em>:ssoToken: session access tokenlobbyId: identifier or name of the lobby to leave<em>Description</em>:Close a lobby and notify all connected clients by sending a ‘disconnect’ message. This is an administrative function intended for use by a chair or system administrator. <strong>Kick</strong>(ssoToken, lobbyId, userId)<em>Params</em>:ssoToken: session access tokenlobbyId: identifier or name of the lobby to leaveuserId: the user to evict<em>Description</em>:Evicts a user from a lobby and sends a ‘disconnect’ message to all clients of the user. Also notifies remaining clients by sending a ‘leave’ notification. This is an administrative function used for moderation purposes and is intended for system administrators. <strong>GetMembers</strong>(ssoToken, lobbyId)<em>Params</em>:ssoToken: session access tokenlobbyId: identifier or name of the lobby<em>Description</em>Retrieve a list of lobby members. <strong>Events</strong><strong>Disconnect</strong>An administrator or chair has disconnected the client’s connection with the lobby.<strong>Joined</strong>A user has joined the lobby.<em>Payload</em>: userId indicating the user that has joined the lobby.<strong>Left</strong>A user has left the lobby.<em>Payload</em>: userId indicating the user that has left the lobby, either explicitly, as a result of a loss of connection with the lobby service or as a result of being evicted from the lobby by a chair or administrator.<strong>Message</strong>A user has broadcast a message<em>Payload</em>: (userId, message) indicating the user that issues the message and the message itself.<strong>Error</strong>This event is received in response to a failed connection attempt such as an authentication failure or the lobby being full.<em>Payload</em>: (error code) indicating type of error |
Excerpt | |
Markdown content | <h2>OVERVIEW</h2>
The lobby service provides a means for users to join a virtual group using a pre-agreed lobby name. It is the on-boarding mechanism used to group multiple layout contexts together for synchronised playback and is used as a filter for real-time communication between different groups of users.
<h2 id="definitions">DEFINITIONS</h2>
<em>Client</em> - is an instance of the 2Immerse application. In general, there is one client running per device, but a web browser could be running multiple instance of the application in different tabs.<em>Layout context</em> - is a temporary collection of clients on the same LAN that are collaborating to present one multi-device broadcast.<em>User</em> - is a person who has signed into a client. A user can be signed into multiple clients simultaneously.<em>Host</em> - is a user who launches a programme and therefore owns the layout context.<em>Lobby</em> - is a set of layout contexts that are watching a synchronised programme together.
<h2 id="lobby-members">LOBBY MEMBERS</h2>
The lobby service displays a table of users grouped by layout context. <img class="wp-image-790 aligncenter" src="https://2immerse.eu/wp-content/uploads/2016/05/lobbyUI.png" alt="lobbyUI" width="599" height="425" /> The challenge is to ensure the table accurately reflects user presence at all times. A user is considered present if a device they are signed into is in regular contact* with the lobby service.<em>* Regular contact can be achieved using a heartbeat mechanism over a stateless connection (e.g. long poll) or inferred by monitoring a permanent socket connection. Socket.io is a node.js library that abstracts away the mechanism used.</em>Each device in the layout context must therefore be instructed to declare and update its presence with the lobby server and must be capable of displaying the lobby table (DMApp component) if instructed to by the layout context. These requirements demand that a lobby client component be instantiated on each device in the layout context.A lobby member is uniquely identified by a (layoutContextId, userId) pair. This is because a user could be signed into several clients with their credentials and those clients could be used in different layout contexts.
<h2>JOINING A LOBBY</h2>
Synchronisation between layout contexts is brokered by the lobby service but it requires one user from each layout context to instigate a ‘Join Lobby’ operation. This must pull other members of the layout context into the lobby automatically. There are two possible schemes:
<ol>
<li>The 'Join Lobby' operation broadcasts a message to all other devices in the layout context asking them to connect to a specified lobby.</li>
<li>The layout context joins the lobby, submitting and updating user presence information on behalf of members of the layout context.</li>
</ol>
In the first approach, the lobby service is duplicating some of the presence tracking done by the layout context. In the second approach, where the layout service is acting as a proxy, the layout service is taking on additional responsibilities that prevent a clear separation of concerns. An alternative to both approaches is to separate out user presence management into a separate service which the lobby and layout services can depend upon. This could also act as a proxy for the call service, keeping it informed of contactable video-chat enabled peers.
<h2 id="functional-description">FUNCTIONAL DESCRIPTION</h2>
The lobby service is multi-tenant, allowing many lobbies to be hosted by a single service instance. Lobby membership information is distributed between lobby server instances as opposed to being persisted to a database. Horizontal scaling of lobby service instances is triggered when memory or network connection limits exceed a pre-defined threshold or latencies increase to a level that delivers a poor user experience.A lobby doesn’t exist prior to the first user joining and ceases to exist after the last user has left. Users can join and leave the lobby at any time and all members are notified of these events.Each client establishes a single secure web socket with one of the lobby service instances, either directly or through a presence service. The connection is used to track the client and to notify them of lobby events. It allows the client to subscribe to user leave/join/message notifications from the server and allows the server to detect disconnections.Clients making a lobby service request directly or indirectly via a presence service must provide a valid session access token in order to use the lobby service API. The token can be sent to the server via a cookie for both REST and web socket communications. The lobby service validates the access token and requests the session’s userId from the session service. Users are internally identified to the lobby service by their unique userId (which is the same as the call service's caller Id).On joining a lobby, a client is issued with a list of existing lobby members. Clients can track changes to lobby membership by processing leave and join notifications from the server. An administrator can connect to the lobby service for the purpose of moderation without joining a lobby.The lobby service subscribes to the session events, such as session expiration, session invalidation and user sign-out. It uses these events to automatically evict users from the lobby and drop their connections.A client wishing to create a new lobby can request a unique lobbyId from the lobby service. The lobbyId is a memorable, human readable string that can be shared by individuals via conventional channels of communication such as over the phone or by embedding it in a URL. It is the key piece of information used to group participants for synchronised media playback between layout contexts. A commonly used scheme is a hyphen separated list of 3-4 of the most commonly used English language nouns.The lobby service can broadcast application-defined messages on behalf of a user to all other users in the lobby and their signed-in clients. This is useful for signalling changes in the state of a shared experience and to synchronise actions between peers without having to establish separate peer-to-peer connections. An example would be an application-defined message instructing each client to begin playing a media stream.
<h2 id="responsibilities">RESPONSIBILITIES</h2>
<ul>
<li>Provide a framework for group activities involving a number of users.</li>
<li>Maintain lobby connections and manage user membership</li>
<li>Share membership information with clients</li>
<li>Generate lobby events on the micro service message bus for subscribers to listen to.</li>
<li>Notify clients of join and leave events</li>
<li>Generate unique lobbyIds</li>
<li>Provide administration and chair functions.</li>
</ul>
<h2 id="voice-video-text-chat">VOICE/VIDEO/TEXT CHAT</h2>
Lobbies provide a means of establishing video and voice chat between groups and users by making their callerIds (userIds) available to other members of the lobby. The call service uses these callerIds to broker connections between peers so that they can execute the session initiation protocols required for real-time video chat. It is then the call service's responsibility to resolve which client device to forward the offer to.Text chat is different to video and voice chat because conversation history must be preserved and unlike video chat, it is unlikely to require a dedicate device. There are two use cases:
<ol>
<li>Communal text chat - everyone in the lobby can see the conversation</li>
<li>One-on-one chat - a user wishes to have a private chat with one of the other lobby members.</li>
</ol>
It is likely that a separate service will be used to manage the lobby's communal chat history and to broadcast text messages to lobby members. Any client can render a view of chat history and add new messages. This doesn't require an offer/answer signalling mechanism; all clients participate. One-on-one chat may still be handled by a separate chat service, but will use offer/answer semantics via the call service to establish communication.
<h2 id="architecture">ARCHITECTURE</h2>
<h3 id="overview">OVERVIEW</h3>
A lobby server is responsible for running the core business logic and for maintaining persistent web socket connections with clients or a separate presence server. The number of server instances can be scaled up and down to meet demand. Clients within the same layout context could therefore be connected to different lobby servers and their connections may be lost if servers are taken offline, requiring the client to reconnect.A load balancer will distribute web socket connections to different server instances to spread the load and then use the publish/subscribe machinery of RabbitMQ, Redis or ZeroMQ to route messages between the server instances. Each server will maintain a list of connected clients grouped by lobbyId and will use routing keys to keep the number of messages between servers to a minimum.
<h3 id="consistency-state">CONSISTENCY & STATE</h3>
Lobby membership and connectivity is shared between lobby server instances without the need for a database. Lobby server instances communicate with each other to keep themselves up-to-date using message queues and routing exchanges. A publisher/subscriber model is used in conjunction with routing keys and RPCs to send lobby events to the right server instances.Changes to lobby membership are published as messages and routed to other server instances. Messages destined for the members of a lobby can be filtered using routing keys. This causes them to be routed to only those server instances responsible for managing connections to other members of the lobby. Servers that manage client connections for a given lobby can subscribe to the message queue using the lobby's routing key. When combined with a limit on lobby occupancy, the number of server-to-server messages can be kept to a minimum, permitting good scalability.
<h3 id="alternatives">ALTERNATIVES</h3>
An alternative scheme is to use a database to manage lobby membership and allow new servers to be provisioned in the event of scaling or failover, but this also requires a garbage collection service to ensure old lobbies and users are removed from the database on client disconnect or server failure. The benefit of using websocket / persistent connections is that garbage collection is implicit and so the record of lobby membership cannot become out of date with respect to the list of server connections.
<h2>Collaborators</h2>
<ul>
<li>Logging service</li>
<li>Session service</li>
<li>User Identity service</li>
<li>Call service</li>
<li>Layout service</li>
</ul>
<h2>Verbs</h2>
<strong>AllocLobbyId</strong>(ssoToken)<em>Params</em>:ssoToken: session access token<em>Returns</em>:Unique lobbyId<em>Description</em>:Generates a unique, human readable lobbyId. <strong>Join</strong>(ssoToken, lobbyId)<em>Params</em>:ssoToken: session access tokenlobbyId: identifier or name of the lobby to join<em>Returns</em>:members: a list of lobby members<em>Description</em>:Adds the user specified by the session access token to the specified lobby and broadcasts a ‘joined’ notification event to each connected client. This method has no effect if the user is already a member of the specified lobby. A list of lobby members is returned in the response. If the lobby is full, the user will not be added to the lobby and an error response is returned. <strong>Leave</strong>(ssoToken, lobbyId)<em>Params</em>:ssoToken: session access tokenlobbyId: identifier or name of the lobby to leave<em>Description</em>Removes the user identified by the session access token from the specified lobby and broadcasts a ‘left’ notification event to each connected client. This method has no effect if user has already left the lobby. <strong>BroadcastMessage</strong>(ssoToken, lobbyId, message)<em>Params</em>:ssoToken: session access tokenlobbyId: identifier or name of the lobby to broadcast tomessage: application defined message payload<em>Description</em>:Broadcast an application-defined message to all connected clients. This is intended to allow application specific functionality to be layered on top of the lobby whilst keeping the lobby service as simple as possible. <strong>Close</strong>(ssoToken, lobbyId)<em>Params</em>:ssoToken: session access tokenlobbyId: identifier or name of the lobby to leave<em>Description</em>:Close a lobby and notify all connected clients by sending a ‘disconnect’ message. This is an administrative function intended for use by a chair or system administrator. <strong>Kick</strong>(ssoToken, lobbyId, userId)<em>Params</em>:ssoToken: session access tokenlobbyId: identifier or name of the lobby to leaveuserId: the user to evict<em>Description</em>:Evicts a user from a lobby and sends a ‘disconnect’ message to all clients of the user. Also notifies remaining clients by sending a ‘leave’ notification. This is an administrative function used for moderation purposes and is intended for system administrators. <strong>GetMembers</strong>(ssoToken, lobbyId)<em>Params</em>:ssoToken: session access tokenlobbyId: identifier or name of the lobby<em>Description</em>Retrieve a list of lobby members. <strong>Events</strong><strong>Disconnect</strong>An administrator or chair has disconnected the client’s connection with the lobby.<strong>Joined</strong>A user has joined the lobby.<em>Payload</em>: userId indicating the user that has joined the lobby.<strong>Left</strong>A user has left the lobby.<em>Payload</em>: userId indicating the user that has left the lobby, either explicitly, as a result of a loss of connection with the lobby service or as a result of being evicted from the lobby by a chair or administrator.<strong>Message</strong>A user has broadcast a message<em>Payload</em>: (userId, message) indicating the user that issues the message and the message itself.<strong>Error</strong>This event is received in response to a failed connection attempt such as an authentication failure or the lobby being full.<em>Payload</em>: (error code) indicating type of error |
Recent Posts