// Decode job from byte slice func decodeJob(data []byte) (job *Job, err error) { if len(data) < 12 { return nil, common.Errorf("Invalid data: %V", data) } datatype := common.BytesToUint32([4]byte{data[4], data[5], data[6], data[7]}) l := common.BytesToUint32([4]byte{data[8], data[9], data[10], data[11]}) if len(data[12:]) != int(l) { return nil, common.Errorf("Invalid data: %V", data) } data = data[12:] job = &Job{magicCode: common.RES, DataType: datatype, c: make(chan bool)} switch datatype { case common.JOB_ASSIGN: s := bytes.SplitN(data, []byte{'\x00'}, 3) if len(s) == 3 { job.Handle = string(s[0]) job.Fn = string(s[1]) data = s[2] } case common.JOB_ASSIGN_UNIQ: s := bytes.SplitN(data, []byte{'\x00'}, 4) if len(s) == 4 { job.Handle = string(s[0]) job.Fn = string(s[1]) job.UniqueId = string(s[2]) data = s[3] } } job.Data = data return }
// Decode a job from byte slice func decodeJob(data []byte) (job *Job, err error) { if len(data) < 12 { return nil, common.Errorf("Invalid data: %V", data) } datatype := common.BytesToUint32([4]byte{data[4], data[5], data[6], data[7]}) l := common.BytesToUint32([4]byte{data[8], data[9], data[10], data[11]}) if len(data[12:]) != int(l) { return nil, common.Errorf("Invalid data: %V", data) } data = data[12:] var handle string switch datatype { case common.WORK_DATA, common.WORK_WARNING, common.WORK_STATUS, common.WORK_COMPLETE, common.WORK_FAIL, common.WORK_EXCEPTION: i := bytes.IndexByte(data, '\x00') if i != -1 { handle = string(data[:i]) data = data[i+1:] } } return &Job{magicCode: common.RES, DataType: datatype, Data: data, Handle: handle}, nil }
// inputing loop func (a *agent) inLoop() { defer func() { if r := recover(); r != nil { a.worker.err(common.Errorf("Exiting: %s", r)) } close(a.in) close(a.out) a.worker.removeAgent(a) }() for a.worker.running { a.WriteJob(newJob(common.REQ, common.PRE_SLEEP, nil)) RESTART: // got noop msg and in queue is zero, grab job rel, err := a.read() if err != nil { if err == common.ErrConnection { for i := 0; i < 3 && a.worker.running; i++ { 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) break } a.worker.err(err) continue } job, err := decodeJob(rel) if err != nil { a.worker.err(err) continue } switch job.DataType { case common.NOOP: a.WriteJob(newJob(common.REQ, common.GRAB_JOB_UNIQ, nil)) case common.ERROR, common.ECHO_RES, common.JOB_ASSIGN_UNIQ, common.JOB_ASSIGN: if a.worker.running { if a.worker.limit != nil { a.worker.limit <- true } job.agent = a a.worker.in <- job } } } }
// Remove a function. // Tell job servers 'I can not do this now' at the same time. func (worker *Worker) RemoveFunc(funcname string) (err error) { if _, ok := worker.funcs[funcname]; !ok { return common.Errorf("The function does not exist: %s", funcname) } delete(worker.funcs, funcname) if worker.running { worker.removeFunc(funcname) } return }
// Add a function. // Plz added job servers first, then functions. // The API will tell every connected job server that 'I can do this' func (worker *Worker) AddFunc(funcname string, f JobFunc, timeout uint32) (err error) { if _, ok := worker.funcs[funcname]; ok { return common.Errorf("The function already exists: %s", funcname) } worker.funcs[funcname] = &jobFunc{f: f, timeout: timeout} if worker.running { worker.addFunc(funcname, timeout) } return }
// status handler func (client *Client) handleStatus(job *Job) { data := bytes.SplitN(job.Data, []byte{'\x00'}, 5) if len(data) != 5 { client.err(common.Errorf("Invalid data: %V", job.Data)) return } status := &Status{} status.Handle = string(data[0]) status.Known = (data[1][0] == '1') status.Running = (data[2][0] == '1') var err error status.Numerator, err = strconv.ParseUint(string(data[3]), 10, 0) if err != nil { client.err(common.Errorf("Invalid Integer: %s", data[3])) return } status.Denominator, err = strconv.ParseUint(string(data[4]), 10, 0) if err != nil { client.err(common.Errorf("Invalid Integer: %s", data[4])) return } client.status <- status }
// Extract the job's result. func (job *Job) Result() (data []byte, err error) { switch job.DataType { case common.WORK_FAIL: job.Handle = string(job.Data) return nil, common.ErrWorkFail case common.WORK_EXCEPTION: err = common.ErrWorkException fallthrough case common.WORK_COMPLETE: s := bytes.SplitN(job.Data, []byte{'\x00'}, 2) if len(s) != 2 { return nil, common.Errorf("Invalid data: %V", job.Data) } job.Handle = string(s[0]) data = s[1] default: err = common.ErrDataType } return }
// Execute the job. And send back the result. func (worker *Worker) exec(job *Job) (err error) { defer func() { if r := recover(); r != nil { if e, ok := r.(error); ok { err = e } else { err = common.ErrUnknown } } }() f, ok := worker.funcs[job.Fn] if !ok { return common.Errorf("The function does not exist: %s", job.Fn) } var r *result if f.timeout == 0 { d, e := f.f(job) r = &result{data: d, err: e} } else { r = execTimeout(f.f, job, time.Duration(f.timeout)*time.Second) } var datatype uint32 if r.err == nil { datatype = common.WORK_COMPLETE } else { if r.data == nil { datatype = common.WORK_FAIL } else { datatype = common.WORK_EXCEPTION } err = r.err } job.magicCode = common.REQ job.DataType = datatype job.Data = r.data if worker.running { job.agent.WriteJob(job) } return }