This commit is contained in:
mikespook 2013-04-23 16:58:06 +08:00
parent 89ef28fb9b
commit 568c70b083
9 changed files with 93 additions and 61 deletions

View File

@ -82,16 +82,12 @@ func New(addr string) (client *Client, err error) {
// //
func (client *Client) connect() (err error) { func (client *Client) connect() (err error) {
client.mutex.Lock()
defer client.mutex.Unlock()
client.conn, err = net.Dial(common.NETWORK, client.addr) client.conn, err = net.Dial(common.NETWORK, client.addr)
return return
} }
// Internal write // Internal write
func (client *Client) write(buf []byte) (err error) { func (client *Client) write(buf []byte) (err error) {
client.mutex.RLock()
defer client.mutex.RUnlock()
var n int var n int
for i := 0; i < len(buf); i += n { for i := 0; i < len(buf); i += n {
n, err = client.conn.Write(buf[i:]) n, err = client.conn.Write(buf[i:])
@ -104,8 +100,6 @@ func (client *Client) write(buf []byte) (err error) {
// read length bytes from the socket // read length bytes from the socket
func (client *Client) readData(length int) (data []byte, err error) { func (client *Client) readData(length int) (data []byte, err error) {
client.mutex.RLock()
defer client.mutex.RUnlock()
n := 0 n := 0
buf := make([]byte, common.BUFFER_SIZE) buf := make([]byte, common.BUFFER_SIZE)
// read until data can be unpacked // read until data can be unpacked
@ -236,9 +230,11 @@ func (client *Client) err (e error) {
// job handler // job handler
func (client *Client) handleJob(job *Job) { func (client *Client) handleJob(job *Job) {
if h, ok := client.jobhandlers[job.UniqueId]; ok { client.mutex.RLock()
defer client.mutex.RUnlock()
if h, ok := client.jobhandlers[job.Handle]; ok {
h(job) h(job)
delete(client.jobhandlers, job.UniqueId) delete(client.jobhandlers, job.Handle)
} }
} }
@ -316,9 +312,11 @@ flag byte, jobhandler JobHandler) (handle string) {
datatype = common.SUBMIT_JOB datatype = common.SUBMIT_JOB
} }
id := IdGen.Id() id := IdGen.Id()
client.mutex.Lock()
defer client.mutex.Unlock()
handle = client.do(funcname, data, datatype, id) handle = client.do(funcname, data, datatype, id)
if jobhandler != nil { if jobhandler != nil {
client.jobhandlers[id] = jobhandler client.jobhandlers[handle] = jobhandler
} }
return return
} }
@ -341,9 +339,13 @@ flag byte) (handle string) {
// Get job status from job server. // Get job status from job server.
// !!!Not fully tested.!!! // !!!Not fully tested.!!!
func (client *Client) Status(handle string) (status *Status) { func (client *Client) Status(handle string, timeout time.Duration) (status *Status, err error) {
client.writeJob(newJob(common.REQ, common.GET_STATUS, []byte(handle))) client.writeJob(newJob(common.REQ, common.GET_STATUS, []byte(handle)))
status = <-client.status select {
case status = <-client.status:
case <-time.NewTimer(timeout).C:
err = common.ErrTimeOut
}
return return
} }

View File

@ -16,7 +16,7 @@ type objectId struct {
} }
func (id *objectId) Id() string { func (id *objectId) Id() string {
return id.String() return id.Hex()
} }
func NewObjectId() IdGenerator { func NewObjectId() IdGenerator {

View File

@ -51,7 +51,6 @@ func decodeJob(data []byte) (job *Job, err error) {
return nil, common.Errorf("Invalid data: %V", data) return nil, common.Errorf("Invalid data: %V", data)
} }
data = data[12:] data = data[12:]
var handle string var handle string
switch datatype { switch datatype {
case common.WORK_DATA, common.WORK_WARNING, common.WORK_STATUS, case common.WORK_DATA, common.WORK_WARNING, common.WORK_STATUS,
@ -59,7 +58,7 @@ func decodeJob(data []byte) (job *Job, err error) {
i := bytes.IndexByte(data, '\x00') i := bytes.IndexByte(data, '\x00')
if i != -1 { if i != -1 {
handle = string(data[:i]) handle = string(data[:i])
data = data[i:] data = data[i + 1:]
} }
} }

View File

@ -7,6 +7,7 @@ package client
import ( import (
"sync" "sync"
"time"
"errors" "errors"
"math/rand" "math/rand"
"github.com/mikespook/gearman-go/common" "github.com/mikespook/gearman-go/common"
@ -115,9 +116,9 @@ flag byte) (addr, handle string) {
// Get job status from job server. // Get job status from job server.
// !!!Not fully tested.!!! // !!!Not fully tested.!!!
func (pool *Pool) Status(addr, handle string) (status *Status, err error) { func (pool *Pool) Status(addr, handle string, timeout time.Duration) (status *Status, err error) {
if client, ok := pool.clients[addr]; ok { if client, ok := pool.clients[addr]; ok {
status = client.Status(handle) status, err = client.Status(handle, timeout)
} else { } else {
err = ErrNotFound err = ErrNotFound
} }

View File

@ -24,7 +24,7 @@ var (
ErrFuncNotFound = errors.New("The function was not found.") ErrFuncNotFound = errors.New("The function was not found.")
ErrConnection = errors.New("Connection error.") ErrConnection = errors.New("Connection error.")
ErrNoActiveAgent = errors.New("No active agent.") ErrNoActiveAgent = errors.New("No active agent.")
ErrExecTimeOut = errors.New("Executing time out.") ErrTimeOut = errors.New("Executing time out.")
ErrUnknown = errors.New("Unknown error.") ErrUnknown = errors.New("Unknown error.")
) )
func DisablePanic() {recover()} func DisablePanic() {recover()}

View File

@ -3,6 +3,7 @@ package main
import ( import (
"log" "log"
"sync" "sync"
"time"
"github.com/mikespook/gearman-go/client" "github.com/mikespook/gearman-go/client"
) )
@ -11,7 +12,7 @@ func main() {
// Set the autoinc id generator // Set the autoinc id generator
// You can write your own id generator // You can write your own id generator
// by implementing IdGenerator interface. // by implementing IdGenerator interface.
client.IdGen = client.NewAutoIncId() // client.IdGen = client.NewAutoIncId()
c, err := client.New("127.0.0.1:4730") c, err := client.New("127.0.0.1:4730")
if err != nil { if err != nil {
@ -20,20 +21,22 @@ func main() {
defer c.Close() defer c.Close()
c.ErrHandler = func(e error) { c.ErrHandler = func(e error) {
log.Println(e) log.Println(e)
panic(e)
} }
echo := []byte("Hello\x00 world") echo := []byte("Hello\x00 world")
wg.Add(1) wg.Add(1)
c.Echo(echo) log.Println(string(c.Echo(echo)))
wg.Add(1) wg.Done()
jobHandler := func(job *client.Job) { jobHandler := func(job *client.Job) {
log.Printf("%s", job.Data) log.Printf("%s", job.Data)
wg.Done() wg.Done()
} }
handle := c.Do("ToUpper", echo, client.JOB_NORMAL, jobHandler) handle := c.Do("ToUpper", echo, client.JOB_NORMAL, jobHandler)
wg.Add(1) wg.Add(1)
log.Printf("%t", c.Status(handle)) status, err := c.Status(handle, time.Second)
if err != nil {
log.Fatalln(err)
}
log.Printf("%t", status)
wg.Wait() wg.Wait()
} }

View File

@ -10,9 +10,7 @@ The protocol was implemented by native way.
package gearman package gearman
import ( import (
"fmt"
"time" "time"
"errors"
"testing" "testing"
"strings" "strings"
"github.com/mikespook/gearman-go/client" "github.com/mikespook/gearman-go/client"
@ -21,7 +19,6 @@ import (
const( const(
STR = "The gearman-go is a pure go implemented library." STR = "The gearman-go is a pure go implemented library."
UPPERSTR = "THE GEARMAN-GO IS A PURE GO IMPLEMENTED LIRBRARY."
GEARMAND = "127.0.0.1:4730" GEARMAND = "127.0.0.1:4730"
) )
@ -51,6 +48,10 @@ func TestJobs(t *testing.T) {
t.Error(err) t.Error(err)
return return
} }
w.ErrHandler = func(e error) {
t.Error(e)
}
go w.Work() go w.Work()
c, err := client.New(GEARMAND) c, err := client.New(GEARMAND)
@ -61,29 +62,37 @@ func TestJobs(t *testing.T) {
defer c.Close() defer c.Close()
c.ErrHandler = func(e error) { c.ErrHandler = func(e error) {
panic(e) t.Error(e)
}
jobHandler := func(job *client.Job) {
if (string(job.Data) != UPPERSTR) {
panic(errors.New(fmt.Sprintf("%s expected, got %s", UPPERSTR, job.Data)))
}
} }
{ {
jobHandler := func(job *client.Job) {
upper := strings.ToUpper(STR)
if (string(job.Data) != upper) {
t.Error("%s expected, got %s", []byte(upper), job.Data)
}
}
handle := c.Do("ToUpper", []byte(STR), client.JOB_NORMAL, jobHandler) handle := c.Do("ToUpper", []byte(STR), client.JOB_NORMAL, jobHandler)
status := c.Status(handle) status, err := c.Status(handle, time.Second)
if err != nil {
t.Error(err)
return
}
if !status.Known { if !status.Known {
t.Errorf("%s should be known", status.Handle) t.Errorf("%s should be known", status.Handle)
return return
} }
} }
{ {
handle := c.DoBg("Sleep", nil, client.JOB_NORMAL) handle := c.DoBg("Sleep", nil, client.JOB_NORMAL)
time.Sleep(time.Second) time.Sleep(time.Second)
status := c.Status(handle) status, err := c.Status(handle, time.Second)
if err != nil {
t.Error(err)
return
}
if !status.Known { if !status.Known {
t.Errorf("%s should be known", status.Handle) t.Errorf("%s should be known", status.Handle)
@ -91,7 +100,23 @@ func TestJobs(t *testing.T) {
} }
if !status.Running { if !status.Running {
t.Errorf("%s shouldn be running", status.Handle) t.Errorf("%s should be running", status.Handle)
}
}
{
status, err := c.Status("not exists handle", time.Second)
if err != nil {
t.Error(err)
return
}
if status.Known {
t.Errorf("%s shouldn't be known", status.Handle)
return
}
if status.Running {
t.Errorf("%s shouldn't be running", status.Handle)
} }
} }
} }

View File

@ -6,6 +6,7 @@
package worker package worker
import ( import (
"bytes"
"strconv" "strconv"
"github.com/mikespook/gearman-go/common" "github.com/mikespook/gearman-go/common"
) )
@ -13,7 +14,7 @@ import (
// Worker side job // Worker side job
type Job struct { type Job struct {
Data []byte Data []byte
Handle, UniqueId string Handle, UniqueId, Fn string
agent *agent agent *agent
magicCode, DataType uint32 magicCode, DataType uint32
c chan bool c chan bool
@ -24,8 +25,7 @@ func newJob(magiccode, datatype uint32, data []byte) (job *Job) {
return &Job{magicCode: magiccode, return &Job{magicCode: magiccode,
DataType: datatype, DataType: datatype,
Data: data, Data: data,
c: make(chan bool), c: make(chan bool),}
}
} }
// Decode job from byte slice // Decode job from byte slice
@ -39,7 +39,25 @@ func decodeJob(data []byte) (job *Job, err error) {
return nil, common.Errorf("Invalid data: %V", data) return nil, common.Errorf("Invalid data: %V", data)
} }
data = data[12:] data = data[12:]
job = newJob(common.RES, datatype, data) 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 return
} }

View File

@ -6,7 +6,6 @@ package worker
import ( import (
"time" "time"
"bytes"
"github.com/mikespook/gearman-go/common" "github.com/mikespook/gearman-go/common"
) )
@ -258,24 +257,9 @@ func (worker *Worker) exec(job *Job) (err error) {
} }
} }
} () } ()
var limit int f, ok := worker.funcs[job.Fn]
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 { if !ok {
return common.Errorf("The function does not exist: %s", funcname) return common.Errorf("The function does not exist: %s", job.Fn)
} }
var r *result var r *result
if f.timeout == 0 { if f.timeout == 0 {
@ -333,7 +317,7 @@ func execTimeout(f JobFunc, job *Job, timeout time.Duration) (r *result) {
case r = <-rslt: case r = <-rslt:
case <-time.After(timeout): case <-time.After(timeout):
go job.cancel() go job.cancel()
return &result{err:common.ErrExecTimeOut} return &result{err:common.ErrTimeOut}
} }
return r return r
} }