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() }