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.
 
 
 

300 lines
6.6 KiB

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