forked from yuxh/gearman-go
		
	merge, client need to refactor
This commit is contained in:
		
						commit
						3aa95042e6
					
				@ -1,3 +1,4 @@
 | 
				
			|||||||
language: go
 | 
					language: go
 | 
				
			||||||
 | 
					  - 1.2
 | 
				
			||||||
before_install:
 | 
					before_install:
 | 
				
			||||||
  - sudo apt-get install -qq gearman-job-server
 | 
					  - sudo apt-get install -qq gearman-job-server
 | 
				
			||||||
 | 
				
			|||||||
@ -34,7 +34,12 @@ Usage
 | 
				
			|||||||
    w.AddServer("127.0.0.1:4730")
 | 
					    w.AddServer("127.0.0.1:4730")
 | 
				
			||||||
    w.AddFunc("ToUpper", ToUpper, worker.Immediately)
 | 
					    w.AddFunc("ToUpper", ToUpper, worker.Immediately)
 | 
				
			||||||
    w.AddFunc("ToUpperTimeOut5", ToUpper, 5)
 | 
					    w.AddFunc("ToUpperTimeOut5", ToUpper, 5)
 | 
				
			||||||
    w.Work()
 | 
						if err := w.Ready(); err != nil {
 | 
				
			||||||
 | 
							log.Fatal(err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						go w.Work()
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Client
 | 
					## Client
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -9,7 +9,7 @@ import (
 | 
				
			|||||||
	"io"
 | 
						"io"
 | 
				
			||||||
	"net"
 | 
						"net"
 | 
				
			||||||
	"sync"
 | 
						"sync"
 | 
				
			||||||
	"fmt"
 | 
					//	"fmt"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
@ -21,13 +21,14 @@ handle := c.Do("foobar", []byte("data here"), JOB_LOW | JOB_BG)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
type Client struct {
 | 
					type Client struct {
 | 
				
			||||||
 | 
						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 []byte
 | 
				
			||||||
	isConn              bool
 | 
						isConn              bool
 | 
				
			||||||
	conn                net.Conn
 | 
						conn                net.Conn
 | 
				
			||||||
	mutex               sync.RWMutex
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ErrorHandler ErrorHandler
 | 
						ErrorHandler ErrorHandler
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -115,7 +116,6 @@ func (client *Client) readLoop() {
 | 
				
			|||||||
			client.err(err)
 | 
								client.err(err)
 | 
				
			||||||
			continue
 | 
								continue
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		fmt.Printf("[%X]", data)
 | 
					 | 
				
			||||||
		client.in <- data
 | 
							client.in <- data
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	close(client.in)
 | 
						close(client.in)
 | 
				
			||||||
@ -141,23 +141,25 @@ func (client *Client) processLoop() {
 | 
				
			|||||||
			continue
 | 
								continue
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		leftdata = nil
 | 
							leftdata = nil
 | 
				
			||||||
 | 
							for resp != nil {
 | 
				
			||||||
			switch resp.DataType {
 | 
								switch resp.DataType {
 | 
				
			||||||
			case ERROR:
 | 
								case ERROR:
 | 
				
			||||||
				if client.lastcall != "" {
 | 
									if client.lastcall != "" {
 | 
				
			||||||
				client.handleInner(client.lastcall, resp)
 | 
										resp = client.handleInner(client.lastcall, resp)
 | 
				
			||||||
					client.lastcall = ""
 | 
										client.lastcall = ""
 | 
				
			||||||
				} else {
 | 
									} else {
 | 
				
			||||||
					client.err(GetError(resp.Data))
 | 
										client.err(GetError(resp.Data))
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			case STATUS_RES:
 | 
								case STATUS_RES:
 | 
				
			||||||
			client.handleInner("s"+resp.Handle, resp)
 | 
									resp = client.handleInner("s"+resp.Handle, resp)
 | 
				
			||||||
			case JOB_CREATED:
 | 
								case JOB_CREATED:
 | 
				
			||||||
			client.handleInner("c", resp)
 | 
									resp = client.handleInner("c", resp)
 | 
				
			||||||
			case ECHO_RES:
 | 
								case ECHO_RES:
 | 
				
			||||||
			client.handleInner("e", resp)
 | 
									resp = client.handleInner("e", resp)
 | 
				
			||||||
			case WORK_DATA, WORK_WARNING, WORK_STATUS, WORK_COMPLETE,
 | 
								case WORK_DATA, WORK_WARNING, WORK_STATUS, WORK_COMPLETE,
 | 
				
			||||||
				WORK_FAIL, WORK_EXCEPTION:
 | 
									WORK_FAIL, WORK_EXCEPTION:
 | 
				
			||||||
			client.handleResponse(resp.Handle, resp)
 | 
									resp = client.handleResponse(resp.Handle, resp)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if len(data) > l {
 | 
							if len(data) > l {
 | 
				
			||||||
			leftdata = data[l:]
 | 
								leftdata = data[l:]
 | 
				
			||||||
@ -173,44 +175,44 @@ func (client *Client) err(e error) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// job handler
 | 
					// job handler
 | 
				
			||||||
func (client *Client) handleResponse(key string, resp *Response) {
 | 
					func (client *Client) handleResponse(key string, resp *Response) *Response {
 | 
				
			||||||
	client.mutex.RLock()
 | 
					 | 
				
			||||||
	defer client.mutex.RUnlock()
 | 
					 | 
				
			||||||
	if h, ok := client.respHandler[key]; ok {
 | 
						if h, ok := client.respHandler[key]; ok {
 | 
				
			||||||
		h(resp)
 | 
							h(resp)
 | 
				
			||||||
		delete(client.respHandler, key)
 | 
							delete(client.respHandler, key)
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						return resp
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// job handler
 | 
					// job handler
 | 
				
			||||||
func (client *Client) handleInner(key string, resp *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)
 | 
				
			||||||
		delete(client.innerHandler, key)
 | 
							delete(client.innerHandler, key)
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						return resp
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Internal do
 | 
					// 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 wg sync.WaitGroup
 | 
						var mutex sync.Mutex
 | 
				
			||||||
	wg.Add(1)
 | 
						mutex.Lock()
 | 
				
			||||||
	client.mutex.RLock()
 | 
					 | 
				
			||||||
	client.lastcall = "c"
 | 
						client.lastcall = "c"
 | 
				
			||||||
	client.innerHandler["c"] = ResponseHandler(func(resp *Response) {
 | 
						client.innerHandler["c"] = func(resp *Response) {
 | 
				
			||||||
		defer wg.Done()
 | 
					 | 
				
			||||||
		defer client.mutex.RUnlock()
 | 
					 | 
				
			||||||
		if resp.DataType == ERROR {
 | 
							if resp.DataType == ERROR {
 | 
				
			||||||
			err = GetError(resp.Data)
 | 
								err = GetError(resp.Data)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		handle = resp.Handle
 | 
							handle = resp.Handle
 | 
				
			||||||
	})
 | 
							mutex.Unlock()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	id := IdGen.Id()
 | 
						id := IdGen.Id()
 | 
				
			||||||
	req := getJob(id, []byte(funcname), data)
 | 
						req := getJob(id, []byte(funcname), data)
 | 
				
			||||||
	req.DataType = flag
 | 
						req.DataType = flag
 | 
				
			||||||
	client.write(req)
 | 
						client.write(req)
 | 
				
			||||||
	wg.Wait()
 | 
						mutex.Lock()
 | 
				
			||||||
	return
 | 
						return
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -232,8 +234,6 @@ func (client *Client) Do(funcname string, data []byte,
 | 
				
			|||||||
		datatype = SUBMIT_JOB
 | 
							datatype = SUBMIT_JOB
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	handle, err = client.do(funcname, data, datatype)
 | 
						handle, err = client.do(funcname, data, datatype)
 | 
				
			||||||
	client.mutex.Lock()
 | 
					 | 
				
			||||||
	defer client.mutex.Unlock()
 | 
					 | 
				
			||||||
	if h != nil {
 | 
						if h != nil {
 | 
				
			||||||
		client.respHandler[handle] = h
 | 
							client.respHandler[handle] = h
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -262,41 +262,39 @@ func (client *Client) DoBg(funcname string, data []byte,
 | 
				
			|||||||
// 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, err error) {
 | 
					func (client *Client) Status(handle string) (status *Status, err error) {
 | 
				
			||||||
	var wg sync.WaitGroup
 | 
						var mutex sync.Mutex
 | 
				
			||||||
	wg.Add(1)
 | 
						mutex.Lock()
 | 
				
			||||||
	client.mutex.Lock()
 | 
					 | 
				
			||||||
	client.lastcall = "s" + handle
 | 
						client.lastcall = "s" + handle
 | 
				
			||||||
	client.innerHandler["s"+handle] = ResponseHandler(func(resp *Response) {
 | 
						client.innerHandler["s"+handle] = func(resp *Response) {
 | 
				
			||||||
		defer wg.Done()
 | 
					 | 
				
			||||||
		defer client.mutex.Unlock()
 | 
					 | 
				
			||||||
		var err error
 | 
							var err error
 | 
				
			||||||
		status, err = resp.Status()
 | 
							status, err = resp.Status()
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			client.err(err)
 | 
								client.err(err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	})
 | 
							mutex.Unlock()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	req := getRequest()
 | 
						req := getRequest()
 | 
				
			||||||
	req.DataType = GET_STATUS
 | 
						req.DataType = GET_STATUS
 | 
				
			||||||
	req.Data = []byte(handle)
 | 
						req.Data = []byte(handle)
 | 
				
			||||||
	client.write(req)
 | 
						client.write(req)
 | 
				
			||||||
	wg.Wait()
 | 
						mutex.Lock()
 | 
				
			||||||
	return
 | 
						return
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Send a something out, get the samething back.
 | 
					// Send a something out, get the samething back.
 | 
				
			||||||
func (client *Client) Echo(data []byte) (echo []byte, err error) {
 | 
					func (client *Client) Echo(data []byte) (echo []byte, err error) {
 | 
				
			||||||
	var wg sync.WaitGroup
 | 
						var mutex sync.Mutex
 | 
				
			||||||
	wg.Add(1)
 | 
						mutex.Lock()
 | 
				
			||||||
	client.innerHandler["e"] = ResponseHandler(func(resp *Response) {
 | 
						client.innerHandler["e"] = func(resp *Response) {
 | 
				
			||||||
		defer wg.Done()
 | 
					 | 
				
			||||||
		echo = resp.Data
 | 
							echo = resp.Data
 | 
				
			||||||
	})
 | 
							mutex.Unlock()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	req := getRequest()
 | 
						req := getRequest()
 | 
				
			||||||
	req.DataType = ECHO_REQ
 | 
						req.DataType = ECHO_REQ
 | 
				
			||||||
	req.Data = data
 | 
						req.Data = data
 | 
				
			||||||
	client.write(req)
 | 
					 | 
				
			||||||
	client.lastcall = "e"
 | 
						client.lastcall = "e"
 | 
				
			||||||
	wg.Wait()
 | 
						client.write(req)
 | 
				
			||||||
 | 
						mutex.Lock()
 | 
				
			||||||
	return
 | 
						return
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -9,7 +9,7 @@ import (
 | 
				
			|||||||
	"encoding/binary"
 | 
						"encoding/binary"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// request
 | 
					// Request from client
 | 
				
			||||||
type request struct {
 | 
					type request struct {
 | 
				
			||||||
	DataType uint32
 | 
						DataType uint32
 | 
				
			||||||
	Data     []byte
 | 
						Data     []byte
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										
											BIN
										
									
								
								example/client/client
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								example/client/client
									
									
									
									
									
										Executable file
									
								
							
										
											Binary file not shown.
										
									
								
							@ -1,9 +1,9 @@
 | 
				
			|||||||
package main
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"github.com/mikespook/gearman-go/client"
 | 
				
			||||||
    "log"
 | 
					    "log"
 | 
				
			||||||
    "sync"
 | 
					    "sync"
 | 
				
			||||||
    "github.com/mikespook/gearman-go/client"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func main() {
 | 
					func main() {
 | 
				
			||||||
@ -29,7 +29,7 @@ func main() {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    log.Println(string(echomsg))
 | 
					    log.Println(string(echomsg))
 | 
				
			||||||
    wg.Done()
 | 
					    wg.Done()
 | 
				
			||||||
    jobHandler := func(job *client.Response) {
 | 
					    jobHandler := func(job *client.Job) {
 | 
				
			||||||
        log.Printf("%s", job.Data)
 | 
					        log.Printf("%s", job.Data)
 | 
				
			||||||
        wg.Done()
 | 
					        wg.Done()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +0,0 @@
 | 
				
			|||||||
= Exec Worker
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
This program execute shell or php scripts as backend jobs.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Scripts can communicat with the worker through STDERR using formated output.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
USE THIS AT YOUR OWN RASK!!!
 | 
					 | 
				
			||||||
@ -1,40 +0,0 @@
 | 
				
			|||||||
<?php
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# create our client object
 | 
					 | 
				
			||||||
$gmclient= new GearmanClient();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# add the default server (localhost)
 | 
					 | 
				
			||||||
$gmclient->addServer();
 | 
					 | 
				
			||||||
$data = array(
 | 
					 | 
				
			||||||
    'Name' => 'foobar',
 | 
					 | 
				
			||||||
    'Args'  => array("0", "1", "2", "3"),
 | 
					 | 
				
			||||||
);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
$c = isset($_SERVER['argv'][1]) ? $_SERVER['argv'][1] : 10; 
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
for ($i = 0; $i < $c; $i ++) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # run reverse client in the background
 | 
					 | 
				
			||||||
    $job_handle = $gmclient->doBackground("execphp", json_encode($data));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if ($gmclient->returnCode() != GEARMAN_SUCCESS) {
 | 
					 | 
				
			||||||
        echo "bad return code\n";
 | 
					 | 
				
			||||||
        exit;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
$data = array(
 | 
					 | 
				
			||||||
    'Name' => 'notexists',
 | 
					 | 
				
			||||||
    'Args'  => array("0", "1", "2", "3"),
 | 
					 | 
				
			||||||
);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# run reverse client in the background
 | 
					 | 
				
			||||||
$job_handle = $gmclient->doBackground("exec", json_encode($data));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
if ($gmclient->returnCode() != GEARMAN_SUCCESS)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  echo "bad return code\n";
 | 
					 | 
				
			||||||
  exit;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
echo "done!\n";
 | 
					 | 
				
			||||||
@ -1,123 +0,0 @@
 | 
				
			|||||||
// Copyright 2012 Xing Xing <mikespook@gmail.com>.
 | 
					 | 
				
			||||||
// All rights reserved.
 | 
					 | 
				
			||||||
// Use of this source code is governed by a commercial
 | 
					 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
package main
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
    "io"
 | 
					 | 
				
			||||||
    "bytes"
 | 
					 | 
				
			||||||
    "os/exec"
 | 
					 | 
				
			||||||
    "encoding/json"
 | 
					 | 
				
			||||||
    "github.com/mikespook/golib/log"
 | 
					 | 
				
			||||||
    "github.com/mikespook/gearman-go/worker"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type outData struct {
 | 
					 | 
				
			||||||
    Numerator, Denominator int
 | 
					 | 
				
			||||||
    Warning bool
 | 
					 | 
				
			||||||
    Data []byte
 | 
					 | 
				
			||||||
    Debug string
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type ShExec struct {
 | 
					 | 
				
			||||||
    Name, basedir string
 | 
					 | 
				
			||||||
    Args []string
 | 
					 | 
				
			||||||
    *exec.Cmd
 | 
					 | 
				
			||||||
    job *worker.Job
 | 
					 | 
				
			||||||
    Logger *log.Logger
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func NewShExec(basedir string, job *worker.Job) (sh *ShExec, err error) {
 | 
					 | 
				
			||||||
    sh = &ShExec{
 | 
					 | 
				
			||||||
        basedir: basedir,
 | 
					 | 
				
			||||||
        job: job,
 | 
					 | 
				
			||||||
        Args: make([]string, 0),
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    if err = sh.parse(job.Data); err != nil {
 | 
					 | 
				
			||||||
        return nil, err
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (sh *ShExec) parse(data []byte) (err error) {
 | 
					 | 
				
			||||||
    if err = json.Unmarshal(data, sh); err != nil {
 | 
					 | 
				
			||||||
        return
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (sh *ShExec) Append(args ... string) {
 | 
					 | 
				
			||||||
    sh.Args = append(sh.Args, args ...)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (sh *ShExec) Prepend(args ... string) {
 | 
					 | 
				
			||||||
    sh.Args = append(args, sh.Args ...)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (sh *ShExec) Exec() (rslt []byte, err error){
 | 
					 | 
				
			||||||
    sh.Logger.Debugf("Executing: Handle=%s, Exec=%s, Args=%v",
 | 
					 | 
				
			||||||
        sh.job.Handle, sh.Name, sh.Args)
 | 
					 | 
				
			||||||
    sh.Cmd = exec.Command(sh.Name, sh.Args ... )
 | 
					 | 
				
			||||||
    go func() {
 | 
					 | 
				
			||||||
        if ok := <-sh.job.Canceled(); ok {
 | 
					 | 
				
			||||||
            sh.Cmd.Process.Kill()
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }()
 | 
					 | 
				
			||||||
    sh.Cmd.Dir = sh.basedir
 | 
					 | 
				
			||||||
    var buf bytes.Buffer
 | 
					 | 
				
			||||||
    sh.Cmd.Stdout = &buf
 | 
					 | 
				
			||||||
    var errPipe io.ReadCloser
 | 
					 | 
				
			||||||
    if errPipe, err = sh.Cmd.StderrPipe(); err != nil {
 | 
					 | 
				
			||||||
        return nil, err
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    defer errPipe.Close()
 | 
					 | 
				
			||||||
    go sh.processErr(errPipe)
 | 
					 | 
				
			||||||
    if err = sh.Cmd.Run(); err != nil {
 | 
					 | 
				
			||||||
        return nil, err
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    rslt = buf.Bytes()
 | 
					 | 
				
			||||||
    return
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (sh *ShExec) processErr(pipe io.ReadCloser) {
 | 
					 | 
				
			||||||
    result := make([]byte, 1024)
 | 
					 | 
				
			||||||
    var more []byte
 | 
					 | 
				
			||||||
    for {
 | 
					 | 
				
			||||||
        n, err := pipe.Read(result)
 | 
					 | 
				
			||||||
        if err != nil {
 | 
					 | 
				
			||||||
            if err != io.EOF {
 | 
					 | 
				
			||||||
                sh.job.UpdateData([]byte(err.Error()), true)
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            return
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if more != nil {
 | 
					 | 
				
			||||||
            result = append(more, result[:n]...)
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            result = result[:n]
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if n < 1024 {
 | 
					 | 
				
			||||||
            var out outData
 | 
					 | 
				
			||||||
            if err := json.Unmarshal(result, &out); err != nil {
 | 
					 | 
				
			||||||
                sh.job.UpdateData([]byte(result), true)
 | 
					 | 
				
			||||||
                return
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            if out.Debug == "" {
 | 
					 | 
				
			||||||
                if out.Data != nil {
 | 
					 | 
				
			||||||
                    sh.job.UpdateData(out.Data, out.Warning)
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                if out.Numerator != 0 || out.Denominator != 0 {
 | 
					 | 
				
			||||||
                    sh.job.UpdateStatus(out.Numerator, out.Denominator)
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                sh.Logger.Debugf("Debug: Handle=%s, Exec=%s, Args=%v, Data=%s",
 | 
					 | 
				
			||||||
                sh.job.Handle, sh.Name, sh.Args, out.Debug)
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            more = nil
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            more = result
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@ -1,38 +0,0 @@
 | 
				
			|||||||
// Copyright 2012 Xing Xing <mikespook@gmail.com>.
 | 
					 | 
				
			||||||
// All rights reserved.
 | 
					 | 
				
			||||||
// Use of this source code is governed by a commercial
 | 
					 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
package main
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
    "github.com/mikespook/golib/log"
 | 
					 | 
				
			||||||
    "github.com/mikespook/gearman-go/worker"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func execShell(job *worker.Job) (result []byte, err error) {
 | 
					 | 
				
			||||||
    log.Messagef("[Shell]Received: Handle=%s", job.Handle)
 | 
					 | 
				
			||||||
    defer log.Messagef("[Shell]Finished: Handle=%s", job.Handle)
 | 
					 | 
				
			||||||
    log.Debugf("[Shell]Received: Handle=%s, UID=%s, Data=%v", job.Handle, job.UniqueId, job.Data)
 | 
					 | 
				
			||||||
    var sh *ShExec
 | 
					 | 
				
			||||||
    if sh, err = NewShExec(*basedir, job); err != nil {
 | 
					 | 
				
			||||||
        return
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    sh.Logger = log.DefaultLogger
 | 
					 | 
				
			||||||
    return sh.Exec()
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func execPHP(job *worker.Job) (result []byte, err error) {
 | 
					 | 
				
			||||||
    log.Messagef("[PHP]Received: Handle=%s", job.Handle)
 | 
					 | 
				
			||||||
    defer log.Messagef("[PHP]Finished: Handle=%s", job.Handle)
 | 
					 | 
				
			||||||
    log.Debugf("[PHP]Received: Handle=%s, UID=%s, Data=%v", job.Handle, job.UniqueId, job.Data)
 | 
					 | 
				
			||||||
    var sh *ShExec
 | 
					 | 
				
			||||||
    if sh, err = NewShExec(*basedir, job); err != nil {
 | 
					 | 
				
			||||||
        return
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    sh.Prepend("-f", sh.Name + ".php")
 | 
					 | 
				
			||||||
    sh.Name = "php"
 | 
					 | 
				
			||||||
    sh.Logger = log.DefaultLogger
 | 
					 | 
				
			||||||
    return sh.Exec()
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@ -1,40 +0,0 @@
 | 
				
			|||||||
<?php
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class X {
 | 
					 | 
				
			||||||
    private $stderr;
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    function __construct() {
 | 
					 | 
				
			||||||
        $this->stderr = fopen("php://stderr", "r");
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public function sendMsg($msg, $warning = false, $numerator = 0, $denominator = 0) {
 | 
					 | 
				
			||||||
        $result = array(
 | 
					 | 
				
			||||||
            'Numerator'     =>  $numerator,
 | 
					 | 
				
			||||||
            'Denominator'   =>  $denominator,
 | 
					 | 
				
			||||||
            'Warning'       =>  $warning,
 | 
					 | 
				
			||||||
            'Data'          =>  $msg,
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
        fwrite($this->stderr, json_encode($result));
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public function debug($msg) {
 | 
					 | 
				
			||||||
         $result = array(
 | 
					 | 
				
			||||||
            'Debug' => $msg,
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
        fwrite($this->stderr, json_encode($result));       
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public function close() {
 | 
					 | 
				
			||||||
        fclose($this->stderr);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public function argv($index) {
 | 
					 | 
				
			||||||
        $argv = $_SERVER['argv'];
 | 
					 | 
				
			||||||
        return isset($argv[$index]) ? $argv[$index] : null;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public function argc() {
 | 
					 | 
				
			||||||
        $argc = $_SERVER['argc'];
 | 
					 | 
				
			||||||
        return $argc;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,11 +0,0 @@
 | 
				
			|||||||
<?php
 | 
					 | 
				
			||||||
function autoloader($class) {
 | 
					 | 
				
			||||||
    $names = split("_", $class);
 | 
					 | 
				
			||||||
    if (count($names) > 1) {
 | 
					 | 
				
			||||||
        include implode(DIRECTORY_SEPARATOR, $names) . '.php';
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
        include $class . '.php';
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
spl_autoload_register('autoloader');
 | 
					 | 
				
			||||||
@ -1,47 +0,0 @@
 | 
				
			|||||||
// Copyright 2012 Xing Xing <mikespook@gmail.com>.
 | 
					 | 
				
			||||||
// All rights reserved.
 | 
					 | 
				
			||||||
// Use of this source code is governed by a commercial
 | 
					 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
package main
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
    "github.com/mikespook/golib/log"
 | 
					 | 
				
			||||||
    "flag"
 | 
					 | 
				
			||||||
    "strings"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var (
 | 
					 | 
				
			||||||
    logfile  = flag.String("log", "",
 | 
					 | 
				
			||||||
        "Log file to write errors and information to." +
 | 
					 | 
				
			||||||
        " Empty string output to STDOUT.")
 | 
					 | 
				
			||||||
    loglevel = flag.String("log-level", "all", "Log level to record." +
 | 
					 | 
				
			||||||
        " Values 'error', 'warning', 'message', 'debug', 'all' and 'none'" +
 | 
					 | 
				
			||||||
        " are accepted. Use '|' to combine more levels.")
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func initLog() {
 | 
					 | 
				
			||||||
    level := log.LogNone
 | 
					 | 
				
			||||||
    levels := strings.SplitN(*loglevel, "|", -1)
 | 
					 | 
				
			||||||
    for _, v := range levels {
 | 
					 | 
				
			||||||
        switch v {
 | 
					 | 
				
			||||||
        case "none":
 | 
					 | 
				
			||||||
            level = level | log.LogNone
 | 
					 | 
				
			||||||
            break
 | 
					 | 
				
			||||||
        case "error":
 | 
					 | 
				
			||||||
            level = level | log.LogError
 | 
					 | 
				
			||||||
        case "warning":
 | 
					 | 
				
			||||||
            level = level | log.LogWarning
 | 
					 | 
				
			||||||
        case "message":
 | 
					 | 
				
			||||||
            level = level | log.LogMessage
 | 
					 | 
				
			||||||
        case "debug":
 | 
					 | 
				
			||||||
            level = level | log.LogDebug
 | 
					 | 
				
			||||||
        case "all":
 | 
					 | 
				
			||||||
            level = log.LogAll
 | 
					 | 
				
			||||||
        default:
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    if err := log.Init(*logfile, level); err != nil {
 | 
					 | 
				
			||||||
        log.Error(err)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,8 +0,0 @@
 | 
				
			|||||||
<?php
 | 
					 | 
				
			||||||
define('ISCLI', PHP_SAPI === 'cli');
 | 
					 | 
				
			||||||
if (!ISCLI) {
 | 
					 | 
				
			||||||
    die("cli only!");
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
define("ROOT", dirname(__FILE__));
 | 
					 | 
				
			||||||
define("LIB", ROOT . "/../lib/");
 | 
					 | 
				
			||||||
include_once(LIB . "bootstrap.php");
 | 
					 | 
				
			||||||
@ -1,8 +0,0 @@
 | 
				
			|||||||
<?php
 | 
					 | 
				
			||||||
include("default.php");
 | 
					 | 
				
			||||||
$x = new X();
 | 
					 | 
				
			||||||
$x->sendMsg("test");
 | 
					 | 
				
			||||||
$x->debug("debug message");
 | 
					 | 
				
			||||||
sleep(10);
 | 
					 | 
				
			||||||
$x->close();
 | 
					 | 
				
			||||||
exit(0);
 | 
					 | 
				
			||||||
@ -1,106 +0,0 @@
 | 
				
			|||||||
// Copyright 2012 Xing Xing <mikespook@gmail.com>.
 | 
					 | 
				
			||||||
// All rights reserved.
 | 
					 | 
				
			||||||
// Use of this source code is governed by a commercial
 | 
					 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
package main
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
    "os"
 | 
					 | 
				
			||||||
    "flag"
 | 
					 | 
				
			||||||
    "time"
 | 
					 | 
				
			||||||
    "github.com/mikespook/golib/log"
 | 
					 | 
				
			||||||
    "github.com/mikespook/golib/pid"
 | 
					 | 
				
			||||||
    "github.com/mikespook/golib/prof"
 | 
					 | 
				
			||||||
    "github.com/mikespook/golib/signal"
 | 
					 | 
				
			||||||
    "github.com/mikespook/gearman-go/worker"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var (
 | 
					 | 
				
			||||||
    pidfile = flag.String("pid", "/run/seedworker.pid",
 | 
					 | 
				
			||||||
        "PID file to write pid")
 | 
					 | 
				
			||||||
    proffile = flag.String("prof", "", "Profiling file")
 | 
					 | 
				
			||||||
    dumpfile = flag.String("dump", "", "Heap dumping file")
 | 
					 | 
				
			||||||
    dumptime = flag.Int("dumptime", 5, "Heap dumping time interval")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    joblimit = flag.Int("job-limit", worker.Unlimited,
 | 
					 | 
				
			||||||
        "Maximum number of concurrently executing job." +
 | 
					 | 
				
			||||||
        " Zero is unlimited.")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    basedir = flag.String("basedir", "", "Working directory of the php scripts.")
 | 
					 | 
				
			||||||
    timeout = flag.Uint("timeout", 30, "Executing time out.")
 | 
					 | 
				
			||||||
    gearmand = flag.String("gearmand", "127.0.0.1:4730", "Address and port of gearmand")
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func init() {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    flag.Parse()
 | 
					 | 
				
			||||||
    initLog()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if *basedir == "" {
 | 
					 | 
				
			||||||
        *basedir = "./script/"
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func main() {
 | 
					 | 
				
			||||||
    log.Message("Starting ... ")
 | 
					 | 
				
			||||||
    defer func() {
 | 
					 | 
				
			||||||
        time.Sleep(time.Second)
 | 
					 | 
				
			||||||
        log.Message("Shutdown complate!")
 | 
					 | 
				
			||||||
    }()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // init profiling file
 | 
					 | 
				
			||||||
    if *proffile != "" {
 | 
					 | 
				
			||||||
        log.Debugf("Open a profiling file: %s", *proffile)
 | 
					 | 
				
			||||||
        if err := prof.Start(*proffile); err != nil {
 | 
					 | 
				
			||||||
            log.Error(err)
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            defer prof.Stop()
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // init heap dumping file
 | 
					 | 
				
			||||||
    if *dumpfile != "" {
 | 
					 | 
				
			||||||
        log.Debugf("Open a heap dumping file: %s", *dumpfile)
 | 
					 | 
				
			||||||
        if err := prof.NewDump(*dumpfile); err != nil {
 | 
					 | 
				
			||||||
            log.Error(err)
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            defer prof.CloseDump()
 | 
					 | 
				
			||||||
            go func() {
 | 
					 | 
				
			||||||
                for prof.Dumping {
 | 
					 | 
				
			||||||
                    time.Sleep(time.Duration(*dumptime) * time.Second)
 | 
					 | 
				
			||||||
                    prof.Dump()
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }()
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // init pid file
 | 
					 | 
				
			||||||
    log.Debugf("Open a pid file: %s", *pidfile)
 | 
					 | 
				
			||||||
    if pidFile, err := pid.New(*pidfile); err != nil {
 | 
					 | 
				
			||||||
        log.Error(err)
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
        defer pidFile.Close()
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    w := worker.New(*joblimit)
 | 
					 | 
				
			||||||
    if err := w.AddServer(*gearmand); err != nil {
 | 
					 | 
				
			||||||
        log.Error(err)
 | 
					 | 
				
			||||||
        return
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    if err := w.AddFunc("exec", execShell, uint32(*timeout)); err != nil {
 | 
					 | 
				
			||||||
        log.Error(err)
 | 
					 | 
				
			||||||
        return
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    if err := w.AddFunc("execphp", execPHP, uint32(*timeout)); err != nil {
 | 
					 | 
				
			||||||
        log.Error(err)
 | 
					 | 
				
			||||||
        return
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    defer w.Close()
 | 
					 | 
				
			||||||
    go w.Work()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // signal handler
 | 
					 | 
				
			||||||
    sh := signal.NewHandler()
 | 
					 | 
				
			||||||
    sh.Bind(os.Interrupt, func() bool {return true})
 | 
					 | 
				
			||||||
    sh.Loop()
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										
											BIN
										
									
								
								example/worker/worker
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								example/worker/worker
									
									
									
									
									
										Executable file
									
								
							
										
											Binary file not shown.
										
									
								
							@ -1,37 +1,33 @@
 | 
				
			|||||||
package main
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
    "os"
 | 
					 | 
				
			||||||
    "log"
 | 
					 | 
				
			||||||
    "time"
 | 
					 | 
				
			||||||
    "strings"
 | 
					 | 
				
			||||||
    "github.com/mikespook/golib/signal"
 | 
					 | 
				
			||||||
	"github.com/mikespook/gearman-go/worker"
 | 
						"github.com/mikespook/gearman-go/worker"
 | 
				
			||||||
 | 
						"github.com/mikespook/golib/signal"
 | 
				
			||||||
 | 
						"log"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func ToUpper(job *worker.Job) ([]byte, error) {
 | 
					func ToUpper(job worker.Job) ([]byte, error) {
 | 
				
			||||||
    log.Printf("ToUpper: Handle=[%s]; UID=[%s], Data=[%s]\n",
 | 
						log.Printf("ToUpper: Data=[%s]\n", job.Data())
 | 
				
			||||||
        job.Handle, job.UniqueId, job.Data)
 | 
						data := []byte(strings.ToUpper(string(job.Data())))
 | 
				
			||||||
    data := []byte(strings.ToUpper(string(job.Data)))
 | 
					 | 
				
			||||||
	return data, nil
 | 
						return data, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func ToUpperDelay10(job *worker.Job) ([]byte, error) {
 | 
					func ToUpperDelay10(job worker.Job) ([]byte, error) {
 | 
				
			||||||
    log.Printf("ToUpperDelay10: Handle=[%s]; UID=[%s], Data=[%s]\n",
 | 
						log.Printf("ToUpper: Data=[%s]\n", job.Data())
 | 
				
			||||||
        job.Handle, job.UniqueId, job.Data)
 | 
					 | 
				
			||||||
	time.Sleep(10 * time.Second)
 | 
						time.Sleep(10 * time.Second)
 | 
				
			||||||
    data := []byte(strings.ToUpper(string(job.Data)))
 | 
						data := []byte(strings.ToUpper(string(job.Data())))
 | 
				
			||||||
	return data, nil
 | 
						return data, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func main() {
 | 
					func main() {
 | 
				
			||||||
	log.Println("Starting ...")
 | 
						log.Println("Starting ...")
 | 
				
			||||||
	defer log.Println("Shutdown complete!")
 | 
						defer log.Println("Shutdown complete!")
 | 
				
			||||||
	w := worker.New(worker.Unlimited)
 | 
						w := worker.New(worker.Unlimited)
 | 
				
			||||||
	defer w.Close()
 | 
						defer w.Close()
 | 
				
			||||||
    w.ErrHandler = func(e error) {
 | 
						w.ErrorHandler = func(e error) {
 | 
				
			||||||
		log.Println(e)
 | 
							log.Println(e)
 | 
				
			||||||
		if e == worker.ErrConnection {
 | 
							if e == worker.ErrConnection {
 | 
				
			||||||
			proc, err := os.FindProcess(os.Getpid())
 | 
								proc, err := os.FindProcess(os.Getpid())
 | 
				
			||||||
@ -43,19 +39,22 @@ func main() {
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
    w.JobHandler = func(job *worker.Job) error {
 | 
						w.JobHandler = func(job worker.Job) error {
 | 
				
			||||||
        log.Printf("H=%s, UID=%s, Data=%s, DataType=%d\n", job.Handle,
 | 
							log.Printf("Data=%s\n", job.Data())
 | 
				
			||||||
            job.UniqueId, job.Data, job.DataType)
 | 
					 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
    w.AddServer("127.0.0.1:4730")
 | 
						w.AddServer("tcp4", "127.0.0.1:4730")
 | 
				
			||||||
	w.AddFunc("ToUpper", ToUpper, worker.Immediately)
 | 
						w.AddFunc("ToUpper", ToUpper, worker.Immediately)
 | 
				
			||||||
	w.AddFunc("ToUpperTimeOut5", ToUpperDelay10, 5)
 | 
						w.AddFunc("ToUpperTimeOut5", ToUpperDelay10, 5)
 | 
				
			||||||
	w.AddFunc("ToUpperTimeOut20", ToUpperDelay10, 20)
 | 
						w.AddFunc("ToUpperTimeOut20", ToUpperDelay10, 20)
 | 
				
			||||||
	w.AddFunc("SysInfo", worker.SysInfo, worker.Immediately)
 | 
						w.AddFunc("SysInfo", worker.SysInfo, worker.Immediately)
 | 
				
			||||||
	w.AddFunc("MemInfo", worker.MemInfo, worker.Immediately)
 | 
						w.AddFunc("MemInfo", worker.MemInfo, worker.Immediately)
 | 
				
			||||||
 | 
						if err := w.Ready(); err != nil {
 | 
				
			||||||
 | 
							log.Fatal(err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	go w.Work()
 | 
						go w.Work()
 | 
				
			||||||
	sh := signal.NewHandler()
 | 
						sh := signal.NewHandler()
 | 
				
			||||||
    sh.Bind(os.Interrupt, func() bool {return true})
 | 
						sh.Bind(os.Interrupt, func() bool { return true })
 | 
				
			||||||
	sh.Loop()
 | 
						sh.Loop()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -54,6 +54,10 @@ func TestJobs(t *testing.T) {
 | 
				
			|||||||
	w.ErrorHandler = func(e error) {
 | 
						w.ErrorHandler = func(e error) {
 | 
				
			||||||
		t.Error(e)
 | 
							t.Error(e)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if err := w.Ready(); err != nil {
 | 
				
			||||||
 | 
							t.Error(err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	go w.Work()
 | 
						go w.Work()
 | 
				
			||||||
	t.Log("Worker is running...")
 | 
						t.Log("Worker is running...")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -15,7 +15,6 @@ type agent struct {
 | 
				
			|||||||
	worker    *Worker
 | 
						worker    *Worker
 | 
				
			||||||
	in        chan []byte
 | 
						in        chan []byte
 | 
				
			||||||
	net, addr string
 | 
						net, addr string
 | 
				
			||||||
	isConn bool
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Create the agent of job server.
 | 
					// Create the agent of job server.
 | 
				
			||||||
@ -34,44 +33,16 @@ func (a *agent) Connect() (err error) {
 | 
				
			|||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	a.isConn = true
 | 
						go a.work()
 | 
				
			||||||
	return
 | 
						return
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (a *agent) Work() {
 | 
					func (a *agent) work() {
 | 
				
			||||||
	go a.readLoop()
 | 
						var inpack *inPack
 | 
				
			||||||
 | 
					 | 
				
			||||||
	var resp *Response
 | 
					 | 
				
			||||||
	var l int
 | 
						var l int
 | 
				
			||||||
	var err error
 | 
						var err error
 | 
				
			||||||
	var data, leftdata []byte
 | 
						var data, leftdata []byte
 | 
				
			||||||
	for data = range a.in {
 | 
						for {
 | 
				
			||||||
		if len(leftdata) > 0 { // some data left for processing
 | 
					 | 
				
			||||||
			data = append(leftdata, data...)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		l = len(data)
 | 
					 | 
				
			||||||
		if l < MIN_PACKET_LEN { // not enough data
 | 
					 | 
				
			||||||
			leftdata = data
 | 
					 | 
				
			||||||
			continue
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if resp, l, err = decodeResponse(data); err != nil {
 | 
					 | 
				
			||||||
			a.worker.err(err)
 | 
					 | 
				
			||||||
			continue
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		leftdata = nil
 | 
					 | 
				
			||||||
		resp.agentId = a.net + a.addr
 | 
					 | 
				
			||||||
		a.worker.in <- resp
 | 
					 | 
				
			||||||
		if len(data) > l {
 | 
					 | 
				
			||||||
			leftdata = data[l:]
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// read data from socket
 | 
					 | 
				
			||||||
func (a *agent) readLoop() {
 | 
					 | 
				
			||||||
	var data []byte
 | 
					 | 
				
			||||||
	var err error
 | 
					 | 
				
			||||||
	for a.isConn {
 | 
					 | 
				
			||||||
		if data, err = a.read(BUFFER_SIZE); err != nil {
 | 
							if data, err = a.read(BUFFER_SIZE); err != nil {
 | 
				
			||||||
			if err == ErrConnClosed {
 | 
								if err == ErrConnClosed {
 | 
				
			||||||
				break
 | 
									break
 | 
				
			||||||
@ -79,15 +50,42 @@ func (a *agent) readLoop() {
 | 
				
			|||||||
			a.worker.err(err)
 | 
								a.worker.err(err)
 | 
				
			||||||
			continue
 | 
								continue
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		a.in <- data
 | 
							if len(leftdata) > 0 { // some data left for processing
 | 
				
			||||||
 | 
								data = append(leftdata, data...)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if len(data) < MIN_PACKET_LEN { // not enough data
 | 
				
			||||||
 | 
								leftdata = data
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if inpack, l, err = decodeInPack(data); err != nil {
 | 
				
			||||||
 | 
								a.worker.err(err)
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							leftdata = nil
 | 
				
			||||||
 | 
							inpack.a = a
 | 
				
			||||||
 | 
							a.worker.in <- inpack
 | 
				
			||||||
 | 
							if len(data) > l {
 | 
				
			||||||
 | 
								leftdata = data[l:]
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	close(a.in)
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (a *agent) Close() {
 | 
					func (a *agent) Close() {
 | 
				
			||||||
	a.conn.Close()
 | 
						a.conn.Close()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (a *agent) Grab() {
 | 
				
			||||||
 | 
						outpack := getOutPack()
 | 
				
			||||||
 | 
						outpack.dataType = GRAB_JOB_UNIQ
 | 
				
			||||||
 | 
						a.write(outpack)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (a *agent) PreSleep() {
 | 
				
			||||||
 | 
						outpack := getOutPack()
 | 
				
			||||||
 | 
						outpack.dataType = PRE_SLEEP
 | 
				
			||||||
 | 
						a.write(outpack)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// read length bytes from the socket
 | 
					// read length bytes from the socket
 | 
				
			||||||
func (a *agent) read(length int) (data []byte, err error) {
 | 
					func (a *agent) read(length int) (data []byte, err error) {
 | 
				
			||||||
	n := 0
 | 
						n := 0
 | 
				
			||||||
@ -95,13 +93,11 @@ func (a *agent) read(length int) (data []byte, err error) {
 | 
				
			|||||||
	// 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) < MIN_PACKET_LEN; i -= n {
 | 
				
			||||||
		if n, err = a.conn.Read(buf); err != nil {
 | 
							if n, err = a.conn.Read(buf); err != nil {
 | 
				
			||||||
			if !a.isConn {
 | 
					 | 
				
			||||||
				err = ErrConnClosed
 | 
					 | 
				
			||||||
				return
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			if err == io.EOF && n == 0 {
 | 
								if err == io.EOF && n == 0 {
 | 
				
			||||||
				if data == nil {
 | 
									if data == nil {
 | 
				
			||||||
					err = ErrConnection
 | 
										err = ErrConnection
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										err = ErrConnClosed
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
@ -115,9 +111,9 @@ func (a *agent) read(length int) (data []byte, err error) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Internal write the encoded job.
 | 
					// Internal write the encoded job.
 | 
				
			||||||
func (a *agent) write(req *request) (err error) {
 | 
					func (a *agent) write(outpack *outPack) (err error) {
 | 
				
			||||||
	var n int
 | 
						var n int
 | 
				
			||||||
	buf := req.Encode()
 | 
						buf := outpack.Encode()
 | 
				
			||||||
	for i := 0; i < len(buf); i += n {
 | 
						for i := 0; i < len(buf); i += n {
 | 
				
			||||||
		n, err = a.conn.Write(buf[i:])
 | 
							n, err = a.conn.Write(buf[i:])
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
 | 
				
			|||||||
@ -41,6 +41,7 @@ const (
 | 
				
			|||||||
	STATUS_RES      = 20
 | 
						STATUS_RES      = 20
 | 
				
			||||||
	SET_CLIENT_ID   = 22
 | 
						SET_CLIENT_ID   = 22
 | 
				
			||||||
	CAN_DO_TIMEOUT  = 23
 | 
						CAN_DO_TIMEOUT  = 23
 | 
				
			||||||
 | 
						ALL_YOURS       = 24
 | 
				
			||||||
	WORK_EXCEPTION  = 25
 | 
						WORK_EXCEPTION  = 25
 | 
				
			||||||
	WORK_DATA       = 28
 | 
						WORK_DATA       = 28
 | 
				
			||||||
	WORK_WARNING    = 29
 | 
						WORK_WARNING    = 29
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										108
									
								
								worker/inpack.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								worker/inpack.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,108 @@
 | 
				
			|||||||
 | 
					// 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 (
 | 
				
			||||||
 | 
						"bytes"
 | 
				
			||||||
 | 
						"encoding/binary"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"strconv"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Worker side job
 | 
				
			||||||
 | 
					type inPack struct {
 | 
				
			||||||
 | 
						dataType             uint32
 | 
				
			||||||
 | 
						data                 []byte
 | 
				
			||||||
 | 
						handle, uniqueId, fn string
 | 
				
			||||||
 | 
						a                    *agent
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Create a new job
 | 
				
			||||||
 | 
					func getInPack() *inPack {
 | 
				
			||||||
 | 
						return &inPack{}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (inpack *inPack) Data() []byte {
 | 
				
			||||||
 | 
						return inpack.data
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Send some datas to client.
 | 
				
			||||||
 | 
					// Using this in a job's executing.
 | 
				
			||||||
 | 
					func (inpack *inPack) SendData(data []byte) {
 | 
				
			||||||
 | 
						outpack := getOutPack()
 | 
				
			||||||
 | 
						outpack.dataType = WORK_DATA
 | 
				
			||||||
 | 
						hl := len(inpack.handle)
 | 
				
			||||||
 | 
						l := hl + len(data) + 1
 | 
				
			||||||
 | 
						outpack.data = getBuffer(l)
 | 
				
			||||||
 | 
						copy(outpack.data, []byte(inpack.handle))
 | 
				
			||||||
 | 
						copy(outpack.data[hl+1:], data)
 | 
				
			||||||
 | 
						inpack.a.write(outpack)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (inpack *inPack) SendWarning(data []byte) {
 | 
				
			||||||
 | 
						outpack := getOutPack()
 | 
				
			||||||
 | 
						outpack.dataType = WORK_WARNING
 | 
				
			||||||
 | 
						hl := len(inpack.handle)
 | 
				
			||||||
 | 
						l := hl + len(data) + 1
 | 
				
			||||||
 | 
						outpack.data = getBuffer(l)
 | 
				
			||||||
 | 
						copy(outpack.data, []byte(inpack.handle))
 | 
				
			||||||
 | 
						copy(outpack.data[hl+1:], data)
 | 
				
			||||||
 | 
						inpack.a.write(outpack)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Update status.
 | 
				
			||||||
 | 
					// Tall client how many percent job has been executed.
 | 
				
			||||||
 | 
					func (inpack *inPack) UpdateStatus(numerator, denominator int) {
 | 
				
			||||||
 | 
						n := []byte(strconv.Itoa(numerator))
 | 
				
			||||||
 | 
						d := []byte(strconv.Itoa(denominator))
 | 
				
			||||||
 | 
						outpack := getOutPack()
 | 
				
			||||||
 | 
						outpack.dataType = WORK_STATUS
 | 
				
			||||||
 | 
						hl := len(inpack.handle)
 | 
				
			||||||
 | 
						nl := len(n)
 | 
				
			||||||
 | 
						dl := len(d)
 | 
				
			||||||
 | 
						outpack.data = getBuffer(hl + nl + dl + 3)
 | 
				
			||||||
 | 
						copy(outpack.data, []byte(inpack.handle))
 | 
				
			||||||
 | 
						copy(outpack.data[hl+1:], n)
 | 
				
			||||||
 | 
						copy(outpack.data[hl+nl+2:], d)
 | 
				
			||||||
 | 
						inpack.a.write(outpack)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Decode job from byte slice
 | 
				
			||||||
 | 
					func decodeInPack(data []byte) (inpack *inPack, l int, err error) {
 | 
				
			||||||
 | 
						if len(data) < MIN_PACKET_LEN { // valid package should not less 12 bytes
 | 
				
			||||||
 | 
							err = fmt.Errorf("Invalid data: %V", data)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						dl := int(binary.BigEndian.Uint32(data[8:12]))
 | 
				
			||||||
 | 
						dt := data[MIN_PACKET_LEN : dl+MIN_PACKET_LEN]
 | 
				
			||||||
 | 
						if len(dt) != int(dl) { // length not equal
 | 
				
			||||||
 | 
							err = fmt.Errorf("Invalid data: %V", data)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						inpack = getInPack()
 | 
				
			||||||
 | 
						inpack.dataType = binary.BigEndian.Uint32(data[4:8])
 | 
				
			||||||
 | 
						switch inpack.dataType {
 | 
				
			||||||
 | 
						case JOB_ASSIGN:
 | 
				
			||||||
 | 
							s := bytes.SplitN(dt, []byte{'\x00'}, 3)
 | 
				
			||||||
 | 
							if len(s) == 3 {
 | 
				
			||||||
 | 
								inpack.handle = string(s[0])
 | 
				
			||||||
 | 
								inpack.fn = string(s[1])
 | 
				
			||||||
 | 
								inpack.data = s[2]
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						case JOB_ASSIGN_UNIQ:
 | 
				
			||||||
 | 
							s := bytes.SplitN(dt, []byte{'\x00'}, 4)
 | 
				
			||||||
 | 
							if len(s) == 4 {
 | 
				
			||||||
 | 
								inpack.handle = string(s[0])
 | 
				
			||||||
 | 
								inpack.fn = string(s[1])
 | 
				
			||||||
 | 
								inpack.uniqueId = string(s[2])
 | 
				
			||||||
 | 
								inpack.data = s[3]
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							inpack.data = dt
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						l = dl + MIN_PACKET_LEN
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										62
									
								
								worker/inpack_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								worker/inpack_test.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,62 @@
 | 
				
			|||||||
 | 
					package worker
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"bytes"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var (
 | 
				
			||||||
 | 
						inpackcases = map[uint32]map[string]string{
 | 
				
			||||||
 | 
							NOOP: map[string]string{
 | 
				
			||||||
 | 
								"src": "\x00RES\x00\x00\x00\x06\x00\x00\x00\x00",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							NO_JOB: map[string]string{
 | 
				
			||||||
 | 
								"src": "\x00RES\x00\x00\x00\x0a\x00\x00\x00\x00",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							JOB_ASSIGN: map[string]string{
 | 
				
			||||||
 | 
								"src":    "\x00RES\x00\x00\x00\x0b\x00\x00\x00\x07a\x00b\x00xyz",
 | 
				
			||||||
 | 
								"handle": "a",
 | 
				
			||||||
 | 
								"fn":     "b",
 | 
				
			||||||
 | 
								"data":   "xyz",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							JOB_ASSIGN_UNIQ: map[string]string{
 | 
				
			||||||
 | 
								"src":    "\x00RES\x00\x00\x00\x1F\x00\x00\x00\x09a\x00b\x00c\x00xyz",
 | 
				
			||||||
 | 
								"handle": "a",
 | 
				
			||||||
 | 
								"fn":     "b",
 | 
				
			||||||
 | 
								"uid":    "c",
 | 
				
			||||||
 | 
								"data":   "xyz",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestInPack(t *testing.T) {
 | 
				
			||||||
 | 
						for k, v := range inpackcases {
 | 
				
			||||||
 | 
							inpack, _, err := decodeInPack([]byte(v["src"]))
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								t.Error(err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if inpack.dataType != k {
 | 
				
			||||||
 | 
								t.Errorf("DataType: %d expected, %d got.", k, inpack.dataType)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if handle, ok := v["handle"]; ok {
 | 
				
			||||||
 | 
								if inpack.handle != handle {
 | 
				
			||||||
 | 
									t.Errorf("Handle: %s expected, %s got.", handle, inpack.handle)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if fn, ok := v["fn"]; ok {
 | 
				
			||||||
 | 
								if inpack.fn != fn {
 | 
				
			||||||
 | 
									t.Errorf("FuncName: %s expected, %s got.", fn, inpack.fn)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if uid, ok := v["uid"]; ok {
 | 
				
			||||||
 | 
								if inpack.uniqueId != uid {
 | 
				
			||||||
 | 
									t.Errorf("UID: %s expected, %s got.", uid, inpack.uniqueId)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if data, ok := v["data"]; ok {
 | 
				
			||||||
 | 
								if bytes.Compare([]byte(data), inpack.data) != 0 {
 | 
				
			||||||
 | 
									t.Errorf("UID: %v expected, %v got.", data, inpack.data)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1,68 +1,8 @@
 | 
				
			|||||||
package worker
 | 
					package worker
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"strconv"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type Job interface {
 | 
					type Job interface {
 | 
				
			||||||
	Data() []byte
 | 
						Data() []byte
 | 
				
			||||||
	SendWarning(data []byte)
 | 
						SendWarning(data []byte)
 | 
				
			||||||
	SendData(data []byte)
 | 
						SendData(data []byte)
 | 
				
			||||||
	UpdateStatus(numerator, denominator int)
 | 
						UpdateStatus(numerator, denominator int)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
type _job struct {
 | 
					 | 
				
			||||||
	a *agent
 | 
					 | 
				
			||||||
	Handle string
 | 
					 | 
				
			||||||
	data []byte
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func getJob() *_job {
 | 
					 | 
				
			||||||
	return &_job{}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (j *_job) Data() []byte {
 | 
					 | 
				
			||||||
	return j.data
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Send some datas to client.
 | 
					 | 
				
			||||||
// Using this in a job's executing.
 | 
					 | 
				
			||||||
func (j *_job) SendData(data []byte) {
 | 
					 | 
				
			||||||
	req := getRequest()
 | 
					 | 
				
			||||||
	req.DataType = WORK_DATA
 | 
					 | 
				
			||||||
	hl := len(j.Handle)
 | 
					 | 
				
			||||||
	l := hl + len(data) + 1
 | 
					 | 
				
			||||||
	req.Data = getBuffer(l)
 | 
					 | 
				
			||||||
	copy(req.Data, []byte(j.Handle))
 | 
					 | 
				
			||||||
	copy(req.Data[hl + 1:], data)
 | 
					 | 
				
			||||||
	j.a.write(req)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (j *_job) SendWarning(data []byte) {
 | 
					 | 
				
			||||||
	req := getRequest()
 | 
					 | 
				
			||||||
	req.DataType = WORK_WARNING
 | 
					 | 
				
			||||||
	hl := len(j.Handle)
 | 
					 | 
				
			||||||
	l := hl + len(data) + 1
 | 
					 | 
				
			||||||
	req.Data = getBuffer(l)
 | 
					 | 
				
			||||||
	copy(req.Data, []byte(j.Handle))
 | 
					 | 
				
			||||||
	copy(req.Data[hl + 1:], data)
 | 
					 | 
				
			||||||
	j.a.write(req)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Update status.
 | 
					 | 
				
			||||||
// Tall client how many percent job has been executed.
 | 
					 | 
				
			||||||
func (j *_job) UpdateStatus(numerator, denominator int) {
 | 
					 | 
				
			||||||
	n := []byte(strconv.Itoa(numerator))
 | 
					 | 
				
			||||||
	d := []byte(strconv.Itoa(denominator))
 | 
					 | 
				
			||||||
	req := getRequest()
 | 
					 | 
				
			||||||
	req.DataType = WORK_STATUS
 | 
					 | 
				
			||||||
	hl := len(j.Handle)
 | 
					 | 
				
			||||||
	nl := len(n)
 | 
					 | 
				
			||||||
	dl := len(d)
 | 
					 | 
				
			||||||
	req.Data = getBuffer(hl + nl + dl + 3)
 | 
					 | 
				
			||||||
	copy(req.Data, []byte(j.Handle))
 | 
					 | 
				
			||||||
	copy(req.Data[hl+1:], n)
 | 
					 | 
				
			||||||
	copy(req.Data[hl+nl+2:], d)
 | 
					 | 
				
			||||||
	j.a.write(req)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										53
									
								
								worker/outpack.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								worker/outpack.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,53 @@
 | 
				
			|||||||
 | 
					// 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 (
 | 
				
			||||||
 | 
						//	"fmt"
 | 
				
			||||||
 | 
						"encoding/binary"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Worker side job
 | 
				
			||||||
 | 
					type outPack struct {
 | 
				
			||||||
 | 
						dataType uint32
 | 
				
			||||||
 | 
						data     []byte
 | 
				
			||||||
 | 
						handle   string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func getOutPack() (outpack *outPack) {
 | 
				
			||||||
 | 
						// TODO pool
 | 
				
			||||||
 | 
						return &outPack{}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Encode a job to byte slice
 | 
				
			||||||
 | 
					func (outpack *outPack) Encode() (data []byte) {
 | 
				
			||||||
 | 
						var l int
 | 
				
			||||||
 | 
						if outpack.dataType == WORK_FAIL {
 | 
				
			||||||
 | 
							l = len(outpack.handle)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							l = len(outpack.data)
 | 
				
			||||||
 | 
							if outpack.handle != "" {
 | 
				
			||||||
 | 
								l += len(outpack.handle) + 1
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						data = getBuffer(l + MIN_PACKET_LEN)
 | 
				
			||||||
 | 
						binary.BigEndian.PutUint32(data[:4], REQ)
 | 
				
			||||||
 | 
						binary.BigEndian.PutUint32(data[4:8], outpack.dataType)
 | 
				
			||||||
 | 
						binary.BigEndian.PutUint32(data[8:MIN_PACKET_LEN], uint32(l))
 | 
				
			||||||
 | 
						i := MIN_PACKET_LEN
 | 
				
			||||||
 | 
						if outpack.handle != "" {
 | 
				
			||||||
 | 
							hi := len(outpack.handle) + i
 | 
				
			||||||
 | 
							copy(data[i:hi], []byte(outpack.handle))
 | 
				
			||||||
 | 
							if outpack.dataType != WORK_FAIL {
 | 
				
			||||||
 | 
								data[hi] = '\x00'
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							i = hi + 1
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if outpack.dataType != WORK_FAIL {
 | 
				
			||||||
 | 
							copy(data[i:], outpack.data)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										83
									
								
								worker/outpack_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								worker/outpack_test.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,83 @@
 | 
				
			|||||||
 | 
					package worker
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"bytes"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var (
 | 
				
			||||||
 | 
						outpackcases = map[uint32]map[string]string{
 | 
				
			||||||
 | 
							CAN_DO: map[string]string{
 | 
				
			||||||
 | 
								"src":  "\x00REQ\x00\x00\x00\x01\x00\x00\x00\x01a",
 | 
				
			||||||
 | 
								"data": "a",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							CAN_DO_TIMEOUT: map[string]string{
 | 
				
			||||||
 | 
								"src":  "\x00REQ\x00\x00\x00\x17\x00\x00\x00\x06a\x00\x00\x00\x00\x01",
 | 
				
			||||||
 | 
								"data": "a\x00\x00\x00\x00\x01",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							CANT_DO: map[string]string{
 | 
				
			||||||
 | 
								"src":  "\x00REQ\x00\x00\x00\x02\x00\x00\x00\x01a",
 | 
				
			||||||
 | 
								"data": "a",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							RESET_ABILITIES: map[string]string{
 | 
				
			||||||
 | 
								"src": "\x00REQ\x00\x00\x00\x03\x00\x00\x00\x00",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							PRE_SLEEP: map[string]string{
 | 
				
			||||||
 | 
								"src": "\x00REQ\x00\x00\x00\x04\x00\x00\x00\x00",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							GRAB_JOB: map[string]string{
 | 
				
			||||||
 | 
								"src": "\x00REQ\x00\x00\x00\x09\x00\x00\x00\x00",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							GRAB_JOB_UNIQ: map[string]string{
 | 
				
			||||||
 | 
								"src": "\x00REQ\x00\x00\x00\x1E\x00\x00\x00\x00",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							WORK_DATA: map[string]string{
 | 
				
			||||||
 | 
								"src":  "\x00REQ\x00\x00\x00\x1C\x00\x00\x00\x03a\x00b",
 | 
				
			||||||
 | 
								"data": "a\x00b",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							WORK_WARNING: map[string]string{
 | 
				
			||||||
 | 
								"src":  "\x00REQ\x00\x00\x00\x1D\x00\x00\x00\x03a\x00b",
 | 
				
			||||||
 | 
								"data": "a\x00b",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							WORK_STATUS: map[string]string{
 | 
				
			||||||
 | 
								"src":  "\x00REQ\x00\x00\x00\x0C\x00\x00\x00\x08a\x0050\x00100",
 | 
				
			||||||
 | 
								"data": "a\x0050\x00100",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							WORK_COMPLETE: map[string]string{
 | 
				
			||||||
 | 
								"src":  "\x00REQ\x00\x00\x00\x0D\x00\x00\x00\x03a\x00b",
 | 
				
			||||||
 | 
								"data": "a\x00b",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							WORK_FAIL: map[string]string{
 | 
				
			||||||
 | 
								"src":    "\x00REQ\x00\x00\x00\x0E\x00\x00\x00\x01a",
 | 
				
			||||||
 | 
								"handle": "a",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							WORK_EXCEPTION: map[string]string{
 | 
				
			||||||
 | 
								"src":  "\x00REQ\x00\x00\x00\x19\x00\x00\x00\x03a\x00b",
 | 
				
			||||||
 | 
								"data": "a\x00b",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							SET_CLIENT_ID: map[string]string{
 | 
				
			||||||
 | 
								"src":  "\x00REQ\x00\x00\x00\x16\x00\x00\x00\x01a",
 | 
				
			||||||
 | 
								"data": "a",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							ALL_YOURS: map[string]string{
 | 
				
			||||||
 | 
								"src": "\x00REQ\x00\x00\x00\x18\x00\x00\x00\x00",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestOutPack(t *testing.T) {
 | 
				
			||||||
 | 
						for k, v := range outpackcases {
 | 
				
			||||||
 | 
							outpack := getOutPack()
 | 
				
			||||||
 | 
							outpack.dataType = k
 | 
				
			||||||
 | 
							if handle, ok := v["handle"]; ok {
 | 
				
			||||||
 | 
								outpack.handle = handle
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if data, ok := v["data"]; ok {
 | 
				
			||||||
 | 
								outpack.data = []byte(data)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							data := outpack.Encode()
 | 
				
			||||||
 | 
							if bytes.Compare([]byte(v["src"]), data) != 0 {
 | 
				
			||||||
 | 
								t.Errorf("%d: %X expected, %X got.", k, v["src"], data)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1,50 +0,0 @@
 | 
				
			|||||||
// 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 (
 | 
					 | 
				
			||||||
	"encoding/binary"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Worker side job
 | 
					 | 
				
			||||||
type request struct {
 | 
					 | 
				
			||||||
	DataType             uint32
 | 
					 | 
				
			||||||
	Data                 []byte
 | 
					 | 
				
			||||||
	Handle, UniqueId, Fn string
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func getRequest() (req *request) {
 | 
					 | 
				
			||||||
	// TODO pool
 | 
					 | 
				
			||||||
	return &request{}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Encode a job to byte slice
 | 
					 | 
				
			||||||
func (req *request) Encode() (data []byte) {
 | 
					 | 
				
			||||||
	var l int
 | 
					 | 
				
			||||||
	if req.DataType == WORK_FAIL {
 | 
					 | 
				
			||||||
		l = len(req.Handle)
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		l = len(req.Data)
 | 
					 | 
				
			||||||
		if req.Handle != "" {
 | 
					 | 
				
			||||||
			l += len(req.Handle) + 1
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	data = getBuffer(l + MIN_PACKET_LEN)
 | 
					 | 
				
			||||||
	binary.BigEndian.PutUint32(data[:4], REQ)
 | 
					 | 
				
			||||||
	binary.BigEndian.PutUint32(data[4:8], req.DataType)
 | 
					 | 
				
			||||||
	binary.BigEndian.PutUint32(data[8:MIN_PACKET_LEN], uint32(l))
 | 
					 | 
				
			||||||
	i := MIN_PACKET_LEN
 | 
					 | 
				
			||||||
	if req.Handle != "" {
 | 
					 | 
				
			||||||
		hi := len(req.Handle) + i
 | 
					 | 
				
			||||||
		copy(data[i:hi], []byte(req.Handle))
 | 
					 | 
				
			||||||
		if req.DataType != WORK_FAIL {
 | 
					 | 
				
			||||||
			data[hi] = '\x00'
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		i = i + hi
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	copy(data[i:], req.Data)
 | 
					 | 
				
			||||||
	return
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,62 +0,0 @@
 | 
				
			|||||||
// 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 (
 | 
					 | 
				
			||||||
	"bytes"
 | 
					 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
	"encoding/binary"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Worker side job
 | 
					 | 
				
			||||||
type Response struct {
 | 
					 | 
				
			||||||
	DataType  uint32
 | 
					 | 
				
			||||||
	Data                 []byte
 | 
					 | 
				
			||||||
	Handle, UniqueId, Fn string
 | 
					 | 
				
			||||||
	agentId	string
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Create a new job
 | 
					 | 
				
			||||||
func getResponse() (resp *Response) {
 | 
					 | 
				
			||||||
	return &Response{}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Decode job from byte slice
 | 
					 | 
				
			||||||
func decodeResponse(data []byte) (resp *Response, l int, err error) {
 | 
					 | 
				
			||||||
	if len(data) < MIN_PACKET_LEN { // valid package should not less 12 bytes
 | 
					 | 
				
			||||||
		err = fmt.Errorf("Invalid data: %V", data)
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	dl := int(binary.BigEndian.Uint32(data[8:12]))
 | 
					 | 
				
			||||||
	dt := data[MIN_PACKET_LEN : dl+MIN_PACKET_LEN]
 | 
					 | 
				
			||||||
	if len(dt) != int(dl) { // length not equal
 | 
					 | 
				
			||||||
		err = fmt.Errorf("Invalid data: %V", data)
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	resp = getResponse()
 | 
					 | 
				
			||||||
	resp.DataType = binary.BigEndian.Uint32(data[4:8])
 | 
					 | 
				
			||||||
	switch resp.DataType {
 | 
					 | 
				
			||||||
	case JOB_ASSIGN:
 | 
					 | 
				
			||||||
		s := bytes.SplitN(dt, []byte{'\x00'}, 3)
 | 
					 | 
				
			||||||
		if len(s) == 3 {
 | 
					 | 
				
			||||||
			resp.Handle = string(s[0])
 | 
					 | 
				
			||||||
			resp.Fn = string(s[1])
 | 
					 | 
				
			||||||
			resp.Data = s[2]
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	case JOB_ASSIGN_UNIQ:
 | 
					 | 
				
			||||||
		s := bytes.SplitN(dt, []byte{'\x00'}, 4)
 | 
					 | 
				
			||||||
		if len(s) == 4 {
 | 
					 | 
				
			||||||
			resp.Handle = string(s[0])
 | 
					 | 
				
			||||||
			resp.Fn = string(s[1])
 | 
					 | 
				
			||||||
			resp.UniqueId = string(s[2])
 | 
					 | 
				
			||||||
			resp.Data = s[3]
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	default:
 | 
					 | 
				
			||||||
		resp.Data = dt
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	l = dl + MIN_PACKET_LEN
 | 
					 | 
				
			||||||
	return
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										173
									
								
								worker/worker.go
									
									
									
									
									
								
							
							
						
						
									
										173
									
								
								worker/worker.go
									
									
									
									
									
								
							@ -5,16 +5,13 @@
 | 
				
			|||||||
package worker
 | 
					package worker
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
	"time"
 | 
					 | 
				
			||||||
	"sync"
 | 
					 | 
				
			||||||
	"encoding/binary"
 | 
						"encoding/binary"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"sync"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
	Unlimited = 0
 | 
					 | 
				
			||||||
	OneByOne  = 1
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	Immediately = 0
 | 
						Immediately = 0
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -22,7 +19,7 @@ const (
 | 
				
			|||||||
Worker side api for gearman
 | 
					Worker side api for gearman
 | 
				
			||||||
 | 
					
 | 
				
			||||||
usage:
 | 
					usage:
 | 
				
			||||||
w = worker.New(worker.Unlimited)
 | 
					w = worker.New()
 | 
				
			||||||
w.AddFunction("foobar", foobar)
 | 
					w.AddFunction("foobar", foobar)
 | 
				
			||||||
w.AddServer("127.0.0.1:4730")
 | 
					w.AddServer("127.0.0.1:4730")
 | 
				
			||||||
w.Work() // Enter the worker's main loop
 | 
					w.Work() // Enter the worker's main loop
 | 
				
			||||||
@ -37,11 +34,10 @@ func foobar(job *Job) (data []byte, err os.Error) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
type Worker struct {
 | 
					type Worker struct {
 | 
				
			||||||
	agents  map[string]*agent
 | 
						agents  []*agent
 | 
				
			||||||
	funcs   JobFuncs
 | 
						funcs   JobFuncs
 | 
				
			||||||
	in      chan *Response
 | 
						in      chan *inPack
 | 
				
			||||||
	running bool
 | 
						running bool
 | 
				
			||||||
	limit   chan bool
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Id string
 | 
						Id string
 | 
				
			||||||
	// assign a ErrFunc to handle errors
 | 
						// assign a ErrFunc to handle errors
 | 
				
			||||||
@ -51,14 +47,11 @@ type Worker struct {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Get a new worker
 | 
					// Get a new worker
 | 
				
			||||||
func New(l int) (worker *Worker) {
 | 
					func New() (worker *Worker) {
 | 
				
			||||||
	worker = &Worker{
 | 
						worker = &Worker{
 | 
				
			||||||
		agents: make(map[string]*agent, QUEUE_SIZE),
 | 
							agents: make([]*agent, 0),
 | 
				
			||||||
		funcs:  make(JobFuncs),
 | 
							funcs:  make(JobFuncs),
 | 
				
			||||||
		in:     make(chan *Response, QUEUE_SIZE),
 | 
							in:     make(chan *inPack, QUEUE_SIZE),
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if l != Unlimited {
 | 
					 | 
				
			||||||
		worker.limit = make(chan bool, l)
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return
 | 
						return
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -78,16 +71,16 @@ func (worker *Worker) AddServer(net, addr string) (err error) {
 | 
				
			|||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	worker.agents[net + addr] = a
 | 
						worker.agents = append(worker.agents, a)
 | 
				
			||||||
	return
 | 
						return
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Write a job to job server.
 | 
					// Write a job to job server.
 | 
				
			||||||
// Here, the job's mean is not the oraginal mean.
 | 
					// 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.
 | 
					// Just looks like a network package for job's result or tell job server, there was a fail.
 | 
				
			||||||
func (worker *Worker) broadcast(req *request) {
 | 
					func (worker *Worker) broadcast(outpack *outPack) {
 | 
				
			||||||
	for _, v := range worker.agents {
 | 
						for _, v := range worker.agents {
 | 
				
			||||||
		v.write(req)
 | 
							v.write(outpack)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -110,19 +103,19 @@ func (worker *Worker) AddFunc(funcname string,
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// inner add function
 | 
					// inner add function
 | 
				
			||||||
func (worker *Worker) addFunc(funcname string, timeout uint32) {
 | 
					func (worker *Worker) addFunc(funcname string, timeout uint32) {
 | 
				
			||||||
	req := getRequest()
 | 
						outpack := getOutPack()
 | 
				
			||||||
	if timeout == 0 {
 | 
						if timeout == 0 {
 | 
				
			||||||
		req.DataType = CAN_DO
 | 
							outpack.dataType = CAN_DO
 | 
				
			||||||
		req.Data = []byte(funcname)
 | 
							outpack.data = []byte(funcname)
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		req.DataType = CAN_DO_TIMEOUT
 | 
							outpack.dataType = CAN_DO_TIMEOUT
 | 
				
			||||||
		l := len(funcname)
 | 
							l := len(funcname)
 | 
				
			||||||
		req.Data = getBuffer(l + 5)
 | 
							outpack.data = getBuffer(l + 5)
 | 
				
			||||||
		copy(req.Data, []byte(funcname))
 | 
							copy(outpack.data, []byte(funcname))
 | 
				
			||||||
		req.Data[l] = '\x00'
 | 
							outpack.data[l] = '\x00'
 | 
				
			||||||
		binary.BigEndian.PutUint32(req.Data[l + 1:], timeout)
 | 
							binary.BigEndian.PutUint32(outpack.data[l+1:], timeout)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	worker.broadcast(req)
 | 
						worker.broadcast(outpack)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Remove a function.
 | 
					// Remove a function.
 | 
				
			||||||
@ -141,63 +134,57 @@ func (worker *Worker) RemoveFunc(funcname string) (err error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// inner remove function
 | 
					// inner remove function
 | 
				
			||||||
func (worker *Worker) removeFunc(funcname string) {
 | 
					func (worker *Worker) removeFunc(funcname string) {
 | 
				
			||||||
	req := getRequest()
 | 
						outpack := getOutPack()
 | 
				
			||||||
	req.DataType = CANT_DO
 | 
						outpack.dataType = CANT_DO
 | 
				
			||||||
	req.Data = []byte(funcname)
 | 
						outpack.data = []byte(funcname)
 | 
				
			||||||
	worker.broadcast(req)
 | 
						worker.broadcast(outpack)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (worker *Worker) dealResp(resp *Response) {
 | 
					func (worker *Worker) handleInPack(inpack *inPack) {
 | 
				
			||||||
	defer func() {
 | 
						switch inpack.dataType {
 | 
				
			||||||
		if worker.running && worker.limit != nil {
 | 
						case NO_JOB:
 | 
				
			||||||
			<-worker.limit
 | 
							inpack.a.PreSleep()
 | 
				
			||||||
		}
 | 
						case NOOP:
 | 
				
			||||||
	}()
 | 
							inpack.a.Grab()
 | 
				
			||||||
	switch resp.DataType {
 | 
					 | 
				
			||||||
	case ERROR:
 | 
						case ERROR:
 | 
				
			||||||
		worker.err(GetError(resp.Data))
 | 
							worker.err(GetError(inpack.data))
 | 
				
			||||||
	case JOB_ASSIGN, JOB_ASSIGN_UNIQ:
 | 
						case JOB_ASSIGN, JOB_ASSIGN_UNIQ:
 | 
				
			||||||
		if err := worker.exec(resp); err != nil {
 | 
							if err := worker.exec(inpack); err != nil {
 | 
				
			||||||
			worker.err(err)
 | 
								worker.err(err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		worker.handleResponse(resp)
 | 
							worker.customeHandler(inpack)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (worker *Worker) Ready() (err error) {
 | 
				
			||||||
 | 
						for _, v := range worker.agents {
 | 
				
			||||||
 | 
							if err = v.Connect(); err != nil {
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for funcname, f := range worker.funcs {
 | 
				
			||||||
 | 
							worker.addFunc(funcname, f.timeout)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Main loop
 | 
					// Main loop
 | 
				
			||||||
func (worker *Worker) Work() {
 | 
					func (worker *Worker) Work() {
 | 
				
			||||||
	defer func() {
 | 
					 | 
				
			||||||
		for _, v := range worker.agents {
 | 
					 | 
				
			||||||
			v.Close()
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}()
 | 
					 | 
				
			||||||
	worker.running = true
 | 
						worker.running = true
 | 
				
			||||||
	for _, v := range worker.agents {
 | 
						for _, v := range worker.agents {
 | 
				
			||||||
		v.Connect()
 | 
							v.Grab()
 | 
				
			||||||
		go v.Work()
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	worker.Reset()
 | 
						var inpack *inPack
 | 
				
			||||||
	for funcname, f := range worker.funcs {
 | 
						for inpack = range worker.in {
 | 
				
			||||||
		worker.addFunc(funcname, f.timeout)
 | 
							go worker.handleInPack(inpack)
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	var resp *Response
 | 
					 | 
				
			||||||
	for resp = range worker.in {
 | 
					 | 
				
			||||||
		fmt.Println(resp)
 | 
					 | 
				
			||||||
		go worker.dealResp(resp)
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// job handler
 | 
					// job handler
 | 
				
			||||||
func (worker *Worker) handleResponse(resp *Response) {
 | 
					func (worker *Worker) customeHandler(inpack *inPack) {
 | 
				
			||||||
	if worker.JobHandler != nil {
 | 
						if worker.JobHandler != nil {
 | 
				
			||||||
		job := getJob()
 | 
							if err := worker.JobHandler(inpack); err != nil {
 | 
				
			||||||
		job.a = worker.agents[resp.agentId]
 | 
					 | 
				
			||||||
		job.Handle = resp.Handle
 | 
					 | 
				
			||||||
		if resp.DataType == ECHO_RES {
 | 
					 | 
				
			||||||
			job.data = resp.Data
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if err := worker.JobHandler(job); err != nil {
 | 
					 | 
				
			||||||
			worker.err(err)
 | 
								worker.err(err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -207,39 +194,36 @@ func (worker *Worker) handleResponse(resp *Response) {
 | 
				
			|||||||
func (worker *Worker) Close() {
 | 
					func (worker *Worker) Close() {
 | 
				
			||||||
	worker.running = false
 | 
						worker.running = false
 | 
				
			||||||
	close(worker.in)
 | 
						close(worker.in)
 | 
				
			||||||
	if worker.limit != nil {
 | 
					 | 
				
			||||||
		close(worker.limit)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Send a something out, get the samething back.
 | 
					// Send a something out, get the samething back.
 | 
				
			||||||
func (worker *Worker) Echo(data []byte) {
 | 
					func (worker *Worker) Echo(data []byte) {
 | 
				
			||||||
	req := getRequest()
 | 
						outpack := getOutPack()
 | 
				
			||||||
	req.DataType = ECHO_REQ
 | 
						outpack.dataType = ECHO_REQ
 | 
				
			||||||
	req.Data = data
 | 
						outpack.data = data
 | 
				
			||||||
	worker.broadcast(req)
 | 
						worker.broadcast(outpack)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Remove all of functions.
 | 
					// Remove all of functions.
 | 
				
			||||||
// Both from the worker or job servers.
 | 
					// Both from the worker or job servers.
 | 
				
			||||||
func (worker *Worker) Reset() {
 | 
					func (worker *Worker) Reset() {
 | 
				
			||||||
	req := getRequest()
 | 
						outpack := getOutPack()
 | 
				
			||||||
	req.DataType = RESET_ABILITIES
 | 
						outpack.dataType = RESET_ABILITIES
 | 
				
			||||||
	worker.broadcast(req)
 | 
						worker.broadcast(outpack)
 | 
				
			||||||
	worker.funcs = make(JobFuncs)
 | 
						worker.funcs = make(JobFuncs)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Set the worker's unique id.
 | 
					// Set the worker's unique id.
 | 
				
			||||||
func (worker *Worker) SetId(id string) {
 | 
					func (worker *Worker) SetId(id string) {
 | 
				
			||||||
	worker.Id = id
 | 
						worker.Id = id
 | 
				
			||||||
	req := getRequest()
 | 
						outpack := getOutPack()
 | 
				
			||||||
	req.DataType = SET_CLIENT_ID
 | 
						outpack.dataType = SET_CLIENT_ID
 | 
				
			||||||
	req.Data = []byte(id)
 | 
						outpack.data = []byte(id)
 | 
				
			||||||
	worker.broadcast(req)
 | 
						worker.broadcast(outpack)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Execute the job. And send back the result.
 | 
					// Execute the job. And send back the result.
 | 
				
			||||||
func (worker *Worker) exec(resp *Response) (err error) {
 | 
					func (worker *Worker) exec(inpack *inPack) (err error) {
 | 
				
			||||||
	defer func() {
 | 
						defer func() {
 | 
				
			||||||
		if r := recover(); r != nil {
 | 
							if r := recover(); r != nil {
 | 
				
			||||||
			if e, ok := r.(error); ok {
 | 
								if e, ok := r.(error); ok {
 | 
				
			||||||
@ -249,34 +233,33 @@ func (worker *Worker) exec(resp *Response) (err error) {
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}()
 | 
						}()
 | 
				
			||||||
	f, ok := worker.funcs[resp.Fn]
 | 
						f, ok := worker.funcs[inpack.fn]
 | 
				
			||||||
	if !ok {
 | 
						if !ok {
 | 
				
			||||||
		return fmt.Errorf("The function does not exist: %s", resp.Fn)
 | 
							return fmt.Errorf("The function does not exist: %s", inpack.fn)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	var r *result
 | 
						var r *result
 | 
				
			||||||
	job := getJob()
 | 
					 | 
				
			||||||
	job.a = worker.agents[resp.agentId]
 | 
					 | 
				
			||||||
	job.Handle = resp.Handle
 | 
					 | 
				
			||||||
	if f.timeout == 0 {
 | 
						if f.timeout == 0 {
 | 
				
			||||||
		d, e := f.f(job)
 | 
							d, e := f.f(inpack)
 | 
				
			||||||
		r = &result{data: d, err: e}
 | 
							r = &result{data: d, err: e}
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		r = execTimeout(f.f, job, time.Duration(f.timeout)*time.Second)
 | 
							r = execTimeout(f.f, inpack, time.Duration(f.timeout)*time.Second)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	req := getRequest()
 | 
						if worker.running {
 | 
				
			||||||
 | 
							outpack := getOutPack()
 | 
				
			||||||
		if r.err == nil {
 | 
							if r.err == nil {
 | 
				
			||||||
		req.DataType = WORK_COMPLETE
 | 
								outpack.dataType = WORK_COMPLETE
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
		if r.data == nil {
 | 
								if len(r.data) == 0 {
 | 
				
			||||||
			req.DataType = WORK_FAIL
 | 
									outpack.dataType = WORK_FAIL
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
			req.DataType = WORK_EXCEPTION
 | 
									outpack.dataType = WORK_EXCEPTION
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			err = r.err
 | 
								err = r.err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	req.Data = r.data
 | 
							outpack.handle = inpack.handle
 | 
				
			||||||
	if worker.running {
 | 
							outpack.data = r.data
 | 
				
			||||||
		job.a.write(req)
 | 
							inpack.a.write(outpack)
 | 
				
			||||||
 | 
							inpack.a.Grab()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return
 | 
						return
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,9 @@
 | 
				
			|||||||
package worker
 | 
					package worker
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import "testing"
 | 
					import (
 | 
				
			||||||
 | 
						"sync"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var worker *Worker
 | 
					var worker *Worker
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -44,7 +47,20 @@ func TestWorkerRemoveFunc(t *testing.T) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestWork(t *testing.T) {
 | 
					func TestWork(t *testing.T) {
 | 
				
			||||||
 | 
						var wg sync.WaitGroup
 | 
				
			||||||
 | 
						worker.JobHandler = func(job Job) error {
 | 
				
			||||||
 | 
							t.Logf("%s", job.Data())
 | 
				
			||||||
 | 
							wg.Done()
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err := worker.Ready(); err != nil {
 | 
				
			||||||
 | 
							t.Error(err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	go worker.Work()
 | 
						go worker.Work()
 | 
				
			||||||
 | 
						wg.Add(1)
 | 
				
			||||||
 | 
						worker.Echo([]byte("Hello"))
 | 
				
			||||||
 | 
						wg.Wait()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestWorkerClose(t *testing.T) {
 | 
					func TestWorkerClose(t *testing.T) {
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user