You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

client.go 5.8 KiB

11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. // The client package helps developers connect to Gearmand, send
  2. // jobs and fetch result.
  3. package client
  4. import (
  5. "io"
  6. "net"
  7. "sync"
  8. )
  9. // One client connect to one server.
  10. // Use Pool for multi-connections.
  11. type Client struct {
  12. sync.Mutex
  13. net, addr, lastcall string
  14. respHandler map[string]ResponseHandler
  15. innerHandler map[string]ResponseHandler
  16. in chan *Response
  17. isConn bool
  18. conn net.Conn
  19. ErrorHandler ErrorHandler
  20. }
  21. // Return a client.
  22. func New(network, addr string) (client *Client, err error) {
  23. client = &Client{
  24. net: network,
  25. addr: addr,
  26. respHandler: make(map[string]ResponseHandler, queueSize),
  27. innerHandler: make(map[string]ResponseHandler, queueSize),
  28. in: make(chan *Response, queueSize),
  29. }
  30. client.conn, err = net.Dial(client.net, client.addr)
  31. if err != nil {
  32. return
  33. }
  34. client.isConn = true
  35. go client.readLoop()
  36. go client.processLoop()
  37. return
  38. }
  39. func (client *Client) write(req *request) (err error) {
  40. var n int
  41. buf := req.Encode()
  42. for i := 0; i < len(buf); i += n {
  43. n, err = client.conn.Write(buf[i:])
  44. if err != nil {
  45. return
  46. }
  47. }
  48. return
  49. }
  50. func (client *Client) read(length int) (data []byte, err error) {
  51. n := 0
  52. buf := getBuffer(bufferSize)
  53. // read until data can be unpacked
  54. for i := length; i > 0 || len(data) < minPacketLength; i -= n {
  55. if n, err = client.conn.Read(buf); err != nil {
  56. if err == io.EOF {
  57. err = ErrLostConn
  58. }
  59. return
  60. }
  61. data = append(data, buf[0:n]...)
  62. if n < bufferSize {
  63. break
  64. }
  65. }
  66. return
  67. }
  68. func (client *Client) readLoop() {
  69. defer close(client.in)
  70. var data, leftdata []byte
  71. var err error
  72. var resp *Response
  73. for {
  74. if data, err = client.read(bufferSize); err != nil {
  75. client.err(err)
  76. if err == ErrLostConn {
  77. break
  78. }
  79. // If it is unexpected error and the connection wasn't
  80. // closed by Gearmand, the client should close the conection
  81. // and reconnect to job server.
  82. client.Close()
  83. client.conn, err = net.Dial(client.net, client.addr)
  84. if err != nil {
  85. client.err(err)
  86. break
  87. }
  88. continue
  89. }
  90. if len(leftdata) > 0 { // some data left for processing
  91. data = append(leftdata, data...)
  92. }
  93. l := len(data)
  94. if l < minPacketLength { // not enough data
  95. leftdata = data
  96. continue
  97. }
  98. if resp, l, err = decodeResponse(data); err != nil {
  99. client.err(err)
  100. continue
  101. }
  102. client.in <- resp
  103. leftdata = nil
  104. if len(data) > l {
  105. leftdata = data[l:]
  106. }
  107. }
  108. }
  109. func (client *Client) processLoop() {
  110. for resp := range client.in {
  111. switch resp.DataType {
  112. case dtError:
  113. if client.lastcall != "" {
  114. resp = client.handleInner(client.lastcall, resp)
  115. client.lastcall = ""
  116. } else {
  117. client.err(getError(resp.Data))
  118. }
  119. case dtStatusRes:
  120. resp = client.handleInner("s"+resp.Handle, resp)
  121. case dtJobCreated:
  122. resp = client.handleInner("c", resp)
  123. case dtEchoRes:
  124. resp = client.handleInner("e", resp)
  125. case dtWorkData, dtWorkWarning, dtWorkStatus, dtWorkComplete,
  126. dtWorkFail, dtWorkException:
  127. resp = client.handleResponse(resp.Handle, resp)
  128. }
  129. }
  130. }
  131. func (client *Client) err(e error) {
  132. if client.ErrorHandler != nil {
  133. client.ErrorHandler(e)
  134. }
  135. }
  136. func (client *Client) handleResponse(key string, resp *Response) *Response {
  137. if h, ok := client.respHandler[key]; ok {
  138. h(resp)
  139. delete(client.respHandler, key)
  140. return nil
  141. }
  142. return resp
  143. }
  144. func (client *Client) handleInner(key string, resp *Response) *Response {
  145. if h, ok := client.innerHandler[key]; ok {
  146. h(resp)
  147. delete(client.innerHandler, key)
  148. return nil
  149. }
  150. return resp
  151. }
  152. func (client *Client) do(funcname string, data []byte,
  153. flag uint32) (handle string, err error) {
  154. var mutex sync.Mutex
  155. mutex.Lock()
  156. client.lastcall = "c"
  157. client.innerHandler["c"] = func(resp *Response) {
  158. if resp.DataType == dtError {
  159. err = getError(resp.Data)
  160. return
  161. }
  162. handle = resp.Handle
  163. mutex.Unlock()
  164. }
  165. id := IdGen.Id()
  166. req := getJob(id, []byte(funcname), data)
  167. req.DataType = flag
  168. client.write(req)
  169. mutex.Lock()
  170. return
  171. }
  172. // Call the function and get a response.
  173. // flag can be set to: JobLow, JobNormal and JobHigh
  174. func (client *Client) Do(funcname string, data []byte,
  175. flag byte, h ResponseHandler) (handle string, err error) {
  176. var datatype uint32
  177. switch flag {
  178. case JobLow:
  179. datatype = dtSubmitJobLow
  180. case JobHigh:
  181. datatype = dtSubmitJobHigh
  182. default:
  183. datatype = dtSubmitJob
  184. }
  185. handle, err = client.do(funcname, data, datatype)
  186. if h != nil {
  187. client.respHandler[handle] = h
  188. }
  189. return
  190. }
  191. // Call the function in background, no response needed.
  192. // flag can be set to: JobLow, JobNormal and JobHigh
  193. func (client *Client) DoBg(funcname string, data []byte,
  194. flag byte) (handle string, err error) {
  195. var datatype uint32
  196. switch flag {
  197. case JobLow:
  198. datatype = dtSubmitJobLowBg
  199. case JobHigh:
  200. datatype = dtSubmitJobHighBg
  201. default:
  202. datatype = dtSubmitJobBg
  203. }
  204. handle, err = client.do(funcname, data, datatype)
  205. return
  206. }
  207. // Get job status from job server.
  208. func (client *Client) Status(handle string) (status *Status, err error) {
  209. var mutex sync.Mutex
  210. mutex.Lock()
  211. client.lastcall = "s" + handle
  212. client.innerHandler["s"+handle] = func(resp *Response) {
  213. var err error
  214. status, err = resp.Status()
  215. if err != nil {
  216. client.err(err)
  217. }
  218. mutex.Unlock()
  219. }
  220. req := getRequest()
  221. req.DataType = dtGetStatus
  222. req.Data = []byte(handle)
  223. client.write(req)
  224. mutex.Lock()
  225. return
  226. }
  227. // Echo.
  228. func (client *Client) Echo(data []byte) (echo []byte, err error) {
  229. var mutex sync.Mutex
  230. mutex.Lock()
  231. client.innerHandler["e"] = func(resp *Response) {
  232. echo = resp.Data
  233. mutex.Unlock()
  234. }
  235. req := getRequest()
  236. req.DataType = dtEchoReq
  237. req.Data = data
  238. client.lastcall = "e"
  239. client.write(req)
  240. mutex.Lock()
  241. return
  242. }
  243. // Close connection
  244. func (client *Client) Close() (err error) {
  245. client.Lock()
  246. defer client.Unlock()
  247. if client.conn != nil {
  248. err = client.conn.Close()
  249. client.conn = nil
  250. }
  251. return
  252. }