gearman-go/worker/worker.go

304 lines
6.3 KiB
Go
Raw Normal View History

// 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 (
2013-08-30 18:01:10 +08:00
"fmt"
2013-08-30 12:36:57 +08:00
"time"
2013-08-30 18:01:10 +08:00
"sync"
"encoding/binary"
)
const (
2013-08-30 12:36:57 +08:00
Unlimited = 0
OneByOne = 1
2013-08-30 12:36:57 +08:00
Immediately = 0
)
2012-06-01 14:28:10 +08:00
/*
Worker side api for gearman
usage:
w = worker.New(worker.Unlimited)
w.AddFunction("foobar", foobar)
w.AddServer("127.0.0.1:4730")
w.Work() // Enter the worker's main loop
The definition of the callback function 'foobar' should suit for the type 'JobFunction'.
It looks like this:
func foobar(job *Job) (data []byte, err os.Error) {
//sth. here
//plaplapla...
return
}
*/
type Worker struct {
2013-08-30 18:01:10 +08:00
agents map[string]*agent
2013-08-30 12:36:57 +08:00
funcs JobFuncs
2013-08-30 18:01:10 +08:00
in chan *Response
2013-08-30 12:36:57 +08:00
running bool
limit chan bool
Id string
// assign a ErrFunc to handle errors
2013-08-30 18:01:10 +08:00
ErrorHandler ErrorHandler
2013-08-30 12:36:57 +08:00
JobHandler JobHandler
2013-08-30 18:01:10 +08:00
mutex sync.Mutex
}
// Get a new worker
func New(l int) (worker *Worker) {
2013-08-30 12:36:57 +08:00
worker = &Worker{
2013-08-30 18:01:10 +08:00
agents: make(map[string]*agent, QUEUE_SIZE),
2013-08-30 12:36:57 +08:00
funcs: make(JobFuncs),
2013-08-30 18:01:10 +08:00
in: make(chan *Response, QUEUE_SIZE),
2013-08-30 12:36:57 +08:00
}
if l != Unlimited {
worker.limit = make(chan bool, l)
}
return
}
2013-08-30 12:36:57 +08:00
//
func (worker *Worker) err(e error) {
2013-08-30 18:01:10 +08:00
if worker.ErrorHandler != nil {
worker.ErrorHandler(e)
2013-08-30 12:36:57 +08:00
}
}
// Add a server. The addr should be 'host:port' format.
// The connection is established at this time.
2013-08-30 18:01:10 +08:00
func (worker *Worker) AddServer(net, addr string) (err error) {
2013-08-30 12:36:57 +08:00
// Create a new job server's client as a agent of server
2013-08-30 18:01:10 +08:00
a, err := newAgent(net, addr, worker)
2013-08-30 12:36:57 +08:00
if err != nil {
return err
}
2013-08-30 18:01:10 +08:00
worker.agents[net + addr] = a
2013-08-30 12:36:57 +08:00
return
}
// Write a job to job server.
// Here, the job's mean is not the oraginal mean.
// Just looks like a network package for job's result or tell job server, there was a fail.
2013-08-30 18:01:10 +08:00
func (worker *Worker) broadcast(req *request) {
2013-08-30 12:36:57 +08:00
for _, v := range worker.agents {
2013-08-30 18:01:10 +08:00
v.write(req)
2013-08-30 12:36:57 +08:00
}
}
// 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,
2013-08-30 12:36:57 +08:00
f JobFunc, timeout uint32) (err error) {
2013-08-30 18:01:10 +08:00
worker.mutex.Lock()
defer worker.mutex.Unlock()
2013-08-30 12:36:57 +08:00
if _, ok := worker.funcs[funcname]; ok {
2013-08-30 18:01:10 +08:00
return fmt.Errorf("The function already exists: %s", funcname)
2013-08-30 12:36:57 +08:00
}
worker.funcs[funcname] = &jobFunc{f: f, timeout: timeout}
if worker.running {
worker.addFunc(funcname, timeout)
}
return
}
// inner add function
func (worker *Worker) addFunc(funcname string, timeout uint32) {
2013-08-30 18:01:10 +08:00
req := getRequest()
2013-08-30 12:36:57 +08:00
if timeout == 0 {
2013-08-30 18:01:10 +08:00
req.DataType = CAN_DO
req.Data = []byte(funcname)
2013-08-30 12:36:57 +08:00
} else {
2013-08-30 18:01:10 +08:00
req.DataType = CAN_DO_TIMEOUT
l := len(funcname)
req.Data = getBuffer(l + 5)
copy(req.Data, []byte(funcname))
req.Data[l] = '\x00'
binary.BigEndian.PutUint32(req.Data[l + 1:], timeout)
2013-08-30 12:36:57 +08:00
}
2013-08-30 18:01:10 +08:00
worker.broadcast(req)
}
// Remove a function.
func (worker *Worker) RemoveFunc(funcname string) (err error) {
2013-08-30 18:01:10 +08:00
worker.mutex.Lock()
defer worker.mutex.Unlock()
2013-08-30 12:36:57 +08:00
if _, ok := worker.funcs[funcname]; !ok {
2013-08-30 18:01:10 +08:00
return fmt.Errorf("The function does not exist: %s", funcname)
2013-08-30 12:36:57 +08:00
}
delete(worker.funcs, funcname)
if worker.running {
worker.removeFunc(funcname)
}
return
}
// inner remove function
func (worker *Worker) removeFunc(funcname string) {
2013-08-30 18:01:10 +08:00
req := getRequest()
req.DataType = CANT_DO
req.Data = []byte(funcname)
worker.broadcast(req)
}
2013-08-30 18:01:10 +08:00
func (worker *Worker) dealResp(resp *Response) {
2013-08-30 12:36:57 +08:00
defer func() {
if worker.running && worker.limit != nil {
<-worker.limit
}
}()
2013-08-30 18:01:10 +08:00
switch resp.DataType {
case ERROR:
worker.err(GetError(resp.Data))
case JOB_ASSIGN, JOB_ASSIGN_UNIQ:
if err := worker.exec(resp); err != nil {
2013-08-30 12:36:57 +08:00
worker.err(err)
}
default:
2013-08-30 18:01:10 +08:00
worker.handleResponse(resp)
2013-08-30 12:36:57 +08:00
}
2012-12-29 23:54:20 +08:00
}
// Main loop
func (worker *Worker) Work() {
2013-08-30 12:36:57 +08:00
defer func() {
for _, v := range worker.agents {
v.Close()
}
}()
worker.running = true
for _, v := range worker.agents {
2013-08-30 18:01:10 +08:00
v.Connect()
2013-08-30 12:36:57 +08:00
go v.Work()
}
2013-08-30 18:01:10 +08:00
worker.Reset()
2013-08-30 12:36:57 +08:00
for funcname, f := range worker.funcs {
worker.addFunc(funcname, f.timeout)
}
2013-08-30 18:01:10 +08:00
var resp *Response
for resp = range worker.in {
fmt.Println(resp)
go worker.dealResp(resp)
2013-08-30 12:36:57 +08:00
}
}
// job handler
2013-08-30 18:01:10 +08:00
func (worker *Worker) handleResponse(resp *Response) {
2013-08-30 12:36:57 +08:00
if worker.JobHandler != nil {
2013-08-30 18:01:10 +08:00
job := getJob()
job.a = worker.agents[resp.agentId]
job.Handle = resp.Handle
if resp.DataType == ECHO_RES {
job.data = resp.Data
}
2013-08-30 12:36:57 +08:00
if err := worker.JobHandler(job); err != nil {
worker.err(err)
}
}
}
// Close.
func (worker *Worker) Close() {
2013-08-30 12:36:57 +08:00
worker.running = false
close(worker.in)
if worker.limit != nil {
close(worker.limit)
}
}
// Send a something out, get the samething back.
func (worker *Worker) Echo(data []byte) {
2013-08-30 18:01:10 +08:00
req := getRequest()
req.DataType = ECHO_REQ
req.Data = data
worker.broadcast(req)
}
// Remove all of functions.
// Both from the worker or job servers.
func (worker *Worker) Reset() {
2013-08-30 18:01:10 +08:00
req := getRequest()
req.DataType = RESET_ABILITIES
worker.broadcast(req)
2013-08-30 12:36:57 +08:00
worker.funcs = make(JobFuncs)
}
// Set the worker's unique id.
func (worker *Worker) SetId(id string) {
2013-08-30 12:36:57 +08:00
worker.Id = id
2013-08-30 18:01:10 +08:00
req := getRequest()
req.DataType = SET_CLIENT_ID
req.Data = []byte(id)
worker.broadcast(req)
}
// Execute the job. And send back the result.
2013-08-30 18:01:10 +08:00
func (worker *Worker) exec(resp *Response) (err error) {
2013-08-30 12:36:57 +08:00
defer func() {
if r := recover(); r != nil {
if e, ok := r.(error); ok {
err = e
} else {
2013-08-30 18:01:10 +08:00
err = ErrUnknown
2013-08-30 12:36:57 +08:00
}
}
}()
2013-08-30 18:01:10 +08:00
f, ok := worker.funcs[resp.Fn]
2013-08-30 12:36:57 +08:00
if !ok {
2013-08-30 18:01:10 +08:00
return fmt.Errorf("The function does not exist: %s", resp.Fn)
2013-08-30 12:36:57 +08:00
}
var r *result
2013-08-30 18:01:10 +08:00
job := getJob()
job.a = worker.agents[resp.agentId]
job.Handle = resp.Handle
2013-08-30 12:36:57 +08:00
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)
}
2013-08-30 18:01:10 +08:00
req := getRequest()
2013-08-30 12:36:57 +08:00
if r.err == nil {
2013-08-30 18:01:10 +08:00
req.DataType = WORK_COMPLETE
2013-08-30 12:36:57 +08:00
} else {
if r.data == nil {
2013-08-30 18:01:10 +08:00
req.DataType = WORK_FAIL
2013-08-30 12:36:57 +08:00
} else {
2013-08-30 18:01:10 +08:00
req.DataType = WORK_EXCEPTION
2013-08-30 12:36:57 +08:00
}
err = r.err
}
2013-08-30 18:01:10 +08:00
req.Data = r.data
2013-08-30 12:36:57 +08:00
if worker.running {
2013-08-30 18:01:10 +08:00
job.a.write(req)
2013-08-30 12:36:57 +08:00
}
return
}
type result struct {
2013-08-30 12:36:57 +08:00
data []byte
err error
}
2013-08-30 18:01:10 +08:00
func execTimeout(f JobFunc, job Job, timeout time.Duration) (r *result) {
2013-08-30 12:36:57 +08:00
rslt := make(chan *result)
defer close(rslt)
go func() {
defer func() { recover() }()
d, e := f(job)
rslt <- &result{data: d, err: e}
}()
select {
case r = <-rslt:
case <-time.After(timeout):
2013-08-30 18:01:10 +08:00
return &result{err: ErrTimeOut}
2013-08-30 12:36:57 +08:00
}
return r
}