func (logger *RedisLogger) Unsubscribe(psc *redis.PubSubConn, groups ...string) error { var channels []interface{} for _, group := range groups { channels = append(channels, redisPubSubGroup+group) } return psc.Unsubscribe(channels...) }
func slaveHandler(s *websocket.Conn, sessionID int, dbStore *Store, redisAddr string) { xlog.Debugf("entering SlaveHandler") c, err := redis.Dial("tcp", redisAddr) if err != nil { xlog.Errorf("redis.Dial failed: %v", err) return } defer c.Close() psc := redis.PubSubConn{Conn: c} topic := fmt.Sprintf("session.%d", sessionID) psc.Subscribe(topic) defer psc.Unsubscribe(topic) for { switch v := psc.Receive().(type) { case redis.Message: StatCount("command for slave", 1) var cmd Command if err := json.Unmarshal(v.Data, &cmd); err != nil { break } if err := websocket.JSON.Send(s, cmd); err != nil { xlog.Errorf("slaveHandler: JSON.Send failed: %v", err) return } if cmd.Cmd == "close" { return } case redis.Subscription: xlog.Debugf("mkay... redis.Subscription received: %#v", v) } } }
// GetTaskResult fetchs task result for the specified taskID func (b *Broker) GetTaskResult(taskID string) <-chan *broker.Message { msg := make(chan *broker.Message) // fetch messages log.Debug("Waiting for Task Result Messages: ", taskID) conn := b.pool.Get() psc := redis.PubSubConn{Conn: conn} psc.Subscribe(taskID) go func() { for { switch v := psc.Receive().(type) { case redis.Message: log.Info("message: ", string(v.Data)) m := &broker.Message{} err := json.Unmarshal(v.Data, &m) if err != nil { log.Error("Failed to unmarshal message.") } else { log.Debug("Task Result message: ", string(m.Body)) msg <- m } psc.Unsubscribe() conn.Close() close(msg) break } } }() log.Debug("Subscribed to Task Result") return msg }
func RedisUnSub(key string, psc redis.PubSubConn) error { if err := psc.Unsubscribe(key); err != nil { Log.Printf("psc.Unsubscribe(\"%s\") faild (%s)", key, err.Error()) return err } return nil }
// Applications can receive pushed messages from one goroutine and manage subscriptions from another goroutine. func ExamplePubSubConn() { c, err := dial() if err != nil { panic(err) } defer c.Close() var wg sync.WaitGroup wg.Add(2) psc := redis.PubSubConn{Conn: c} // This goroutine receives and prints pushed notifications from the server. // The goroutine exits when the connection is unsubscribed from all // channels or there is an error. go func() { defer wg.Done() for { switch n := psc.Receive().(type) { case redis.Message: fmt.Printf("Message: %s %s\n", n.Channel, n.Data) case redis.PMessage: fmt.Printf("PMessage: %s %s %s\n", n.Pattern, n.Channel, n.Data) case redis.Subscription: fmt.Printf("Subscription: %s %s %d\n", n.Kind, n.Channel, n.Count) if n.Count == 0 { return } case error: fmt.Printf("error: %v\n", n) return } } }() // This goroutine manages subscriptions for the connection. go func() { defer wg.Done() psc.Subscribe("example") psc.PSubscribe("p*") // The following function calls publish a message using another // connection to the Redis server. publish("example", "hello") publish("example", "world") publish("pexample", "foo") publish("pexample", "bar") // Unsubscribe from all connections. This will cause the receiving // goroutine to exit. psc.Unsubscribe() psc.PUnsubscribe() }() wg.Wait() // Output: // Subscription: subscribe example 1 // Subscription: psubscribe p* 2 // Message: example hello // Message: example world // PMessage: p* pexample foo // PMessage: p* pexample bar // Subscription: unsubscribe example 1 // Subscription: punsubscribe p* 0 }
func testPublishChannel(t *testing.T, cfg map[string]interface{}) { batches := 100 batchSize := 1000 total := batches & batchSize db := 0 index := cfg["index"].(string) if v, ok := cfg["db"]; ok { db = v.(int) } conn, err := redis.Dial("tcp", getRedisAddr(), redis.DialDatabase(db)) if err != nil { t.Fatalf("redis.Dial failed %v", err) } // delete old key if present defer conn.Close() conn.Do("DEL", index) // subscribe to packetbeat channel psc := redis.PubSubConn{conn} if err := psc.Subscribe(index); err != nil { t.Fatal(err) } defer psc.Unsubscribe(index) // connect and publish events var wg sync.WaitGroup var pubErr error out := newRedisTestingOutput(t, cfg) wg.Add(1) go func() { defer wg.Done() pubErr = sendTestEvents(out, batches, batchSize) }() // collect published events by subscription var messages [][]byte assert.NoError(t, conn.Err()) for conn.Err() == nil { t.Logf("try collect message") switch v := psc.Receive().(type) { case redis.Message: messages = append(messages, v.Data) case error: t.Error(v) default: t.Logf("received: %#v", v) } if len(messages) == total { break } } wg.Wait() // validate assert.NoError(t, pubErr) assert.Equal(t, total, len(messages)) for i, raw := range messages { evt := struct{ Message int }{} err = json.Unmarshal(raw, &evt) assert.NoError(t, err) assert.Equal(t, i+1, evt.Message) } }
func TestPubSub(t *testing.T) { conn := NewConn() conn.ReceiveWait = true redisChannel := "subchannel" conn.Command("SUBSCRIBE", redisChannel).Expect([]interface{}{ []byte("subscribe"), []byte(redisChannel), []byte("1"), }) messages := [][]byte{ []byte("value1"), []byte("value2"), []byte("value3"), []byte("finished"), } for _, message := range messages { conn.AddSubscriptionMessage([]interface{}{ []byte("message"), []byte(redisChannel), message, }) } //Check some values are correct if len(conn.commands) != 1 { t.Error("Initial subscription message not set correctly") } if len(conn.SubResponses) != 4 { t.Error("PubSub messages not queued up corectly") } //Use the pub sub connection nextMessage := func() { conn.ReceiveNow <- true } go nextMessage() //Allow the subscribe message to come through psc := redis.PubSubConn{Conn: conn} psc.Subscribe(redisChannel) defer psc.Unsubscribe() //Receive the subscribe message subResponse := psc.Receive() switch smsg := subResponse.(type) { case redis.Subscription: if smsg.Kind != "subscribe" { t.Error("Subscription ack kind is wrong") } if smsg.Channel != redisChannel { t.Error("Subscription ack channel is wrong") } if smsg.Count != 1 { t.Error("Subscription ack count is wrong") } default: t.Error("Got wrong type back on initial subscription") } //Receive the other messages - control when they come testResponse := make(chan []byte, 1) go func() { for { switch msg := psc.Receive().(type) { case redis.Message: testResponse <- msg.Data } } }() for _, expMsg := range messages { assertChannelEmpty(t, testResponse) go nextMessage() msg := <-testResponse if !reflect.DeepEqual(msg, expMsg) { t.Error("Expected message", string(expMsg), "got", string(msg)) } assertChannelEmpty(t, testResponse) } }
func main() { flag.Parse() redisPool := redis.NewPool(func() (redis.Conn, error) { c, err := redis.Dial("tcp", *redisAddress) if err != nil { return nil, err } return c, err }, *maxConnections) defer redisPool.Close() /* c, err := redis.Dial("tcp", ":6379") if err != nil { panic(err) } defer c.Close() */ var wg sync.WaitGroup wg.Add(2) // psc := redis.PubSubConn{Conn: c} psc := redis.PubSubConn{Conn: redisPool.Get()} // This goroutine receives and prints pushed notifications from the server. // The goroutine exits when the connection is unsubscribed from all // channels or there is an error. go func() { defer wg.Done() for { switch n := psc.Receive().(type) { case redis.Message: fmt.Printf("Message: %s %s\n", n.Channel, n.Data) case redis.PMessage: fmt.Printf("PMessage: %s %s %s\n", n.Pattern, n.Channel, n.Data) case redis.Subscription: fmt.Printf("Subscription: %s %s %d\n", n.Kind, n.Channel, n.Count) if n.Count == 0 { return } case error: fmt.Printf("error: %v\n", n) return } } }() // This goroutine manages subscriptions for the connection. go func() { defer wg.Done() psc.Subscribe("example") psc.PSubscribe("p*") psc.PSubscribe("bigbluebutton:to-bbb-apps:system") // The following function calls publish a message using another // connection to the Redis server. publish("example", "hello") publish("example", "world") publish("pexample", "foo") publish("pexample", "bar") // Unsubscribe from all connections. This will cause the receiving // goroutine to exit. psc.Unsubscribe() //psc.PUnsubscribe() }() wg.Wait() // Output: // Subscription: subscribe example 1 // Subscription: psubscribe p* 2 // Message: example hello // Message: example world // PMessage: p* pexample foo // PMessage: p* pexample bar // Subscription: unsubscribe example 1 // Subscription: punsubscribe p* 0 }
func (this __red) Subscribe(messages ...interface{}) (notify chan interface{}, shutdown func(wait bool)) { log.Println("SUBSCRIBE") channels := []interface{}{} channelsMap := map[string]interface{}{} for _, message := range messages { switch message := message.(type) { default: _ = message panic("Unsupported message type") } } conn := this.__.(redis.Conn) psc := redis.PubSubConn{Conn: conn} err := psc.Subscribe(channels...) if err != nil { panic(err) } notify = make(chan interface{}, 1024) notifyInternal := make(chan interface{}, 1024) closeInternal := make(chan error, 1) go func() { defer func() { if rec := recover(); rec != nil { log.Println("PANIC ! RECEIVER:", rec, __red_stack()) closeInternal <- errors.New(fmt.Sprint(rec)) } else { log.Println("RECEIVER No more subscriptions") closeInternal <- nil } }() for { switch n := psc.Receive().(type) { case redis.Message: if m, ok := channelsMap[n.Channel]; ok { switch m := m.(type) { default: _ = m panic("Unsupported message type") } } else { // TODO } case redis.Subscription: if n.Count == 0 { return } } } }() var doneWG sync.WaitGroup doneWG.Add(1) go func() { defer func() { defer doneWG.Done() if rec := recover(); rec != nil { log.Println("PANIC ! PINGER:", rec, __red_stack()) notify <- errors.New(fmt.Sprint(rec)) } else { // TODO log.Println("PUBSUB | CLOSED") } }() Q: for { select { case err := <-closeInternal: notify <- err break Q case <-notifyInternal: // notify <- msg case <-time.After(__red_ops.PingPeriod): err := psc.Ping("") if err != nil { notify <- err break Q } } } }() shutdown = func(wait bool) { err := psc.Unsubscribe() if err != nil { panic(err) } if wait { doneWG.Wait() } } return }