Initial cloud-services repo - gateway service + pkg modules
This commit is contained in:
158
pkg/redis/pubsub.go
Normal file
158
pkg/redis/pubsub.go
Normal file
@@ -0,0 +1,158 @@
|
||||
package redis
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
"fiskerinc.com/modules/logger"
|
||||
"github.com/gomodule/redigo/redis"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// NewPubSub generates a new working PubSub object
|
||||
func NewPubSub(args ...redis.Conn) *PubSub {
|
||||
var conn redis.Conn
|
||||
if len(args) > 0 {
|
||||
conn = args[0]
|
||||
} else {
|
||||
conn = NewClient().GetConn()
|
||||
}
|
||||
|
||||
return &PubSub{
|
||||
connection: redis.PubSubConn{Conn: conn},
|
||||
subscriptions: make(Set),
|
||||
}
|
||||
}
|
||||
|
||||
// PubSub is a struct used for subscribing to Redis channels
|
||||
//
|
||||
// follows the Listener interface
|
||||
type PubSub struct {
|
||||
connection PubSubClient
|
||||
subscriptions Set
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
// PubSubClient provides necessary functions needed for connection
|
||||
//
|
||||
// within PubSub struct
|
||||
type PubSubClient interface {
|
||||
Receive() interface{}
|
||||
Subscribe(...interface{}) error
|
||||
Unsubscribe(...interface{}) error
|
||||
Close() error
|
||||
}
|
||||
|
||||
// Add an ID to subscriber
|
||||
func (ps *PubSub) Add(id string) error {
|
||||
ps.mu.Lock()
|
||||
defer ps.mu.Unlock()
|
||||
|
||||
ok := ps.subscriptions.Add(id)
|
||||
if !ok {
|
||||
return errors.Errorf("%v already in subscriptions", id)
|
||||
}
|
||||
|
||||
if err := ps.connection.Subscribe(ChannelKey(id)); err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Remove an ID from subscriber
|
||||
func (ps *PubSub) Remove(id string) error {
|
||||
ps.mu.Lock()
|
||||
defer ps.mu.Unlock()
|
||||
|
||||
ok := ps.subscriptions.Remove(id)
|
||||
if !ok {
|
||||
return errors.Errorf("%v does not exist in subscriptions", id)
|
||||
}
|
||||
|
||||
if err := ps.connection.Unsubscribe(ChannelKey(id)); err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Listen loops on receiving messages from subscriptions until cancelled
|
||||
func (ps *PubSub) Listen(ctx context.Context, handler func(string, []byte) error) error {
|
||||
isListening := true
|
||||
done := make(chan error, 1)
|
||||
go func() {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
isListening = false
|
||||
break
|
||||
case <-done:
|
||||
return
|
||||
}
|
||||
|
||||
ps.mu.Lock()
|
||||
defer ps.mu.Unlock()
|
||||
if err := ps.connection.Unsubscribe(); err != nil {
|
||||
logger.Error().Err(err).Send()
|
||||
}
|
||||
}()
|
||||
|
||||
for isListening {
|
||||
switch m := ps.connection.Receive().(type) {
|
||||
case error:
|
||||
done <- m
|
||||
return errors.WithStack(m)
|
||||
case redis.Message:
|
||||
id := ParseChannelKey(m.Channel)
|
||||
go func(channel string, data []byte) {
|
||||
if err := handler(channel, data); err != nil {
|
||||
logger.At(logger.Error(), channel, "redis").
|
||||
Err(err).Send()
|
||||
} else {
|
||||
logger.At(logger.Debug(), channel, "redis").
|
||||
Str("msg", string(data)).
|
||||
Msgf("sent published msg to %s", channel)
|
||||
}
|
||||
}(id, m.Data)
|
||||
case redis.Subscription:
|
||||
switch m.Count {
|
||||
case 0:
|
||||
if !isListening {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListenChannel dumps redis messages to channel rather
|
||||
//
|
||||
// than using a traditional handler
|
||||
func (ps *PubSub) ListenChannel() error {
|
||||
// stub
|
||||
return nil
|
||||
}
|
||||
|
||||
// Length returns number of subscriptions
|
||||
func (ps *PubSub) Length() int {
|
||||
return len(ps.subscriptions)
|
||||
}
|
||||
|
||||
// Restart re-initializes pubsub connection
|
||||
func (ps *PubSub) Restart() error {
|
||||
ps.mu.Lock()
|
||||
defer ps.mu.Unlock()
|
||||
if ps.connection != nil {
|
||||
ps.connection.Close()
|
||||
}
|
||||
|
||||
ps.connection = redis.PubSubConn{Conn: NewClient().GetConn()}
|
||||
for id := range ps.subscriptions {
|
||||
if err := ps.connection.Subscribe(ChannelKey(id)); err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user