예제 #1
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())
					}
				}
			}
		}
	}()
}
예제 #2
0
// zpop pops a value from the ZSET key using WATCH/MULTI/EXEC commands.
func zpop(c redis.Conn, key string) (result string, err error) {

	defer func() {
		// Return connection to normal state on error.
		if err != nil {
			c.Do("DISCARD")
		}
	}()

	// Loop until transaction is successful.
	for {
		if _, err := c.Do("WATCH", key); err != nil {
			return "", err
		}

		members, err := redis.Strings(c.Do("ZRANGE", key, 0, 0))
		if err != nil {
			return "", err
		}
		if len(members) != 1 {
			return "", redis.ErrNil
		}

		c.Send("MULTI")
		c.Send("ZREM", key, members[0])
		queued, err := c.Do("EXEC")
		if err != nil {
			return "", err
		}

		if queued != nil {
			result = members[0]
			break
		}
	}

	return result, nil
}