Files
sacrificial-socket/backend/ssredis/ssredis.go
2018-03-20 21:07:20 +00:00

182 lines
4.1 KiB
Go

/*
Package ssredis provides a ss.MultihomeBackend interface that uses Redis for synchronizing broadcasts and roomcasts between multiple Sacrificial Socket instances.
*/
package ssredis
import (
"github.com/go-redis/redis"
ss "github.com/raz-varren/sacrificial-socket"
"github.com/raz-varren/log"
"github.com/raz-varren/sacrificial-socket/tools"
)
const (
//the default redis.PubSub channel that will be subscribed to
DefServerGroup = "ss-rmhb-group-default"
)
//RMHB implements the ss.MultihomeBackend interface and uses
//Redis to syncronize between multiple machines running ss.SocketServer
type RMHB struct {
r *redis.Client
o *Options
rps *redis.PubSub
bps *redis.PubSub
roomPSName string
bcastPSName string
}
type Options struct {
//ServerName is a unique name for the ss.MultihomeBackend instance.
//This name must be unique per backend instance or the backend
//will not broadcast and roomcast properly.
//
//Leave this name blank to auto generate a unique name.
ServerName string
//ServerGroup is the server pool name that this instance's broadcasts
//and roomcasts will be published to. This can be used to break up
//ss.MultihomeBackend instances into separate domains.
//
//Leave this empty to use the default group "ss-rmhb-group-default"
ServerGroup string
}
//NewBackend creates a new *RMHB specified by redis Options and ssredis Options
func NewBackend(rOpts *redis.Options, ssrOpts *Options) (*RMHB, error) {
rClient := redis.NewClient(rOpts)
_, err := rClient.Ping().Result()
if err != nil {
return nil, err
}
if ssrOpts == nil {
ssrOpts = &Options{}
}
if ssrOpts.ServerGroup == "" {
ssrOpts.ServerGroup = DefServerGroup
}
if ssrOpts.ServerName == "" {
ssrOpts.ServerName = tools.UID()
}
roomPSName := ssrOpts.ServerGroup + ":_ss_roomcasts"
bcastPSName := ssrOpts.ServerGroup + ":_ss_broadcasts"
rmhb := &RMHB{
r: rClient,
rps: rClient.Subscribe(roomPSName),
bps: rClient.Subscribe(bcastPSName),
roomPSName: roomPSName,
bcastPSName: bcastPSName,
o: ssrOpts,
}
return rmhb, nil
}
//Init is just here to satisfy the ss.MultihomeBackend interface.
func (r *RMHB) Init() {
}
//Shutdown closes the subscribed redis channel, then the redis connection.
func (r *RMHB) Shutdown() {
r.rps.Close()
r.bps.Close()
r.r.Close()
}
//BroadcastToBackend will publish a broadcast message to the redis backend
func (r *RMHB) BroadcastToBackend(b *ss.BroadcastMsg) {
t := &transmission{
ServerName: r.o.ServerName,
EventName: b.EventName,
Data: b.Data,
}
data, err := t.toJSON()
if err != nil {
log.Err.Println(err)
return
}
err = r.r.Publish(r.bcastPSName, string(data)).Err()
if err != nil {
log.Err.Println(err)
}
}
//RoomcastToBackend will publish a roomcast message to the redis backend
func (r *RMHB) RoomcastToBackend(rm *ss.RoomMsg) {
t := &transmission{
ServerName: r.o.ServerName,
EventName: rm.EventName,
RoomName: rm.RoomName,
Data: rm.Data,
}
data, err := t.toJSON()
if err != nil {
log.Err.Println(err)
return
}
err = r.r.Publish(r.roomPSName, string(data)).Err()
if err != nil {
log.Err.Println(err)
}
}
//BroadcastFromBackend will receive broadcast messages from redis and propogate them to the neccessary sockets
func (r *RMHB) BroadcastFromBackend(bc chan<- *ss.BroadcastMsg) {
bChan := r.bps.Channel()
for d := range bChan {
var t transmission
err := t.fromJSON([]byte(d.Payload))
if err != nil {
log.Err.Println(err)
continue
}
if t.ServerName == r.o.ServerName {
continue
}
bc <- &ss.BroadcastMsg{
EventName: t.EventName,
Data: t.Data,
}
}
}
//RoomcastFromBackend will receive roomcast messages from redis and propogate them to the neccessary sockets
func (r *RMHB) RoomcastFromBackend(rc chan<- *ss.RoomMsg) {
rChan := r.rps.Channel()
for d := range rChan {
var t transmission
err := t.fromJSON([]byte(d.Payload))
if err != nil {
log.Err.Println(err)
continue
}
if t.ServerName == r.o.ServerName {
continue
}
rc <- &ss.RoomMsg{
EventName: t.EventName,
RoomName: t.RoomName,
Data: t.Data,
}
}
}