http urls monitor.

redis.go 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520
  1. package redis
  2. import (
  3. "context"
  4. "fmt"
  5. "log"
  6. "os"
  7. "time"
  8. "github.com/go-redis/redis/internal"
  9. "github.com/go-redis/redis/internal/pool"
  10. "github.com/go-redis/redis/internal/proto"
  11. )
  12. // Nil reply Redis returns when key does not exist.
  13. const Nil = proto.Nil
  14. func init() {
  15. SetLogger(log.New(os.Stderr, "redis: ", log.LstdFlags|log.Lshortfile))
  16. }
  17. func SetLogger(logger *log.Logger) {
  18. internal.Logger = logger
  19. }
  20. type baseClient struct {
  21. opt *Options
  22. connPool pool.Pooler
  23. process func(Cmder) error
  24. processPipeline func([]Cmder) error
  25. processTxPipeline func([]Cmder) error
  26. onClose func() error // hook called when client is closed
  27. }
  28. func (c *baseClient) init() {
  29. c.process = c.defaultProcess
  30. c.processPipeline = c.defaultProcessPipeline
  31. c.processTxPipeline = c.defaultProcessTxPipeline
  32. }
  33. func (c *baseClient) String() string {
  34. return fmt.Sprintf("Redis<%s db:%d>", c.getAddr(), c.opt.DB)
  35. }
  36. func (c *baseClient) newConn() (*pool.Conn, error) {
  37. cn, err := c.connPool.NewConn()
  38. if err != nil {
  39. return nil, err
  40. }
  41. if cn.InitedAt.IsZero() {
  42. if err := c.initConn(cn); err != nil {
  43. _ = c.connPool.CloseConn(cn)
  44. return nil, err
  45. }
  46. }
  47. return cn, nil
  48. }
  49. func (c *baseClient) getConn() (*pool.Conn, error) {
  50. cn, err := c.connPool.Get()
  51. if err != nil {
  52. return nil, err
  53. }
  54. if cn.InitedAt.IsZero() {
  55. err := c.initConn(cn)
  56. if err != nil {
  57. c.connPool.Remove(cn)
  58. return nil, err
  59. }
  60. }
  61. return cn, nil
  62. }
  63. func (c *baseClient) releaseConn(cn *pool.Conn, err error) bool {
  64. if internal.IsBadConn(err, false) {
  65. c.connPool.Remove(cn)
  66. return false
  67. }
  68. c.connPool.Put(cn)
  69. return true
  70. }
  71. func (c *baseClient) initConn(cn *pool.Conn) error {
  72. cn.InitedAt = time.Now()
  73. if c.opt.Password == "" &&
  74. c.opt.DB == 0 &&
  75. !c.opt.readOnly &&
  76. c.opt.OnConnect == nil {
  77. return nil
  78. }
  79. conn := newConn(c.opt, cn)
  80. _, err := conn.Pipelined(func(pipe Pipeliner) error {
  81. if c.opt.Password != "" {
  82. pipe.Auth(c.opt.Password)
  83. }
  84. if c.opt.DB > 0 {
  85. pipe.Select(c.opt.DB)
  86. }
  87. if c.opt.readOnly {
  88. pipe.ReadOnly()
  89. }
  90. return nil
  91. })
  92. if err != nil {
  93. return err
  94. }
  95. if c.opt.OnConnect != nil {
  96. return c.opt.OnConnect(conn)
  97. }
  98. return nil
  99. }
  100. // Do creates a Cmd from the args and processes the cmd.
  101. func (c *baseClient) Do(args ...interface{}) *Cmd {
  102. cmd := NewCmd(args...)
  103. c.Process(cmd)
  104. return cmd
  105. }
  106. // WrapProcess wraps function that processes Redis commands.
  107. func (c *baseClient) WrapProcess(
  108. fn func(oldProcess func(cmd Cmder) error) func(cmd Cmder) error,
  109. ) {
  110. c.process = fn(c.process)
  111. }
  112. func (c *baseClient) Process(cmd Cmder) error {
  113. return c.process(cmd)
  114. }
  115. func (c *baseClient) defaultProcess(cmd Cmder) error {
  116. for attempt := 0; attempt <= c.opt.MaxRetries; attempt++ {
  117. if attempt > 0 {
  118. time.Sleep(c.retryBackoff(attempt))
  119. }
  120. cn, err := c.getConn()
  121. if err != nil {
  122. cmd.setErr(err)
  123. if internal.IsRetryableError(err, true) {
  124. continue
  125. }
  126. return err
  127. }
  128. err = cn.WithWriter(c.opt.WriteTimeout, func(wr *proto.Writer) error {
  129. return writeCmd(wr, cmd)
  130. })
  131. if err != nil {
  132. c.releaseConn(cn, err)
  133. cmd.setErr(err)
  134. if internal.IsRetryableError(err, true) {
  135. continue
  136. }
  137. return err
  138. }
  139. err = cn.WithReader(c.cmdTimeout(cmd), func(rd *proto.Reader) error {
  140. return cmd.readReply(rd)
  141. })
  142. c.releaseConn(cn, err)
  143. if err != nil && internal.IsRetryableError(err, cmd.readTimeout() == nil) {
  144. continue
  145. }
  146. return err
  147. }
  148. return cmd.Err()
  149. }
  150. func (c *baseClient) retryBackoff(attempt int) time.Duration {
  151. return internal.RetryBackoff(attempt, c.opt.MinRetryBackoff, c.opt.MaxRetryBackoff)
  152. }
  153. func (c *baseClient) cmdTimeout(cmd Cmder) time.Duration {
  154. if timeout := cmd.readTimeout(); timeout != nil {
  155. return readTimeout(*timeout)
  156. }
  157. return c.opt.ReadTimeout
  158. }
  159. // Close closes the client, releasing any open resources.
  160. //
  161. // It is rare to Close a Client, as the Client is meant to be
  162. // long-lived and shared between many goroutines.
  163. func (c *baseClient) Close() error {
  164. var firstErr error
  165. if c.onClose != nil {
  166. if err := c.onClose(); err != nil && firstErr == nil {
  167. firstErr = err
  168. }
  169. }
  170. if err := c.connPool.Close(); err != nil && firstErr == nil {
  171. firstErr = err
  172. }
  173. return firstErr
  174. }
  175. func (c *baseClient) getAddr() string {
  176. return c.opt.Addr
  177. }
  178. func (c *baseClient) WrapProcessPipeline(
  179. fn func(oldProcess func([]Cmder) error) func([]Cmder) error,
  180. ) {
  181. c.processPipeline = fn(c.processPipeline)
  182. c.processTxPipeline = fn(c.processTxPipeline)
  183. }
  184. func (c *baseClient) defaultProcessPipeline(cmds []Cmder) error {
  185. return c.generalProcessPipeline(cmds, c.pipelineProcessCmds)
  186. }
  187. func (c *baseClient) defaultProcessTxPipeline(cmds []Cmder) error {
  188. return c.generalProcessPipeline(cmds, c.txPipelineProcessCmds)
  189. }
  190. type pipelineProcessor func(*pool.Conn, []Cmder) (bool, error)
  191. func (c *baseClient) generalProcessPipeline(cmds []Cmder, p pipelineProcessor) error {
  192. for attempt := 0; attempt <= c.opt.MaxRetries; attempt++ {
  193. if attempt > 0 {
  194. time.Sleep(c.retryBackoff(attempt))
  195. }
  196. cn, err := c.getConn()
  197. if err != nil {
  198. setCmdsErr(cmds, err)
  199. return err
  200. }
  201. canRetry, err := p(cn, cmds)
  202. if err == nil || internal.IsRedisError(err) {
  203. c.connPool.Put(cn)
  204. break
  205. }
  206. c.connPool.Remove(cn)
  207. if !canRetry || !internal.IsRetryableError(err, true) {
  208. break
  209. }
  210. }
  211. return cmdsFirstErr(cmds)
  212. }
  213. func (c *baseClient) pipelineProcessCmds(cn *pool.Conn, cmds []Cmder) (bool, error) {
  214. err := cn.WithWriter(c.opt.WriteTimeout, func(wr *proto.Writer) error {
  215. return writeCmd(wr, cmds...)
  216. })
  217. if err != nil {
  218. setCmdsErr(cmds, err)
  219. return true, err
  220. }
  221. err = cn.WithReader(c.opt.ReadTimeout, func(rd *proto.Reader) error {
  222. return pipelineReadCmds(rd, cmds)
  223. })
  224. return true, err
  225. }
  226. func pipelineReadCmds(rd *proto.Reader, cmds []Cmder) error {
  227. for _, cmd := range cmds {
  228. err := cmd.readReply(rd)
  229. if err != nil && !internal.IsRedisError(err) {
  230. return err
  231. }
  232. }
  233. return nil
  234. }
  235. func (c *baseClient) txPipelineProcessCmds(cn *pool.Conn, cmds []Cmder) (bool, error) {
  236. err := cn.WithWriter(c.opt.WriteTimeout, func(wr *proto.Writer) error {
  237. return txPipelineWriteMulti(wr, cmds)
  238. })
  239. if err != nil {
  240. setCmdsErr(cmds, err)
  241. return true, err
  242. }
  243. err = cn.WithReader(c.opt.ReadTimeout, func(rd *proto.Reader) error {
  244. err := txPipelineReadQueued(rd, cmds)
  245. if err != nil {
  246. setCmdsErr(cmds, err)
  247. return err
  248. }
  249. return pipelineReadCmds(rd, cmds)
  250. })
  251. return false, err
  252. }
  253. func txPipelineWriteMulti(wr *proto.Writer, cmds []Cmder) error {
  254. multiExec := make([]Cmder, 0, len(cmds)+2)
  255. multiExec = append(multiExec, NewStatusCmd("MULTI"))
  256. multiExec = append(multiExec, cmds...)
  257. multiExec = append(multiExec, NewSliceCmd("EXEC"))
  258. return writeCmd(wr, multiExec...)
  259. }
  260. func txPipelineReadQueued(rd *proto.Reader, cmds []Cmder) error {
  261. // Parse queued replies.
  262. var statusCmd StatusCmd
  263. err := statusCmd.readReply(rd)
  264. if err != nil {
  265. return err
  266. }
  267. for _ = range cmds {
  268. err = statusCmd.readReply(rd)
  269. if err != nil && !internal.IsRedisError(err) {
  270. return err
  271. }
  272. }
  273. // Parse number of replies.
  274. line, err := rd.ReadLine()
  275. if err != nil {
  276. if err == Nil {
  277. err = TxFailedErr
  278. }
  279. return err
  280. }
  281. switch line[0] {
  282. case proto.ErrorReply:
  283. return proto.ParseErrorReply(line)
  284. case proto.ArrayReply:
  285. // ok
  286. default:
  287. err := fmt.Errorf("redis: expected '*', but got line %q", line)
  288. return err
  289. }
  290. return nil
  291. }
  292. //------------------------------------------------------------------------------
  293. // Client is a Redis client representing a pool of zero or more
  294. // underlying connections. It's safe for concurrent use by multiple
  295. // goroutines.
  296. type Client struct {
  297. baseClient
  298. cmdable
  299. ctx context.Context
  300. }
  301. // NewClient returns a client to the Redis Server specified by Options.
  302. func NewClient(opt *Options) *Client {
  303. opt.init()
  304. c := Client{
  305. baseClient: baseClient{
  306. opt: opt,
  307. connPool: newConnPool(opt),
  308. },
  309. }
  310. c.baseClient.init()
  311. c.init()
  312. return &c
  313. }
  314. func (c *Client) init() {
  315. c.cmdable.setProcessor(c.Process)
  316. }
  317. func (c *Client) Context() context.Context {
  318. if c.ctx != nil {
  319. return c.ctx
  320. }
  321. return context.Background()
  322. }
  323. func (c *Client) WithContext(ctx context.Context) *Client {
  324. if ctx == nil {
  325. panic("nil context")
  326. }
  327. c2 := c.copy()
  328. c2.ctx = ctx
  329. return c2
  330. }
  331. func (c *Client) copy() *Client {
  332. cp := *c
  333. cp.init()
  334. return &cp
  335. }
  336. // Options returns read-only Options that were used to create the client.
  337. func (c *Client) Options() *Options {
  338. return c.opt
  339. }
  340. type PoolStats pool.Stats
  341. // PoolStats returns connection pool stats.
  342. func (c *Client) PoolStats() *PoolStats {
  343. stats := c.connPool.Stats()
  344. return (*PoolStats)(stats)
  345. }
  346. func (c *Client) Pipelined(fn func(Pipeliner) error) ([]Cmder, error) {
  347. return c.Pipeline().Pipelined(fn)
  348. }
  349. func (c *Client) Pipeline() Pipeliner {
  350. pipe := Pipeline{
  351. exec: c.processPipeline,
  352. }
  353. pipe.statefulCmdable.setProcessor(pipe.Process)
  354. return &pipe
  355. }
  356. func (c *Client) TxPipelined(fn func(Pipeliner) error) ([]Cmder, error) {
  357. return c.TxPipeline().Pipelined(fn)
  358. }
  359. // TxPipeline acts like Pipeline, but wraps queued commands with MULTI/EXEC.
  360. func (c *Client) TxPipeline() Pipeliner {
  361. pipe := Pipeline{
  362. exec: c.processTxPipeline,
  363. }
  364. pipe.statefulCmdable.setProcessor(pipe.Process)
  365. return &pipe
  366. }
  367. func (c *Client) pubSub() *PubSub {
  368. pubsub := &PubSub{
  369. opt: c.opt,
  370. newConn: func(channels []string) (*pool.Conn, error) {
  371. return c.newConn()
  372. },
  373. closeConn: c.connPool.CloseConn,
  374. }
  375. pubsub.init()
  376. return pubsub
  377. }
  378. // Subscribe subscribes the client to the specified channels.
  379. // Channels can be omitted to create empty subscription.
  380. func (c *Client) Subscribe(channels ...string) *PubSub {
  381. pubsub := c.pubSub()
  382. if len(channels) > 0 {
  383. _ = pubsub.Subscribe(channels...)
  384. }
  385. return pubsub
  386. }
  387. // PSubscribe subscribes the client to the given patterns.
  388. // Patterns can be omitted to create empty subscription.
  389. func (c *Client) PSubscribe(channels ...string) *PubSub {
  390. pubsub := c.pubSub()
  391. if len(channels) > 0 {
  392. _ = pubsub.PSubscribe(channels...)
  393. }
  394. return pubsub
  395. }
  396. //------------------------------------------------------------------------------
  397. // Conn is like Client, but its pool contains single connection.
  398. type Conn struct {
  399. baseClient
  400. statefulCmdable
  401. }
  402. func newConn(opt *Options, cn *pool.Conn) *Conn {
  403. c := Conn{
  404. baseClient: baseClient{
  405. opt: opt,
  406. connPool: pool.NewSingleConnPool(cn),
  407. },
  408. }
  409. c.baseClient.init()
  410. c.statefulCmdable.setProcessor(c.Process)
  411. return &c
  412. }
  413. func (c *Conn) Pipelined(fn func(Pipeliner) error) ([]Cmder, error) {
  414. return c.Pipeline().Pipelined(fn)
  415. }
  416. func (c *Conn) Pipeline() Pipeliner {
  417. pipe := Pipeline{
  418. exec: c.processPipeline,
  419. }
  420. pipe.statefulCmdable.setProcessor(pipe.Process)
  421. return &pipe
  422. }
  423. func (c *Conn) TxPipelined(fn func(Pipeliner) error) ([]Cmder, error) {
  424. return c.TxPipeline().Pipelined(fn)
  425. }
  426. // TxPipeline acts like Pipeline, but wraps queued commands with MULTI/EXEC.
  427. func (c *Conn) TxPipeline() Pipeliner {
  428. pipe := Pipeline{
  429. exec: c.processTxPipeline,
  430. }
  431. pipe.statefulCmdable.setProcessor(pipe.Process)
  432. return &pipe
  433. }