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  }