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.
 
 
 

306 lines
6.5 KiB

  1. // Copyright 2011 Xing Xing <mikespook@gmail.com>.
  2. // All rights reserved.
  3. // Use of this source code is governed by a MIT
  4. // license that can be found in the LICENSE file.
  5. package client
  6. import (
  7. "io"
  8. "net"
  9. "sync"
  10. // "fmt"
  11. )
  12. /*
  13. The client side api for gearman
  14. usage:
  15. c := client.New("tcp4", "127.0.0.1:4730")
  16. handle := c.Do("foobar", []byte("data here"), JOB_LOW | JOB_BG)
  17. */
  18. type Client struct {
  19. sync.Mutex
  20. net, addr, lastcall string
  21. respHandler map[string]ResponseHandler
  22. innerHandler map[string]ResponseHandler
  23. in chan []byte
  24. isConn bool
  25. conn net.Conn
  26. ErrorHandler ErrorHandler
  27. }
  28. // Create a new client.
  29. // Connect to "addr" through "network"
  30. // Eg.
  31. // client, err := client.New("127.0.0.1:4730")
  32. func New(net, addr string) (client *Client, err error) {
  33. client = &Client{
  34. net: net,
  35. addr: addr,
  36. respHandler: make(map[string]ResponseHandler, QUEUE_SIZE),
  37. innerHandler: make(map[string]ResponseHandler, QUEUE_SIZE),
  38. in: make(chan []byte, QUEUE_SIZE),
  39. }
  40. if err = client.connect(); err != nil {
  41. return
  42. }
  43. go client.readLoop()
  44. go client.processLoop()
  45. return
  46. }
  47. // {{{ private functions
  48. //
  49. func (client *Client) connect() (err error) {
  50. client.conn, err = net.Dial(client.net, client.addr)
  51. if err != nil {
  52. return
  53. }
  54. client.isConn = true
  55. return
  56. }
  57. // Internal write
  58. func (client *Client) write(req *request) (err error) {
  59. var n int
  60. buf := req.Encode()
  61. for i := 0; i < len(buf); i += n {
  62. n, err = client.conn.Write(buf[i:])
  63. if err != nil {
  64. return
  65. }
  66. }
  67. return
  68. }
  69. // read length bytes from the socket
  70. func (client *Client) read(length int) (data []byte, err error) {
  71. n := 0
  72. buf := getBuffer(BUFFER_SIZE)
  73. // read until data can be unpacked
  74. for i := length; i > 0 || len(data) < MIN_PACKET_LEN; i -= n {
  75. if n, err = client.conn.Read(buf); err != nil {
  76. if !client.isConn {
  77. err = ErrConnClosed
  78. return
  79. }
  80. if err == io.EOF && n == 0 {
  81. if data == nil {
  82. err = ErrConnection
  83. }
  84. }
  85. return
  86. }
  87. data = append(data, buf[0:n]...)
  88. if n < BUFFER_SIZE {
  89. break
  90. }
  91. }
  92. return
  93. }
  94. // read data from socket
  95. func (client *Client) readLoop() {
  96. var data []byte
  97. var err error
  98. for client.isConn {
  99. if data, err = client.read(BUFFER_SIZE); err != nil {
  100. if err == ErrConnClosed {
  101. break
  102. }
  103. client.err(err)
  104. continue
  105. }
  106. client.in <- data
  107. }
  108. close(client.in)
  109. }
  110. // decode data & process it
  111. func (client *Client) processLoop() {
  112. var resp *Response
  113. var l int
  114. var err error
  115. var data, leftdata []byte
  116. for data = range client.in {
  117. if len(leftdata) > 0 { // some data left for processing
  118. data = append(leftdata, data...)
  119. }
  120. l = len(data)
  121. if l < MIN_PACKET_LEN { // not enough data
  122. leftdata = data
  123. continue
  124. }
  125. if resp, l, err = decodeResponse(data); err != nil {
  126. client.err(err)
  127. continue
  128. }
  129. leftdata = nil
  130. for resp != nil {
  131. switch resp.DataType {
  132. case ERROR:
  133. if client.lastcall != "" {
  134. resp = client.handleInner(client.lastcall, resp)
  135. client.lastcall = ""
  136. } else {
  137. client.err(GetError(resp.Data))
  138. }
  139. case STATUS_RES:
  140. resp = client.handleInner("s"+resp.Handle, resp)
  141. case JOB_CREATED:
  142. resp = client.handleInner("c", resp)
  143. case ECHO_RES:
  144. resp = client.handleInner("e", resp)
  145. case WORK_DATA, WORK_WARNING, WORK_STATUS, WORK_COMPLETE,
  146. WORK_FAIL, WORK_EXCEPTION:
  147. resp = client.handleResponse(resp.Handle, resp)
  148. }
  149. }
  150. if len(data) > l {
  151. leftdata = data[l:]
  152. }
  153. }
  154. }
  155. // error handler
  156. func (client *Client) err(e error) {
  157. if client.ErrorHandler != nil {
  158. client.ErrorHandler(e)
  159. }
  160. }
  161. // job handler
  162. func (client *Client) handleResponse(key string, resp *Response) *Response {
  163. if h, ok := client.respHandler[key]; ok {
  164. h(resp)
  165. delete(client.respHandler, key)
  166. return nil
  167. }
  168. return resp
  169. }
  170. // job handler
  171. func (client *Client) handleInner(key string, resp *Response) * Response {
  172. if h, ok := client.innerHandler[key]; ok {
  173. h(resp)
  174. delete(client.innerHandler, key)
  175. return nil
  176. }
  177. return resp
  178. }
  179. // Internal do
  180. func (client *Client) do(funcname string, data []byte,
  181. flag uint32) (handle string, err error) {
  182. var mutex sync.Mutex
  183. mutex.Lock()
  184. client.lastcall = "c"
  185. client.innerHandler["c"] = func(resp *Response) {
  186. if resp.DataType == ERROR {
  187. err = GetError(resp.Data)
  188. return
  189. }
  190. handle = resp.Handle
  191. mutex.Unlock()
  192. }
  193. id := IdGen.Id()
  194. req := getJob(id, []byte(funcname), data)
  195. req.DataType = flag
  196. client.write(req)
  197. mutex.Lock()
  198. return
  199. }
  200. // }}}
  201. // Do the function.
  202. // funcname is a string with function name.
  203. // data is encoding to byte array.
  204. // flag set the job type, include running level: JOB_LOW, JOB_NORMAL, JOB_HIGH
  205. func (client *Client) Do(funcname string, data []byte,
  206. flag byte, h ResponseHandler) (handle string, err error) {
  207. var datatype uint32
  208. switch flag {
  209. case JOB_LOW:
  210. datatype = SUBMIT_JOB_LOW
  211. case JOB_HIGH:
  212. datatype = SUBMIT_JOB_HIGH
  213. default:
  214. datatype = SUBMIT_JOB
  215. }
  216. handle, err = client.do(funcname, data, datatype)
  217. if h != nil {
  218. client.respHandler[handle] = h
  219. }
  220. return
  221. }
  222. // Do the function at background.
  223. // funcname is a string with function name.
  224. // data is encoding to byte array.
  225. // flag set the job type, include running level: JOB_LOW, JOB_NORMAL, JOB_HIGH
  226. func (client *Client) DoBg(funcname string, data []byte,
  227. flag byte) (handle string, err error) {
  228. var datatype uint32
  229. switch flag {
  230. case JOB_LOW:
  231. datatype = SUBMIT_JOB_LOW_BG
  232. case JOB_HIGH:
  233. datatype = SUBMIT_JOB_HIGH_BG
  234. default:
  235. datatype = SUBMIT_JOB_BG
  236. }
  237. handle, err = client.do(funcname, data, datatype)
  238. return
  239. }
  240. // Get job status from job server.
  241. // !!!Not fully tested.!!!
  242. func (client *Client) Status(handle string) (status *Status, err error) {
  243. var mutex sync.Mutex
  244. mutex.Lock()
  245. client.lastcall = "s" + handle
  246. client.innerHandler["s"+handle] = func(resp *Response) {
  247. var err error
  248. status, err = resp.Status()
  249. if err != nil {
  250. client.err(err)
  251. }
  252. mutex.Unlock()
  253. }
  254. req := getRequest()
  255. req.DataType = GET_STATUS
  256. req.Data = []byte(handle)
  257. client.write(req)
  258. mutex.Lock()
  259. return
  260. }
  261. // Send a something out, get the samething back.
  262. func (client *Client) Echo(data []byte) (echo []byte, err error) {
  263. var mutex sync.Mutex
  264. mutex.Lock()
  265. client.innerHandler["e"] = func(resp *Response) {
  266. echo = resp.Data
  267. mutex.Unlock()
  268. }
  269. req := getRequest()
  270. req.DataType = ECHO_REQ
  271. req.Data = data
  272. client.lastcall = "e"
  273. client.write(req)
  274. mutex.Lock()
  275. return
  276. }
  277. // Close
  278. func (client *Client) Close() (err error) {
  279. client.isConn = false
  280. return client.conn.Close()
  281. }