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.
 
 
 

271 lines
5.8 KiB

  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. }