a better documents for the client package

This commit is contained in:
Xing Xing 2013-12-26 15:28:42 +08:00
parent 59941371fb
commit bf25cc1728
14 changed files with 177 additions and 206 deletions

View File

@ -1,66 +1,47 @@
// The client package helps developers connect to Gearmand, send
// jobs and fetch result.
package client package client
import ( import (
"io" "io"
"net" "net"
"sync" "sync"
// "fmt"
) )
/* // One client connect to one server.
The client side api for gearman // Use Pool for multi-connections.
usage:
c := client.New("tcp4", "127.0.0.1:4730")
handle := c.Do("foobar", []byte("data here"), JOB_LOW | JOB_BG)
*/
type Client struct { type Client struct {
sync.Mutex sync.Mutex
net, addr, lastcall string net, addr, lastcall string
respHandler map[string]ResponseHandler respHandler map[string]ResponseHandler
innerHandler map[string]ResponseHandler innerHandler map[string]ResponseHandler
in chan []byte in chan *Response
isConn bool isConn bool
conn net.Conn conn net.Conn
ErrorHandler ErrorHandler ErrorHandler ErrorHandler
} }
// Create a new client. // Return a client.
// Connect to "addr" through "network" func New(network, addr string) (client *Client, err error) {
// Eg.
// client, err := client.New("127.0.0.1:4730")
func New(net, addr string) (client *Client, err error) {
client = &Client{ client = &Client{
net: net, net: network,
addr: addr, addr: addr,
respHandler: make(map[string]ResponseHandler, QUEUE_SIZE), respHandler: make(map[string]ResponseHandler, queueSize),
innerHandler: make(map[string]ResponseHandler, QUEUE_SIZE), innerHandler: make(map[string]ResponseHandler, queueSize),
in: make(chan []byte, QUEUE_SIZE), in: make(chan *Response, queueSize),
} }
if err = client.connect(); err != nil {
return
}
go client.readLoop()
go client.processLoop()
return
}
// {{{ private functions
//
func (client *Client) connect() (err error) {
client.conn, err = net.Dial(client.net, client.addr) client.conn, err = net.Dial(client.net, client.addr)
if err != nil { if err != nil {
return return
} }
client.isConn = true client.isConn = true
go client.readLoop()
go client.processLoop()
return return
} }
// Internal write
func (client *Client) write(req *request) (err error) { func (client *Client) write(req *request) (err error) {
var n int var n int
buf := req.Encode() buf := req.Encode()
@ -73,61 +54,52 @@ func (client *Client) write(req *request) (err error) {
return return
} }
// read length bytes from the socket
func (client *Client) read(length int) (data []byte, err error) { func (client *Client) read(length int) (data []byte, err error) {
n := 0 n := 0
buf := getBuffer(BUFFER_SIZE) buf := getBuffer(bufferSize)
// read until data can be unpacked // read until data can be unpacked
for i := length; i > 0 || len(data) < MIN_PACKET_LEN; i -= n { for i := length; i > 0 || len(data) < minPacketLength; i -= n {
if n, err = client.conn.Read(buf); err != nil { if n, err = client.conn.Read(buf); err != nil {
if !client.isConn { if err == io.EOF {
err = ErrConnClosed err = ErrLostConn
return
}
if err == io.EOF && n == 0 {
if data == nil {
err = ErrConnection
}
} }
return return
} }
data = append(data, buf[0:n]...) data = append(data, buf[0:n]...)
if n < BUFFER_SIZE { if n < bufferSize {
break break
} }
} }
return return
} }
// read data from socket
func (client *Client) readLoop() { func (client *Client) readLoop() {
var data []byte defer close(client.in)
var data, leftdata []byte
var err error var err error
for client.isConn { var resp *Response
if data, err = client.read(BUFFER_SIZE); err != nil { for {
if err == ErrConnClosed { if data, err = client.read(bufferSize); err != nil {
client.err(err)
if err == ErrLostConn {
break
}
// If it is unexpected error and the connection wasn't
// closed by Gearmand, the client should close the conection
// and reconnect to job server.
client.Close()
client.conn, err = net.Dial(client.net, client.addr)
if err != nil {
client.err(err)
break break
} }
client.err(err)
continue continue
} }
client.in <- data
}
close(client.in)
}
// decode data & process it
func (client *Client) processLoop() {
var resp *Response
var l int
var err error
var data, leftdata []byte
for data = range client.in {
if len(leftdata) > 0 { // some data left for processing if len(leftdata) > 0 { // some data left for processing
data = append(leftdata, data...) data = append(leftdata, data...)
} }
l = len(data) l := len(data)
if l < MIN_PACKET_LEN { // not enough data if l < minPacketLength { // not enough data
leftdata = data leftdata = data
continue continue
} }
@ -135,41 +107,43 @@ func (client *Client) processLoop() {
client.err(err) client.err(err)
continue continue
} }
client.in <- resp
leftdata = nil leftdata = nil
for resp != nil {
switch resp.DataType {
case ERROR:
if client.lastcall != "" {
resp = client.handleInner(client.lastcall, resp)
client.lastcall = ""
} else {
client.err(GetError(resp.Data))
}
case STATUS_RES:
resp = client.handleInner("s"+resp.Handle, resp)
case JOB_CREATED:
resp = client.handleInner("c", resp)
case ECHO_RES:
resp = client.handleInner("e", resp)
case WORK_DATA, WORK_WARNING, WORK_STATUS, WORK_COMPLETE,
WORK_FAIL, WORK_EXCEPTION:
resp = client.handleResponse(resp.Handle, resp)
}
}
if len(data) > l { if len(data) > l {
leftdata = data[l:] leftdata = data[l:]
} }
} }
} }
// error handler func (client *Client) processLoop() {
for resp := range client.in {
switch resp.DataType {
case dtError:
if client.lastcall != "" {
resp = client.handleInner(client.lastcall, resp)
client.lastcall = ""
} else {
client.err(getError(resp.Data))
}
case dtStatusRes:
resp = client.handleInner("s"+resp.Handle, resp)
case dtJobCreated:
resp = client.handleInner("c", resp)
case dtEchoRes:
resp = client.handleInner("e", resp)
case dtWorkData, dtWorkWarning, dtWorkStatus, dtWorkComplete,
dtWorkFail, dtWorkException:
resp = client.handleResponse(resp.Handle, resp)
}
}
}
func (client *Client) err(e error) { func (client *Client) err(e error) {
if client.ErrorHandler != nil { if client.ErrorHandler != nil {
client.ErrorHandler(e) client.ErrorHandler(e)
} }
} }
// job handler
func (client *Client) handleResponse(key string, resp *Response) *Response { func (client *Client) handleResponse(key string, resp *Response) *Response {
if h, ok := client.respHandler[key]; ok { if h, ok := client.respHandler[key]; ok {
h(resp) h(resp)
@ -179,7 +153,6 @@ func (client *Client) handleResponse(key string, resp *Response) *Response {
return resp return resp
} }
// job handler
func (client *Client) handleInner(key string, resp *Response) *Response { func (client *Client) handleInner(key string, resp *Response) *Response {
if h, ok := client.innerHandler[key]; ok { if h, ok := client.innerHandler[key]; ok {
h(resp) h(resp)
@ -189,15 +162,14 @@ func (client *Client) handleInner(key string, resp *Response) *Response {
return resp return resp
} }
// Internal do
func (client *Client) do(funcname string, data []byte, func (client *Client) do(funcname string, data []byte,
flag uint32) (handle string, err error) { flag uint32) (handle string, err error) {
var mutex sync.Mutex var mutex sync.Mutex
mutex.Lock() mutex.Lock()
client.lastcall = "c" client.lastcall = "c"
client.innerHandler["c"] = func(resp *Response) { client.innerHandler["c"] = func(resp *Response) {
if resp.DataType == ERROR { if resp.DataType == dtError {
err = GetError(resp.Data) err = getError(resp.Data)
return return
} }
handle = resp.Handle handle = resp.Handle
@ -211,22 +183,18 @@ func (client *Client) do(funcname string, data []byte,
return return
} }
// }}} // Call the function and get a response.
// flag can be set to: JobLow, JobNormal and JobHigh
// Do the function.
// funcname is a string with function name.
// data is encoding to byte array.
// flag set the job type, include running level: JOB_LOW, JOB_NORMAL, JOB_HIGH
func (client *Client) Do(funcname string, data []byte, func (client *Client) Do(funcname string, data []byte,
flag byte, h ResponseHandler) (handle string, err error) { flag byte, h ResponseHandler) (handle string, err error) {
var datatype uint32 var datatype uint32
switch flag { switch flag {
case JOB_LOW: case JobLow:
datatype = SUBMIT_JOB_LOW datatype = dtSubmitJobLow
case JOB_HIGH: case JobHigh:
datatype = SUBMIT_JOB_HIGH datatype = dtSubmitJobHigh
default: default:
datatype = SUBMIT_JOB datatype = dtSubmitJob
} }
handle, err = client.do(funcname, data, datatype) handle, err = client.do(funcname, data, datatype)
if h != nil { if h != nil {
@ -235,27 +203,24 @@ func (client *Client) Do(funcname string, data []byte,
return return
} }
// Do the function at background. // Call the function in background, no response needed.
// funcname is a string with function name. // flag can be set to: JobLow, JobNormal and JobHigh
// data is encoding to byte array.
// flag set the job type, include running level: JOB_LOW, JOB_NORMAL, JOB_HIGH
func (client *Client) DoBg(funcname string, data []byte, func (client *Client) DoBg(funcname string, data []byte,
flag byte) (handle string, err error) { flag byte) (handle string, err error) {
var datatype uint32 var datatype uint32
switch flag { switch flag {
case JOB_LOW: case JobLow:
datatype = SUBMIT_JOB_LOW_BG datatype = dtSubmitJobLowBg
case JOB_HIGH: case JobHigh:
datatype = SUBMIT_JOB_HIGH_BG datatype = dtSubmitJobHighBg
default: default:
datatype = SUBMIT_JOB_BG datatype = dtSubmitJobBg
} }
handle, err = client.do(funcname, data, datatype) handle, err = client.do(funcname, data, datatype)
return return
} }
// Get job status from job server. // Get job status from job server.
// !!!Not fully tested.!!!
func (client *Client) Status(handle string) (status *Status, err error) { func (client *Client) Status(handle string) (status *Status, err error) {
var mutex sync.Mutex var mutex sync.Mutex
mutex.Lock() mutex.Lock()
@ -269,14 +234,14 @@ func (client *Client) Status(handle string) (status *Status, err error) {
mutex.Unlock() mutex.Unlock()
} }
req := getRequest() req := getRequest()
req.DataType = GET_STATUS req.DataType = dtGetStatus
req.Data = []byte(handle) req.Data = []byte(handle)
client.write(req) client.write(req)
mutex.Lock() mutex.Lock()
return return
} }
// Send a something out, get the samething back. // Echo.
func (client *Client) Echo(data []byte) (echo []byte, err error) { func (client *Client) Echo(data []byte) (echo []byte, err error) {
var mutex sync.Mutex var mutex sync.Mutex
mutex.Lock() mutex.Lock()
@ -285,7 +250,7 @@ func (client *Client) Echo(data []byte) (echo []byte, err error) {
mutex.Unlock() mutex.Unlock()
} }
req := getRequest() req := getRequest()
req.DataType = ECHO_REQ req.DataType = dtEchoReq
req.Data = data req.Data = data
client.lastcall = "e" client.lastcall = "e"
client.write(req) client.write(req)
@ -293,8 +258,13 @@ func (client *Client) Echo(data []byte) (echo []byte, err error) {
return return
} }
// Close // Close connection
func (client *Client) Close() (err error) { func (client *Client) Close() (err error) {
client.isConn = false client.Lock()
return client.conn.Close() defer client.Unlock()
if client.conn != nil {
err = client.conn.Close()
client.conn = nil
}
return
} }

View File

@ -13,7 +13,7 @@ var client *Client
func TestClientAddServer(t *testing.T) { func TestClientAddServer(t *testing.T) {
t.Log("Add local server 127.0.0.1:4730") t.Log("Add local server 127.0.0.1:4730")
var err error var err error
if client, err = New("tcp4", "127.0.0.1:4730"); err != nil { if client, err = New(Network, "127.0.0.1:4730"); err != nil {
t.Fatal(err) t.Fatal(err)
} }
client.ErrorHandler = func(e error) { client.ErrorHandler = func(e error) {
@ -34,7 +34,7 @@ func TestClientEcho(t *testing.T) {
} }
func TestClientDoBg(t *testing.T) { func TestClientDoBg(t *testing.T) {
handle, err := client.DoBg("ToUpper", []byte("abcdef"), JOB_LOW) handle, err := client.DoBg("ToUpper", []byte("abcdef"), JobLow)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
return return
@ -57,7 +57,7 @@ func TestClientDo(t *testing.T) {
return return
} }
handle, err := client.Do("ToUpper", []byte("abcdef"), handle, err := client.Do("ToUpper", []byte("abcdef"),
JOB_LOW, jobHandler) JobLow, jobHandler)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
return return
@ -84,7 +84,7 @@ func TestClientStatus(t *testing.T) {
return return
} }
handle, err := client.Do("Delay5sec", []byte("abcdef"), JOB_LOW, nil) handle, err := client.Do("Delay5sec", []byte("abcdef"), JobLow, nil)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
return return

View File

@ -1,66 +1,67 @@
package client package client
const ( const (
NETWORK = "tcp" Network = "tcp"
// queue size // queue size
QUEUE_SIZE = 8 queueSize = 8
// read buffer size // read buffer size
BUFFER_SIZE = 1024 bufferSize = 1024
// min packet length // min packet length
MIN_PACKET_LEN = 12 minPacketLength = 12
// \x00REQ // \x00REQ
REQ = 5391697 req = 5391697
REQ_STR = "\x00REQ" reqStr = "\x00REQ"
// \x00RES // \x00RES
RES = 5391699 res = 5391699
RES_STR = "\x00RES" resStr = "\x00RES"
// package data type // package data type
CAN_DO = 0x1 dtCanDo = 1
CANT_DO = 0x2 dtCantDo = 2
RESET_ABILITIES = 0x3 dtResetAbilities = 3
PRE_SLEEP = 0x4 dtPreSleep = 4
NOOP = 0x6 dtNoop = 6
JOB_CREATED = 0x8 dtJobCreated = 8
GRAB_JOB = 0x9 dtGrabJob = 9
NO_JOB = 0xa dtNoJob = 10
JOB_ASSIGN = 0xb dtJobAssign = 11
WORK_STATUS = 0xc dtWorkStatus = 12
WORK_COMPLETE = 0xd dtWorkComplete = 13
WORK_FAIL = 0xe dtWorkFail = 14
GET_STATUS = 0xf dtGetStatus = 15
ECHO_REQ = 0x10 dtEchoReq = 16
ECHO_RES = 0x11 dtEchoRes = 17
ERROR = 0x13 dtError = 19
STATUS_RES = 0x14 dtStatusRes = 20
SET_CLIENT_ID = 0x16 dtSetClientId = 22
CAN_DO_TIMEOUT = 0x17 dtCanDoTimeout = 23
WORK_EXCEPTION = 0x19 dtAllYours = 24
WORK_DATA = 0x1c dtWorkException = 25
WORK_WARNING = 0x1d dtWorkData = 28
GRAB_JOB_UNIQ = 0x1e dtWorkWarning = 29
JOB_ASSIGN_UNIQ = 0x1f dtGrabJobUniq = 30
dtJobAssignUniq = 31
SUBMIT_JOB = 7 dtSubmitJob = 7
SUBMIT_JOB_BG = 18 dtSubmitJobBg = 18
SUBMIT_JOB_HIGH = 21 dtSubmitJobHigh = 21
SUBMIT_JOB_HIGH_BG = 32 dtSubmitJobHighBg = 32
SUBMIT_JOB_LOW = 33 dtSubmitJobLow = 33
SUBMIT_JOB_LOW_BG = 34 dtSubmitJobLowBg = 34
) )
const ( const (
// Job type // Job type
// JOB_NORMAL | JOB_BG means a normal level job run in background // JOB_NORMAL | JOB_BG means a normal level job run in background
// normal level // normal level
JOB_NORMAL = 0 JobNormal = 0
// background job // background job
JOB_BG = 1 JobBg = 1
// low level // low level
JOB_LOW = 2 JobLow = 2
// high level // high level
JOB_HIGH = 4 JobHigh = 4
) )
func getBuffer(l int) (buf []byte) { func getBuffer(l int) (buf []byte) {

View File

@ -7,24 +7,16 @@ import (
) )
var ( var (
ErrJobTimeOut = errors.New("Do a job time out")
ErrInvalidData = errors.New("Invalid data")
ErrWorkWarning = errors.New("Work warning") ErrWorkWarning = errors.New("Work warning")
ErrInvalidData = errors.New("Invalid data")
ErrWorkFail = errors.New("Work fail") ErrWorkFail = errors.New("Work fail")
ErrWorkException = errors.New("Work exeption") ErrWorkException = errors.New("Work exeption")
ErrDataType = errors.New("Invalid data type") ErrDataType = errors.New("Invalid data type")
ErrOutOfCap = errors.New("Out of the capability") ErrLostConn = errors.New("Lost connection with Gearmand")
ErrNotConn = errors.New("Did not connect to job server")
ErrFuncNotFound = errors.New("The function was not found")
ErrConnection = errors.New("Connection error")
ErrNoActiveAgent = errors.New("No active agent")
ErrTimeOut = errors.New("Executing time out")
ErrUnknown = errors.New("Unknown error")
ErrConnClosed = errors.New("Connection closed")
) )
// Extract the error message // Extract the error message
func GetError(data []byte) (err error) { func getError(data []byte) (err error) {
rel := bytes.SplitN(data, []byte{'\x00'}, 2) rel := bytes.SplitN(data, []byte{'\x00'}, 2)
if len(rel) != 2 { if len(rel) != 2 {
err = fmt.Errorf("Not a error data: %V", data) err = fmt.Errorf("Not a error data: %V", data)

View File

@ -7,6 +7,8 @@ import (
) )
var ( var (
// Global ID generator
// Default is an autoincrement ID generator
IdGen IdGenerator IdGen IdGenerator
) )
@ -14,6 +16,8 @@ func init() {
IdGen = NewAutoIncId() IdGen = NewAutoIncId()
} }
// ID generator interface. Users can implament this for
// their own generator.
type IdGenerator interface { type IdGenerator interface {
Id() string Id() string
} }
@ -28,6 +32,7 @@ func (ai *autoincId) Id() string {
return strconv.FormatInt(next, 10) return strconv.FormatInt(next, 10)
} }
// Return an autoincrement ID generator
func NewAutoIncId() IdGenerator { func NewAutoIncId() IdGenerator {
// we'll consider the nano fraction of a second at startup unique // we'll consider the nano fraction of a second at startup unique
// and count up from there. // and count up from there.

View File

@ -7,11 +7,13 @@ import (
) )
const ( const (
PoolSize = 10 poolSize = 10
) )
var ( var (
ErrNotFound = errors.New("Server Not Found") ErrNotFound = errors.New("Server Not Found")
SelectWithRate = selectWithRate
SelectRandom = selectRandom
) )
type poolClient struct { type poolClient struct {
@ -21,7 +23,7 @@ type poolClient struct {
type SelectionHandler func(map[string]*poolClient, string) string type SelectionHandler func(map[string]*poolClient, string) string
func SelectWithRate(pool map[string]*poolClient, func selectWithRate(pool map[string]*poolClient,
last string) (addr string) { last string) (addr string) {
total := 0 total := 0
for _, item := range pool { for _, item := range pool {
@ -33,7 +35,7 @@ func SelectWithRate(pool map[string]*poolClient,
return last return last
} }
func SelectRandom(pool map[string]*poolClient, func selectRandom(pool map[string]*poolClient,
last string) (addr string) { last string) (addr string) {
r := rand.Intn(len(pool)) r := rand.Intn(len(pool))
i := 0 i := 0
@ -56,10 +58,10 @@ type Pool struct {
mutex sync.Mutex mutex sync.Mutex
} }
// Create a new pool. // Return a new pool.
func NewPool() (pool *Pool) { func NewPool() (pool *Pool) {
return &Pool{ return &Pool{
clients: make(map[string]*poolClient, PoolSize), clients: make(map[string]*poolClient, poolSize),
SelectionHandler: SelectWithRate, SelectionHandler: SelectWithRate,
} }
} }

View File

@ -42,7 +42,7 @@ func TestPoolEcho(t *testing.T) {
func TestPoolDoBg(t *testing.T) { func TestPoolDoBg(t *testing.T) {
addr, handle, err := pool.DoBg("ToUpper", addr, handle, err := pool.DoBg("ToUpper",
[]byte("abcdef"), JOB_LOW) []byte("abcdef"), JobLow)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
return return
@ -65,7 +65,7 @@ func TestPoolDo(t *testing.T) {
return return
} }
addr, handle, err := pool.Do("ToUpper", addr, handle, err := pool.Do("ToUpper",
[]byte("abcdef"), JOB_LOW, jobHandler) []byte("abcdef"), JobLow, jobHandler)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
@ -89,7 +89,7 @@ func TestPoolStatus(t *testing.T) {
t.Errorf("The job (%s) shouldn't be running.", status.Handle) t.Errorf("The job (%s) shouldn't be running.", status.Handle)
} }
addr, handle, err := pool.Do("Delay5sec", addr, handle, err := pool.Do("Delay5sec",
[]byte("abcdef"), JOB_LOW, nil) []byte("abcdef"), JobLow, nil)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
return return

View File

@ -12,13 +12,13 @@ type request struct {
// Encode a Request to byte slice // Encode a Request to byte slice
func (req *request) Encode() (data []byte) { func (req *request) Encode() (data []byte) {
l := len(req.Data) // length of data l := len(req.Data) // length of data
tl := l + MIN_PACKET_LEN // add 12 bytes head tl := l + minPacketLength // add 12 bytes head
data = getBuffer(tl) data = getBuffer(tl)
copy(data[:4], REQ_STR) copy(data[:4], reqStr)
binary.BigEndian.PutUint32(data[4:8], req.DataType) binary.BigEndian.PutUint32(data[4:8], req.DataType)
binary.BigEndian.PutUint32(data[8:12], uint32(l)) binary.BigEndian.PutUint32(data[8:12], uint32(l))
copy(data[MIN_PACKET_LEN:], req.Data) copy(data[minPacketLength:], req.Data)
return return
} }

View File

@ -24,14 +24,14 @@ type Response struct {
// after calling this method, the Response.Handle will be filled // after calling this method, the Response.Handle will be filled
func (resp *Response) Result() (data []byte, err error) { func (resp *Response) Result() (data []byte, err error) {
switch resp.DataType { switch resp.DataType {
case WORK_FAIL: case dtWorkFail:
resp.Handle = string(resp.Data) resp.Handle = string(resp.Data)
err = ErrWorkFail err = ErrWorkFail
return return
case WORK_EXCEPTION: case dtWorkException:
err = ErrWorkException err = ErrWorkException
fallthrough fallthrough
case WORK_COMPLETE: case dtWorkComplete:
s := bytes.SplitN(resp.Data, []byte{'\x00'}, 2) s := bytes.SplitN(resp.Data, []byte{'\x00'}, 2)
if len(s) != 2 { if len(s) != 2 {
err = fmt.Errorf("Invalid data: %V", resp.Data) err = fmt.Errorf("Invalid data: %V", resp.Data)
@ -47,8 +47,8 @@ func (resp *Response) Result() (data []byte, err error) {
// Extract the job's update // Extract the job's update
func (resp *Response) Update() (data []byte, err error) { func (resp *Response) Update() (data []byte, err error) {
if resp.DataType != WORK_DATA && if resp.DataType != dtWorkData &&
resp.DataType != WORK_WARNING { resp.DataType != dtWorkWarning {
err = ErrDataType err = ErrDataType
return return
} }
@ -57,7 +57,7 @@ func (resp *Response) Update() (data []byte, err error) {
err = ErrInvalidData err = ErrInvalidData
return return
} }
if resp.DataType == WORK_WARNING { if resp.DataType == dtWorkWarning {
err = ErrWorkWarning err = ErrWorkWarning
} }
resp.Handle = string(s[0]) resp.Handle = string(s[0])
@ -67,12 +67,12 @@ func (resp *Response) Update() (data []byte, err error) {
// Decode a job from byte slice // Decode a job from byte slice
func decodeResponse(data []byte) (resp *Response, l int, err error) { func decodeResponse(data []byte) (resp *Response, l int, err error) {
if len(data) < MIN_PACKET_LEN { // valid package should not less 12 bytes if len(data) < minPacketLength { // valid package should not less 12 bytes
err = fmt.Errorf("Invalid data: %V", data) err = fmt.Errorf("Invalid data: %V", data)
return return
} }
dl := int(binary.BigEndian.Uint32(data[8:12])) dl := int(binary.BigEndian.Uint32(data[8:12]))
dt := data[MIN_PACKET_LEN : dl+MIN_PACKET_LEN] dt := data[minPacketLength : dl+minPacketLength]
if len(dt) != int(dl) { // length not equal if len(dt) != int(dl) { // length not equal
err = fmt.Errorf("Invalid data: %V", data) err = fmt.Errorf("Invalid data: %V", data)
return return
@ -80,10 +80,10 @@ func decodeResponse(data []byte) (resp *Response, l int, err error) {
resp = getResponse() resp = getResponse()
resp.DataType = binary.BigEndian.Uint32(data[4:8]) resp.DataType = binary.BigEndian.Uint32(data[4:8])
switch resp.DataType { switch resp.DataType {
case JOB_CREATED: case dtJobCreated:
resp.Handle = string(dt) resp.Handle = string(dt)
case STATUS_RES, WORK_DATA, WORK_WARNING, WORK_STATUS, case dtStatusRes, dtWorkData, dtWorkWarning, dtWorkStatus,
WORK_COMPLETE, WORK_FAIL, WORK_EXCEPTION: dtWorkComplete, dtWorkFail, dtWorkException:
s := bytes.SplitN(dt, []byte{'\x00'}, 2) s := bytes.SplitN(dt, []byte{'\x00'}, 2)
if len(s) >= 2 { if len(s) >= 2 {
resp.Handle = string(s[0]) resp.Handle = string(s[0])
@ -92,12 +92,12 @@ func decodeResponse(data []byte) (resp *Response, l int, err error) {
err = fmt.Errorf("Invalid data: %V", data) err = fmt.Errorf("Invalid data: %V", data)
return return
} }
case ECHO_RES: case dtEchoRes:
fallthrough fallthrough
default: default:
resp.Data = dt resp.Data = dt
} }
l = dl + MIN_PACKET_LEN l = dl + minPacketLength
return return
} }

Binary file not shown.

View File

@ -13,7 +13,7 @@ func main() {
// by implementing IdGenerator interface. // by implementing IdGenerator interface.
// client.IdGen = client.NewAutoIncId() // client.IdGen = client.NewAutoIncId()
c, err := client.New("tcp4", "127.0.0.1:4730") c, err := client.New(client.Network, "127.0.0.1:4730")
if err != nil { if err != nil {
log.Fatalln(err) log.Fatalln(err)
} }
@ -29,11 +29,11 @@ func main() {
} }
log.Println(string(echomsg)) log.Println(string(echomsg))
wg.Done() wg.Done()
jobHandler := func(job *client.Job) { jobHandler := func(resp *client.Response) {
log.Printf("%s", job.Data) log.Printf("%s", resp.Data)
wg.Done() wg.Done()
} }
handle, err := c.Do("ToUpper", echo, client.JOB_NORMAL, jobHandler) handle, err := c.Do("ToUpper", echo, client.JobNormal, jobHandler)
if err != nil { if err != nil {
log.Fatalln(err) log.Fatalln(err)
} }

View File

@ -29,7 +29,7 @@ func main() {
defer w.Close() defer w.Close()
w.ErrorHandler = func(e error) { w.ErrorHandler = func(e error) {
log.Println(e) log.Println(e)
if e == worker.ErrConnection { if e == worker.ErrLostConn {
proc, err := os.FindProcess(os.Getpid()) proc, err := os.FindProcess(os.Getpid())
if err != nil { if err != nil {
log.Println(err) log.Println(err)

View File

@ -49,8 +49,9 @@ func (a *agent) work() {
break break
} }
// If it is unexpected error and the connection wasn't // If it is unexpected error and the connection wasn't
// closed by Gearmand, the agent should colse the conection // closed by Gearmand, the agent should close the conection
// and reconnect to job server. // and reconnect to job server.
a.Close()
a.conn, err = net.Dial(a.net, a.addr) a.conn, err = net.Dial(a.net, a.addr)
if err != nil { if err != nil {
a.worker.err(err) a.worker.err(err)

View File

@ -20,7 +20,7 @@ func TestWorkerErrNoneAgents(t *testing.T) {
func TestWorkerAddServer(t *testing.T) { func TestWorkerAddServer(t *testing.T) {
t.Log("Add local server 127.0.0.1:4730.") t.Log("Add local server 127.0.0.1:4730.")
if err := worker.AddServer("tcp4", "127.0.0.1:4730"); err != nil { if err := worker.AddServer(Network, "127.0.0.1:4730"); err != nil {
t.Error(err) t.Error(err)
} }