github.com/whoyao/protocol@v0.0.0-20230519045905-2d8ace718ca5/egress/rpc.go (about) 1 package egress 2 3 import ( 4 "context" 5 "errors" 6 "time" 7 8 "github.com/redis/go-redis/v9" 9 "google.golang.org/protobuf/proto" 10 11 "github.com/whoyao/protocol/livekit" 12 "github.com/whoyao/protocol/logger" 13 "github.com/whoyao/protocol/rpc" 14 "github.com/whoyao/protocol/utils" 15 ) 16 17 const ( 18 newEgressChannel = "EG_START" 19 updateChannel = "EG_RESULTS" 20 requestChannelPrefix = "REQ_" 21 responseChannelPrefix = "RES_" 22 23 RequestExpiration = time.Second * 2 24 requestTimeout = time.Second * 3 25 lockDuration = time.Second * 3 26 ) 27 28 // RPCClient is used by LiveKit Server 29 // Deprecated 30 type RPCClient interface { 31 // GetUpdateChannel returns a subscription for egress info updates 32 GetUpdateChannel(ctx context.Context) (utils.PubSub, error) 33 // SendRequest sends a request to all available instances 34 SendRequest(ctx context.Context, req proto.Message) (*livekit.EgressInfo, error) 35 } 36 37 // RPCServer is used by Egress 38 // Deprecated 39 type RPCServer interface { 40 // GetRequestChannel returns a subscription for egress requests 41 GetRequestChannel(ctx context.Context) (utils.PubSub, error) 42 // ClaimRequest is used to take ownership of a request 43 ClaimRequest(ctx context.Context, request *livekit.StartEgressRequest) (bool, error) 44 // EgressSubscription subscribes to requests for a specific egress ID 45 EgressSubscription(ctx context.Context, egressID string) (utils.PubSub, error) 46 // SendResponse returns an RPC response 47 SendResponse(ctx context.Context, request proto.Message, info *livekit.EgressInfo, err error) error 48 // SendUpdate sends an egress info update 49 SendUpdate(ctx context.Context, info *livekit.EgressInfo) error 50 } 51 52 type RedisRPC struct { 53 nodeID livekit.NodeID 54 bus *utils.RedisMessageBus 55 } 56 57 // Deprecated 58 func NewRedisRPCClient(nodeID livekit.NodeID, rc redis.UniversalClient) RPCClient { 59 if rc == nil { 60 return nil 61 } 62 63 bus := utils.NewRedisMessageBus(rc) 64 return &RedisRPC{ 65 nodeID: nodeID, 66 bus: bus.(*utils.RedisMessageBus), 67 } 68 } 69 70 func (r *RedisRPC) GetUpdateChannel(ctx context.Context) (utils.PubSub, error) { 71 return r.bus.SubscribeQueue(context.Background(), updateChannel) 72 } 73 74 func (r *RedisRPC) SendRequest(ctx context.Context, request proto.Message) (*livekit.EgressInfo, error) { 75 requestID := utils.NewGuid(utils.RPCPrefix) 76 var channel string 77 78 switch req := request.(type) { 79 case *rpc.StartEgressRequest: 80 dep := &livekit.StartEgressRequest{ 81 EgressId: req.EgressId, 82 RequestId: requestID, 83 SentAt: time.Now().UnixNano(), 84 SenderId: string(r.nodeID), 85 RoomId: req.RoomId, 86 Token: req.Token, 87 WsUrl: req.WsUrl, 88 } 89 if dep.EgressId == "" { 90 dep.EgressId = utils.NewGuid(utils.EgressPrefix) 91 } 92 switch r := req.Request.(type) { 93 case *rpc.StartEgressRequest_RoomComposite: 94 dep.Request = &livekit.StartEgressRequest_RoomComposite{RoomComposite: r.RoomComposite} 95 case *rpc.StartEgressRequest_Web: 96 dep.Request = &livekit.StartEgressRequest_Web{Web: r.Web} 97 case *rpc.StartEgressRequest_TrackComposite: 98 dep.Request = &livekit.StartEgressRequest_TrackComposite{TrackComposite: r.TrackComposite} 99 case *rpc.StartEgressRequest_Track: 100 dep.Request = &livekit.StartEgressRequest_Track{Track: r.Track} 101 } 102 request = dep 103 channel = newEgressChannel 104 105 case *livekit.StartEgressRequest: 106 if req.EgressId == "" { 107 req.EgressId = utils.NewGuid(utils.EgressPrefix) 108 } 109 req.RequestId = requestID 110 req.SentAt = time.Now().UnixNano() 111 req.SenderId = string(r.nodeID) 112 channel = newEgressChannel 113 114 case *livekit.EgressRequest: 115 req.RequestId = requestID 116 req.SenderId = string(r.nodeID) 117 channel = requestChannel(req.EgressId) 118 119 default: 120 return nil, errors.New("invalid request type") 121 } 122 123 sub, err := r.bus.Subscribe(ctx, responseChannel(requestID)) 124 if err != nil { 125 return nil, err 126 } 127 defer func() { 128 err := sub.Close() 129 if err != nil { 130 logger.Errorw("failed to unsubscribe from response channel", err) 131 } 132 }() 133 134 err = r.bus.Publish(ctx, channel, request) 135 if err != nil { 136 return nil, err 137 } 138 139 select { 140 case raw := <-sub.Channel(): 141 res := &livekit.EgressResponse{} 142 err := proto.Unmarshal(sub.Payload(raw), res) 143 if err != nil { 144 return nil, err 145 } else if res.Error != "" { 146 return nil, errors.New(res.Error) 147 } else { 148 return res.Info, nil 149 } 150 151 case <-time.After(requestTimeout): 152 return nil, ErrNoResponse 153 } 154 } 155 156 // Deprecated 157 func NewRedisRPCServer(rc redis.UniversalClient) RPCServer { 158 bus := utils.NewRedisMessageBus(rc) 159 return &RedisRPC{ 160 bus: bus.(*utils.RedisMessageBus), 161 } 162 } 163 164 func (r *RedisRPC) GetRequestChannel(ctx context.Context) (utils.PubSub, error) { 165 return r.bus.Subscribe(ctx, newEgressChannel) 166 } 167 168 func (r *RedisRPC) ClaimRequest(ctx context.Context, req *livekit.StartEgressRequest) (bool, error) { 169 claimed, err := r.bus.Lock(ctx, requestChannel(req.EgressId), lockDuration) 170 if err != nil || !claimed { 171 return false, err 172 } 173 return true, nil 174 } 175 176 func (r *RedisRPC) EgressSubscription(ctx context.Context, egressID string) (utils.PubSub, error) { 177 return r.bus.Subscribe(ctx, requestChannel(egressID)) 178 } 179 180 func (r *RedisRPC) SendResponse(ctx context.Context, request proto.Message, info *livekit.EgressInfo, err error) error { 181 res := &livekit.EgressResponse{ 182 Info: info, 183 } 184 185 switch req := request.(type) { 186 case *livekit.StartEgressRequest: 187 res.RequestId = req.RequestId 188 case *livekit.EgressRequest: 189 res.RequestId = req.RequestId 190 } 191 192 if err != nil { 193 res.Error = err.Error() 194 } 195 196 return r.bus.Publish(ctx, responseChannel(res.RequestId), res) 197 } 198 199 func (r *RedisRPC) SendUpdate(ctx context.Context, info *livekit.EgressInfo) error { 200 return r.bus.Publish(ctx, updateChannel, info) 201 } 202 203 func requestChannel(egressID string) string { 204 return requestChannelPrefix + egressID 205 } 206 207 func responseChannel(nodeID string) string { 208 return responseChannelPrefix + nodeID 209 }