// 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 }
// 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:] return newJob(common.RES, datatype, data), 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 } } } }
// 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 } } }() if worker.limit != nil { <-worker.limit defer func() { worker.limit <- true }() } var limit int if job.DataType == common.JOB_ASSIGN { limit = 3 } else { limit = 4 } jobdata := bytes.SplitN(job.Data, []byte{'\x00'}, limit) job.Handle = string(jobdata[0]) funcname := string(jobdata[1]) if job.DataType == common.JOB_ASSIGN { job.Data = jobdata[2] } else { job.UniqueId = string(jobdata[2]) job.Data = jobdata[3] } f, ok := worker.funcs[funcname] if !ok { return common.Errorf("The function does not exist: %s", funcname) } 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 job.agent.WriteJob(job) return }
// Internal read func (a *agent) read() (data []byte, err error) { BEGIN: inlen := len(a.in) if inlen > 0 { // in queue is not empty for i := 0; i < inlen; i++ { data = append(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.ErrConnection return } break } return } data = append(data, buf[0:n]...) if n < common.BUFFER_SIZE { break } } } // split package tl := len(data) if tl < 12 { // too few data to unpack, read more goto BEGIN } start := 0 for i := 0; i < tl-11; i++ { if start+12 > tl { // too few data to unpack, read more goto BEGIN } 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 { // data is what we want return } else if total < tl { // data[:total] is what we want, data[total:] is the more a.in <- data[total:] data = data[:total] return } else { // ops! It won't be possible. goto BEGIN } } else { // flag was not found, move to next step start++ } } goto BEGIN return nil, common.Errorf("Invalid data: %V", data) }
// 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][0]), 10, 0) if err != nil { client.err(common.Errorf("Invalid handle: %s", data[3][0])) return } status.Denominator, err = strconv.ParseUint(string(data[4][0]), 10, 0) if err != nil { client.err(common.Errorf("Invalid handle: %s", data[4][0])) return } client.status <- status }
// status handler func (client *Client) handleStatus(job *Job) { if client.StatusHandler != nil { data := bytes.SplitN(job.Data, []byte{'\x00'}, 5) if len(data) != 5 { client.err(common.Errorf("Invalid data: %V", job.Data)) return } handle := string(data[0]) known := (data[1][0] == '1') running := (data[2][0] == '1') numerator, err := strconv.ParseUint(string(data[3][0]), 10, 0) if err != nil { client.err(common.Errorf("Invalid handle: %s", data[3][0])) return } denominator, err := strconv.ParseUint(string(data[4][0]), 10, 0) if err != nil { client.err(common.Errorf("Invalid handle: %s", data[4][0])) return } client.StatusHandler(handle, known, running, numerator, denominator) } }
// inner read func (client *Client) read() (data []byte, err error) { if len(client.in) > 0 { // incoming queue is not empty data = <-client.in } else { // empty queue, read data from socket for { buf := make([]byte, common.BUFFER_SIZE) var n int if n, err = client.conn.Read(buf); err != nil { if err == io.EOF && n == 0 { if data == nil { err = common.ErrConnection return } break } return } data = append(data, buf[0:n]...) if n < common.BUFFER_SIZE { break } } } // split package tl := len(data) start, end := 0, 4 for i := 0; i < tl; i++ { if string(data[start:end]) == 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 { client.in <- data[total:] data = data[:total] return } } else { start++ end++ } } return nil, common.Errorf("Invalid data: %V", data) }
// 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 }