mirror of
https://github.com/livekit/livekit.git
synced 2026-08-22 16:29:52 +00:00
rework multi-node routing, cleaner design for reconnection
This commit is contained in:
@@ -23,7 +23,7 @@ type MessageSource interface {
|
||||
ReadChan() <-chan proto.Message
|
||||
}
|
||||
|
||||
type ParticipantCallback func(roomId, participantId, participantName string, requestSource MessageSource, responseSink MessageSink)
|
||||
type ParticipantCallback func(roomName, identity string, requestSource MessageSource, responseSink MessageSink)
|
||||
|
||||
// Router allows multiple nodes to coordinate the participant session
|
||||
//counterfeiter:generate . Router
|
||||
@@ -36,11 +36,8 @@ type Router interface {
|
||||
GetNode(nodeId string) (*livekit.Node, error)
|
||||
ListNodes() ([]*livekit.Node, error)
|
||||
|
||||
// functions for websocket handler
|
||||
GetRequestSink(participantId string) (MessageSink, error)
|
||||
GetResponseSource(participantId string) (MessageSource, error)
|
||||
// participant signal connection is ready to start
|
||||
StartParticipantSignal(roomName, participantId, participantName string) error
|
||||
StartParticipantSignal(roomName, identity string) (reqSink MessageSink, resSource MessageSource, err error)
|
||||
|
||||
// when a new participant's RTC connection is ready to start
|
||||
OnNewParticipantRTC(callback ParticipantCallback)
|
||||
|
||||
+16
-20
@@ -59,30 +59,26 @@ func (r *LocalRouter) ListNodes() ([]*livekit.Node, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *LocalRouter) StartParticipantSignal(roomName, participantId, participantName string) error {
|
||||
func (r *LocalRouter) StartParticipantSignal(roomName, identity string) (reqSink MessageSink, resSource MessageSource, err error) {
|
||||
// treat it as a new participant connecting
|
||||
if r.onNewParticipant == nil {
|
||||
return ErrHandlerNotDefined
|
||||
return nil, nil, ErrHandlerNotDefined
|
||||
}
|
||||
|
||||
// index channels by roomName | identity
|
||||
key := participantKey(roomName, identity)
|
||||
reqChan := r.getOrCreateMessageChannel(r.requestChannels, key)
|
||||
resChan := r.getOrCreateMessageChannel(r.responseChannels, key)
|
||||
|
||||
r.onNewParticipant(
|
||||
roomName,
|
||||
participantId,
|
||||
participantName,
|
||||
identity,
|
||||
// request source
|
||||
r.getOrCreateMessageChannel(r.requestChannels, participantId),
|
||||
reqChan,
|
||||
// response sink
|
||||
r.getOrCreateMessageChannel(r.responseChannels, participantId),
|
||||
resChan,
|
||||
)
|
||||
return nil
|
||||
}
|
||||
|
||||
// for a local router, sink and source are pointing to the same spot
|
||||
func (r *LocalRouter) GetRequestSink(participantId string) (MessageSink, error) {
|
||||
return r.getOrCreateMessageChannel(r.requestChannels, participantId), nil
|
||||
}
|
||||
|
||||
func (r *LocalRouter) GetResponseSource(participantId string) (MessageSource, error) {
|
||||
return r.getOrCreateMessageChannel(r.responseChannels, participantId), nil
|
||||
return reqChan, resChan, nil
|
||||
}
|
||||
|
||||
func (r *LocalRouter) OnNewParticipantRTC(callback ParticipantCallback) {
|
||||
@@ -106,10 +102,10 @@ func (r *LocalRouter) statsWorker() {
|
||||
}
|
||||
}
|
||||
|
||||
func (r *LocalRouter) getOrCreateMessageChannel(target map[string]*MessageChannel, participantId string) *MessageChannel {
|
||||
func (r *LocalRouter) getOrCreateMessageChannel(target map[string]*MessageChannel, key string) *MessageChannel {
|
||||
r.lock.Lock()
|
||||
defer r.lock.Unlock()
|
||||
mc := target[participantId]
|
||||
mc := target[key]
|
||||
|
||||
if mc != nil {
|
||||
return mc
|
||||
@@ -118,10 +114,10 @@ func (r *LocalRouter) getOrCreateMessageChannel(target map[string]*MessageChanne
|
||||
mc = NewMessageChannel()
|
||||
mc.OnClose(func() {
|
||||
r.lock.Lock()
|
||||
delete(target, participantId)
|
||||
delete(target, key)
|
||||
r.lock.Unlock()
|
||||
})
|
||||
target[participantId] = mc
|
||||
target[key] = mc
|
||||
|
||||
return mc
|
||||
}
|
||||
|
||||
@@ -24,12 +24,12 @@ func TestMessageChannel_WriteMessageClosed(t *testing.T) {
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for i := 0; i < 100; i++ {
|
||||
m.WriteMessage(&livekit.RouterMessage{})
|
||||
m.WriteMessage(&livekit.RTCNodeMessage{})
|
||||
}
|
||||
}()
|
||||
m.WriteMessage(&livekit.RouterMessage{})
|
||||
m.WriteMessage(&livekit.RTCNodeMessage{})
|
||||
m.Close()
|
||||
m.WriteMessage(&livekit.RouterMessage{})
|
||||
m.WriteMessage(&livekit.RTCNodeMessage{})
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
+85
-30
@@ -21,38 +21,57 @@ const (
|
||||
var redisCtx = context.Background()
|
||||
|
||||
// location of the participant's RTC connection, hash
|
||||
func participantRTCKey(participantId string) string {
|
||||
return "participant_rtc:" + participantId
|
||||
func participantRTCKey(participantKey string) string {
|
||||
return "participant_rtc:" + participantKey
|
||||
}
|
||||
|
||||
// location of the participant's Signal connection, hash
|
||||
func participantSignalKey(participantId string) string {
|
||||
return "participant_signal:" + participantId
|
||||
func participantSignalKey(connectionId string) string {
|
||||
return "participant_signal:" + connectionId
|
||||
}
|
||||
|
||||
func nodeChannel(nodeId string) string {
|
||||
return "node_channel:" + nodeId
|
||||
func rtcNodeChannel(nodeId string) string {
|
||||
return "rtc_channel:" + nodeId
|
||||
}
|
||||
|
||||
func publishRouterMessage(rc *redis.Client, nodeId string, participantId string, msg proto.Message) error {
|
||||
rm := &livekit.RouterMessage{
|
||||
ParticipantId: participantId,
|
||||
func signalNodeChannel(nodeId string) string {
|
||||
return "signal_channel:" + nodeId
|
||||
}
|
||||
|
||||
func publishRTCMessage(rc *redis.Client, nodeId string, participantKey string, msg proto.Message) error {
|
||||
rm := &livekit.RTCNodeMessage{
|
||||
ParticipantKey: participantKey,
|
||||
}
|
||||
switch o := msg.(type) {
|
||||
case *livekit.StartSession:
|
||||
rm.Message = &livekit.RouterMessage_StartSession{
|
||||
rm.Message = &livekit.RTCNodeMessage_StartSession{
|
||||
StartSession: o,
|
||||
}
|
||||
case *livekit.SignalRequest:
|
||||
rm.Message = &livekit.RouterMessage_Request{
|
||||
rm.Message = &livekit.RTCNodeMessage_Request{
|
||||
Request: o,
|
||||
}
|
||||
default:
|
||||
return errInvalidRouterMessage
|
||||
}
|
||||
data, err := proto.Marshal(rm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return rc.Publish(redisCtx, rtcNodeChannel(nodeId), data).Err()
|
||||
}
|
||||
|
||||
func publishSignalMessage(rc *redis.Client, nodeId string, connectionId string, msg proto.Message) error {
|
||||
rm := &livekit.SignalNodeMessage{
|
||||
ConnectionId: connectionId,
|
||||
}
|
||||
switch o := msg.(type) {
|
||||
case *livekit.SignalResponse:
|
||||
rm.Message = &livekit.RouterMessage_Response{
|
||||
rm.Message = &livekit.SignalNodeMessage_Response{
|
||||
Response: o,
|
||||
}
|
||||
case *livekit.EndSession:
|
||||
rm.Message = &livekit.RouterMessage_EndSession{
|
||||
rm.Message = &livekit.SignalNodeMessage_EndSession{
|
||||
EndSession: o,
|
||||
}
|
||||
default:
|
||||
@@ -62,42 +81,78 @@ func publishRouterMessage(rc *redis.Client, nodeId string, participantId string,
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return rc.Publish(redisCtx, nodeChannel(nodeId), data).Err()
|
||||
return rc.Publish(redisCtx, signalNodeChannel(nodeId), data).Err()
|
||||
}
|
||||
|
||||
type RedisSink struct {
|
||||
rc *redis.Client
|
||||
nodeId string
|
||||
participantId string
|
||||
isClosed utils.AtomicFlag
|
||||
onClose func()
|
||||
type RTCNodeSink struct {
|
||||
rc *redis.Client
|
||||
nodeId string
|
||||
participantKey string
|
||||
isClosed utils.AtomicFlag
|
||||
onClose func()
|
||||
}
|
||||
|
||||
func NewRedisSink(rc *redis.Client, nodeId, participantId string) *RedisSink {
|
||||
return &RedisSink{
|
||||
rc: rc,
|
||||
nodeId: nodeId,
|
||||
participantId: participantId,
|
||||
func NewRTCNodeSink(rc *redis.Client, nodeId, participantKey string) *RTCNodeSink {
|
||||
return &RTCNodeSink{
|
||||
rc: rc,
|
||||
nodeId: nodeId,
|
||||
participantKey: participantKey,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *RedisSink) WriteMessage(msg proto.Message) error {
|
||||
func (s *RTCNodeSink) WriteMessage(msg proto.Message) error {
|
||||
if s.isClosed.Get() {
|
||||
return ErrChannelClosed
|
||||
}
|
||||
return publishRouterMessage(s.rc, s.nodeId, s.participantId, msg)
|
||||
return publishRTCMessage(s.rc, s.nodeId, s.participantKey, msg)
|
||||
}
|
||||
|
||||
func (s *RedisSink) Close() {
|
||||
func (s *RTCNodeSink) Close() {
|
||||
if !s.isClosed.TrySet(true) {
|
||||
return
|
||||
}
|
||||
publishRouterMessage(s.rc, s.nodeId, s.participantId, &livekit.EndSession{})
|
||||
if s.onClose != nil {
|
||||
s.onClose()
|
||||
}
|
||||
}
|
||||
|
||||
func (s *RedisSink) OnClose(f func()) {
|
||||
func (s *RTCNodeSink) OnClose(f func()) {
|
||||
s.onClose = f
|
||||
}
|
||||
|
||||
type SignalNodeSink struct {
|
||||
rc *redis.Client
|
||||
nodeId string
|
||||
connectionId string
|
||||
isClosed utils.AtomicFlag
|
||||
onClose func()
|
||||
}
|
||||
|
||||
func NewSignalNodeSink(rc *redis.Client, nodeId, connectionId string) *SignalNodeSink {
|
||||
return &SignalNodeSink{
|
||||
rc: rc,
|
||||
nodeId: nodeId,
|
||||
connectionId: connectionId,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SignalNodeSink) WriteMessage(msg proto.Message) error {
|
||||
if s.isClosed.Get() {
|
||||
return ErrChannelClosed
|
||||
}
|
||||
return publishSignalMessage(s.rc, s.nodeId, s.connectionId, msg)
|
||||
}
|
||||
|
||||
func (s *SignalNodeSink) Close() {
|
||||
if !s.isClosed.TrySet(true) {
|
||||
return
|
||||
}
|
||||
publishSignalMessage(s.rc, s.nodeId, s.connectionId, &livekit.EndSession{})
|
||||
if s.onClose != nil {
|
||||
s.onClose()
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SignalNodeSink) OnClose(f func()) {
|
||||
s.onClose = f
|
||||
}
|
||||
|
||||
+141
-90
@@ -30,8 +30,11 @@ type RedisRouter struct {
|
||||
ctx context.Context
|
||||
once sync.Once
|
||||
|
||||
redisSinks map[string]*RedisSink
|
||||
cancel func()
|
||||
// map of participantKey => RTCNodeSink
|
||||
rtcSinks map[string]*RTCNodeSink
|
||||
// map of connectionId => SignalNodeSink
|
||||
signalSinks map[string]*SignalNodeSink
|
||||
cancel func()
|
||||
}
|
||||
|
||||
func NewRedisRouter(currentNode LocalNode, rc *redis.Client) *RedisRouter {
|
||||
@@ -39,7 +42,8 @@ func NewRedisRouter(currentNode LocalNode, rc *redis.Client) *RedisRouter {
|
||||
LocalRouter: *NewLocalRouter(currentNode),
|
||||
rc: rc,
|
||||
once: sync.Once{},
|
||||
redisSinks: make(map[string]*RedisSink),
|
||||
rtcSinks: make(map[string]*RTCNodeSink),
|
||||
signalSinks: make(map[string]*SignalNodeSink),
|
||||
}
|
||||
rr.ctx, rr.cancel = context.WithCancel(context.Background())
|
||||
rr.cr = utils.NewCachedRedis(rr.ctx, rr.rc)
|
||||
@@ -110,55 +114,44 @@ func (r *RedisRouter) ListNodes() ([]*livekit.Node, error) {
|
||||
return nodes, nil
|
||||
}
|
||||
|
||||
// for a local router, sink and source are pointing to the same spot
|
||||
func (r *RedisRouter) GetRequestSink(participantId string) (MessageSink, error) {
|
||||
// request should go to RTC node
|
||||
rtcNode, err := r.getParticipantRTCNode(participantId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sink := r.getOrCreateRedisSink(rtcNode, participantId)
|
||||
return sink, nil
|
||||
}
|
||||
|
||||
func (r *RedisRouter) GetResponseSource(participantId string) (MessageSource, error) {
|
||||
// a message channel that we'll send data into
|
||||
source := r.getOrCreateMessageChannel(r.responseChannels, participantId)
|
||||
return source, nil
|
||||
}
|
||||
|
||||
// signal connection sets up paths to the RTC node, and starts to route messages to that message queue
|
||||
func (r *RedisRouter) StartParticipantSignal(roomName, participantId, participantName string) error {
|
||||
func (r *RedisRouter) StartParticipantSignal(roomName, identity string) (reqSink MessageSink, resSource MessageSource, err error) {
|
||||
// find the node where the room is hosted at
|
||||
rtcNode, err := r.GetNodeForRoom(roomName)
|
||||
if err != nil {
|
||||
return err
|
||||
return
|
||||
}
|
||||
|
||||
// create a new connection id
|
||||
connectionId := utils.NewGuid("CO_")
|
||||
pKey := participantKey(roomName, identity)
|
||||
|
||||
// map signal & rtc nodes
|
||||
if err = r.setParticipantSignalNode(participantId, r.currentNode.Id); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := r.setParticipantRTCNode(participantId, rtcNode); err != nil {
|
||||
return err
|
||||
if err = r.setParticipantSignalNode(connectionId, r.currentNode.Id); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
sink, err := r.GetRequestSink(participantId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sink := r.getOrCreateRTCSink(rtcNode, pKey)
|
||||
|
||||
// sends a message to start session
|
||||
return sink.WriteMessage(&livekit.StartSession{
|
||||
RoomName: roomName,
|
||||
ParticipantName: participantName,
|
||||
err = sink.WriteMessage(&livekit.StartSession{
|
||||
RoomName: roomName,
|
||||
Identity: identity,
|
||||
// connection id is to allow the RTC node to identify where to route the message back to
|
||||
ConnectionId: connectionId,
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// index by connectionId, since there may be multiple connections for the participant
|
||||
resChan := r.getOrCreateMessageChannel(r.responseChannels, connectionId)
|
||||
return sink, resChan, nil
|
||||
}
|
||||
|
||||
func (r *RedisRouter) startParticipantRTC(roomName, participantId, participantName string) error {
|
||||
func (r *RedisRouter) startParticipantRTC(ss *livekit.StartSession, participantKey string) error {
|
||||
// find the node where the room is hosted at
|
||||
rtcNode, err := r.GetNodeForRoom(roomName)
|
||||
rtcNode, err := r.GetNodeForRoom(ss.RoomName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -169,8 +162,12 @@ func (r *RedisRouter) startParticipantRTC(roomName, participantId, participantNa
|
||||
return ErrIncorrectRTCNode
|
||||
}
|
||||
|
||||
if err := r.setParticipantRTCNode(participantKey, rtcNode); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// find signal node to send responses back
|
||||
signalNode, err := r.getParticipantSignalNode(participantId)
|
||||
signalNode, err := r.getParticipantSignalNode(ss.ConnectionId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -180,12 +177,12 @@ func (r *RedisRouter) startParticipantRTC(roomName, participantId, participantNa
|
||||
return ErrHandlerNotDefined
|
||||
}
|
||||
|
||||
resSink := r.getOrCreateRedisSink(signalNode, participantId)
|
||||
reqChan := r.getOrCreateMessageChannel(r.requestChannels, participantKey)
|
||||
resSink := r.getOrCreateSignalSink(signalNode, ss.ConnectionId)
|
||||
r.onNewParticipant(
|
||||
roomName,
|
||||
participantId,
|
||||
participantName,
|
||||
r.getOrCreateMessageChannel(r.requestChannels, participantId),
|
||||
ss.RoomName,
|
||||
ss.Identity,
|
||||
reqChan,
|
||||
resSink,
|
||||
)
|
||||
return nil
|
||||
@@ -194,7 +191,8 @@ func (r *RedisRouter) startParticipantRTC(roomName, participantId, participantNa
|
||||
func (r *RedisRouter) Start() error {
|
||||
r.once.Do(func() {
|
||||
go r.statsWorker()
|
||||
go r.subscribeWorker()
|
||||
go r.rtcWorker()
|
||||
go r.signalWorker()
|
||||
})
|
||||
return nil
|
||||
}
|
||||
@@ -203,48 +201,67 @@ func (r *RedisRouter) Stop() {
|
||||
r.cancel()
|
||||
}
|
||||
|
||||
func (r *RedisRouter) setParticipantRTCNode(participantId, nodeId string) error {
|
||||
r.cr.Expire(participantRTCKey(participantId))
|
||||
err := r.rc.Set(r.ctx, participantRTCKey(participantId), nodeId, participantMappingTTL).Err()
|
||||
func (r *RedisRouter) setParticipantRTCNode(participantKey, nodeId string) error {
|
||||
r.cr.Expire(participantRTCKey(participantKey))
|
||||
err := r.rc.Set(r.ctx, participantRTCKey(participantKey), nodeId, participantMappingTTL).Err()
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "could not set rtc node")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *RedisRouter) setParticipantSignalNode(participantId, nodeId string) error {
|
||||
r.cr.Expire(participantSignalKey(participantId))
|
||||
if err := r.rc.Set(r.ctx, participantSignalKey(participantId), nodeId, participantMappingTTL).Err(); err != nil {
|
||||
func (r *RedisRouter) setParticipantSignalNode(connectionId, nodeId string) error {
|
||||
r.cr.Expire(participantSignalKey(connectionId))
|
||||
if err := r.rc.Set(r.ctx, participantSignalKey(connectionId), nodeId, participantMappingTTL).Err(); err != nil {
|
||||
return errors.Wrap(err, "could not set signal node")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *RedisRouter) getOrCreateRedisSink(nodeId string, participantId string) *RedisSink {
|
||||
func (r *RedisRouter) getOrCreateRTCSink(nodeId string, participantKey string) *RTCNodeSink {
|
||||
r.lock.Lock()
|
||||
defer r.lock.Unlock()
|
||||
sink := r.redisSinks[participantId]
|
||||
sink := r.rtcSinks[participantKey]
|
||||
|
||||
if sink != nil {
|
||||
return sink
|
||||
}
|
||||
|
||||
sink = NewRedisSink(r.rc, nodeId, participantId)
|
||||
sink = NewRTCNodeSink(r.rc, nodeId, participantKey)
|
||||
sink.OnClose(func() {
|
||||
r.lock.Lock()
|
||||
delete(r.redisSinks, participantId)
|
||||
delete(r.rtcSinks, participantKey)
|
||||
r.lock.Unlock()
|
||||
})
|
||||
r.redisSinks[participantId] = sink
|
||||
r.rtcSinks[participantKey] = sink
|
||||
return sink
|
||||
}
|
||||
|
||||
func (r *RedisRouter) getParticipantRTCNode(participantId string) (string, error) {
|
||||
return r.cr.CachedGet(participantRTCKey(participantId))
|
||||
func (r *RedisRouter) getOrCreateSignalSink(nodeId string, connectionId string) *SignalNodeSink {
|
||||
r.lock.Lock()
|
||||
defer r.lock.Unlock()
|
||||
sink := r.signalSinks[connectionId]
|
||||
|
||||
if sink != nil {
|
||||
return sink
|
||||
}
|
||||
|
||||
sink = NewSignalNodeSink(r.rc, nodeId, connectionId)
|
||||
sink.OnClose(func() {
|
||||
r.lock.Lock()
|
||||
delete(r.signalSinks, connectionId)
|
||||
r.lock.Unlock()
|
||||
})
|
||||
r.signalSinks[connectionId] = sink
|
||||
return sink
|
||||
}
|
||||
|
||||
func (r *RedisRouter) getParticipantSignalNode(participantId string) (nodeId string, err error) {
|
||||
return r.cr.CachedGet(participantSignalKey(participantId))
|
||||
func (r *RedisRouter) getParticipantRTCNode(participantKey string) (string, error) {
|
||||
return r.cr.CachedGet(participantRTCKey(participantKey))
|
||||
}
|
||||
|
||||
func (r *RedisRouter) getParticipantSignalNode(connectionId string) (nodeId string, err error) {
|
||||
return r.cr.CachedGet(participantSignalKey(connectionId))
|
||||
}
|
||||
|
||||
// update node stats and cleanup
|
||||
@@ -259,13 +276,13 @@ func (r *RedisRouter) statsWorker() {
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RedisRouter) subscribeWorker() {
|
||||
sub := r.rc.Subscribe(redisCtx, nodeChannel(r.currentNode.Id))
|
||||
|
||||
// worker that consumes signal channel and processes
|
||||
func (r *RedisRouter) signalWorker() {
|
||||
sub := r.rc.Subscribe(redisCtx, signalNodeChannel(r.currentNode.Id))
|
||||
defer func() {
|
||||
logger.Debugw("finishing redis subscribeWorker", "node", r.currentNode.Id)
|
||||
logger.Debugw("finishing redis signalWorker", "node", r.currentNode.Id)
|
||||
}()
|
||||
logger.Debugw("starting redis subscribeWorker", "node", r.currentNode.Id)
|
||||
logger.Debugw("starting redis signalWorker", "node", r.currentNode.Id)
|
||||
for r.ctx.Err() == nil {
|
||||
obj, err := sub.Receive(r.ctx)
|
||||
if err != nil {
|
||||
@@ -283,42 +300,24 @@ func (r *RedisRouter) subscribeWorker() {
|
||||
continue
|
||||
}
|
||||
|
||||
rm := livekit.RouterMessage{}
|
||||
rm := livekit.SignalNodeMessage{}
|
||||
err = proto.Unmarshal([]byte(msg.Payload), &rm)
|
||||
pId := rm.ParticipantId
|
||||
connectionId := rm.ConnectionId
|
||||
|
||||
switch rmb := rm.Message.(type) {
|
||||
case *livekit.RouterMessage_StartSession:
|
||||
logger.Infow("received router startSession", "node", r.currentNode.Id,
|
||||
"participant", pId)
|
||||
// RTC session should start on this node
|
||||
err = r.startParticipantRTC(rmb.StartSession.RoomName, pId, rmb.StartSession.ParticipantName)
|
||||
if err != nil {
|
||||
logger.Errorw("could not start participant", "error", err)
|
||||
}
|
||||
|
||||
case *livekit.RouterMessage_Request:
|
||||
// in the event the current node is an RTC node, push to request channels
|
||||
reqSink := r.getOrCreateMessageChannel(r.requestChannels, pId)
|
||||
err = reqSink.WriteMessage(rmb.Request)
|
||||
if err != nil {
|
||||
logger.Errorw("could not write to request channel",
|
||||
"participant", pId,
|
||||
"error", err)
|
||||
}
|
||||
|
||||
case *livekit.RouterMessage_Response:
|
||||
case *livekit.SignalNodeMessage_Response:
|
||||
// in the event the current node is an Signal node, push to response channels
|
||||
resSink := r.getOrCreateMessageChannel(r.responseChannels, pId)
|
||||
resSink := r.getOrCreateMessageChannel(r.responseChannels, connectionId)
|
||||
err = resSink.WriteMessage(rmb.Response)
|
||||
if err != nil {
|
||||
logger.Errorw("could not write to response channel",
|
||||
"participant", pId,
|
||||
"connectionId", connectionId,
|
||||
"error", err)
|
||||
}
|
||||
|
||||
case *livekit.RouterMessage_EndSession:
|
||||
signalNode, err := r.getParticipantRTCNode(pId)
|
||||
case *livekit.SignalNodeMessage_EndSession:
|
||||
signalNode, err := r.getParticipantSignalNode(connectionId)
|
||||
if err != nil {
|
||||
logger.Errorw("could not get participant RTC node",
|
||||
"error", err)
|
||||
@@ -326,9 +325,61 @@ func (r *RedisRouter) subscribeWorker() {
|
||||
}
|
||||
// EndSession can only be initiated on an RTC node, is handled on the signal node
|
||||
if signalNode == r.currentNode.Id {
|
||||
resSink := r.getOrCreateMessageChannel(r.responseChannels, pId)
|
||||
resSink := r.getOrCreateMessageChannel(r.responseChannels, connectionId)
|
||||
resSink.Close()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// worker that consumes RTC channel and processes
|
||||
func (r *RedisRouter) rtcWorker() {
|
||||
sub := r.rc.Subscribe(redisCtx, rtcNodeChannel(r.currentNode.Id))
|
||||
|
||||
defer func() {
|
||||
logger.Debugw("finishing redis rtcWorker", "node", r.currentNode.Id)
|
||||
}()
|
||||
logger.Debugw("starting redis rtcWorker", "node", r.currentNode.Id)
|
||||
for r.ctx.Err() == nil {
|
||||
obj, err := sub.Receive(r.ctx)
|
||||
if err != nil {
|
||||
logger.Warnw("error receiving redis message", "error", err)
|
||||
// TODO: retry? ignore? at a minimum need to sleep here to retry
|
||||
time.Sleep(time.Second)
|
||||
continue
|
||||
}
|
||||
if obj == nil {
|
||||
return
|
||||
}
|
||||
|
||||
msg, ok := obj.(*redis.Message)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
rm := livekit.RTCNodeMessage{}
|
||||
err = proto.Unmarshal([]byte(msg.Payload), &rm)
|
||||
pKey := rm.ParticipantKey
|
||||
|
||||
switch rmb := rm.Message.(type) {
|
||||
case *livekit.RTCNodeMessage_StartSession:
|
||||
logger.Debugw("received router startSession", "node", r.currentNode.Id,
|
||||
"participant", pKey)
|
||||
// RTC session should start on this node
|
||||
err = r.startParticipantRTC(rmb.StartSession, pKey)
|
||||
if err != nil {
|
||||
logger.Errorw("could not start participant", "error", err)
|
||||
}
|
||||
|
||||
case *livekit.RTCNodeMessage_Request:
|
||||
// in the event the current node is an RTC node, push to request channels
|
||||
reqSink := r.getOrCreateMessageChannel(r.requestChannels, pKey)
|
||||
err = reqSink.WriteMessage(rmb.Request)
|
||||
if err != nil {
|
||||
logger.Errorw("could not write to request channel",
|
||||
"participant", pKey,
|
||||
"error", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,32 +46,6 @@ type FakeRouter struct {
|
||||
result1 string
|
||||
result2 error
|
||||
}
|
||||
GetRequestSinkStub func(string) (routing.MessageSink, error)
|
||||
getRequestSinkMutex sync.RWMutex
|
||||
getRequestSinkArgsForCall []struct {
|
||||
arg1 string
|
||||
}
|
||||
getRequestSinkReturns struct {
|
||||
result1 routing.MessageSink
|
||||
result2 error
|
||||
}
|
||||
getRequestSinkReturnsOnCall map[int]struct {
|
||||
result1 routing.MessageSink
|
||||
result2 error
|
||||
}
|
||||
GetResponseSourceStub func(string) (routing.MessageSource, error)
|
||||
getResponseSourceMutex sync.RWMutex
|
||||
getResponseSourceArgsForCall []struct {
|
||||
arg1 string
|
||||
}
|
||||
getResponseSourceReturns struct {
|
||||
result1 routing.MessageSource
|
||||
result2 error
|
||||
}
|
||||
getResponseSourceReturnsOnCall map[int]struct {
|
||||
result1 routing.MessageSource
|
||||
result2 error
|
||||
}
|
||||
ListNodesStub func() ([]*livekit.Node, error)
|
||||
listNodesMutex sync.RWMutex
|
||||
listNodesArgsForCall []struct {
|
||||
@@ -121,18 +95,21 @@ type FakeRouter struct {
|
||||
startReturnsOnCall map[int]struct {
|
||||
result1 error
|
||||
}
|
||||
StartParticipantSignalStub func(string, string, string) error
|
||||
StartParticipantSignalStub func(string, string) (routing.MessageSink, routing.MessageSource, error)
|
||||
startParticipantSignalMutex sync.RWMutex
|
||||
startParticipantSignalArgsForCall []struct {
|
||||
arg1 string
|
||||
arg2 string
|
||||
arg3 string
|
||||
}
|
||||
startParticipantSignalReturns struct {
|
||||
result1 error
|
||||
result1 routing.MessageSink
|
||||
result2 routing.MessageSource
|
||||
result3 error
|
||||
}
|
||||
startParticipantSignalReturnsOnCall map[int]struct {
|
||||
result1 error
|
||||
result1 routing.MessageSink
|
||||
result2 routing.MessageSource
|
||||
result3 error
|
||||
}
|
||||
StopStub func()
|
||||
stopMutex sync.RWMutex
|
||||
@@ -341,134 +318,6 @@ func (fake *FakeRouter) GetNodeForRoomReturnsOnCall(i int, result1 string, resul
|
||||
}{result1, result2}
|
||||
}
|
||||
|
||||
func (fake *FakeRouter) GetRequestSink(arg1 string) (routing.MessageSink, error) {
|
||||
fake.getRequestSinkMutex.Lock()
|
||||
ret, specificReturn := fake.getRequestSinkReturnsOnCall[len(fake.getRequestSinkArgsForCall)]
|
||||
fake.getRequestSinkArgsForCall = append(fake.getRequestSinkArgsForCall, struct {
|
||||
arg1 string
|
||||
}{arg1})
|
||||
stub := fake.GetRequestSinkStub
|
||||
fakeReturns := fake.getRequestSinkReturns
|
||||
fake.recordInvocation("GetRequestSink", []interface{}{arg1})
|
||||
fake.getRequestSinkMutex.Unlock()
|
||||
if stub != nil {
|
||||
return stub(arg1)
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1, ret.result2
|
||||
}
|
||||
return fakeReturns.result1, fakeReturns.result2
|
||||
}
|
||||
|
||||
func (fake *FakeRouter) GetRequestSinkCallCount() int {
|
||||
fake.getRequestSinkMutex.RLock()
|
||||
defer fake.getRequestSinkMutex.RUnlock()
|
||||
return len(fake.getRequestSinkArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeRouter) GetRequestSinkCalls(stub func(string) (routing.MessageSink, error)) {
|
||||
fake.getRequestSinkMutex.Lock()
|
||||
defer fake.getRequestSinkMutex.Unlock()
|
||||
fake.GetRequestSinkStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeRouter) GetRequestSinkArgsForCall(i int) string {
|
||||
fake.getRequestSinkMutex.RLock()
|
||||
defer fake.getRequestSinkMutex.RUnlock()
|
||||
argsForCall := fake.getRequestSinkArgsForCall[i]
|
||||
return argsForCall.arg1
|
||||
}
|
||||
|
||||
func (fake *FakeRouter) GetRequestSinkReturns(result1 routing.MessageSink, result2 error) {
|
||||
fake.getRequestSinkMutex.Lock()
|
||||
defer fake.getRequestSinkMutex.Unlock()
|
||||
fake.GetRequestSinkStub = nil
|
||||
fake.getRequestSinkReturns = struct {
|
||||
result1 routing.MessageSink
|
||||
result2 error
|
||||
}{result1, result2}
|
||||
}
|
||||
|
||||
func (fake *FakeRouter) GetRequestSinkReturnsOnCall(i int, result1 routing.MessageSink, result2 error) {
|
||||
fake.getRequestSinkMutex.Lock()
|
||||
defer fake.getRequestSinkMutex.Unlock()
|
||||
fake.GetRequestSinkStub = nil
|
||||
if fake.getRequestSinkReturnsOnCall == nil {
|
||||
fake.getRequestSinkReturnsOnCall = make(map[int]struct {
|
||||
result1 routing.MessageSink
|
||||
result2 error
|
||||
})
|
||||
}
|
||||
fake.getRequestSinkReturnsOnCall[i] = struct {
|
||||
result1 routing.MessageSink
|
||||
result2 error
|
||||
}{result1, result2}
|
||||
}
|
||||
|
||||
func (fake *FakeRouter) GetResponseSource(arg1 string) (routing.MessageSource, error) {
|
||||
fake.getResponseSourceMutex.Lock()
|
||||
ret, specificReturn := fake.getResponseSourceReturnsOnCall[len(fake.getResponseSourceArgsForCall)]
|
||||
fake.getResponseSourceArgsForCall = append(fake.getResponseSourceArgsForCall, struct {
|
||||
arg1 string
|
||||
}{arg1})
|
||||
stub := fake.GetResponseSourceStub
|
||||
fakeReturns := fake.getResponseSourceReturns
|
||||
fake.recordInvocation("GetResponseSource", []interface{}{arg1})
|
||||
fake.getResponseSourceMutex.Unlock()
|
||||
if stub != nil {
|
||||
return stub(arg1)
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1, ret.result2
|
||||
}
|
||||
return fakeReturns.result1, fakeReturns.result2
|
||||
}
|
||||
|
||||
func (fake *FakeRouter) GetResponseSourceCallCount() int {
|
||||
fake.getResponseSourceMutex.RLock()
|
||||
defer fake.getResponseSourceMutex.RUnlock()
|
||||
return len(fake.getResponseSourceArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeRouter) GetResponseSourceCalls(stub func(string) (routing.MessageSource, error)) {
|
||||
fake.getResponseSourceMutex.Lock()
|
||||
defer fake.getResponseSourceMutex.Unlock()
|
||||
fake.GetResponseSourceStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeRouter) GetResponseSourceArgsForCall(i int) string {
|
||||
fake.getResponseSourceMutex.RLock()
|
||||
defer fake.getResponseSourceMutex.RUnlock()
|
||||
argsForCall := fake.getResponseSourceArgsForCall[i]
|
||||
return argsForCall.arg1
|
||||
}
|
||||
|
||||
func (fake *FakeRouter) GetResponseSourceReturns(result1 routing.MessageSource, result2 error) {
|
||||
fake.getResponseSourceMutex.Lock()
|
||||
defer fake.getResponseSourceMutex.Unlock()
|
||||
fake.GetResponseSourceStub = nil
|
||||
fake.getResponseSourceReturns = struct {
|
||||
result1 routing.MessageSource
|
||||
result2 error
|
||||
}{result1, result2}
|
||||
}
|
||||
|
||||
func (fake *FakeRouter) GetResponseSourceReturnsOnCall(i int, result1 routing.MessageSource, result2 error) {
|
||||
fake.getResponseSourceMutex.Lock()
|
||||
defer fake.getResponseSourceMutex.Unlock()
|
||||
fake.GetResponseSourceStub = nil
|
||||
if fake.getResponseSourceReturnsOnCall == nil {
|
||||
fake.getResponseSourceReturnsOnCall = make(map[int]struct {
|
||||
result1 routing.MessageSource
|
||||
result2 error
|
||||
})
|
||||
}
|
||||
fake.getResponseSourceReturnsOnCall[i] = struct {
|
||||
result1 routing.MessageSource
|
||||
result2 error
|
||||
}{result1, result2}
|
||||
}
|
||||
|
||||
func (fake *FakeRouter) ListNodes() ([]*livekit.Node, error) {
|
||||
fake.listNodesMutex.Lock()
|
||||
ret, specificReturn := fake.listNodesReturnsOnCall[len(fake.listNodesArgsForCall)]
|
||||
@@ -725,25 +574,24 @@ func (fake *FakeRouter) StartReturnsOnCall(i int, result1 error) {
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeRouter) StartParticipantSignal(arg1 string, arg2 string, arg3 string) error {
|
||||
func (fake *FakeRouter) StartParticipantSignal(arg1 string, arg2 string) (routing.MessageSink, routing.MessageSource, error) {
|
||||
fake.startParticipantSignalMutex.Lock()
|
||||
ret, specificReturn := fake.startParticipantSignalReturnsOnCall[len(fake.startParticipantSignalArgsForCall)]
|
||||
fake.startParticipantSignalArgsForCall = append(fake.startParticipantSignalArgsForCall, struct {
|
||||
arg1 string
|
||||
arg2 string
|
||||
arg3 string
|
||||
}{arg1, arg2, arg3})
|
||||
}{arg1, arg2})
|
||||
stub := fake.StartParticipantSignalStub
|
||||
fakeReturns := fake.startParticipantSignalReturns
|
||||
fake.recordInvocation("StartParticipantSignal", []interface{}{arg1, arg2, arg3})
|
||||
fake.recordInvocation("StartParticipantSignal", []interface{}{arg1, arg2})
|
||||
fake.startParticipantSignalMutex.Unlock()
|
||||
if stub != nil {
|
||||
return stub(arg1, arg2, arg3)
|
||||
return stub(arg1, arg2)
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
return ret.result1, ret.result2, ret.result3
|
||||
}
|
||||
return fakeReturns.result1
|
||||
return fakeReturns.result1, fakeReturns.result2, fakeReturns.result3
|
||||
}
|
||||
|
||||
func (fake *FakeRouter) StartParticipantSignalCallCount() int {
|
||||
@@ -752,40 +600,46 @@ func (fake *FakeRouter) StartParticipantSignalCallCount() int {
|
||||
return len(fake.startParticipantSignalArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeRouter) StartParticipantSignalCalls(stub func(string, string, string) error) {
|
||||
func (fake *FakeRouter) StartParticipantSignalCalls(stub func(string, string) (routing.MessageSink, routing.MessageSource, error)) {
|
||||
fake.startParticipantSignalMutex.Lock()
|
||||
defer fake.startParticipantSignalMutex.Unlock()
|
||||
fake.StartParticipantSignalStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeRouter) StartParticipantSignalArgsForCall(i int) (string, string, string) {
|
||||
func (fake *FakeRouter) StartParticipantSignalArgsForCall(i int) (string, string) {
|
||||
fake.startParticipantSignalMutex.RLock()
|
||||
defer fake.startParticipantSignalMutex.RUnlock()
|
||||
argsForCall := fake.startParticipantSignalArgsForCall[i]
|
||||
return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3
|
||||
return argsForCall.arg1, argsForCall.arg2
|
||||
}
|
||||
|
||||
func (fake *FakeRouter) StartParticipantSignalReturns(result1 error) {
|
||||
func (fake *FakeRouter) StartParticipantSignalReturns(result1 routing.MessageSink, result2 routing.MessageSource, result3 error) {
|
||||
fake.startParticipantSignalMutex.Lock()
|
||||
defer fake.startParticipantSignalMutex.Unlock()
|
||||
fake.StartParticipantSignalStub = nil
|
||||
fake.startParticipantSignalReturns = struct {
|
||||
result1 error
|
||||
}{result1}
|
||||
result1 routing.MessageSink
|
||||
result2 routing.MessageSource
|
||||
result3 error
|
||||
}{result1, result2, result3}
|
||||
}
|
||||
|
||||
func (fake *FakeRouter) StartParticipantSignalReturnsOnCall(i int, result1 error) {
|
||||
func (fake *FakeRouter) StartParticipantSignalReturnsOnCall(i int, result1 routing.MessageSink, result2 routing.MessageSource, result3 error) {
|
||||
fake.startParticipantSignalMutex.Lock()
|
||||
defer fake.startParticipantSignalMutex.Unlock()
|
||||
fake.StartParticipantSignalStub = nil
|
||||
if fake.startParticipantSignalReturnsOnCall == nil {
|
||||
fake.startParticipantSignalReturnsOnCall = make(map[int]struct {
|
||||
result1 error
|
||||
result1 routing.MessageSink
|
||||
result2 routing.MessageSource
|
||||
result3 error
|
||||
})
|
||||
}
|
||||
fake.startParticipantSignalReturnsOnCall[i] = struct {
|
||||
result1 error
|
||||
}{result1}
|
||||
result1 routing.MessageSink
|
||||
result2 routing.MessageSource
|
||||
result3 error
|
||||
}{result1, result2, result3}
|
||||
}
|
||||
|
||||
func (fake *FakeRouter) Stop() {
|
||||
@@ -874,10 +728,6 @@ func (fake *FakeRouter) Invocations() map[string][][]interface{} {
|
||||
defer fake.getNodeMutex.RUnlock()
|
||||
fake.getNodeForRoomMutex.RLock()
|
||||
defer fake.getNodeForRoomMutex.RUnlock()
|
||||
fake.getRequestSinkMutex.RLock()
|
||||
defer fake.getRequestSinkMutex.RUnlock()
|
||||
fake.getResponseSourceMutex.RLock()
|
||||
defer fake.getResponseSourceMutex.RUnlock()
|
||||
fake.listNodesMutex.RLock()
|
||||
defer fake.listNodesMutex.RUnlock()
|
||||
fake.onNewParticipantRTCMutex.RLock()
|
||||
|
||||
@@ -20,3 +20,7 @@ func GetAvailableNodes(nodes []*livekit.Node) []*livekit.Node {
|
||||
return IsAvailable(node)
|
||||
}).([]*livekit.Node)
|
||||
}
|
||||
|
||||
func participantKey(roomName, identity string) string {
|
||||
return roomName + "|" + identity
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user