156 lines
		
	
	
		
			3.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			156 lines
		
	
	
		
			3.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// 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 (
 | 
						|
    "io"
 | 
						|
    "net"
 | 
						|
    "bitbucket.org/mikespook/gearman-go/common"
 | 
						|
)
 | 
						|
 | 
						|
// The agent of job server.
 | 
						|
type agent struct {
 | 
						|
    conn        net.Conn
 | 
						|
    worker      *Worker
 | 
						|
    in chan     []byte
 | 
						|
    out chan    *Job
 | 
						|
}
 | 
						|
 | 
						|
// Create the agent of job server.
 | 
						|
func newAgent(addr string, worker *Worker) (a *agent, err error) {
 | 
						|
    conn, err := net.Dial(common.NETWORK, addr)
 | 
						|
    if err != nil {
 | 
						|
        return
 | 
						|
    }
 | 
						|
    a = &agent{
 | 
						|
        conn: conn,
 | 
						|
        worker: worker,
 | 
						|
        in: make(chan []byte, common.QUEUE_SIZE),
 | 
						|
        out: make(chan *Job, common.QUEUE_SIZE),
 | 
						|
    }
 | 
						|
    return
 | 
						|
}
 | 
						|
 | 
						|
// outputing loop
 | 
						|
func (a *agent) outLoop() {
 | 
						|
    ok := true
 | 
						|
    for ok {
 | 
						|
        if job, ok := <-a.out; ok {
 | 
						|
            if err := a.write(job.Encode()); err != nil {
 | 
						|
                a.worker.err(err)
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
// inputing loop
 | 
						|
func (a *agent) inLoop() {
 | 
						|
    defer func() {
 | 
						|
        a.conn.Close()
 | 
						|
        close(a.in)
 | 
						|
        close(a.out)
 | 
						|
        a.worker.removeAgent(a)
 | 
						|
    }()
 | 
						|
    noop := true
 | 
						|
    for a.worker.running {
 | 
						|
        // got noop msg and in queue is zero, grab job
 | 
						|
        if noop && len(a.in) == 0 {
 | 
						|
            a.WriteJob(newJob(common.REQ, common.GRAB_JOB, nil))
 | 
						|
        }
 | 
						|
        rel, err := a.read()
 | 
						|
        if err != nil {
 | 
						|
            if err == common.ErrEmptyReading {
 | 
						|
                break
 | 
						|
            }
 | 
						|
            a.worker.err(err)
 | 
						|
            continue
 | 
						|
        }
 | 
						|
        job, err := decodeJob(rel)
 | 
						|
        if err != nil {
 | 
						|
            a.worker.err(err)
 | 
						|
            continue
 | 
						|
        }
 | 
						|
        switch job.DataType {
 | 
						|
        case common.NOOP:
 | 
						|
            noop = true
 | 
						|
        case common.NO_JOB:
 | 
						|
            noop = false
 | 
						|
            a.WriteJob(newJob(common.REQ, common.PRE_SLEEP, nil))
 | 
						|
        case common.ERROR, common.ECHO_RES, common.JOB_ASSIGN_UNIQ, common.JOB_ASSIGN:
 | 
						|
            job.agent = a
 | 
						|
            a.worker.in <- job
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
func (a *agent) Work() {
 | 
						|
    go a.outLoop()
 | 
						|
    go a.inLoop()
 | 
						|
}
 | 
						|
 | 
						|
// Internal read
 | 
						|
func (a *agent) read() (data []byte, err error) {
 | 
						|
    if len(a.in) > 0 {
 | 
						|
        // in queue is not empty
 | 
						|
        data = <-a.in
 | 
						|
    } else {
 | 
						|
        for {
 | 
						|
            buf := make([]byte, common.BUFFER_SIZE)
 | 
						|
            var n int
 | 
						|
            if n, err = a.conn.Read(buf); err != nil {
 | 
						|
                if err == io.EOF && n == 0 {
 | 
						|
                    if data == nil {
 | 
						|
                        err = common.ErrEmptyReading
 | 
						|
                        return
 | 
						|
                    }
 | 
						|
                    break
 | 
						|
                }
 | 
						|
                return
 | 
						|
            }
 | 
						|
            data = append(data, buf[0:n]...)
 | 
						|
            if n < common.BUFFER_SIZE {
 | 
						|
                break
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
    // split package
 | 
						|
    tl := len(data)
 | 
						|
    start := 0
 | 
						|
    for i := 0; i < tl; i++ {
 | 
						|
        if string(data[start:start+4]) == common.RES_STR {
 | 
						|
            l := int(common.BytesToUint32([4]byte{data[start+8],
 | 
						|
                data[start+9], data[start+10], data[start+11]}))
 | 
						|
            total := l + 12
 | 
						|
            if total == tl {
 | 
						|
                return
 | 
						|
            } else {
 | 
						|
                a.in <- data[total:]
 | 
						|
                data = data[:total]
 | 
						|
                return
 | 
						|
            }
 | 
						|
        } else {
 | 
						|
            start++
 | 
						|
        }
 | 
						|
    }
 | 
						|
    return nil, common.Errorf("Invalid data: %V", data)
 | 
						|
}
 | 
						|
 | 
						|
// Send a job to the job server.
 | 
						|
func (a *agent) WriteJob(job *Job) {
 | 
						|
    a.out <- job
 | 
						|
}
 | 
						|
 | 
						|
// Internal write the encoded job.
 | 
						|
func (a *agent) write(buf []byte) (err error) {
 | 
						|
    var n int
 | 
						|
    for i := 0; i < len(buf); i += n {
 | 
						|
        n, err = a.conn.Write(buf[i:])
 | 
						|
        if err != nil {
 | 
						|
            return err
 | 
						|
        }
 | 
						|
    }
 | 
						|
    return
 | 
						|
}
 |