134 lines
3.5 KiB
Go
134 lines
3.5 KiB
Go
package etcd
|
||
|
||
import (
|
||
"context"
|
||
"reflect"
|
||
"time"
|
||
|
||
"go.etcd.io/etcd/client/v3"
|
||
|
||
"mongo.games.com/goserver/core"
|
||
"mongo.games.com/goserver/core/basic"
|
||
"mongo.games.com/goserver/core/logger"
|
||
|
||
"mongo.games.com/game/proto"
|
||
)
|
||
|
||
/*
|
||
ETCD Manager
|
||
*/
|
||
|
||
var (
|
||
defaultUrl = []string{"localhost:2379"}
|
||
defaultUser = ""
|
||
defaultPasswd = ""
|
||
defaultDialTimeout = time.Minute
|
||
)
|
||
|
||
var globalClient = new(Client)
|
||
|
||
// Register 注册etcd监听方法
|
||
// key:监听的key
|
||
// msgType:数据类型
|
||
// f:数据变更回调方法, completeKey:完整键, isInit:第一次主动拉取数据,event:事件类型, data:已经反序列化的数据,类型为msgType,是指针类型
|
||
func Register(key string, msgType interface{}, f func(ctx context.Context, completeKey string, isInit bool, event *clientv3.Event, data interface{})) {
|
||
createFunc := func() interface{} {
|
||
tp := reflect.TypeOf(msgType)
|
||
if tp.Kind() == reflect.Ptr {
|
||
tp = tp.Elem()
|
||
}
|
||
return reflect.New(tp).Interface()
|
||
}
|
||
|
||
initFunc := func() int64 {
|
||
logger.Logger.Info("ETCD 拉取数据:", key)
|
||
res, err := globalClient.GetValueWithPrefix(key)
|
||
if err == nil {
|
||
for i := int64(0); i < res.Count; i++ {
|
||
data := createFunc()
|
||
v, ok := data.(proto.Message)
|
||
if !ok {
|
||
logger.Logger.Errorf("ETCD %v error: not proto message", key)
|
||
continue
|
||
}
|
||
err := proto.Unmarshal(res.Kvs[i].Value, v)
|
||
if err != nil {
|
||
logger.Logger.Errorf("ETCD %v unmarshal error:%v", key, err)
|
||
continue
|
||
}
|
||
logger.Logger.Tracef("ETCD 拉取成功 %v ==> %v", string(res.Kvs[i].Key), v)
|
||
event := &clientv3.Event{
|
||
Type: 0,
|
||
}
|
||
f(context.TODO(), string(res.Kvs[i].Key), true, event, v)
|
||
}
|
||
if res.Header != nil {
|
||
return res.Header.Revision
|
||
}
|
||
} else {
|
||
logger.Logger.Errorf("ETCD get WithPrefix(%v) panic:%v", key, err)
|
||
}
|
||
return -1
|
||
}
|
||
|
||
// 监控数据变动
|
||
watchFunc := func(ctx context.Context, revision int64) {
|
||
globalClient.GoWatch(ctx, revision, key, func(res clientv3.WatchResponse) error {
|
||
obj := core.CoreObject()
|
||
if obj != nil {
|
||
obj.SendCommand(basic.CommandWrapper(func(*basic.Object) error {
|
||
for _, ev := range res.Events {
|
||
switch ev.Type {
|
||
case clientv3.EventTypePut:
|
||
data := createFunc()
|
||
v, ok := data.(proto.Message)
|
||
if !ok {
|
||
logger.Logger.Errorf("ETCD %v error: not proto message", string(ev.Kv.Key))
|
||
continue
|
||
}
|
||
err := proto.Unmarshal(ev.Kv.Value, v)
|
||
if err != nil {
|
||
logger.Logger.Errorf("etcd unmarshal(%v) error:%v", string(ev.Kv.Key), err)
|
||
continue
|
||
}
|
||
logger.Logger.Tracef("ETCD 更新事件 %v ==> %v", string(ev.Kv.Key), v)
|
||
f(ctx, string(ev.Kv.Key), false, ev, v)
|
||
case clientv3.EventTypeDelete:
|
||
logger.Logger.Tracef("ETCD 删除事件 %v", string(ev.Kv.Key))
|
||
f(ctx, string(ev.Kv.Key), false, ev, nil)
|
||
}
|
||
}
|
||
return nil
|
||
}), true)
|
||
}
|
||
return nil
|
||
})
|
||
}
|
||
|
||
globalClient.AddFunc(initFunc, watchFunc)
|
||
}
|
||
|
||
func Reset() {
|
||
globalClient.Close()
|
||
Start(defaultUrl, defaultUser, defaultPasswd, defaultDialTimeout)
|
||
}
|
||
|
||
func Close() {
|
||
globalClient.Close()
|
||
}
|
||
|
||
func Start(url []string, user, passwd string, dialTimeout time.Duration) {
|
||
defaultUrl = url
|
||
defaultUser = user
|
||
defaultPasswd = passwd
|
||
if dialTimeout > 0 {
|
||
defaultDialTimeout = dialTimeout
|
||
}
|
||
|
||
err := globalClient.Open(defaultUrl, defaultUser, defaultPasswd, defaultDialTimeout)
|
||
if err != nil {
|
||
logger.Logger.Errorf("etcd.Open err:%v", err)
|
||
}
|
||
globalClient.ReInitAndWatchAll()
|
||
}
|