github.com/oarkflow/sio@v0.0.6/adapters/redis.go (about)

     1  package adapters
     2  
     3  /*
     4  
     5  import (
     6  	"context"
     7  	"encoding/hex"
     8  	"log/slog"
     9  
    10  	"github.com/redis/go-redis/v9"
    11  
    12  	"github.com/oarkflow/sio"
    13  )
    14  
    15  var rng *sio.RNG
    16  
    17  func init() {
    18  	rng = sio.NewRNG()
    19  }
    20  
    21  const (
    22  	// DefServerGroup the default redis.PubSub channel that will be subscribed to
    23  	DefServerGroup = "ss-rmhb-group-default"
    24  )
    25  
    26  // RedisAdapter implements the sio.Adapter interface and uses
    27  // Redis to syncronize between multiple machines running sio.Server
    28  type RedisAdapter struct {
    29  	r           *redis.Client
    30  	o           *Options
    31  	rps         *redis.PubSub
    32  	bps         *redis.PubSub
    33  	ctx         context.Context
    34  	roomPSName  string
    35  	bcastPSName string
    36  }
    37  
    38  type Options struct {
    39  
    40  	//ServerName is a unique name for the sio.Adapter instance.
    41  	//This name must be unique per backend instance or the backend
    42  	//will not broadcast and roomcast properly.
    43  	//
    44  	//Leave this name blank to auto generate a unique name.
    45  	ServerName string
    46  
    47  	//ServerGroup is the server pool name that this instance's broadcasts
    48  	//and roomcasts will be published to. This can be used to break up
    49  	//sio.Adapter instances into separate domains.
    50  	//
    51  	//Leave this empty to use the default group "ss-rmhb-group-default"
    52  	ServerGroup string
    53  }
    54  
    55  // NewRedisAdapter creates a new *RedisAdapter specified by redis Options and ssredis Options
    56  func NewRedisAdapter(ctx context.Context, rOpts *redis.Options, ssrOpts *Options) (*RedisAdapter, error) {
    57  	rClient := redis.NewClient(rOpts)
    58  	_, err := rClient.Ping(context.Background()).Result()
    59  	if err != nil {
    60  		return nil, err
    61  	}
    62  
    63  	if ssrOpts == nil {
    64  		ssrOpts = &Options{}
    65  	}
    66  
    67  	if ssrOpts.ServerGroup == "" {
    68  		ssrOpts.ServerGroup = DefServerGroup
    69  	}
    70  
    71  	if ssrOpts.ServerName == "" {
    72  		uid := make([]byte, 16)
    73  		_, err := rng.Read(uid)
    74  		if err != nil {
    75  			return nil, err
    76  		}
    77  		ssrOpts.ServerName = hex.EncodeToString(uid)
    78  	}
    79  
    80  	roomPSName := ssrOpts.ServerGroup + ":_ss_roomcasts"
    81  	bcastPSName := ssrOpts.ServerGroup + ":_ss_broadcasts"
    82  
    83  	rmhb := &RedisAdapter{
    84  		r:           rClient,
    85  		rps:         rClient.Subscribe(ctx, roomPSName),
    86  		bps:         rClient.Subscribe(ctx, bcastPSName),
    87  		roomPSName:  roomPSName,
    88  		bcastPSName: bcastPSName,
    89  		o:           ssrOpts,
    90  		ctx:         ctx,
    91  	}
    92  
    93  	return rmhb, nil
    94  }
    95  
    96  // Init is just here to satisfy the sio.Adapter interface.
    97  func (r *RedisAdapter) Init() {
    98  
    99  }
   100  
   101  // Shutdown closes the subscribed redis channel, then the redis connection.
   102  func (r *RedisAdapter) Shutdown() error {
   103  	err := r.rps.Close()
   104  	if err != nil {
   105  		return err
   106  	}
   107  	err = r.bps.Close()
   108  	if err != nil {
   109  		return err
   110  	}
   111  	return r.r.Close()
   112  }
   113  
   114  // BroadcastToBackend will publish a broadcast message to the redis backend
   115  func (r *RedisAdapter) BroadcastToBackend(b *sio.BroadcastMsg) {
   116  	t := &transmission{
   117  		ServerName: r.o.ServerName,
   118  		EventName:  b.EventName,
   119  		Data:       b.Data,
   120  	}
   121  
   122  	data, err := t.toJSON()
   123  	if err != nil {
   124  		slog.Error(err.Error())
   125  		return
   126  	}
   127  
   128  	err = r.r.Publish(context.Background(), r.bcastPSName, string(data)).Err()
   129  	if err != nil {
   130  		slog.Error(err.Error())
   131  	}
   132  }
   133  
   134  // RoomcastToBackend will publish a roomcast message to the redis backend
   135  func (r *RedisAdapter) RoomcastToBackend(rm *sio.RoomMsg) {
   136  	t := &transmission{
   137  		ServerName: r.o.ServerName,
   138  		EventName:  rm.EventName,
   139  		RoomName:   rm.RoomName,
   140  		Data:       rm.Data,
   141  	}
   142  
   143  	data, err := t.toJSON()
   144  	if err != nil {
   145  		slog.Error(err.Error())
   146  		return
   147  	}
   148  
   149  	err = r.r.Publish(context.Background(), r.roomPSName, string(data)).Err()
   150  	if err != nil {
   151  		slog.Error(err.Error())
   152  	}
   153  }
   154  
   155  // BroadcastFromBackend will receive broadcast messages from redis and propogate them to the neccessary sockets
   156  func (r *RedisAdapter) BroadcastFromBackend(bc chan<- *sio.BroadcastMsg) {
   157  	bChan := r.bps.Channel()
   158  
   159  	for d := range bChan {
   160  		var t transmission
   161  
   162  		err := t.fromJSON([]byte(d.Payload))
   163  		if err != nil {
   164  			slog.Error(err.Error())
   165  			continue
   166  		}
   167  
   168  		if t.ServerName == r.o.ServerName {
   169  			continue
   170  		}
   171  
   172  		bc <- &sio.BroadcastMsg{
   173  			EventName: t.EventName,
   174  			Data:      t.Data,
   175  		}
   176  	}
   177  }
   178  
   179  // RoomcastFromBackend will receive roomcast messages from redis and propogate them to the neccessary sockets
   180  func (r *RedisAdapter) RoomcastFromBackend(rc chan<- *sio.RoomMsg) {
   181  	rChan := r.rps.Channel()
   182  
   183  	for d := range rChan {
   184  		var t transmission
   185  
   186  		err := t.fromJSON([]byte(d.Payload))
   187  		if err != nil {
   188  			slog.Error(err.Error())
   189  			continue
   190  		}
   191  
   192  		if t.ServerName == r.o.ServerName {
   193  			continue
   194  		}
   195  
   196  		rc <- &sio.RoomMsg{
   197  			EventName: t.EventName,
   198  			RoomName:  t.RoomName,
   199  			Data:      t.Data,
   200  		}
   201  	}
   202  }
   203  */