redis_conn.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. package redis
  2. import (
  3. "git.aionnect.com/aionnect/go-common/utils"
  4. "git.aionnect.com/aionnect/go-common/utils/logger"
  5. "github.com/gomodule/redigo/redis"
  6. "github.com/mna/redisc"
  7. "github.com/spf13/viper"
  8. "os"
  9. "os/signal"
  10. "strings"
  11. "sync"
  12. "syscall"
  13. "time"
  14. )
  15. type Hub struct {
  16. LOG func() *logger.Logger
  17. pool *redis.Pool
  18. cluster *redisc.Cluster
  19. once sync.Once
  20. }
  21. func NewHub() *Hub {
  22. viper.SetDefault("redis.max_idle", 300)
  23. viper.SetDefault("redis.max_active", 1000)
  24. viper.SetDefault("redis.timeout", time.Duration(60000))
  25. return &Hub{
  26. LOG: func() *logger.Logger {
  27. return logger.New()
  28. },
  29. }
  30. }
  31. func (h *Hub) createPool(addr string, opts ...redis.DialOption) (*redis.Pool, error) {
  32. maxIdle := viper.GetInt("redis.max_idle")
  33. maxActive := viper.GetInt("redis.max_active")
  34. idleTimeout := viper.GetDuration("redis.timeout")
  35. return &redis.Pool{
  36. MaxIdle: maxIdle,
  37. MaxActive: maxActive,
  38. IdleTimeout: idleTimeout,
  39. Dial: func() (redis.Conn, error) {
  40. if conn, err := redis.Dial("tcp", addr, opts...); err != nil {
  41. h.LOG().Warnf("Get Redis connection failed: %s", err.Error())
  42. return nil, err
  43. } else {
  44. return conn, nil
  45. }
  46. },
  47. TestOnBorrow: func(c redis.Conn, t time.Time) error {
  48. _, err := c.Do("PING")
  49. return err
  50. },
  51. }, nil
  52. }
  53. func (h *Hub) conn() error {
  54. dialOpts := []redis.DialOption{
  55. redis.DialConnectTimeout(5 * time.Second),
  56. }
  57. nodes := viper.GetStringSlice("redis.nodes")
  58. if nil != nodes && len(nodes) > 0 { // 集群
  59. cluster := &redisc.Cluster{
  60. StartupNodes: nodes,
  61. DialOptions: dialOpts,
  62. CreatePool: h.createPool,
  63. }
  64. h.cluster = cluster
  65. } else { // 单点
  66. var pool *redis.Pool
  67. host := viper.GetString("redis.host")
  68. password := viper.GetString("redis.password")
  69. if len(strings.TrimSpace(password)) > 0 {
  70. dialOpts = append(dialOpts, redis.DialPassword(strings.TrimSpace(password)))
  71. }
  72. pool, _ = h.createPool(host, dialOpts...)
  73. h.pool = pool
  74. }
  75. return nil
  76. }
  77. func (h *Hub) test() error {
  78. // 连接测试
  79. conn, err := h.Get()
  80. if nil != err {
  81. return err
  82. }
  83. defer func(conn redis.Conn) {
  84. _ = conn.Close()
  85. }(conn)
  86. if reply, err := redis.String(conn.Do("PING")); err != nil || reply != "PONG" {
  87. if err != nil {
  88. h.LOG().Warnf("Can not connect to redis: %s", err.Error())
  89. } else {
  90. h.LOG().Warnf("Can not connect to redis: %s", reply)
  91. }
  92. return err
  93. }
  94. h.LOG().Info("Redis connected")
  95. return nil
  96. }
  97. func (h *Hub) closeWait() {
  98. go func(h *Hub) {
  99. defer utils.DefaultGoroutineRecover(nil, `Redis连接池关闭`)
  100. var c chan os.Signal
  101. var s os.Signal
  102. c = make(chan os.Signal, 1)
  103. signal.Notify(c, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGKILL)
  104. for {
  105. s = <-c
  106. switch s {
  107. case syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGKILL:
  108. if err := h.Close(); nil != err {
  109. h.LOG().Error("Close redis connection failed:", err.Error())
  110. }
  111. h.LOG().Info("Redis connection closed")
  112. return
  113. default:
  114. return
  115. }
  116. }
  117. }(h)
  118. }
  119. func (h *Hub) Close() error {
  120. var err error
  121. if nil != h.cluster {
  122. err = h.cluster.Close()
  123. } else if nil != h.pool {
  124. err = h.pool.Close()
  125. }
  126. return err
  127. }
  128. func (h *Hub) Do(cmd string, args ...interface{}) (interface{}, error) {
  129. conn, err := h.Get()
  130. if nil != err {
  131. return nil, err
  132. }
  133. defer func(conn redis.Conn) {
  134. _ = conn.Close()
  135. }(conn)
  136. if nil == conn {
  137. return nil, ErrRedisConnNil
  138. } else {
  139. reply, err := conn.Do(cmd, args...)
  140. if nil != err {
  141. return nil, err
  142. }
  143. return reply, nil
  144. }
  145. }
  146. const (
  147. ErrRedisConnNil = ErrRedis("redis conn nil")
  148. )
  149. type ErrRedis string
  150. func (err ErrRedis) Error() string {
  151. return string(err)
  152. }
  153. func (h *Hub) Get() (redis.Conn, error) {
  154. var err error
  155. h.once.Do(func() {
  156. e := h.conn()
  157. if nil != e {
  158. err = e
  159. } else {
  160. h.closeWait()
  161. }
  162. })
  163. if nil != err {
  164. return nil, err
  165. }
  166. if nil != h.pool {
  167. return h.pool.Get(), nil
  168. } else if nil != h.cluster {
  169. return h.cluster.Get(), nil
  170. } else {
  171. return nil, ErrRedisConnNil
  172. }
  173. }