You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

295 lines
6.5 KiB

// The client package helps developers connect to Gearmand, send
// jobs and fetch result.
package client
import (
преди 11 години
"bufio"
преди 11 години
"net"
"sync"
)
// One client connect to one server.
// Use Pool for multi-connections.
type Client struct {
преди 11 години
sync.Mutex
преди 11 години
net, addr, lastcall string
respHandler map[string]ResponseHandler
innerHandler map[string]ResponseHandler
in chan *Response
преди 11 години
conn net.Conn
преди 11 години
rw *bufio.ReadWriter
преди 11 години
преди 11 години
ErrorHandler ErrorHandler
}
// Return a client.
func New(network, addr string) (client *Client, err error) {
преди 11 години
client = &Client{
net: network,
преди 11 години
addr: addr,
respHandler: make(map[string]ResponseHandler, queueSize),
innerHandler: make(map[string]ResponseHandler, queueSize),
in: make(chan *Response, queueSize),
преди 11 години
}
client.conn, err = net.Dial(client.net, client.addr)
преди 11 години
if err != nil {
return
}
преди 11 години
client.rw = bufio.NewReadWriter(bufio.NewReader(client.conn),
bufio.NewWriter(client.conn))
go client.readLoop()
go client.processLoop()
преди 11 години
return
преди 12 години
}
преди 11 години
func (client *Client) write(req *request) (err error) {
var n int
buf := req.Encode()
for i := 0; i < len(buf); i += n {
преди 11 години
n, err = client.rw.Write(buf[i:])
преди 11 години
if err != nil {
return
}
}
преди 11 години
return client.rw.Flush()
преди 12 години
}
преди 11 години
func (client *Client) read(length int) (data []byte, err error) {
n := 0
buf := getBuffer(bufferSize)
преди 11 години
// read until data can be unpacked
for i := length; i > 0 || len(data) < minPacketLength; i -= n {
преди 11 години
if n, err = client.rw.Read(buf); err != nil {
преди 11 години
return
}
data = append(data, buf[0:n]...)
if n < bufferSize {
преди 11 години
break
}
}
return
преди 12 години
}
преди 11 години
func (client *Client) readLoop() {
defer close(client.in)
var data, leftdata []byte
преди 11 години
var err error
var resp *Response
преди 11 години
ReadLoop:
преди 11 години
for client.conn != nil {
if data, err = client.read(bufferSize); err != nil {
преди 11 години
if opErr, ok := err.(*net.OpError); ok {
if opErr.Timeout() {
client.err(err)
}
if opErr.Temporary() {
continue
}
break
}
преди 11 години
client.err(err)
// 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)
преди 11 години
break
}
преди 11 години
client.rw = bufio.NewReadWriter(bufio.NewReader(client.conn),
bufio.NewWriter(client.conn))
преди 11 години
continue
}
if len(leftdata) > 0 { // some data left for processing
преди 11 години
data = append(leftdata, data...)
преди 11 години
}
преди 11 години
for {
l := len(data)
if l < minPacketLength { // not enough data
leftdata = data
continue ReadLoop
}
if resp, l, err = decodeResponse(data); err != nil {
leftdata = data[l:]
continue ReadLoop
} else {
client.in <- resp
}
data = data[l:]
if len(data) > 0 {
continue
}
break
преди 11 години
}
}
}
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)
преди 11 години
case dtWorkData, dtWorkWarning, dtWorkStatus:
resp = client.handleResponse(resp.Handle, resp)
case dtWorkComplete, dtWorkFail, dtWorkException:
client.handleResponse(resp.Handle, resp)
delete(client.respHandler, resp.Handle)
}
}
}
преди 11 години
func (client *Client) err(e error) {
if client.ErrorHandler != nil {
client.ErrorHandler(e)
}
}
преди 11 години
func (client *Client) handleResponse(key string, resp *Response) *Response {
преди 11 години
if h, ok := client.respHandler[key]; ok {
h(resp)
преди 11 години
return nil
преди 11 години
}
преди 11 години
return resp
преди 11 години
}
преди 11 години
func (client *Client) handleInner(key string, resp *Response) *Response {
преди 11 години
if h, ok := client.innerHandler[key]; ok {
h(resp)
delete(client.innerHandler, key)
преди 11 години
return nil
преди 11 години
}
преди 11 години
return resp
преди 12 години
}
преди 12 години
func (client *Client) do(funcname string, data []byte,
преди 11 години
flag uint32) (handle string, err error) {
преди 11 години
if client.conn == nil {
return "", ErrLostConn
}
преди 11 години
var mutex sync.Mutex
mutex.Lock()
преди 11 години
client.lastcall = "c"
преди 11 години
client.innerHandler["c"] = func(resp *Response) {
преди 11 години
defer mutex.Unlock()
if resp.DataType == dtError {
err = getError(resp.Data)
преди 11 години
return
}
преди 11 години
handle = resp.Handle
преди 11 години
}
преди 11 години
id := IdGen.Id()
req := getJob(id, []byte(funcname), data)
req.DataType = flag
client.write(req)
преди 11 години
mutex.Lock()
преди 11 години
return
преди 12 години
}
// Call the function and get a response.
// flag can be set to: JobLow, JobNormal and JobHigh
преди 12 години
func (client *Client) Do(funcname string, data []byte,
преди 11 години
flag byte, h ResponseHandler) (handle string, err error) {
преди 11 години
var datatype uint32
switch flag {
case JobLow:
datatype = dtSubmitJobLow
case JobHigh:
datatype = dtSubmitJobHigh
преди 11 години
default:
datatype = dtSubmitJob
преди 11 години
}
преди 11 години
handle, err = client.do(funcname, data, datatype)
преди 11 години
if err == nil && h != nil {
преди 11 години
client.respHandler[handle] = h
преди 11 години
}
return
преди 12 години
}
// Call the function in background, no response needed.
// flag can be set to: JobLow, JobNormal and JobHigh
преди 12 години
func (client *Client) DoBg(funcname string, data []byte,
преди 11 години
flag byte) (handle string, err error) {
преди 11 години
if client.conn == nil {
return "", ErrLostConn
}
преди 11 години
var datatype uint32
switch flag {
case JobLow:
datatype = dtSubmitJobLowBg
case JobHigh:
datatype = dtSubmitJobHighBg
преди 11 години
default:
datatype = dtSubmitJobBg
преди 11 години
}
преди 11 години
handle, err = client.do(funcname, data, datatype)
преди 11 години
return
}
// Get job status from job server.
преди 11 години
func (client *Client) Status(handle string) (status *Status, err error) {
преди 11 години
if client.conn == nil {
return nil, ErrLostConn
}
преди 11 години
var mutex sync.Mutex
mutex.Lock()
преди 11 години
client.lastcall = "s" + handle
преди 11 години
client.innerHandler["s"+handle] = func(resp *Response) {
преди 11 години
defer mutex.Unlock()
преди 11 години
var err error
преди 11 години
status, err = resp._status()
преди 11 години
if err != nil {
client.err(err)
}
преди 11 години
}
преди 11 години
req := getRequest()
req.DataType = dtGetStatus
преди 11 години
req.Data = []byte(handle)
client.write(req)
преди 11 години
mutex.Lock()
преди 11 години
return
}
// Echo.
преди 11 години
func (client *Client) Echo(data []byte) (echo []byte, err error) {
преди 11 години
if client.conn == nil {
return nil, ErrLostConn
}
преди 11 години
var mutex sync.Mutex
mutex.Lock()
client.innerHandler["e"] = func(resp *Response) {
преди 11 години
echo = resp.Data
преди 11 години
mutex.Unlock()
}
преди 11 години
req := getRequest()
req.DataType = dtEchoReq
преди 11 години
req.Data = data
client.lastcall = "e"
преди 11 години
client.write(req)
mutex.Lock()
преди 11 години
return
}
// Close connection
func (client *Client) Close() (err error) {
client.Lock()
defer client.Unlock()
if client.conn != nil {
err = client.conn.Close()
client.conn = nil
}
return
}