redis.go 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  1. package cache
  2. import (
  3. "context"
  4. "time"
  5. "git.bvbej.com/bvbej/base-golang/pkg/errors"
  6. "git.bvbej.com/bvbej/base-golang/pkg/time_parse"
  7. "git.bvbej.com/bvbej/base-golang/pkg/trace"
  8. "github.com/redis/go-redis/v9"
  9. )
  10. type Option func(*option)
  11. type Trace = trace.T
  12. type option struct {
  13. Trace *trace.Trace
  14. Redis *trace.Redis
  15. }
  16. type RedisConfig struct {
  17. Addr string `yaml:"addr"`
  18. Pass string `yaml:"pass"`
  19. DB int `yaml:"db"`
  20. MaxRetries int `yaml:"maxRetries"` // 最大重试次数
  21. PoolSize int `yaml:"poolSize"` // Redis连接池大小
  22. MinIdleConn int `yaml:"minIdleConn"` // 最小空闲连接数
  23. }
  24. func newOption() *option {
  25. return &option{}
  26. }
  27. var _ Repo = (*cacheRepo)(nil)
  28. type Repo interface {
  29. i()
  30. Client() *redis.Client
  31. Set(key, value string, ttl time.Duration, options ...Option) error
  32. Get(key string, options ...Option) (string, error)
  33. TTL(key string) (time.Duration, error)
  34. Expire(key string, ttl time.Duration) bool
  35. ExpireAt(key string, ttl time.Time) bool
  36. Del(key string, options ...Option) bool
  37. Exists(keys ...string) bool
  38. Incr(key string, options ...Option) int64
  39. Decr(key string, options ...Option) int64
  40. HGet(key, field string, options ...Option) (string, error)
  41. HSet(key, field, value string, options ...Option) error
  42. HDel(key, field string, options ...Option) error
  43. HGetAll(key string, options ...Option) (map[string]string, error)
  44. LPush(key, value string, options ...Option) error
  45. LLen(key string, options ...Option) (int64, error)
  46. BRPop(key string, timeout time.Duration, options ...Option) (string, error)
  47. Close() error
  48. }
  49. type cacheRepo struct {
  50. client *redis.Client
  51. ctx context.Context
  52. }
  53. func New(cfg RedisConfig) (Repo, error) {
  54. client := redis.NewClient(&redis.Options{
  55. Addr: cfg.Addr,
  56. Password: cfg.Pass,
  57. DB: cfg.DB,
  58. MaxRetries: cfg.MaxRetries,
  59. PoolSize: cfg.PoolSize,
  60. MinIdleConns: cfg.MinIdleConn,
  61. })
  62. ctx := context.TODO()
  63. if err := client.Ping(ctx).Err(); err != nil {
  64. return nil, errors.Wrap(err, "ping redis err")
  65. }
  66. return &cacheRepo{
  67. client: client,
  68. ctx: ctx,
  69. }, nil
  70. }
  71. func WithTrace(t Trace) Option {
  72. return func(opt *option) {
  73. if t != nil {
  74. opt.Trace = t.(*trace.Trace)
  75. opt.Redis = new(trace.Redis)
  76. }
  77. }
  78. }
  79. func (c *cacheRepo) i() {}
  80. func (c *cacheRepo) Client() *redis.Client {
  81. return c.client
  82. }
  83. func (c *cacheRepo) Set(key, value string, ttl time.Duration, options ...Option) error {
  84. ts := time.Now()
  85. opt := newOption()
  86. defer func() {
  87. if opt.Trace != nil {
  88. opt.Redis.Timestamp = time_parse.CSTLayoutString()
  89. opt.Redis.Handle = "set"
  90. opt.Redis.Key = key
  91. opt.Redis.Value = value
  92. opt.Redis.TTL = ttl.Minutes()
  93. opt.Redis.CostSeconds = time.Since(ts).Seconds()
  94. opt.Trace.AppendRedis(opt.Redis)
  95. }
  96. }()
  97. for _, f := range options {
  98. f(opt)
  99. }
  100. if err := c.client.Set(c.ctx, key, value, ttl).Err(); err != nil {
  101. return errors.Wrapf(err, "redis set key: %s err", key)
  102. }
  103. return nil
  104. }
  105. func (c *cacheRepo) Get(key string, options ...Option) (string, error) {
  106. ts := time.Now()
  107. opt := newOption()
  108. defer func() {
  109. if opt.Trace != nil {
  110. opt.Redis.Timestamp = time_parse.CSTLayoutString()
  111. opt.Redis.Handle = "get"
  112. opt.Redis.Key = key
  113. opt.Redis.CostSeconds = time.Since(ts).Seconds()
  114. opt.Trace.AppendRedis(opt.Redis)
  115. }
  116. }()
  117. for _, f := range options {
  118. f(opt)
  119. }
  120. value, err := c.client.Get(c.ctx, key).Result()
  121. if err != nil {
  122. return "", errors.Wrapf(err, "redis get key: %s err", key)
  123. }
  124. return value, nil
  125. }
  126. func (c *cacheRepo) TTL(key string) (time.Duration, error) {
  127. ttl, err := c.client.TTL(c.ctx, key).Result()
  128. if err != nil {
  129. return -1, errors.Wrapf(err, "redis get key: %s err", key)
  130. }
  131. return ttl, nil
  132. }
  133. func (c *cacheRepo) Expire(key string, ttl time.Duration) bool {
  134. ok, _ := c.client.Expire(c.ctx, key, ttl).Result()
  135. return ok
  136. }
  137. func (c *cacheRepo) ExpireAt(key string, ttl time.Time) bool {
  138. ok, _ := c.client.ExpireAt(c.ctx, key, ttl).Result()
  139. return ok
  140. }
  141. func (c *cacheRepo) Exists(keys ...string) bool {
  142. if len(keys) == 0 {
  143. return true
  144. }
  145. value, _ := c.client.Exists(c.ctx, keys...).Result()
  146. return value > 0
  147. }
  148. func (c *cacheRepo) Del(key string, options ...Option) bool {
  149. ts := time.Now()
  150. opt := newOption()
  151. defer func() {
  152. if opt.Trace != nil {
  153. opt.Redis.Timestamp = time_parse.CSTLayoutString()
  154. opt.Redis.Handle = "del"
  155. opt.Redis.Key = key
  156. opt.Redis.CostSeconds = time.Since(ts).Seconds()
  157. opt.Trace.AppendRedis(opt.Redis)
  158. }
  159. }()
  160. for _, f := range options {
  161. f(opt)
  162. }
  163. if key == "" {
  164. return true
  165. }
  166. value, _ := c.client.Del(c.ctx, key).Result()
  167. return value > 0
  168. }
  169. func (c *cacheRepo) Incr(key string, options ...Option) int64 {
  170. ts := time.Now()
  171. opt := newOption()
  172. defer func() {
  173. if opt.Trace != nil {
  174. opt.Redis.Timestamp = time_parse.CSTLayoutString()
  175. opt.Redis.Handle = "incr"
  176. opt.Redis.Key = key
  177. opt.Redis.CostSeconds = time.Since(ts).Seconds()
  178. opt.Trace.AppendRedis(opt.Redis)
  179. }
  180. }()
  181. for _, f := range options {
  182. f(opt)
  183. }
  184. value, _ := c.client.Incr(c.ctx, key).Result()
  185. return value
  186. }
  187. func (c *cacheRepo) Decr(key string, options ...Option) int64 {
  188. ts := time.Now()
  189. opt := newOption()
  190. defer func() {
  191. if opt.Trace != nil {
  192. opt.Redis.Timestamp = time_parse.CSTLayoutString()
  193. opt.Redis.Handle = "decr"
  194. opt.Redis.Key = key
  195. opt.Redis.CostSeconds = time.Since(ts).Seconds()
  196. opt.Trace.AppendRedis(opt.Redis)
  197. }
  198. }()
  199. for _, f := range options {
  200. f(opt)
  201. }
  202. value, _ := c.client.Decr(c.ctx, key).Result()
  203. return value
  204. }
  205. func (c *cacheRepo) HGet(key, field string, options ...Option) (string, error) {
  206. ts := time.Now()
  207. opt := newOption()
  208. defer func() {
  209. if opt.Trace != nil {
  210. opt.Redis.Timestamp = time_parse.CSTLayoutString()
  211. opt.Redis.Handle = "hash get"
  212. opt.Redis.Key = key
  213. opt.Redis.Value = field
  214. opt.Redis.CostSeconds = time.Since(ts).Seconds()
  215. opt.Trace.AppendRedis(opt.Redis)
  216. }
  217. }()
  218. for _, f := range options {
  219. f(opt)
  220. }
  221. value, err := c.client.HGet(c.ctx, key, field).Result()
  222. if err != nil {
  223. return "", errors.Wrapf(err, "redis hget key: %s field: %s", key, field)
  224. }
  225. return value, nil
  226. }
  227. func (c *cacheRepo) HSet(key, field, value string, options ...Option) error {
  228. ts := time.Now()
  229. opt := newOption()
  230. defer func() {
  231. if opt.Trace != nil {
  232. opt.Redis.Timestamp = time_parse.CSTLayoutString()
  233. opt.Redis.Handle = "hash set"
  234. opt.Redis.Key = key
  235. opt.Redis.Value = field + "/" + value
  236. opt.Redis.CostSeconds = time.Since(ts).Seconds()
  237. opt.Trace.AppendRedis(opt.Redis)
  238. }
  239. }()
  240. for _, f := range options {
  241. f(opt)
  242. }
  243. if err := c.client.HSet(c.ctx, key, field, value).Err(); err != nil {
  244. return errors.Wrapf(err, "redis set key: %s field: %s err", key, field)
  245. }
  246. return nil
  247. }
  248. func (c *cacheRepo) HDel(key, field string, options ...Option) error {
  249. ts := time.Now()
  250. opt := newOption()
  251. defer func() {
  252. if opt.Trace != nil {
  253. opt.Redis.Timestamp = time_parse.CSTLayoutString()
  254. opt.Redis.Handle = "hash del"
  255. opt.Redis.Key = key
  256. opt.Redis.Value = field
  257. opt.Redis.CostSeconds = time.Since(ts).Seconds()
  258. opt.Trace.AppendRedis(opt.Redis)
  259. }
  260. }()
  261. for _, f := range options {
  262. f(opt)
  263. }
  264. if err := c.client.HDel(c.ctx, key, field).Err(); err != nil {
  265. return errors.Wrapf(err, "redis del field: %s err", key, field)
  266. }
  267. return nil
  268. }
  269. func (c *cacheRepo) HGetAll(key string, options ...Option) (map[string]string, error) {
  270. ts := time.Now()
  271. opt := newOption()
  272. defer func() {
  273. if opt.Trace != nil {
  274. opt.Redis.Timestamp = time_parse.CSTLayoutString()
  275. opt.Redis.Handle = "hash get all"
  276. opt.Redis.Key = key
  277. opt.Redis.CostSeconds = time.Since(ts).Seconds()
  278. opt.Trace.AppendRedis(opt.Redis)
  279. }
  280. }()
  281. for _, f := range options {
  282. f(opt)
  283. }
  284. value, err := c.client.HGetAll(c.ctx, key).Result()
  285. if err != nil {
  286. return nil, errors.Wrapf(err, "redis get all key: %s err", key)
  287. }
  288. return value, nil
  289. }
  290. func (c *cacheRepo) LPush(key, value string, options ...Option) error {
  291. ts := time.Now()
  292. opt := newOption()
  293. defer func() {
  294. if opt.Trace != nil {
  295. opt.Redis.Timestamp = time_parse.CSTLayoutString()
  296. opt.Redis.Handle = "list push"
  297. opt.Redis.Key = key
  298. opt.Redis.Value = value
  299. opt.Redis.CostSeconds = time.Since(ts).Seconds()
  300. opt.Trace.AppendRedis(opt.Redis)
  301. }
  302. }()
  303. for _, f := range options {
  304. f(opt)
  305. }
  306. _, err := c.client.LPush(c.ctx, key, value).Result()
  307. if err != nil {
  308. return errors.Wrapf(err, "redis list push key: %s value: %s", key, value)
  309. }
  310. return nil
  311. }
  312. func (c *cacheRepo) LLen(key string, options ...Option) (int64, error) {
  313. ts := time.Now()
  314. opt := newOption()
  315. defer func() {
  316. if opt.Trace != nil {
  317. opt.Redis.Timestamp = time_parse.CSTLayoutString()
  318. opt.Redis.Handle = "list len"
  319. opt.Redis.Key = key
  320. opt.Redis.CostSeconds = time.Since(ts).Seconds()
  321. opt.Trace.AppendRedis(opt.Redis)
  322. }
  323. }()
  324. for _, f := range options {
  325. f(opt)
  326. }
  327. value, err := c.client.LLen(c.ctx, key).Result()
  328. if err != nil {
  329. return 0, errors.Wrapf(err, "redis list len key: %s err", key)
  330. }
  331. return value, nil
  332. }
  333. func (c *cacheRepo) BRPop(key string, timeout time.Duration, options ...Option) (string, error) {
  334. ts := time.Now()
  335. opt := newOption()
  336. defer func() {
  337. if opt.Trace != nil {
  338. opt.Redis.Timestamp = time_parse.CSTLayoutString()
  339. opt.Redis.Handle = "list brpop"
  340. opt.Redis.Key = key
  341. opt.Redis.TTL = timeout.Seconds()
  342. opt.Redis.CostSeconds = time.Since(ts).Seconds()
  343. opt.Trace.AppendRedis(opt.Redis)
  344. }
  345. }()
  346. for _, f := range options {
  347. f(opt)
  348. }
  349. value, err := c.client.BRPop(c.ctx, timeout, key).Result()
  350. if err != nil {
  351. return "", errors.Wrapf(err, "redis list len key: %s err", key)
  352. }
  353. return value[0], nil
  354. }
  355. func (c *cacheRepo) Close() error {
  356. return c.client.Close()
  357. }