mirror of https://github.com/shunfei/cronsun
models: 定义整体结构
parent
413254b9d2
commit
28bc0af0c4
|
@ -41,9 +41,9 @@ func Init() error {
|
||||||
type Conf struct {
|
type Conf struct {
|
||||||
Root string // 项目根目录
|
Root string // 项目根目录
|
||||||
|
|
||||||
Proc string // proc 路径
|
Proc string // proc 路径
|
||||||
Cmd string // cmd 路径
|
Cmd string // cmd 路径
|
||||||
NodeGroup string // 节点分组
|
Group string // 节点分组
|
||||||
|
|
||||||
Ttl int64 // 节点超时时间,单位秒
|
Ttl int64 // 节点超时时间,单位秒
|
||||||
ReqTimeout int // 请求超时时间,单位秒
|
ReqTimeout int // 请求超时时间,单位秒
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"Web": "@extend:web.json",
|
"Web": "@extend:web.json",
|
||||||
"Proc": "/cronsun/proc/",
|
"Proc": "/cronsun/proc/",
|
||||||
"Cmd": "/cronsun/cmd/",
|
"Cmd": "/cronsun/cmd/",
|
||||||
"NodeGroup": "/cronsun/nodeGroup/",
|
"Group": "/cronsun/group/",
|
||||||
"Ttl": 10,
|
"Ttl": 10,
|
||||||
"ReqTimeout": 2,
|
"ReqTimeout": 2,
|
||||||
"Log": "@extend:log.json",
|
"Log": "@extend:log.json",
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
client "github.com/coreos/etcd/clientv3"
|
||||||
|
|
||||||
|
"sunteng/cronsun/conf"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
DefalutClient *Client
|
||||||
|
|
||||||
|
initialized bool
|
||||||
|
)
|
||||||
|
|
||||||
|
func Init() (err error) {
|
||||||
|
if initialized {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = initID(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = conf.Init(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if DefalutClient, err = NewClient(conf.Config); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
initialized = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type Client struct {
|
||||||
|
*client.Client
|
||||||
|
|
||||||
|
reqTimeout time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClient(cfg *conf.Conf) (c *Client, err error) {
|
||||||
|
cli, err := client.New(cfg.Etcd)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c = &Client{
|
||||||
|
Client: cli,
|
||||||
|
|
||||||
|
reqTimeout: time.Duration(cfg.ReqTimeout) * time.Second,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) Put(key, val string, opts ...client.OpOption) (*client.PutResponse, error) {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), c.reqTimeout)
|
||||||
|
defer cancel()
|
||||||
|
return c.Client.Put(ctx, key, val, opts...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) Get(key string, opts ...client.OpOption) (*client.GetResponse, error) {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), c.reqTimeout)
|
||||||
|
defer cancel()
|
||||||
|
return c.Client.Get(ctx, key, opts...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) Del(key string, opts ...client.OpOption) (*client.DeleteResponse, error) {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), c.reqTimeout)
|
||||||
|
defer cancel()
|
||||||
|
return c.Client.Delete(ctx, key, opts...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) Watch(key string, opts ...client.OpOption) client.WatchChan {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), c.reqTimeout)
|
||||||
|
defer cancel()
|
||||||
|
return c.Client.Watch(ctx, key, opts...)
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
package models
|
||||||
|
|
||||||
|
// 结点类型分组
|
||||||
|
// 注册到 /cronsun/group/<id>
|
||||||
|
type Group struct {
|
||||||
|
ID string `json:"-"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
|
||||||
|
NodeIDs []string `json:"nids"`
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
|
||||||
|
"github.com/rogpeppe/fastuuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
var generator *fastuuid.Generator
|
||||||
|
|
||||||
|
func initID() (err error) {
|
||||||
|
generator, err = fastuuid.NewGenerator()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func NextID() string {
|
||||||
|
id := generator.Next()
|
||||||
|
return hex.EncodeToString(id[:4])
|
||||||
|
}
|
|
@ -1,16 +1,19 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
|
// 需要执行的 cron cmd 命令
|
||||||
|
// 注册到 /cronsun/cmd/<id>
|
||||||
type Job struct {
|
type Job struct {
|
||||||
Id string `json:"id"`
|
ID string `json:"-"`
|
||||||
Group string `json:"group"`
|
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Command string `json:"command"`
|
Group string `json:"group"`
|
||||||
|
Command string `json:"cmd"`
|
||||||
Rule *JobRule `json:"rule"`
|
Rule *JobRule `json:"rule"`
|
||||||
|
Status int `json:"status"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type JobRule struct {
|
type JobRule struct {
|
||||||
Timer []string `json:"timer"`
|
Timer []string `json:"timer"`
|
||||||
Nodes []string `json:"nodes"`
|
NodeIDs []string `json:"nids"`
|
||||||
Groups []string `json:"groups"`
|
GroupIDs []string `json:"gids"`
|
||||||
ExcludeNodes []string `json:"excludeNodes"`
|
ExcludeNodeIDs []string `json:"exclude_bids"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
|
// 执行 cron cmd 的进程
|
||||||
|
// 注册到 /cronsun/proc/<id>
|
||||||
type Node struct {
|
type Node struct {
|
||||||
Pid int `json:"pid"`
|
ID string `json:"-"` // ip
|
||||||
IP string `json:"ip"`
|
PID string `json:"pid"` // 进程 pid
|
||||||
Port int `json:"port"`
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
|
@ -13,7 +14,6 @@ import (
|
||||||
"sunteng/commons/log"
|
"sunteng/commons/log"
|
||||||
"sunteng/commons/util"
|
"sunteng/commons/util"
|
||||||
"sunteng/cronsun/conf"
|
"sunteng/cronsun/conf"
|
||||||
"syscall"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Node 执行 cron 命令服务的结构体
|
// Node 执行 cron 命令服务的结构体
|
||||||
|
@ -49,9 +49,9 @@ func NewNode(cfg *conf.Conf) (n *Node, err error) {
|
||||||
|
|
||||||
ttl: cfg.Ttl,
|
ttl: cfg.Ttl,
|
||||||
reqTimeout: time.Duration(cfg.ReqTimeout) * time.Second,
|
reqTimeout: time.Duration(cfg.ReqTimeout) * time.Second,
|
||||||
prefix: cfg.Proc + cfg.Sep,
|
prefix: cfg.Proc,
|
||||||
|
|
||||||
Key: cfg.Proc + cfg.Sep + ip.String(),
|
Key: cfg.Proc + ip.String(),
|
||||||
PID: strconv.Itoa(os.Getpid()),
|
PID: strconv.Itoa(os.Getpid()),
|
||||||
|
|
||||||
done: make(chan struct{}),
|
done: make(chan struct{}),
|
||||||
|
|
|
@ -1,47 +1,22 @@
|
||||||
package web
|
package web
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/sha1"
|
|
||||||
"encoding/hex"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"path"
|
"path"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/coreos/etcd/clientv3"
|
"github.com/coreos/etcd/clientv3"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"golang.org/x/net/context"
|
|
||||||
|
|
||||||
"sunteng/commons/log"
|
"sunteng/commons/log"
|
||||||
"sunteng/cronsun/conf"
|
"sunteng/cronsun/conf"
|
||||||
"sunteng/cronsun/models"
|
"sunteng/cronsun/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
var etcdClient *clientv3.Client
|
|
||||||
|
|
||||||
func EtcdInstance() (*clientv3.Client, error) {
|
|
||||||
if etcdClient != nil {
|
|
||||||
return etcdClient, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := conf.Init(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
etcdClient, err := clientv3.New(conf.Config.Etcd)
|
|
||||||
return etcdClient, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func InitRouters() (s *http.Server, err error) {
|
func InitRouters() (s *http.Server, err error) {
|
||||||
etcdClient, err = EtcdInstance()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
r := mux.NewRouter()
|
r := mux.NewRouter()
|
||||||
subrouter := r.PathPrefix("/v1").Subrouter()
|
subrouter := r.PathPrefix("/v1").Subrouter()
|
||||||
|
|
||||||
|
@ -75,7 +50,7 @@ func InitRouters() (s *http.Server, err error) {
|
||||||
var cmdKeyDeepLen = len(strings.Split(conf.Config.Cmd, "/"))
|
var cmdKeyDeepLen = len(strings.Split(conf.Config.Cmd, "/"))
|
||||||
|
|
||||||
func getJobGroups(w http.ResponseWriter, r *http.Request) {
|
func getJobGroups(w http.ResponseWriter, r *http.Request) {
|
||||||
resp, err := etcdClient.Get(context.TODO(), conf.Config.Cmd, clientv3.WithPrefix(), clientv3.WithKeysOnly())
|
resp, err := models.DefalutClient.Get(conf.Config.Cmd, clientv3.WithPrefix(), clientv3.WithKeysOnly())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
outJSONError(w, http.StatusInternalServerError, err.Error())
|
outJSONError(w, http.StatusInternalServerError, err.Error())
|
||||||
return
|
return
|
||||||
|
@ -98,7 +73,7 @@ func getJobGroups(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
func getJobsByGroupName(w http.ResponseWriter, r *http.Request) {
|
func getJobsByGroupName(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
resp, err := etcdClient.Get(context.TODO(), path.Join(conf.Config.Cmd, vars["name"]), clientv3.WithPrefix(), clientv3.WithSort(clientv3.SortByKey, clientv3.SortAscend))
|
resp, err := models.DefalutClient.Get(path.Join(conf.Config.Cmd, vars["name"]), clientv3.WithPrefix(), clientv3.WithSort(clientv3.SortByKey, clientv3.SortAscend))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
outJSONError(w, http.StatusInternalServerError, err.Error())
|
outJSONError(w, http.StatusInternalServerError, err.Error())
|
||||||
return
|
return
|
||||||
|
@ -129,11 +104,9 @@ func updateJob(w http.ResponseWriter, r *http.Request) {
|
||||||
r.Body.Close()
|
r.Body.Close()
|
||||||
|
|
||||||
var creation bool
|
var creation bool
|
||||||
if len(job.Id) == 0 {
|
if len(job.ID) == 0 {
|
||||||
creation = true
|
creation = true
|
||||||
now := time.Now()
|
job.ID = models.NextID()
|
||||||
h := sha1.Sum([]byte(strconv.FormatInt(now.Unix(), 10) + strconv.FormatInt(now.UnixNano(), 10)))
|
|
||||||
job.Id = hex.EncodeToString(h[:])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
jobb, err := json.Marshal(job)
|
jobb, err := json.Marshal(job)
|
||||||
|
@ -142,7 +115,7 @@ func updateJob(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = etcdClient.Put(context.TODO(), path.Join(conf.Config.Cmd, job.Group, job.Id), string(jobb))
|
_, err = models.DefalutClient.Put(path.Join(conf.Config.Cmd, job.Group, job.ID), string(jobb))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
outJSONError(w, http.StatusInternalServerError, err.Error())
|
outJSONError(w, http.StatusInternalServerError, err.Error())
|
||||||
return
|
return
|
||||||
|
@ -155,10 +128,10 @@ func updateJob(w http.ResponseWriter, r *http.Request) {
|
||||||
outJSONWithCode(w, statusCode, nil)
|
outJSONWithCode(w, statusCode, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
var ngKeyDeepLen = len(conf.Config.NodeGroup)
|
var ngKeyDeepLen = len(conf.Config.Group)
|
||||||
|
|
||||||
func getNodeGroups(w http.ResponseWriter, r *http.Request) {
|
func getNodeGroups(w http.ResponseWriter, r *http.Request) {
|
||||||
resp, err := etcdClient.Get(context.TODO(), conf.Config.NodeGroup, clientv3.WithPrefix(), clientv3.WithKeysOnly())
|
resp, err := models.DefalutClient.Get(conf.Config.Group, clientv3.WithPrefix(), clientv3.WithKeysOnly())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
outJSONError(w, http.StatusInternalServerError, err.Error())
|
outJSONError(w, http.StatusInternalServerError, err.Error())
|
||||||
return
|
return
|
||||||
|
@ -181,7 +154,7 @@ func getNodeGroups(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
func getNodeGroupByName(w http.ResponseWriter, r *http.Request) {
|
func getNodeGroupByName(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
resp, err := etcdClient.Get(context.TODO(), path.Join(conf.Config.NodeGroup, vars["name"]), clientv3.WithPrefix(), clientv3.WithSort(clientv3.SortByKey, clientv3.SortAscend))
|
resp, err := models.DefalutClient.Get(path.Join(conf.Config.Group, vars["name"]), clientv3.WithPrefix(), clientv3.WithSort(clientv3.SortByKey, clientv3.SortAscend))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
outJSONError(w, http.StatusInternalServerError, err.Error())
|
outJSONError(w, http.StatusInternalServerError, err.Error())
|
||||||
return
|
return
|
||||||
|
@ -214,7 +187,7 @@ func nodeJoinGroup(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
gresp, err := etcdClient.Get(context.TODO(), conf.Config.Proc, clientv3.WithPrefix(), clientv3.WithKeysOnly())
|
gresp, err := models.DefalutClient.Get(conf.Config.Proc, clientv3.WithPrefix(), clientv3.WithKeysOnly())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("get nodes list failed: %s", err.Error())
|
log.Errorf("get nodes list failed: %s", err.Error())
|
||||||
outJSONError(w, http.StatusInternalServerError, err.Error())
|
outJSONError(w, http.StatusInternalServerError, err.Error())
|
||||||
|
@ -251,7 +224,7 @@ NGLOOP:
|
||||||
break NGLOOP
|
break NGLOOP
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = etcdClient.Put(context.TODO(), path.Join(conf.Config.NodeGroup, g, n), "")
|
_, err = models.DefalutClient.Put(path.Join(conf.Config.Group, g, n), "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errMsg = "join failed: " + err.Error()
|
errMsg = "join failed: " + err.Error()
|
||||||
status = http.StatusInternalServerError
|
status = http.StatusInternalServerError
|
||||||
|
|
Loading…
Reference in New Issue