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.
 
 
 

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