PageRenderTime 55ms CodeModel.GetById 31ms RepoModel.GetById 0ms app.codeStats 0ms

/exp/godis.go

http://github.com/simonz05/godis
Go | 210 lines | 99 code | 30 blank | 81 comment | 21 complexity | eebfce36fa125b58a327e1977f9ab8cb MD5 | raw file
Possible License(s): BSD-3-Clause
  1. // Package redis implements a db client for Redis.
  2. //
  3. // Connection interface
  4. //
  5. // The Connection interface is a very simple interface to Redis. The Conn
  6. // struct implements this interface and can be used to write commands and read
  7. // replies from Redis.
  8. //
  9. // The Connection interface is used to implement the Client and AsyncClient.
  10. // Unless you like to implment your own client, use either of them instead of a
  11. // single connection.
  12. //
  13. // Client
  14. //
  15. // The Client implements one method; Call(). This writes your command to the
  16. // database, then reads the subsequent reply and returns it to you.
  17. //
  18. // The Client struct also has a pool of connections so it's safe to use a
  19. // client in a concurrent context. You can create one client for your entire
  20. // program and share it between go routines.
  21. //
  22. // c := redis.NewClient("tcp:127.0.0.1:6379")
  23. // reply, e := c.Call("GET", "foo")
  24. //
  25. // if e != nil {
  26. // // handle error
  27. // }
  28. //
  29. // println(reply.Elem.String())
  30. //
  31. // AsyncClient
  32. //
  33. // The AsyncClient works exactly like the regular Client, and implements a
  34. // single method Call(), but this method does not return any reply, only an
  35. // error or nil.
  36. //
  37. // c := redis.NewAsyncClient("tcp:127.0.0.1:6379")
  38. // c.Call("SET", "foo", 1)
  39. // c.Call("GET", "foo")
  40. //
  41. // When we send our command and arguments to the Call() method nothing is sent
  42. // to the Redis server. To get the reply for our commands from Redis we use the
  43. // Read() method. Read sends any buffered commands to the Redis server, and
  44. // then reads one reply. Subsequent calls to Read will return more replies or
  45. // block if there are none.
  46. //
  47. // // reply from SET
  48. // reply, _ := c.Read()
  49. //
  50. // // reply from GET
  51. // reply, _ = c.Read()
  52. //
  53. // println(reply.Elem.Int()) // prints 1
  54. //
  55. // Due to the nature of how the AsyncClient works, it's not safe to share it
  56. // between go routines.
  57. package redis
  58. import (
  59. "bytes"
  60. "strings"
  61. )
  62. // Client implements a Redis client which handles connections to the database
  63. // in a pool. The size of the pool can be adjusted with by setting the
  64. // MaxConnections variable before creating a client.
  65. type Client struct {
  66. Addr string
  67. Proto string
  68. Db int
  69. Password string
  70. pool *connPool
  71. }
  72. // NewClient expects a addr like "tcp:127.0.0.1:6379"
  73. // It returns a new *Client.
  74. func NewClient(addr string, db int, password string) *Client {
  75. if addr == "" {
  76. addr = "tcp:127.0.0.1:6379"
  77. }
  78. na := strings.SplitN(addr, ":", 2)
  79. return &Client{na[1], na[0], db, password, newConnPool()}
  80. }
  81. // Call is the canonical way of talking to Redis. It accepts any
  82. // Redis command and a arbitrary number of arguments.
  83. // Call returns a Reply object or an error.
  84. func (c *Client) Call(args ...interface{}) (*Reply, error) {
  85. conn, err := c.connect()
  86. defer c.pool.push(conn)
  87. if err != nil {
  88. return nil, err
  89. }
  90. conn.Write(args...)
  91. if err != nil {
  92. return nil, err
  93. }
  94. return conn.Read()
  95. }
  96. // Pop a connection from pool
  97. func (c *Client) connect() (conn Connection, err error) {
  98. conn = c.pool.pop()
  99. if conn == nil {
  100. conn, err = NewConn(c.Addr, c.Proto, c.Db, c.Password)
  101. if err != nil {
  102. return nil, err
  103. }
  104. }
  105. return conn, nil
  106. }
  107. // Use the connection settings from Client to create a new AsyncClient
  108. func (c *Client) AsyncClient() *AsyncClient {
  109. return &AsyncClient{c, bytes.NewBuffer(make([]byte, 0, 1024*16)), nil, 0}
  110. }
  111. // Async client implements an asynchronous client. It is very similar to Client
  112. // except that it maintains a buffer of commands which first are sent to Redis
  113. // once we explicitly request a reply.
  114. type AsyncClient struct {
  115. *Client
  116. buf *bytes.Buffer
  117. conn Connection
  118. queued int
  119. }
  120. // NewAsyncClient expects a addr like "tcp:127.0.0.1:6379"
  121. // It returns a new *Client.
  122. func NewAsyncClient(addr string, db int, password string) *AsyncClient {
  123. return &AsyncClient{
  124. NewClient(addr, db, password),
  125. bytes.NewBuffer(make([]byte, 0, 1024*16)),
  126. nil,
  127. 0,
  128. }
  129. }
  130. // Call appends a command to the write buffer or returns an error.
  131. func (ac *AsyncClient) Call(args ...interface{}) (err error) {
  132. _, err = ac.buf.Write(format(args...))
  133. ac.queued++
  134. return err
  135. }
  136. // Read does three things.
  137. //
  138. // 1) Open connection to Redis server, if there is none.
  139. // 2) Write any buffered commands to the server.
  140. // 3) Try to read a reply from the server, or block on read.
  141. //
  142. // Read returns a Reply or error.
  143. func (ac *AsyncClient) Read() (*Reply, error) {
  144. if ac.conn == nil {
  145. conn, e := NewConn(ac.Addr, ac.Proto, ac.Db, ac.Password)
  146. if e != nil {
  147. return nil, e
  148. }
  149. ac.conn = conn
  150. }
  151. if ac.buf.Len() > 0 {
  152. _, err := ac.buf.WriteTo(ac.conn.Sock())
  153. if err != nil {
  154. return nil, err
  155. }
  156. }
  157. reply, e := ac.conn.Read()
  158. ac.queued--
  159. return reply, e
  160. }
  161. func (ac *AsyncClient) Queued() int {
  162. return ac.queued
  163. }
  164. func (ac *AsyncClient) ReadAll() ([]*Reply, error) {
  165. replies := make([]*Reply, 0, ac.queued)
  166. for ac.Queued() > 0 {
  167. r, e := ac.Read()
  168. if e != nil {
  169. return nil, e
  170. }
  171. replies = append(replies, r)
  172. }
  173. return replies, nil
  174. }
  175. // The AsyncClient will only open one connection. This is not automatically
  176. // closed, so to close it we need to call this method.
  177. func (ac *AsyncClient) Close() {
  178. ac.conn.Close()
  179. ac.conn = nil
  180. }