예제 #1
0
func (c *client) handleRemoveJob(msgID, payload []byte) (err error) {
	var job driver.Job
	var e error
	var conn = c.conn
	var sched = c.sched
	defer sched.jobLocker.Unlock()
	sched.jobLocker.Lock()
	job, e = driver.NewJob(payload)
	if e != nil {
		err = conn.Send([]byte(e.Error()))
		return
	}
	job, e = sched.driver.GetOne(job.Func, job.Name)
	if e == nil && job.ID > 0 {
		if _, ok := sched.procQueue[job.ID]; ok {
			delete(sched.procQueue, job.ID)
		}
		sched.driver.Delete(job.ID)
		sched.decrStatJob(job)
		if job.IsProc() {
			sched.decrStatProc(job)
			sched.removeRevertPQ(job)
		}
		sched.notifyJobTimer()
	}

	if e != nil {
		err = conn.Send([]byte(e.Error()))
	} else {
		err = c.handleCommand(msgID, protocol.SUCCESS)
	}
	return
}
예제 #2
0
파일: sched.go 프로젝트: 4honor/periodic
func (sched *Sched) SubmitJob(grabItem GrabItem, job driver.Job) bool {
	defer sched.JobLocker.Unlock()
	sched.JobLocker.Lock()
	if job.Name == "" {
		sched.driver.Delete(job.Id)
		return true
	}
	if _, ok := sched.procQueue[job.Id]; ok {
		return true
	}

	if !grabItem.w.alive {
		return false
	}
	if err := grabItem.w.HandleDo(grabItem.msgId, job); err != nil {
		grabItem.w.alive = false
		return false
	}
	now := time.Now()
	current := int64(now.Unix())
	job.Status = driver.JOB_STATUS_PROC
	job.RunAt = current
	sched.driver.Save(&job)
	sched.IncrStatProc(job)
	sched.pushRevertPQ(job)
	sched.NotifyRevertTimer()
	sched.procQueue[job.Id] = job
	sched.grabQueue.Remove(grabItem)
	return true
}
예제 #3
0
func (sched *Sched) pushJobPQ(job driver.Job) bool {
	defer sched.PQLocker.Unlock()
	sched.PQLocker.Lock()
	if job.IsReady() {
		item := &queue.Item{
			Value:    job.ID,
			Priority: job.SchedAt,
		}
		if sched.cacheItem != nil && item.Priority < sched.cacheItem.Priority {
			if job.ID == sched.cacheItem.Value {
				return true
			}
			job, _ = sched.driver.Get(sched.cacheItem.Value)
			sched.cacheItem = item
			if job.ID <= 0 || !job.IsReady() {
				return false
			}
		}
		pq, ok := sched.jobPQ[job.Func]
		if !ok {
			pq1 := make(queue.PriorityQueue, 0)
			pq = &pq1
			sched.jobPQ[job.Func] = pq
			heap.Init(pq)
		}
		heap.Push(pq, item)
		return true
	}
	return false
}
예제 #4
0
파일: submit.go 프로젝트: banyue/periodic
func SubmitJob(entryPoint string, job driver.Job) {
	parts := strings.SplitN(entryPoint, "://", 2)
	c, err := net.Dial(parts[0], parts[1])
	if err != nil {
		log.Fatal(err)
	}
	conn := protocol.NewClientConn(c)
	defer conn.Close()
	err = conn.Send(protocol.TYPE_CLIENT.Bytes())
	if err != nil {
		log.Fatal(err)
	}
	var msgId = []byte("100")
	buf := bytes.NewBuffer(nil)
	buf.Write(msgId)
	buf.Write(protocol.NULL_CHAR)
	buf.WriteByte(byte(protocol.SUBMIT_JOB))
	buf.Write(protocol.NULL_CHAR)
	buf.Write(job.Bytes())
	err = conn.Send(buf.Bytes())
	if err != nil {
		log.Fatal(err)
	}
	payload, err := conn.Receive()
	if err != nil {
		log.Fatal(err)
	}
	_, cmd, _ := protocol.ParseCommand(payload)
	fmt.Printf("%s\n", cmd.String())
}
예제 #5
0
func (sched *Sched) submitJob(item grabItem, job driver.Job) bool {
	defer sched.jobLocker.Unlock()
	sched.jobLocker.Lock()
	if job.Name == "" {
		sched.driver.Delete(job.ID)
		return true
	}
	if _, ok := sched.procQueue[job.ID]; ok {
		return true
	}

	if !item.w.alive {
		return false
	}
	if err := item.w.handleJobAssign(item.msgID, job); err != nil {
		item.w.alive = false
		return false
	}
	now := time.Now()
	current := int64(now.Unix())
	job.SetProc()
	job.RunAt = current
	sched.driver.Save(&job)
	sched.incrStatProc(job)
	sched.pushRevertPQ(job)
	sched.notifyRevertTimer()
	sched.procQueue[job.ID] = job
	sched.grabQueue.remove(item)
	return true
}
예제 #6
0
파일: http.go 프로젝트: monsterwof/periodic
func (c *httpClient) handleRemoveJob(req *http.Request) {
	var job driver.Job
	var e error
	var sched = c.sched
	defer sched.jobLocker.Unlock()
	sched.jobLocker.Lock()
	url := req.URL.String()
	funcName := url[1:]
	if funcName == "" {
		funcName = req.FormValue("func")
	}
	name := req.FormValue("name")
	job, e = sched.driver.GetOne(funcName, name)
	if e == nil && job.ID > 0 {
		if _, ok := sched.procQueue[job.ID]; ok {
			delete(sched.procQueue, job.ID)
		}
		sched.driver.Delete(job.ID)
		sched.decrStatJob(job)
		if job.IsProc() {
			sched.decrStatProc(job)
			sched.removeRevertPQ(job)
		}
		sched.notifyJobTimer()
	}

	if e != nil {
		c.sendErrResponse(e)
	} else {
		c.sendResponse("200 OK", []byte("{\"msg\": \""+protocol.SUCCESS.String()+"\"}"))
	}
}
예제 #7
0
func (sched *Sched) removeRevertPQ(job driver.Job) {
	defer sched.PQLocker.Unlock()
	sched.PQLocker.Lock()
	if job.IsProc() && job.Timeout > 0 {
		for _, item := range sched.revertPQ {
			if item.Value == job.ID {
				heap.Remove(&sched.revertPQ, item.Index)
				break
			}
		}
	}
}
예제 #8
0
파일: worker.go 프로젝트: 4honor/periodic
func (worker *Worker) HandleDo(msgId int64, job driver.Job) (err error) {
	defer worker.locker.Unlock()
	worker.locker.Lock()
	worker.jobQueue[job.Id] = job
	buf := bytes.NewBuffer(nil)
	buf.WriteString(strconv.FormatInt(msgId, 10))
	buf.Write(protocol.NULL_CHAR)
	buf.WriteString(strconv.FormatInt(job.Id, 10))
	buf.Write(protocol.NULL_CHAR)
	buf.Write(job.Bytes())
	err = worker.conn.Send(buf.Bytes())
	return
}
예제 #9
0
파일: worker.go 프로젝트: banyue/periodic
func (w *worker) handleJobAssign(msgId []byte, job driver.Job) (err error) {
	defer w.locker.Unlock()
	w.locker.Lock()
	w.jobQueue[job.Id] = job
	buf := bytes.NewBuffer(nil)
	buf.Write(msgId)
	buf.Write(protocol.NULL_CHAR)
	buf.Write(protocol.JOB_ASSIGN.Bytes())
	buf.Write(protocol.NULL_CHAR)
	buf.WriteString(strconv.FormatInt(job.Id, 10))
	buf.Write(protocol.NULL_CHAR)
	buf.Write(job.Bytes())
	err = w.conn.Send(buf.Bytes())
	return
}
예제 #10
0
func (sched *Sched) pushRevertPQ(job driver.Job) {
	defer sched.PQLocker.Unlock()
	sched.PQLocker.Lock()
	if job.IsProc() && job.Timeout > 0 {
		runAt := job.RunAt
		if runAt == 0 {
			runAt = job.SchedAt
		}
		item := &queue.Item{
			Value:    job.ID,
			Priority: runAt + job.Timeout,
		}
		heap.Push(&sched.revertPQ, item)
	}
}
예제 #11
0
파일: http.go 프로젝트: monsterwof/periodic
func (c *httpClient) handleSubmitJob(req *http.Request) {
	var job driver.Job
	var e error
	var sched = c.sched
	defer sched.jobLocker.Unlock()
	sched.jobLocker.Lock()
	url := req.URL.String()
	funcName := url[1:]
	if funcName == "" {
		funcName = req.FormValue("func")
	}
	job.Name = req.FormValue("name")
	job.Func = funcName
	job.Args = req.FormValue("args")
	job.Timeout, _ = strconv.ParseInt(req.FormValue("timeout"), 10, 64)
	job.SchedAt, _ = strconv.ParseInt(req.FormValue("sched_at"), 10, 64)

	if job.Name == "" || job.Func == "" {
		c.sendErrResponse(errors.New("job name or func is required"))
		return
	}

	isNew := true
	changed := false
	job.SetReady()
	oldJob, e := sched.driver.GetOne(job.Func, job.Name)
	if e == nil && oldJob.ID > 0 {
		job.ID = oldJob.ID
		if job.IsProc() {
			sched.decrStatProc(oldJob)
			sched.removeRevertPQ(job)
			changed = true
		}
		isNew = false
	}
	e = sched.driver.Save(&job)
	if e != nil {
		c.sendErrResponse(e)
		return
	}

	if isNew {
		sched.incrStatJob(job)
	}
	if isNew || changed {
		sched.pushJobPQ(job)
	}
	sched.notifyJobTimer()
	c.sendResponse("200 OK", []byte("{\"msg\": \""+protocol.SUCCESS.String()+"\"}"))
	return
}
예제 #12
0
파일: redis.go 프로젝트: 4honor/periodic
func (r RedisDriver) Save(job *driver.Job) (err error) {
	defer r.RWLocker.Unlock()
	r.RWLocker.Lock()
	var key string
	var prefix = REDIS_PREFIX + job.Func + ":"
	var conn = r.pool.Get()
	defer conn.Close()
	if job.Id > 0 {
		old, e := r.get(job.Id)
		key = REDIS_PREFIX + strconv.FormatInt(job.Id, 10)
		if e != nil || old.Id < 1 {
			err = errors.New(fmt.Sprintf("Update Job %d fail, the old job is not exists.", job.Id))
			return
		}
		r.cache.Remove(key)
		if old.Name != job.Name {
			if _, e := conn.Do("ZERM", prefix+"name", old.Name); e != nil {
				log.Printf("Error: ZREM %s %s failed\n", prefix+"name", old.Name)
			}
		}
	} else {
		job.Id, err = redis.Int64(conn.Do("INCRBY", REDIS_PREFIX+"sequence", 1))
		if err != nil {
			return
		}
	}
	idx, _ := redis.Int64(conn.Do("ZSCORE", prefix+"name", job.Name))
	if idx > 0 && idx != job.Id {
		err = errors.New("Duplicate Job name: " + job.Name)
		return
	}
	key = REDIS_PREFIX + strconv.FormatInt(job.Id, 10)
	_, err = conn.Do("SET", key, job.Bytes())
	if err == nil {
		if _, e := conn.Do("ZADD", prefix+"name", job.Id, job.Name); e != nil {
			log.Printf("Error: ZADD %s %d %s fail\n", prefix+"name", job.Id, job.Name)
		}
		if _, e := conn.Do("ZADD", REDIS_PREFIX+"ID", job.Id, strconv.FormatInt(job.Id, 10)); e != nil {
			log.Printf("Error: ZADD %s %d %d fail\n", REDIS_PREFIX+"ID", job.Id, job.Id)
		}
	}
	return
}
예제 #13
0
func (c *client) handleSubmitJob(msgID []byte, payload []byte) (err error) {
	var job driver.Job
	var e error
	var conn = c.conn
	var sched = c.sched
	defer sched.jobLocker.Unlock()
	sched.jobLocker.Lock()
	job, e = driver.NewJob(payload)
	if e != nil {
		err = conn.Send([]byte(e.Error()))
		return
	}
	isNew := true
	changed := false
	job.SetReady()
	oldJob, e := sched.driver.GetOne(job.Func, job.Name)
	if e == nil && oldJob.ID > 0 {
		job.ID = oldJob.ID
		if oldJob.IsProc() {
			sched.decrStatProc(oldJob)
			sched.removeRevertPQ(job)
			changed = true
		}
		isNew = false
	}
	e = sched.driver.Save(&job)
	if e != nil {
		err = conn.Send([]byte(e.Error()))
		return
	}

	if isNew {
		sched.incrStatJob(job)
	}
	if isNew || changed {
		sched.pushJobPQ(job)
	}
	sched.notifyJobTimer()
	err = c.handleCommand(msgID, protocol.SUCCESS)
	return
}
예제 #14
0
파일: client.go 프로젝트: 4honor/periodic
func (client *Client) HandleSubmitJob(msgId int64, payload []byte) (err error) {
	var job driver.Job
	var e error
	var conn = client.conn
	var sched = client.sched
	defer sched.JobLocker.Unlock()
	sched.JobLocker.Lock()
	job, e = driver.NewJob(payload)
	if e != nil {
		err = conn.Send([]byte(e.Error()))
		return
	}
	is_new := true
	changed := false
	job.Status = driver.JOB_STATUS_READY
	oldJob, e := sched.driver.GetOne(job.Func, job.Name)
	if e == nil && oldJob.Id > 0 {
		job.Id = oldJob.Id
		if oldJob.Status == driver.JOB_STATUS_PROC {
			sched.DecrStatProc(oldJob)
			sched.removeRevertPQ(job)
			changed = true
		}
		is_new = false
	}
	e = sched.driver.Save(&job)
	if e != nil {
		err = conn.Send([]byte(e.Error()))
		return
	}

	if is_new {
		sched.IncrStatJob(job)
	}
	if is_new || changed {
		sched.pushJobPQ(job)
	}
	sched.NotifyJobTimer()
	err = client.HandleCommand(msgId, protocol.SUCCESS)
	return
}
예제 #15
0
파일: leveldb.go 프로젝트: 4honor/periodic
func (l LevelDBDriver) Save(job *driver.Job) (err error) {
	defer l.RWLocker.Unlock()
	l.RWLocker.Lock()
	batch := new(leveldb.Batch)
	var isNew = true
	if job.Id > 0 {
		isNew = false
	} else {
		last_id, e := l.db.Get([]byte(PRE_SEQUENCE+"JOB"), nil)
		if e != nil || last_id == nil {
			job.Id = 1
		} else {
			id, _ := strconv.ParseInt(string(last_id), 10, 64)
			job.Id = id + 1
		}
	}
	var strId = strconv.FormatInt(job.Id, 10)
	if isNew {
		batch.Put([]byte(PRE_SEQUENCE+"JOB"), []byte(strId))
		batch.Put([]byte(PRE_JOB_FUNC+job.Func+":"+job.Name), []byte(strId))
	} else {
		old, e := l.get(job.Id)
		if e != nil || old.Id == 0 {
			err = errors.New(fmt.Sprintf("Update Job %d fail, the old job is not exists.", job.Id))
			return
		}
		l.cache.Remove(PRE_JOB + strId)
		if old.Name != job.Name {
			batch.Delete([]byte(PRE_JOB_FUNC + job.Func + ":" + old.Name))
			batch.Put([]byte(PRE_JOB_FUNC+job.Func+":"+job.Name), []byte(strId))
		}
	}
	batch.Put([]byte(PRE_JOB+strId), job.Bytes())
	err = l.db.Write(batch, nil)
	return
}
예제 #16
0
// Save job. when job is exists update it, other create one.
func (l Driver) Save(job *driver.Job, force ...bool) (err error) {
	defer l.RWLocker.Unlock()
	l.RWLocker.Lock()
	batch := new(leveldb.Batch)
	var isNew = true
	if job.ID > 0 {
		isNew = false
	} else {
		lastID, e := l.db.Get([]byte(PRESEQUENCE+"JOB"), nil)
		if e != nil || lastID == nil {
			job.ID = 1
		} else {
			id, _ := strconv.ParseInt(string(lastID), 10, 64)
			job.ID = id + 1
		}
	}
	var strID = strconv.FormatInt(job.ID, 10)
	if isNew {
		batch.Put([]byte(PRESEQUENCE+"JOB"), []byte(strID))
		batch.Put([]byte(PREFUNC+job.Func+":"+job.Name), []byte(strID))
	} else if len(force) == 0 || !force[0] {
		old, e := l.get(job.ID)
		if e != nil || old.ID == 0 {
			err = fmt.Errorf("Update Job %d fail, the old job is not exists.", job.ID)
			return
		}
		l.cache.Remove(PREJOB + strID)
		if old.Name != job.Name {
			batch.Delete([]byte(PREFUNC + job.Func + ":" + old.Name))
			batch.Put([]byte(PREFUNC+job.Func+":"+job.Name), []byte(strID))
		}
	}
	batch.Put([]byte(PREJOB+strID), job.Bytes())
	err = l.db.Write(batch, nil)
	return
}
예제 #17
0
func (sched *Sched) decrStatProc(job driver.Job) {
	stat := sched.getFuncStat(job.Func)
	if job.IsProc() {
		stat.Processing.Decr()
	}
}
예제 #18
0
파일: main.go 프로젝트: huaban/periodic
func main() {
	app := cli.NewApp()
	app.Name = "periodic"
	app.Usage = "Periodic task system"
	app.Version = periodic.Version
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:   "H",
			Value:  "unix:///tmp/periodic.sock",
			Usage:  "the server address eg: tcp://127.0.0.1:5000",
			EnvVar: "PERIODIC_PORT",
		},
		cli.StringFlag{
			Name:  "redis",
			Value: "tcp://127.0.0.1:6379",
			Usage: "The redis server address, required for driver redis",
		},
		cli.StringFlag{
			Name:  "driver",
			Value: "memstore",
			Usage: "The driver [memstore, leveldb, redis]",
		},
		cli.StringFlag{
			Name:  "dbpath",
			Value: "leveldb",
			Usage: "The db path, required for driver leveldb",
		},
		cli.BoolFlag{
			Name:  "d",
			Usage: "Enable daemon mode",
		},
		cli.IntFlag{
			Name:  "timeout",
			Value: 0,
			Usage: "The socket timeout",
		},
		cli.IntFlag{
			Name:   "cpus",
			Value:  runtime.NumCPU(),
			Usage:  "The runtime.GOMAXPROCS",
			EnvVar: "GOMAXPROCS",
		},
		cli.StringFlag{
			Name:  "cpuprofile",
			Value: "",
			Usage: "write cpu profile to file",
		},
	}
	app.Commands = []cli.Command{
		{
			Name:  "status",
			Usage: "Show status",
			Action: func(c *cli.Context) {
				subcmd.ShowStatus(c.GlobalString("H"))
			},
		},
		{
			Name:  "submit",
			Usage: "Submit job",
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "f",
					Value: "",
					Usage: "function name",
				},
				cli.StringFlag{
					Name:  "n",
					Value: "",
					Usage: "job name",
				},
				cli.StringFlag{
					Name:  "args",
					Value: "",
					Usage: "job workload",
				},
				cli.IntFlag{
					Name:  "t",
					Value: 0,
					Usage: "job running timeout",
				},
				cli.IntFlag{
					Name:  "sched_later",
					Value: 0,
					Usage: "job sched_later",
				},
			},
			Action: func(c *cli.Context) {
				var job = driver.Job{
					Name:    c.String("n"),
					Func:    c.String("f"),
					Args:    c.String("args"),
					Timeout: int64(c.Int("t")),
				}
				if len(job.Name) == 0 || len(job.Func) == 0 {
					cli.ShowCommandHelp(c, "submit")
					log.Fatal("Job name and func is require")
				}
				delay := c.Int("sched_later")
				var now = time.Now()
				job.SchedAt = int64(now.Unix()) + int64(delay)
				subcmd.SubmitJob(c.GlobalString("H"), job)
			},
		},
		{
			Name:  "remove",
			Usage: "Remove job",
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "f",
					Value: "",
					Usage: "function name",
				},
				cli.StringFlag{
					Name:  "n",
					Value: "",
					Usage: "job name",
				},
			},
			Action: func(c *cli.Context) {
				var job = driver.Job{
					Name: c.String("n"),
					Func: c.String("f"),
				}
				if len(job.Name) == 0 || len(job.Func) == 0 {
					cli.ShowCommandHelp(c, "remove")
					log.Fatal("Job name and func is require")
				}
				subcmd.RemoveJob(c.GlobalString("H"), job)
			},
		},
		{
			Name:  "drop",
			Usage: "Drop func",
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "f",
					Value: "",
					Usage: "function name",
				},
			},
			Action: func(c *cli.Context) {
				Func := c.String("f")
				if len(Func) == 0 {
					cli.ShowCommandHelp(c, "drop")
					log.Fatal("function name is required")
				}
				subcmd.DropFunc(c.GlobalString("H"), Func)
			},
		},
		{
			Name:  "run",
			Usage: "Run func",
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "f",
					Value: "",
					Usage: "function name required",
				},
				cli.StringFlag{
					Name:  "exec",
					Value: "",
					Usage: "command required",
				},
			},
			Action: func(c *cli.Context) {
				Func := c.String("f")
				exec := c.String("exec")
				if len(Func) == 0 {
					cli.ShowCommandHelp(c, "run")
					log.Fatal("function name is required")
				}
				if len(exec) == 0 {
					cli.ShowCommandHelp(c, "run")
					log.Fatal("command is required")
				}
				subcmd.Run(c.GlobalString("H"), Func, exec)
			},
		},
		{
			Name:  "dump",
			Usage: "Dump database to file.",
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "o",
					Value: "dump.db",
					Usage: "output file name required",
				},
			},
			Action: func(c *cli.Context) {
				subcmd.Dump(c.GlobalString("H"), c.String("o"))
			},
		},
		{
			Name:  "load",
			Usage: "Load file to database.",
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "i",
					Value: "dump.db",
					Usage: "input file name required",
				},
			},
			Action: func(c *cli.Context) {
				subcmd.Load(c.GlobalString("H"), c.String("i"))
			},
		},
	}
	app.Action = func(c *cli.Context) {
		if c.Bool("d") {
			if c.String("cpuprofile") != "" {
				f, err := os.Create(c.String("cpuprofile"))
				if err != nil {
					log.Fatal(err)
				}
				pprof.StartCPUProfile(f)
				defer pprof.StopCPUProfile()
			}
			var store driver.StoreDriver
			switch c.String("driver") {
			case "memstore":
				store = driver.NewMemStroeDriver()
				break
			case "redis":
				store = redis.NewDriver(c.String("redis"))
				break
			case "leveldb":
				store = leveldb.NewDriver(c.String("dbpath"))
				break
			default:
				store = driver.NewMemStroeDriver()
				break
			}

			runtime.GOMAXPROCS(c.Int("cpus"))
			timeout := time.Duration(c.Int("timeout"))
			periodicd := periodic.NewSched(c.String("H"), store, timeout)
			go periodicd.Serve()
			s := make(chan os.Signal, 1)
			signal.Notify(s, os.Interrupt, os.Kill)
			<-s
			periodicd.Close()
		} else {
			cli.ShowAppHelp(c)
		}
	}

	app.Run(os.Args)
}