| 
									
										
										
										
											2012-03-26 13:32:59 +08:00
										 |  |  | // Copyright 2011 Xing Xing <mikespook@gmail.com> All rights reserved.
 | 
					
						
							|  |  |  | // Use of this source code is governed by a MIT
 | 
					
						
							|  |  |  | // license that can be found in the LICENSE file.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | package worker | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2012-12-28 21:19:58 +08:00
										 |  |  |     "github.com/miraclesu/gearman-go/common" | 
					
						
							| 
									
										
										
										
											2012-03-26 13:32:59 +08:00
										 |  |  |     "io" | 
					
						
							|  |  |  |     "net" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // The agent of job server.
 | 
					
						
							| 
									
										
										
										
											2012-05-24 16:49:35 +08:00
										 |  |  | type agent struct { | 
					
						
							| 
									
										
										
										
											2012-12-28 21:19:58 +08:00
										 |  |  |     conn   net.Conn | 
					
						
							|  |  |  |     worker *Worker | 
					
						
							|  |  |  |     in     chan []byte | 
					
						
							|  |  |  |     out    chan *Job | 
					
						
							|  |  |  |     addr   string | 
					
						
							| 
									
										
										
										
											2012-03-26 13:32:59 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Create the agent of job server.
 | 
					
						
							| 
									
										
										
										
											2012-05-24 16:49:35 +08:00
										 |  |  | func newAgent(addr string, worker *Worker) (a *agent, err error) { | 
					
						
							| 
									
										
										
										
											2012-05-23 17:45:52 +08:00
										 |  |  |     conn, err := net.Dial(common.NETWORK, addr) | 
					
						
							| 
									
										
										
										
											2012-03-26 13:32:59 +08:00
										 |  |  |     if err != nil { | 
					
						
							| 
									
										
										
										
											2012-05-24 16:49:35 +08:00
										 |  |  |         return | 
					
						
							| 
									
										
										
										
											2012-03-26 13:32:59 +08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2012-05-24 16:49:35 +08:00
										 |  |  |     a = &agent{ | 
					
						
							| 
									
										
										
										
											2012-12-28 21:19:58 +08:00
										 |  |  |         conn:   conn, | 
					
						
							| 
									
										
										
										
											2012-05-23 17:45:52 +08:00
										 |  |  |         worker: worker, | 
					
						
							| 
									
										
										
										
											2012-12-28 21:19:58 +08:00
										 |  |  |         addr:   addr, | 
					
						
							|  |  |  |         in:     make(chan []byte, common.QUEUE_SIZE), | 
					
						
							|  |  |  |         out:    make(chan *Job, common.QUEUE_SIZE), | 
					
						
							| 
									
										
										
										
											2012-05-23 17:45:52 +08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2012-08-30 16:12:15 +08:00
										 |  |  |     // reset abilities
 | 
					
						
							|  |  |  |     a.WriteJob(newJob(common.REQ, common.RESET_ABILITIES, nil)) | 
					
						
							| 
									
										
										
										
											2012-05-24 16:49:35 +08:00
										 |  |  |     return | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // outputing loop
 | 
					
						
							|  |  |  | func (a *agent) outLoop() { | 
					
						
							|  |  |  |     ok := true | 
					
						
							| 
									
										
										
										
											2012-05-28 10:56:01 +08:00
										 |  |  |     var job *Job | 
					
						
							| 
									
										
										
										
											2012-05-24 16:49:35 +08:00
										 |  |  |     for ok { | 
					
						
							| 
									
										
										
										
											2012-05-28 10:56:01 +08:00
										 |  |  |         if job, ok = <-a.out; ok { | 
					
						
							| 
									
										
										
										
											2012-05-24 16:49:35 +08:00
										 |  |  |             if err := a.write(job.Encode()); err != nil { | 
					
						
							|  |  |  |                 a.worker.err(err) | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // inputing loop
 | 
					
						
							|  |  |  | func (a *agent) inLoop() { | 
					
						
							|  |  |  |     defer func() { | 
					
						
							| 
									
										
										
										
											2012-08-30 16:12:15 +08:00
										 |  |  |         if r := recover(); r != nil { | 
					
						
							|  |  |  |             a.worker.err(common.Errorf("Exiting: %s", r)) | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2012-05-24 16:49:35 +08:00
										 |  |  |         close(a.in) | 
					
						
							|  |  |  |         close(a.out) | 
					
						
							|  |  |  |         a.worker.removeAgent(a) | 
					
						
							|  |  |  |     }() | 
					
						
							|  |  |  |     for a.worker.running { | 
					
						
							| 
									
										
										
										
											2012-08-30 16:12:15 +08:00
										 |  |  |         a.WriteJob(newJob(common.REQ, common.PRE_SLEEP, nil)) | 
					
						
							| 
									
										
										
										
											2012-12-28 21:19:58 +08:00
										 |  |  |     RESTART: | 
					
						
							| 
									
										
										
										
											2012-05-24 16:49:35 +08:00
										 |  |  |         // got noop msg and in queue is zero, grab job
 | 
					
						
							|  |  |  |         rel, err := a.read() | 
					
						
							|  |  |  |         if err != nil { | 
					
						
							| 
									
										
										
										
											2012-05-24 21:17:06 +08:00
										 |  |  |             if err == common.ErrConnection { | 
					
						
							| 
									
										
										
										
											2012-12-28 21:19:58 +08:00
										 |  |  |                 for i := 0; i < 3 && a.worker.running; i++ { | 
					
						
							| 
									
										
										
										
											2012-05-30 11:20:29 +08:00
										 |  |  |                     if conn, err := net.Dial(common.NETWORK, a.addr); err != nil { | 
					
						
							|  |  |  |                         a.worker.err(common.Errorf("Reconnection: %d faild", i)) | 
					
						
							|  |  |  |                         continue | 
					
						
							|  |  |  |                     } else { | 
					
						
							|  |  |  |                         a.conn = conn | 
					
						
							|  |  |  |                         goto RESTART | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 a.worker.err(err) | 
					
						
							| 
									
										
										
										
											2012-05-24 16:49:35 +08:00
										 |  |  |                 break | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             a.worker.err(err) | 
					
						
							|  |  |  |             continue | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         job, err := decodeJob(rel) | 
					
						
							|  |  |  |         if err != nil { | 
					
						
							|  |  |  |             a.worker.err(err) | 
					
						
							|  |  |  |             continue | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         switch job.DataType { | 
					
						
							|  |  |  |         case common.NOOP: | 
					
						
							| 
									
										
										
										
											2012-08-30 16:12:15 +08:00
										 |  |  |             a.WriteJob(newJob(common.REQ, common.GRAB_JOB_UNIQ, nil)) | 
					
						
							| 
									
										
										
										
											2012-05-24 16:49:35 +08:00
										 |  |  |         case common.ERROR, common.ECHO_RES, common.JOB_ASSIGN_UNIQ, common.JOB_ASSIGN: | 
					
						
							|  |  |  |             job.agent = a | 
					
						
							|  |  |  |             a.worker.in <- job | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-28 10:34:16 +08:00
										 |  |  | func (a *agent) Close() { | 
					
						
							|  |  |  |     a.conn.Close() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-24 16:49:35 +08:00
										 |  |  | func (a *agent) Work() { | 
					
						
							|  |  |  |     go a.outLoop() | 
					
						
							|  |  |  |     go a.inLoop() | 
					
						
							| 
									
										
										
										
											2012-03-26 13:32:59 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-28 21:19:58 +08:00
										 |  |  | func (a *agent) readData(length int) (data []byte, err error) { | 
					
						
							|  |  |  |     n := 0 | 
					
						
							|  |  |  |     buf := make([]byte, common.BUFFER_SIZE) | 
					
						
							|  |  |  |     // read until data can be unpacked
 | 
					
						
							|  |  |  |     for i := length; i > 0 || len(data) < common.PACKET_LEN; i -= n { | 
					
						
							|  |  |  |         if n, err = a.conn.Read(buf); err != nil { | 
					
						
							|  |  |  |             if err == io.EOF && n == 0 { | 
					
						
							|  |  |  |                 if data == nil { | 
					
						
							|  |  |  |                     err = common.ErrConnection | 
					
						
							|  |  |  |                     return | 
					
						
							| 
									
										
										
										
											2012-03-26 13:32:59 +08:00
										 |  |  |                 } | 
					
						
							| 
									
										
										
										
											2012-12-28 21:19:58 +08:00
										 |  |  |                 return data, nil | 
					
						
							| 
									
										
										
										
											2012-03-26 13:32:59 +08:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2012-12-28 21:19:58 +08:00
										 |  |  |             return | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         data = append(data, buf[0:n]...) | 
					
						
							|  |  |  |         if n < common.BUFFER_SIZE { | 
					
						
							|  |  |  |             break | 
					
						
							| 
									
										
										
										
											2012-03-26 13:32:59 +08:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2012-12-28 21:19:58 +08:00
										 |  |  |     return | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (a *agent) unpack(data []byte) ([]byte, int, bool) { | 
					
						
							| 
									
										
										
										
											2012-03-26 13:32:59 +08:00
										 |  |  |     tl := len(data) | 
					
						
							| 
									
										
										
										
											2012-05-24 16:49:35 +08:00
										 |  |  |     start := 0 | 
					
						
							| 
									
										
										
										
											2012-12-28 21:19:58 +08:00
										 |  |  |     for i := 0; i < tl+1-common.PACKET_LEN; i++ { | 
					
						
							|  |  |  |         if start+common.PACKET_LEN > tl { // too few data to unpack, read more
 | 
					
						
							|  |  |  |             return nil, common.PACKET_LEN, false | 
					
						
							| 
									
										
										
										
											2012-12-27 12:32:41 +08:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2012-05-23 17:45:52 +08:00
										 |  |  |         if string(data[start:start+4]) == common.RES_STR { | 
					
						
							| 
									
										
										
										
											2012-05-24 16:49:35 +08:00
										 |  |  |             l := int(common.BytesToUint32([4]byte{data[start+8], | 
					
						
							| 
									
										
										
										
											2012-12-28 21:19:58 +08:00
										 |  |  |                 data[start+9], data[start+10], data[start+11]})) | 
					
						
							|  |  |  |             total := l + common.PACKET_LEN | 
					
						
							| 
									
										
										
										
											2012-12-26 17:03:15 +08:00
										 |  |  |             if total == tl { // data is what we want
 | 
					
						
							| 
									
										
										
										
											2012-12-28 21:19:58 +08:00
										 |  |  |                 return data, common.PACKET_LEN, true | 
					
						
							|  |  |  |             } else if total < tl { // data[:total] is what we want, data[total:] is the more 
 | 
					
						
							| 
									
										
										
										
											2012-05-24 16:49:35 +08:00
										 |  |  |                 a.in <- data[total:] | 
					
						
							| 
									
										
										
										
											2012-03-26 13:32:59 +08:00
										 |  |  |                 data = data[:total] | 
					
						
							| 
									
										
										
										
											2012-12-28 21:19:58 +08:00
										 |  |  |                 return data, common.PACKET_LEN, true | 
					
						
							| 
									
										
										
										
											2012-12-27 11:36:11 +08:00
										 |  |  |             } else { // ops! It won't be possible.
 | 
					
						
							| 
									
										
										
										
											2012-12-28 21:19:58 +08:00
										 |  |  |                 return nil, total - tl, false | 
					
						
							| 
									
										
										
										
											2012-03-26 13:32:59 +08:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2012-12-27 12:32:41 +08:00
										 |  |  |         } else { // flag was not found, move to next step
 | 
					
						
							| 
									
										
										
										
											2012-03-26 13:32:59 +08:00
										 |  |  |             start++ | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2012-12-28 21:19:58 +08:00
										 |  |  |     return nil, common.PACKET_LEN, false | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (a *agent) read() (rel []byte, err error) { | 
					
						
							|  |  |  |     var data []byte | 
					
						
							|  |  |  |     ok := false | 
					
						
							|  |  |  |     l := common.PACKET_LEN | 
					
						
							|  |  |  |     for !ok { | 
					
						
							|  |  |  |         inlen := len(a.in) | 
					
						
							|  |  |  |         if inlen > 0 { | 
					
						
							|  |  |  |             // in queue is not empty
 | 
					
						
							|  |  |  |             for i := 0; i < inlen; i++ { | 
					
						
							|  |  |  |                 data = append(data, <-a.in...) | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             var d []byte | 
					
						
							|  |  |  |             d, err = a.readData(l) | 
					
						
							|  |  |  |             if err != nil { | 
					
						
							|  |  |  |                 return | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             data = append(data, d...) | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         rel, l, ok = a.unpack(data) | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return | 
					
						
							| 
									
										
										
										
											2012-03-26 13:32:59 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Send a job to the job server.
 | 
					
						
							| 
									
										
										
										
											2012-05-24 16:49:35 +08:00
										 |  |  | func (a *agent) WriteJob(job *Job) { | 
					
						
							|  |  |  |     a.out <- job | 
					
						
							| 
									
										
										
										
											2012-03-26 13:32:59 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Internal write the encoded job.
 | 
					
						
							| 
									
										
										
										
											2012-05-24 16:49:35 +08:00
										 |  |  | func (a *agent) write(buf []byte) (err error) { | 
					
						
							| 
									
										
										
										
											2012-03-26 13:32:59 +08:00
										 |  |  |     var n int | 
					
						
							|  |  |  |     for i := 0; i < len(buf); i += n { | 
					
						
							| 
									
										
										
										
											2012-05-24 16:49:35 +08:00
										 |  |  |         n, err = a.conn.Write(buf[i:]) | 
					
						
							| 
									
										
										
										
											2012-03-26 13:32:59 +08:00
										 |  |  |         if err != nil { | 
					
						
							|  |  |  |             return err | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return | 
					
						
							|  |  |  | } |