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.
 
 
 

264 lines
7.1 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. "bytes"
  8. "io"
  9. "net"
  10. "strconv"
  11. "bitbucket.org/mikespook/golib/autoinc"
  12. "bitbucket.org/mikespook/gearman-go/common"
  13. )
  14. // Job handler
  15. type JobHandler func(*Job) error
  16. // Status handler
  17. // handle, known, running, numerator, denominator
  18. type StatusHandler func(string, bool, bool, uint64, uint64)
  19. /*
  20. The client side api for gearman
  21. usage:
  22. c := client.New("tcp4", "127.0.0.1:4730")
  23. handle := c.Do("foobar", []byte("data here"), JOB_LOW | JOB_BG)
  24. */
  25. type Client struct {
  26. ErrHandler common.ErrorHandler
  27. JobHandler JobHandler
  28. StatusHandler StatusHandler
  29. in chan []byte
  30. out chan *Job
  31. jobCreated chan *Job
  32. conn net.Conn
  33. ai *autoinc.AutoInc
  34. }
  35. // Create a new client.
  36. // Connect to "addr" through "network"
  37. // Eg.
  38. // client, err := client.New("tcp4", "127.0.0.1:4730")
  39. func New(network, addr string) (client *Client, err error) {
  40. conn, err := net.Dial(network, addr)
  41. if err != nil {
  42. return
  43. }
  44. client = &Client{
  45. jobCreated: make(chan *Job),
  46. in: make(chan []byte, common.QUEUE_SIZE),
  47. out: make(chan *Job, common.QUEUE_SIZE),
  48. conn: conn,
  49. ai: autoinc.New(0, 1),
  50. }
  51. go client.outLoop()
  52. go client.inLoop()
  53. return
  54. }
  55. // out loop
  56. func (client *Client) outLoop() {
  57. ok := true
  58. for ok {
  59. if job, ok := <-client.out; ok {
  60. client.write(job.Encode())
  61. }
  62. }
  63. }
  64. // in loop
  65. func (client *Client) inLoop() {
  66. for {
  67. rel, err := client.read()
  68. if err != nil {
  69. client.err(err)
  70. continue
  71. }
  72. job, err := decodeJob(rel)
  73. if err != nil {
  74. client.err(err)
  75. continue
  76. }
  77. switch job.DataType {
  78. case common.ERROR:
  79. _, err := common.GetError(job.Data)
  80. client.err(err)
  81. case common.WORK_DATA, common.WORK_WARNING, common.WORK_STATUS,
  82. common.WORK_COMPLETE, common.WORK_FAIL, common.WORK_EXCEPTION,
  83. common.ECHO_RES:
  84. client.handleJob(job)
  85. case common.JOB_CREATED:
  86. client.jobCreated <- job
  87. case common.STATUS_RES:
  88. client.handleStatus(job)
  89. }
  90. }
  91. }
  92. // inner read
  93. func (client *Client) read() (data []byte, err error) {
  94. if len(client.in) > 0 {
  95. // incoming queue is not empty
  96. data = <-client.in
  97. } else {
  98. // empty queue, read data from socket
  99. for {
  100. buf := make([]byte, common.BUFFER_SIZE)
  101. var n int
  102. if n, err = client.conn.Read(buf); err != nil {
  103. if err == io.EOF && n == 0 {
  104. break
  105. }
  106. return
  107. }
  108. data = append(data, buf[0:n]...)
  109. if n < common.BUFFER_SIZE {
  110. break
  111. }
  112. }
  113. }
  114. // split package
  115. start, end := 0, 4
  116. tl := len(data)
  117. for i := 0; i < tl; i++ {
  118. if string(data[start:end]) == common.RES_STR {
  119. l := int(common.BytesToUint32([4]byte{data[start+8], data[start+9], data[start+10], data[start+11]}))
  120. total := l + 12
  121. if total == tl {
  122. return
  123. } else {
  124. client.in <- data[total:]
  125. data = data[:total]
  126. return
  127. }
  128. } else {
  129. start++
  130. end++
  131. }
  132. }
  133. return nil, common.Errorf("Invalid data: %V", data)
  134. }
  135. // error handler
  136. func (client *Client) err (e error) {
  137. if client.ErrHandler != nil {
  138. client.ErrHandler(e)
  139. }
  140. }
  141. // job handler
  142. func (client *Client) handleJob(job *Job) {
  143. if client.JobHandler != nil {
  144. if err := client.JobHandler(job); err != nil {
  145. client.err(err)
  146. }
  147. }
  148. }
  149. // status handler
  150. func (client *Client) handleStatus(job *Job) {
  151. if client.StatusHandler != nil {
  152. data := bytes.SplitN(job.Data, []byte{'\x00'}, 5)
  153. if len(data) != 5 {
  154. client.err(common.Errorf("Invalid data: %V", job.Data))
  155. return
  156. }
  157. handle := string(data[0])
  158. known := (data[1][0] == '1')
  159. running := (data[2][0] == '1')
  160. numerator, err := strconv.ParseUint(string(data[3][0]), 10, 0)
  161. if err != nil {
  162. client.err(common.Errorf("Invalid handle: %s", data[3][0]))
  163. return
  164. }
  165. denominator, err := strconv.ParseUint(string(data[4][0]), 10, 0)
  166. if err != nil {
  167. client.err(common.Errorf("Invalid handle: %s", data[4][0]))
  168. return
  169. }
  170. client.StatusHandler(handle, known, running, numerator, denominator)
  171. }
  172. }
  173. // Do the function.
  174. // funcname is a string with function name.
  175. // data is encoding to byte array.
  176. // flag set the job type, include running level: JOB_LOW, JOB_NORMAL, JOB_HIGH,
  177. // and if it is background job: JOB_BG.
  178. // JOB_LOW | JOB_BG means the job is running with low level in background.
  179. func (client *Client) Do(funcname string, data []byte, flag byte) (handle string, err error) {
  180. var datatype uint32
  181. if flag & common.JOB_LOW == common.JOB_LOW {
  182. if flag & common.JOB_BG == common.JOB_BG {
  183. datatype = common.SUBMIT_JOB_LOW_BG
  184. } else {
  185. datatype = common.SUBMIT_JOB_LOW
  186. }
  187. } else if flag & common.JOB_HIGH == common.JOB_HIGH {
  188. if flag & common.JOB_BG == common.JOB_BG {
  189. datatype = common.SUBMIT_JOB_HIGH_BG
  190. } else {
  191. datatype = common.SUBMIT_JOB_HIGH
  192. }
  193. } else if flag & common.JOB_BG == common.JOB_BG {
  194. datatype = common.SUBMIT_JOB_BG
  195. } else {
  196. datatype = common.SUBMIT_JOB
  197. }
  198. uid := strconv.Itoa(int(client.ai.Id()))
  199. l := len(funcname) + len(uid) + len(data) + 2
  200. rel := make([]byte, l)
  201. rel = append(rel, []byte(funcname)...) // len(funcname)
  202. rel = append(rel, '\x00') // 1 Byte
  203. rel = append(rel, []byte(uid)...) // len(uid)
  204. rel = append(rel, '\x00') // 1 Byte
  205. rel = append(rel, data...) // len(data)
  206. client.writeJob(newJob(common.REQ, datatype, rel))
  207. // Waiting for JOB_CREATED
  208. job := <-client.jobCreated
  209. return string(job.Data), nil
  210. }
  211. // Get job status from job server.
  212. // !!!Not fully tested.!!!
  213. func (client *Client) Status(handle string) {
  214. job := newJob(common.REQ, common.GET_STATUS, []byte(handle))
  215. client.writeJob(job)
  216. }
  217. // Send a something out, get the samething back.
  218. func (client *Client) Echo(data []byte) {
  219. client.writeJob(newJob(common.REQ, common.ECHO_REQ, data))
  220. }
  221. // Send the job to job server.
  222. func (client *Client) writeJob(job *Job) {
  223. client.out <- job
  224. }
  225. // Internal write
  226. func (client *Client) write(buf []byte) (err error) {
  227. var n int
  228. for i := 0; i < len(buf); i += n {
  229. n, err = client.conn.Write(buf[i:])
  230. if err != nil {
  231. return
  232. }
  233. }
  234. return
  235. }
  236. // Close
  237. func (client *Client) Close() (err error) {
  238. close(client.jobCreated)
  239. close(client.in)
  240. return client.conn.Close();
  241. }