redis_conn.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  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. // 自定义错误
  16. const (
  17. ErrRedisConnNil = ErrRedis("redis conn nil")
  18. )
  19. type ErrRedis string
  20. func (err ErrRedis) Error() string {
  21. return string(err)
  22. }
  23. // Redis连接池适配器
  24. type Hub struct {
  25. LOG func() *logger.Logger
  26. pool *redis.Pool
  27. cluster *redisc.Cluster
  28. once sync.Once
  29. }
  30. // 获取Redis连接池适配器的新实例
  31. func NewHub() *Hub {
  32. viper.SetDefault("redis.max_idle", 300)
  33. viper.SetDefault("redis.max_active", 1000)
  34. viper.SetDefault("redis.timeout", time.Duration(60000))
  35. return &Hub{
  36. LOG: func() *logger.Logger {
  37. return logger.New()
  38. },
  39. }
  40. }
  41. // 创建连接池
  42. func (h *Hub) createPool(addr string, opts ...redis.DialOption) (*redis.Pool, error) {
  43. maxIdle := viper.GetInt("redis.max_idle")
  44. maxActive := viper.GetInt("redis.max_active")
  45. idleTimeout := viper.GetDuration("redis.timeout")
  46. return &redis.Pool{
  47. MaxIdle: maxIdle,
  48. MaxActive: maxActive,
  49. IdleTimeout: idleTimeout,
  50. Dial: func() (redis.Conn, error) {
  51. if conn, err := redis.Dial("tcp", addr, opts...); err != nil {
  52. h.LOG().Warnf("Get Redis connection failed: %s", err.Error())
  53. return nil, err
  54. } else {
  55. return conn, nil
  56. }
  57. },
  58. TestOnBorrow: func(c redis.Conn, t time.Time) error {
  59. _, err := c.Do("PING")
  60. return err
  61. },
  62. }, nil
  63. }
  64. // 建立与Redis的连接
  65. func (h *Hub) conn() error {
  66. dialOpts := []redis.DialOption{
  67. redis.DialConnectTimeout(5 * time.Second),
  68. //redis.DialReadTimeout(5 * time.Second),
  69. //redis.DialWriteTimeout(5 * time.Second),
  70. }
  71. nodes := viper.GetStringSlice("redis.nodes")
  72. if nil != nodes && len(nodes) > 0 { // 集群
  73. cluster := &redisc.Cluster{
  74. StartupNodes: nodes,
  75. DialOptions: dialOpts,
  76. CreatePool: h.createPool,
  77. }
  78. h.cluster = cluster
  79. } else { // 单点
  80. var pool *redis.Pool
  81. host := viper.GetString("redis.host")
  82. password := viper.GetString("redis.password")
  83. if len(strings.TrimSpace(password)) > 0 {
  84. dialOpts = append(dialOpts, redis.DialPassword(strings.TrimSpace(password)))
  85. }
  86. pool, _ = h.createPool(host, dialOpts...)
  87. h.pool = pool
  88. }
  89. return h.ping()
  90. }
  91. // 连接测试
  92. func (h *Hub) ping() error {
  93. // 避免循环调用,此处不调用已包装好的Get和Do方法
  94. var conn redis.Conn
  95. if nil != h.pool {
  96. conn = h.pool.Get()
  97. } else if nil != h.cluster {
  98. conn = h.cluster.Get()
  99. } else {
  100. return ErrRedisConnNil
  101. }
  102. defer func(conn redis.Conn) {
  103. _ = conn.Close()
  104. }(conn)
  105. if reply, err := redis.String(conn.Do("PING")); err != nil || reply != "PONG" {
  106. if err != nil {
  107. h.LOG().Warnf("Can not connect to redis: %s", err.Error())
  108. } else {
  109. h.LOG().Warnf("Can not connect to redis: %s", reply)
  110. }
  111. return err
  112. }
  113. h.LOG().Info("Redis connected")
  114. return nil
  115. }
  116. // 监听系统退出信号量,自动关闭Redis连接
  117. func (h *Hub) closeWait() {
  118. go func(h *Hub) {
  119. defer utils.DefaultGoroutineRecover(nil, `Redis连接池关闭`)
  120. var c chan os.Signal
  121. var s os.Signal
  122. c = make(chan os.Signal, 1)
  123. signal.Notify(c, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGKILL)
  124. for {
  125. s = <-c
  126. switch s {
  127. case syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGKILL:
  128. if err := h.Close(); nil != err {
  129. h.LOG().Error("Close redis connection failed:", err.Error())
  130. }
  131. h.LOG().Info("Redis connection closed")
  132. return
  133. default:
  134. return
  135. }
  136. }
  137. }(h)
  138. }
  139. // 关闭Redis连接
  140. func (h *Hub) Close() error {
  141. var err error
  142. if nil != h.cluster {
  143. err = h.cluster.Close()
  144. } else if nil != h.pool {
  145. err = h.pool.Close()
  146. }
  147. return err
  148. }
  149. // 执行Redis命令
  150. func (h *Hub) Do(cmd string, args ...interface{}) (interface{}, error) {
  151. conn, err := h.Get()
  152. if nil != err {
  153. return nil, err
  154. }
  155. defer func(conn redis.Conn) {
  156. _ = conn.Close()
  157. }(conn)
  158. if nil == conn {
  159. return nil, ErrRedisConnNil
  160. } else {
  161. reply, err := conn.Do(cmd, args...)
  162. if nil != err {
  163. return nil, err
  164. }
  165. return reply, nil
  166. }
  167. }
  168. // 从连接池中获取一个连接
  169. func (h *Hub) Get() (redis.Conn, error) {
  170. var err error
  171. // 延迟一次性初始化Redis连接池
  172. h.once.Do(func() {
  173. e := h.conn()
  174. if nil != e {
  175. err = e
  176. } else {
  177. h.closeWait()
  178. }
  179. })
  180. if nil != err {
  181. return nil, err
  182. }
  183. if nil != h.pool {
  184. return h.pool.Get(), nil
  185. } else if nil != h.cluster {
  186. return h.cluster.Get(), nil
  187. } else {
  188. return nil, ErrRedisConnNil
  189. }
  190. }