// 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 (s *Storage) PubSub(redis_server, redis_auth, node_name string) { pool := &redis.Pool{ MaxIdle: 3, IdleTimeout: 240 * time.Second, Dial: func() (redis.Conn, error) { c, err := redis.Dial("tcp", redis_server) if err != nil { return nil, err } if redis_auth != "" { if _, err := c.Do("AUTH", redis_auth); err != nil { c.Close() return nil, err } } return c, err }, TestOnBorrow: func(c redis.Conn, t time.Time) error { _, err := c.Do("PING") return err }, } pubsub_channel := "solocounter" if node_name == "" { node_name = randomString() } go func() { var conn redis.Conn var psc redis.PubSubConn __connect__ := func() { conn = pool.Get() psc = redis.PubSubConn{conn} psc.Subscribe(pubsub_channel) } __connect__() defer conn.Close() for { switch v := psc.Receive().(type) { case redis.Message: if v.Channel == pubsub_channel { var entry RedisEntry err := json.Unmarshal(v.Data, &entry) if err != nil { log.Println("[error][json][unmarshal] : " + err.Error()) } else { if entry.Node != node_name { if s.Verbose { log.Printf("[push][redis] %s, %s, %d", entry.Path, entry.Address, entry.Time) } s.get(entry.Path).add(entry.Address, entry.Time) } } } case redis.Subscription: fmt.Printf("%s: %s %d\n", v.Channel, v.Kind, v.Count) case error: switch v.Error() { case fmt.Sprintf("dial tcp %s: connection refused", redis_server): fallthrough case "EOF": log.Println("[error][redis][subscribe] : " + v.Error() + " -> will reconnect 1 second later") time.Sleep(time.Second * REDIS_RECONNECT_INTERVAL) __connect__() default: log.Println("[error][redis][subscribe] : " + v.Error()) } } } }() s.c = make(chan RedisEntry, CHAN_CAPACITY_REDIS_ENTRY) go func() { conn := pool.Get() defer conn.Close() for { select { case entry := <-s.c: entry.Node = node_name b, err := json.Marshal(entry) if err != nil { log.Println("[error][json][marshal] : " + err.Error()) } else { _, err := conn.Do("PUBLISH", pubsub_channel, b) if err != nil { log.Println("[error][redis][publish] : " + err.Error()) } } } } }() }
func expectPushed(t *testing.T, c redis.PubSubConn, message string, expected interface{}) { actual := c.Receive() if !reflect.DeepEqual(actual, expected) { t.Errorf("%s = %v, want %v", message, actual, expected) } }