2017-01-04 09:27:36 +00:00
|
|
|
|
package node
|
|
|
|
|
|
|
|
|
|
import (
|
2017-01-05 12:35:41 +00:00
|
|
|
|
"fmt"
|
2017-01-05 02:37:48 +00:00
|
|
|
|
"os"
|
2017-01-05 12:35:41 +00:00
|
|
|
|
"strconv"
|
2017-01-09 09:13:56 +00:00
|
|
|
|
"syscall"
|
2017-01-05 12:35:41 +00:00
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
"golang.org/x/net/context"
|
2017-01-05 02:37:48 +00:00
|
|
|
|
|
2017-01-04 09:27:36 +00:00
|
|
|
|
client "github.com/coreos/etcd/clientv3"
|
2017-01-05 02:37:48 +00:00
|
|
|
|
|
2017-01-06 08:44:42 +00:00
|
|
|
|
"sunteng/commons/log"
|
2017-01-05 02:37:48 +00:00
|
|
|
|
"sunteng/commons/util"
|
2017-01-05 12:35:41 +00:00
|
|
|
|
"sunteng/cronsun/conf"
|
|
|
|
|
)
|
|
|
|
|
|
2017-01-04 09:27:36 +00:00
|
|
|
|
// Node 执行 cron 命令服务的结构体
|
|
|
|
|
type Node struct {
|
|
|
|
|
*client.Client
|
2017-01-05 02:37:48 +00:00
|
|
|
|
|
2017-01-06 08:59:31 +00:00
|
|
|
|
ttl int64
|
|
|
|
|
reqTimeout time.Duration
|
|
|
|
|
prefix string
|
2017-01-06 08:44:42 +00:00
|
|
|
|
|
2017-01-05 12:35:41 +00:00
|
|
|
|
Key string
|
|
|
|
|
PID string
|
|
|
|
|
|
|
|
|
|
lID client.LeaseID // lease id
|
2017-01-06 08:44:42 +00:00
|
|
|
|
lch <-chan *client.LeaseKeepAliveResponse
|
|
|
|
|
|
|
|
|
|
done chan struct{}
|
2017-01-04 09:27:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-01-06 08:44:42 +00:00
|
|
|
|
func NewNode(cfg *conf.Conf) (n *Node, err error) {
|
2017-01-05 02:37:48 +00:00
|
|
|
|
ip, err := util.GetLocalIP()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-06 08:44:42 +00:00
|
|
|
|
cli, err := client.New(cfg.Etcd)
|
2017-01-05 02:37:48 +00:00
|
|
|
|
if err != nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
n = &Node{
|
|
|
|
|
Client: cli,
|
|
|
|
|
|
2017-01-06 08:59:31 +00:00
|
|
|
|
ttl: cfg.Ttl,
|
|
|
|
|
reqTimeout: time.Duration(cfg.ReqTimeout) * time.Second,
|
2017-01-09 09:13:56 +00:00
|
|
|
|
prefix: cfg.Proc,
|
2017-01-06 08:44:42 +00:00
|
|
|
|
|
2017-01-09 09:13:56 +00:00
|
|
|
|
Key: cfg.Proc + ip.String(),
|
2017-01-05 12:35:41 +00:00
|
|
|
|
PID: strconv.Itoa(os.Getpid()),
|
2017-01-06 08:44:42 +00:00
|
|
|
|
|
|
|
|
|
done: make(chan struct{}),
|
2017-01-05 02:37:48 +00:00
|
|
|
|
}
|
|
|
|
|
return
|
2017-01-04 09:27:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-01-06 08:44:42 +00:00
|
|
|
|
func (n *Node) String() string {
|
|
|
|
|
return "node[" + n.Key[len(n.prefix):] + "] pid[" + n.PID + "]"
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-04 09:27:36 +00:00
|
|
|
|
// 注册到 /cronsun/proc/xx
|
2017-01-05 12:35:41 +00:00
|
|
|
|
func (n *Node) Register() (err error) {
|
|
|
|
|
pid, err := n.Exist()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if pid != -1 {
|
2017-01-06 08:44:42 +00:00
|
|
|
|
return fmt.Errorf("node[%s] pid[%d] exist", n.Key[len(n.prefix):], pid)
|
2017-01-05 12:35:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-01-06 08:59:31 +00:00
|
|
|
|
resp, err := n.Client.Grant(context.TODO(), n.ttl)
|
2017-01-05 12:35:41 +00:00
|
|
|
|
if err != nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if _, err = n.Client.Put(context.TODO(), n.Key, n.PID, client.WithLease(resp.ID)); err != nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
2017-01-06 08:44:42 +00:00
|
|
|
|
|
|
|
|
|
ch, err := n.Client.KeepAlive(context.TODO(), resp.ID)
|
|
|
|
|
if err != nil {
|
2017-01-05 12:35:41 +00:00
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-06 08:44:42 +00:00
|
|
|
|
n.lID, n.lch = resp.ID, ch
|
2017-01-05 12:35:41 +00:00
|
|
|
|
return
|
2017-01-04 09:27:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-01-05 12:35:41 +00:00
|
|
|
|
// 判断 node 是否已注册到 etcd
|
|
|
|
|
// 存在则返回进行 pid,不存在返回 -1
|
|
|
|
|
func (n *Node) Exist() (pid int, err error) {
|
2017-01-06 08:59:31 +00:00
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), n.reqTimeout)
|
2017-01-05 12:35:41 +00:00
|
|
|
|
resp, err := n.Client.Get(ctx, n.Key, client.WithFromKey())
|
|
|
|
|
defer cancel()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(resp.Kvs) == 0 {
|
|
|
|
|
return -1, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if pid, err = strconv.Atoi(string(resp.Kvs[0].Value)); err != nil {
|
|
|
|
|
if _, err = n.Client.Delete(ctx, n.Key, client.WithFromKey()); err != nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
2017-01-06 08:44:42 +00:00
|
|
|
|
return -1, nil
|
2017-01-05 12:35:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
p, err := os.FindProcess(pid)
|
|
|
|
|
if err != nil {
|
2017-01-06 08:44:42 +00:00
|
|
|
|
return -1, nil
|
2017-01-05 12:35:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-01-06 08:44:42 +00:00
|
|
|
|
// TODO: 暂时不考虑 linux/unix 以外的系统
|
|
|
|
|
if p != nil && p.Signal(syscall.Signal(0)) == nil {
|
2017-01-05 12:35:41 +00:00
|
|
|
|
return
|
|
|
|
|
}
|
2017-01-04 09:27:36 +00:00
|
|
|
|
|
2017-01-05 12:35:41 +00:00
|
|
|
|
return -1, nil
|
2017-01-04 09:27:36 +00:00
|
|
|
|
}
|
2017-01-05 02:37:48 +00:00
|
|
|
|
|
|
|
|
|
// 启动服务
|
|
|
|
|
func (n *Node) Run() {
|
2017-01-06 08:44:42 +00:00
|
|
|
|
go n.keepAlive()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 断网掉线重新注册
|
|
|
|
|
func (n *Node) keepAlive() {
|
|
|
|
|
for {
|
|
|
|
|
for _ = range n.lch {
|
|
|
|
|
}
|
2017-01-05 02:37:48 +00:00
|
|
|
|
|
2017-01-06 08:44:42 +00:00
|
|
|
|
select {
|
|
|
|
|
case <-n.done:
|
|
|
|
|
return
|
|
|
|
|
default:
|
|
|
|
|
}
|
|
|
|
|
time.Sleep(time.Duration(n.ttl+1) * time.Second)
|
|
|
|
|
|
|
|
|
|
log.Noticef("%s has dropped, try to reconnect...", n.String())
|
|
|
|
|
if err := n.Register(); err != nil {
|
|
|
|
|
log.Warn(err.Error())
|
|
|
|
|
} else {
|
|
|
|
|
log.Noticef("%s reconnected", n.String())
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-01-05 02:37:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 启动服务
|
|
|
|
|
func (n *Node) Stop(i interface{}) {
|
2017-01-06 08:44:42 +00:00
|
|
|
|
close(n.done)
|
|
|
|
|
// 防止断网时卡住
|
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
|
|
|
|
|
n.Client.Delete(ctx, n.Key, client.WithFromKey())
|
|
|
|
|
cancel()
|
2017-01-05 02:37:48 +00:00
|
|
|
|
n.Client.Close()
|
|
|
|
|
}
|